diff -u --recursive --new-file v2.3.51/linux/CREDITS linux/CREDITS
--- v2.3.51/linux/CREDITS	Fri Mar 10 16:40:38 2000
+++ linux/CREDITS	Tue Mar 14 18:05:19 2000
@@ -1035,7 +1035,8 @@
 S: United Kingdom
 
 N: Ron Holt
-E: ron@sovereign.org
+E: ron@holt.org
+E: rholt@netcom.com
 W: http://www.holt.org/
 W: http://www.ronholt.com/
 D: Kernel development
@@ -2396,12 +2397,8 @@
 S: Germany
 
 N: Greg Ungerer
-E: gerg@stallion.com
+E: gerg@moreton.com.au
 D: Author of Stallion multiport serial drivers
-S: Stallion Technologies
-S: 33 Woodstock Rd
-S: Toowong, QLD. 4066
-S: Australia
 
 N: Jeffrey A. Uphoff
 E: juphoff@transmeta.com
diff -u --recursive --new-file v2.3.51/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.3.51/linux/Documentation/Configure.help	Fri Mar 10 16:40:38 2000
+++ linux/Documentation/Configure.help	Tue Mar 14 18:04:50 2000
@@ -2109,6 +2109,13 @@
   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, which you need to mount before programs can use shared memory. 
+  To do this automatically at system startup just add the following line 
+  to your /etc/fstab:
+
+  none	/var/shm	shm	defaults	0 0
+
   Saying Y here enlarges your kernel by about 18 KB. Just say Y.
 
 BSD Process Accounting
@@ -2546,6 +2553,16 @@
   Answer Y here will make the kernel provide only the 8x8 fonts (these
   are the less readable).
 
+Sparc console 8x16 font
+CONFIG_FONT_SUN8x16
+  This is the high resolution console font for Sun machines. Say Y.
+
+Sparc console 12x22 font (not supported by all drivers)
+CONFIG_FONT_SUN12x22
+  This is the high resolution console font for Sun machines with very big
+  letters (like the letters used in the SPARC PROM). If the standard font
+  is unreadable for you, say Y, otherwise say N.
+
 VGA 8x8 font
 CONFIG_FONT_8x8
   This is the "high resolution" font for the VGA frame buffer (the one
@@ -2877,6 +2894,12 @@
   FIFO. See Documentation/parport.txt to find out how to specify
   which IRQ/DMA to use.
 
+SuperIO chipset support (EXPERIMENTAL)
+CONFIG_PARPORT_PC_SUPERIO
+  Saying Y here enables some probes for Super-IO chipsets in order to
+  find out things like base addresses, IRQ lines and DMA channels.  It
+  is safe to say N.
+
 Support for PCMCIA management for PC-style ports
 CONFIG_PARPORT_PC_PCMCIA
   Say Y here if you need PCMCIA support for your PC-style parallel
@@ -4478,18 +4501,6 @@
   module, say M here and read Documentation/modules.txt and
   Documentation/scsi.txt .
 
-Extra SCSI Tapes
-CONFIG_ST_EXTRA_DEVS
-  This controls the amount of additional space allocated in tables for
-  drivers that are loaded as modules after the kernel is booted.  In the
-  event that the SCSI core itself was loaded as a module, this this value
-  is the number of additional tape devices that can be loaded after the
-  first host driver is loaded.
-
-  Admittedly this isn't pretty, but there are tons of race conditions
-  involved with resizing the internal arrays on the fly.  Someday this
-  flag will go away, and everything will work automatically.
-
 SCSI CDROM support
 CONFIG_BLK_DEV_SR
   If you want to use a SCSI CDROM under Linux, say Y and read the
@@ -5650,21 +5661,55 @@
   the system using Fibre Optic and the "X3.269-199X Fibre Channel
   Protocol for SCSI" specification. You'll also need the generic SCSI
   support, as well as the drivers for the storage array itself and
-  for the interface adapter such as SOC. This subsystem could even
-  serve for IP networking, with some code extensions.
-
-  If unsure, say N.
+  for the interface adapter such as SOC or SOC+. This subsystem could even
+  serve for IP networking, with some code extensions. If unsure, say N.
 
 Sun SOC
 CONFIG_FC4_SOC
   Serial Optical Channel is an interface card with one or two Fibre
-  Optic ports, each of which can be connected to a disk array. Only
-  the SBus incarnation of the adapter is supported at the moment.
+  Optic ports, each of which can be connected to a disk array. Note that
+  if you have older firmware in the card, you'll need the microcode from
+  the Solaris driver to make it work.
+
+  This support is also available as a module called soc.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
+
+Sun SOC+ (aka SOCAL)
+CONFIG_FC4_SOCAL
+  Serial Optical Channel Plus is an interface card with up to two Fibre
+  Optic ports. This card supports FC Arbitrated Loop (usually A5000 or
+  internal FC disks in E[3-6]000 machines through the Interface Board).
+  You'll probably need the microcode from the Solaris driver to make it
+  work.
+
+  This support is also available as a module called socal.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
 
 SparcSTORAGE Array 100 and 200 series
 CONFIG_SCSI_PLUTO
   If you never bought a disk array made by Sun, go with N. 
 
+  This support is also available as a module called pluto.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
+
+Sun Enterprise Network Array (A5000 and EX500)
+CONFIG_SCSI_FCAL
+  This driver drives FC-AL disks connected through a Fibre Channel card
+  using the drivers/fc4 layer (currently only SOCAL).
+  The most common is either A5000 array or internal disks in E[3-6]000
+  machines.
+
+  This support is also available as a module called fcal.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt. If unsure, say N.
+
 AcornSCSI support
 CONFIG_SCSI_ACORNSCSI_3
   This enables support for the Acorn SCSI card (aka30). If you have an
@@ -6185,16 +6230,6 @@
   The module will be called 3c575_cb.o. If you want to do that, say M
   here and read Documentation/modules.txt.  If unsure, say N.
 
-DEC Tulip CardBus support
-CONFIG_PCMCIA_TULIP
-  This driver supports CardBus Fast Ethernet adapters based on DEC
-  Tulip and compatible chipsets.
-
-  This driver can only 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 tulip_cb.o. If you want to do that, say M
-  here and read Documentation/modules.txt.  If unsure, say N.
-
 SMC EPIC CardBus support
 CONFIG_PCMCIA_EPIC100
   This driver supports CardBus Fast Ethernet adapters based on the SMC
@@ -6809,6 +6844,77 @@
   you say N, the PPP support will not be included in the driver (saves
   about 16 KB of kernel memory).
 
+MultiGate/COMX support
+CONFIG_COMX
+  Say Y if you want to use any board from the MultiGate (COMX) family. 
+  These boards are synchronous serial adapters for the PC, manufactured 
+  by ITConsult-Pro Co, Hungary. 
+
+  Read linux/Documentation/networking/comx.txt  for help on configuring 
+  and using COMX interfaces. Further info on these cards can be found at 
+  http://www.itc.hu or <info@itc.hu>.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx.o.
+
+COMX/CMX/HiCOMX board support
+CONFIG_COMX_HW_COMX
+  Hardware driver for the 'CMX', 'COMX' and 'HiCOMX' boards from the
+  MultiGate family. Say Y if you have one of these. 
+
+  You will need additional firmware to use these cards, which are
+  downloadable from ftp://ftp.itc.hu/.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-comx.o.
+
+LoCOMX board support
+CONFIG_COMX_HW_LOCOMX
+  Hardware driver for the 'LoCOMX' board from the MultiGate family. Say Y
+  if you have a board like this. 
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-locomx.o.
+
+MixCOM board support
+CONFIG_COMX_HW_MIXCOM
+  Hardware driver for the 'MixCOM' board from the MultiGate family. Say Y
+  if you have a board like this.
+
+  If you want to use the watchdog device on this card, you should
+  select it in the Watchdog Cards section of the Character Devices
+  configuration. The ISDN interface of this card is Teles 16.3 compatible,
+  you should enable it in the ISDN configuration menu. The driver for the 
+  flash ROM of this card is available separately on ftp://ftp.itc.hu/.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-mixcom.o.
+
+MultiGate Cisco-HDLC and synchronous PPP protocol support
+CONFIG_COMX_PROTO_PPP
+  Cisco-HDLC and synchronous PPP protocol driver for all MultiGate boards. 
+  Say Y if you want to use either protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called 
+  comx-proto-ppp.o.
+
+MultiGate LAPB protocol support
+CONFIG_COMX_PROTO_LAPB
+  LAPB protocol driver for all MultiGate boards. Say Y if you 
+  want to use this protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-proto-lapb.o.
+
+MultiGate Frame Relay protocol support
+CONFIG_COMX_PROTO_FR
+  Frame Relay protocol driver for all MultiGate boards. Say Y if you 
+  want to use this protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-proto-fr.o.
+
 Cyclom 2X(tm) multiprotocol cards (EXPERIMENTAL)
 CONFIG_CYCLADES_SYNC
   Cyclom 2X from Cyclades Corporation (http://www.cyclades.com and
@@ -7162,6 +7268,10 @@
   say M here and read Documentation/modules.txt. This is recommended.
   The module will be called sk98lin.o.
 
+MyriCOM Gigabit Ethernet support
+CONFIG_MYRI_SBUS
+  This driver supports MyriCOM Sbus gigabit ethernet cards.
+
 AMD LANCE and PCnet (AT1500 and NE2100) support
 CONFIG_LANCE
   If you have a network (Ethernet) card of this type, say Y and read
@@ -7943,6 +8053,19 @@
   Linux Token Ring Project site for the latest information at
   http://www.linuxtr.net
 
+IBM Lanstreamer chipset PCI adapter support
+CONFIG_IBMLS
+  This is support for IBM Lanstreamer PCI Token Ring Cards.
+
+  If you have such an adapter, say Y and read the Token-Ring mini-HOWTO
+  available via FTP (user:anonymous) from
+  ftp://metalab.unc/edu/pub/Linux/docs/HOWTO.
+
+  This driver is also available as a modules ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The modules will be called lanstreamer.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
 Generic TMS380 Token Ring ISA/PCI/MCA/EISA adapter support
 CONFIG_TMS380TR
   This driver provides generic support for token ring adapters
@@ -7989,6 +8112,49 @@
   read the Token-Ring mini-HOWTO, available from
   http://www.linuxdoc.org/docs.html#howto .
 
+Sun Happy Meal 10/100baseT support
+CONFIG_HAPPYMEAL
+  This driver supports the "hme" interface present on most Ultra systems
+  and as an option on older Sbus systems. This driver supports both PCI
+  and Sbus devices. This driver also supports the "qfe" quad 100baseT
+  device available in both PCI and Sbus configurations.
+
+  This support is also available as a module called sunhme.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
+
+Sun Lance support
+CONFIG_SUNLANCE
+  This driver supports the "le" interface present on all 32-bit Sparc
+  systems, on some older Ultra systems and as an Sbus option.
+
+  This support is also available as a module called sunlance.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
+
+Sun BigMAC 10/100baseT support (EXPERIMENTAL)
+CONFIG_SUNBMAC
+  This driver supports the "be" interface available as an Sbus option.
+  This is Sun's older 100baseT ethernet device.
+
+  This support is also available as a module called sunbmac.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
+
+Sun QuadEthernet support
+CONFIG_SUNQE
+  This driver supports the "qe" 10baseT ethernet device, available as
+  an Sbus option. Note that this is not the same as Quad FastEthernet
+  "qfe" which is supported by the Happy Meal driver instead.
+
+  This support is also available as a module called sunqe.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
+
 Traffic Shaper (EXPERIMENTAL)
 CONFIG_SHAPER
   The traffic shaper is a virtual network device that allows you to
@@ -8778,7 +8944,25 @@
   inserted in and removed from the running kernel whenever you want).
   The module will be called rio500.o. If you want to compile it as
   a module, say M here and read Documentation/modules.txt.
-  
+
+D-Link DSB-R100 FM radio upport
+CONFIG_USB_DSBR
+  Say Y here if you want to connect this type of radio to your
+  computer's USB port. Note that the audio is not digital, and
+  you must connect the line out connector to a sound card or a
+  set of speakers.
+
+  This driver uses the Video For Linux API.  You must enable
+  (Y or M in config) Video For Linux (under Character Devices)
+  to use this driver.  Information on this API and pointers to
+  "v4l" programs may be found on the WWW at
+  http://roadrunner.swansea.uk.linux.org/v4l.shtml .
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called dsbr100.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
 ACPI support
 CONFIG_ACPI
   Advanced Configuration and Power Interface (ACPI) is an interface
@@ -12465,7 +12649,24 @@
 SPARC ESP SCSI support
 CONFIG_SCSI_SUNESP
   This is the driver for the Sun ESP SCSI host adapter. The ESP
-  chipset is present in most SPARC-based computers.
+  chipset is present in most SPARC SBUS-based computers.
+
+  This support is also available as a module called esp.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
+
+PTI Qlogic, ISP Driver
+CONFIG_SCSI_QLOGICPTI
+  This driver supports SBUS SCSI controllers from PTI or QLogic. These
+  controllers are known under Solaris as qpti and in the openprom as
+  PTI,ptisp or QLGC,isp. Note that PCI QLogic SCSI controllers are driven
+  by a different driver.
+
+  This support is also available as a module called qlogicpti.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
 
 SPARC /dev/openprom compatibility driver (EXPERIMENTAL)
 CONFIG_SUN_OPENPROMIO
@@ -12477,24 +12678,101 @@
   inserted in and removed from the running kernel whenever you want),
   say M and read Documentation/modules.txt. If unsure, say Y.
 
+Openprom tree appears in /proc/openprom
+CONFIG_SUN_OPENPROMFS
+  If you say Y, the OpenPROM device tree will be available as a virtual
+  file system, which you can mount to /proc/openprom by
+  "mount -t openpromfs none /proc/openprom".
+
+  If you want to compile the /proc/openprom support as a module ( = code
+  which can be inserted in and removed from the running kernel whenever
+  you want), say M here and read Documentation/modules.txt. The module
+  will be called openpromfs.o. If unsure, say M.
+
+Kernel support for Linux/Sparc 32bit binary compatibility
+CONFIG_SPARC32_COMPAT
+  This allows you to run 32-bit binaries on your Ultra.
+  Everybody wants this; say Y.
+
+Kernel support for 32-bit ELF binaries
+CONFIG_BINFMT_ELF32
+  This allows you to run 32-bit Linux/ELF binaries on your Ultra.
+  Everybody wants this; say Y.
+
+Kernel support for 32-bit (ie. SunOS) a.out binaries
+CONFIG_BINFMT_AOUT32
+  This allows you to run 32-bit a.out format binaries on your Ultra.
+  If you want to run SunOS binaries (see SunOS binary emulation below)
+  or other a.out binaries, say Y. If unsure, say N.
+
+SunOS binary emulation
+CONFIG_SUNOS_EMUL
+  This allows you to run most SunOS binaries. If you want to do this,
+  say Y here and place appropriate files in /usr/gnemul/sunos. See
+  http://www.ultralinux.org/faq.html for more information. If you want
+  to run SunOS binaries on an Ultra you must also say Y to "Kernel
+  support for 32-bit a.out binaries" above.
+
 Mostek real time clock support
 CONFIG_SUN_MOSTEK_RTC
-  The Mostek RTC chip is used on all knows Sun computers except
+  The Mostek RTC chip is used on all known Sun computers except
   some JavaStations. For a JavaStation you need to say Y both here
   and to "Enhanced Real Time Clock Support".
 
   Say Y here unless you are building a special purpose kernel.
 
+OBP Flash Device support
+CONFIG_OBP_FLASH
+  The OpenBoot PROM on Ultra systems is flashable. If you want to be
+  able to upgrade the OBP firmware, say Y here.
+
 JavaStation OS Flash SIMM (EXPERIMENTAL)
 CONFIG_SUN_JSFLASH
   This option enables a driver for JavaStation OS Flash driver.
   Say N unless you want to boot from your Flash SIMM.
 
-#Siemens SAB82532 serial support
-#CONFIG_SAB82532
-###
-### Please someone fill these in.
-###
+Siemens SAB82532 serial support
+CONFIG_SAB82532
+  This driver supports the serial ports on newer (PCI) Ultra systems.
+  Say Y if you want to be able to use your serial ports.
+
+Aurora Multiboard 1600se (EXPERIMENTAL)
+CONFIG_SUN_AURORA
+  The Aurora Multiboard is a multi-port high-speed serial controller.
+  If you have one of these, say Y.
+
+Audio support (EXPERIMENTAL)
+CONFIG_SPARCAUDIO
+  This driver provides support for the build-in sound devices on most
+  Sun machines. If you want to be able to use this, select this option
+  and one or more of the lowlevel drivers below. See 
+  http://www.dementia.org/~shadow/sparcaudio.html for more information.
+
+AMD7930 Lowlevel Driver
+CONFIG_SPARCAUDIO_AMD7930
+  This driver supports the AMD 7930 chip found on sun4c, 4/6xx, and
+  SparcClassic systems.
+
+CS4231 Lowlevel Driver
+CONFIG_SPARCAUDIO_CS4231
+  This driver supports the Crystal Semiconductor CS4231 chip found on
+  the SS4, SS5, and Ultras.
+
+DBRI Lowlevel Driver
+CONFIG_SPARCAUDIO_DBRI
+  This driver supports the DBRI audio interface found on the SS10, SS20,
+  Sparcbook 3, and Voyager systems.
+
+Dummy lowlevel Driver
+CONFIG_SPARCAUDIO_DUMMY
+  This is a pseudo-driver used for debugging and testing the sparcaudio
+  subsystem. Say N unless you want to work on this subsystem.
+
+Sparc hardware (EXPERIMENTAL)
+CONFIG_PARPORT_SUNBPP
+  This driver provides support for the bidirectional parallel port found
+  on many Sun machines. Note that many of the newer Ultras actually have
+  pc style hardware instead.
 
 IEEE 1394 (aka FireWire) support
 CONFIG_IEEE1394
@@ -13628,6 +13906,20 @@
 Include support for the NetWinder
 CONFIG_ARCH_NETWINDER
   Say Y here if you intend to run this kernel on the NetWinder.
+
+Include support for the Compaq Personal Server
+CONFIG_PERSONAL_SERVER
+  Say Y here if you intend to run this kernel on the Compaq
+  Personal Server.
+  
+  The Compaq Personal Server is not available for purchase. 
+  There are no product plans beyond the current research
+  prototypes at this time.  Information is available at:
+  
+  http://crl.research.compaq.com/projects/personalserver
+  
+  If you have any questions or comments about the  Compaq Personal
+  Server, send e-mail to skiff@crl.dec.com
 
 Virtual/Physical Memory Split
 CONFIG_1GB
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/Makefile linux/Documentation/DocBook/Makefile
--- v2.3.51/linux/Documentation/DocBook/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/Makefile	Tue Mar 14 18:05:12 2000
@@ -0,0 +1,29 @@
+BOOKS	:= wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml
+	
+books:	docproc $(BOOKS)
+
+docproc:
+	$(MAKE) -C $(TOPDIR)/scripts docproc
+
+wanbook.sgml: wanbook.tmpl
+	$(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/syncppp.c \
+		<wanbook.tmpl >wanbook.sgml
+
+z8530book.sgml: z8530book.tmpl
+	$(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/syncppp.c \
+		<z8530book.tmpl >z8530book.sgml
+
+mcabook.sgml: mcabook.tmpl
+	$(TOPDIR)/scripts/docgen $(TOPDIR)/arch/i386/kernel/mca.c \
+		<mcabook.tmpl >mcabook.sgml
+
+videobook.sgml: videobook.tmpl
+	$(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/char/videodev.c \
+		<videobook.tmpl >videobook.sgml
+
+clean:
+	rm -f core *~
+	rm -r $(BOOKS)
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/mcabook.tmpl linux/Documentation/DocBook/mcabook.tmpl
--- v2.3.51/linux/Documentation/DocBook/mcabook.tmpl	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/mcabook.tmpl	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,105 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="MCAGuide">
+ <bookinfo>
+  <title>MCA Driver Programming Interface</title>
+  
+  <authorgroup>
+   <author>
+    <firstname>Alan</firstname>
+    <surname>Cox</surname>
+    <affiliation>
+     <address>
+      <email>alan@redhat.com</email>
+     </address>
+    </affiliation>
+   </author>
+   <author>
+    <firstname>David</firstname>
+    <surname>Weinehall</surname>
+   </author>
+   <author>
+    <firstname>Chris</firstname>
+    <surname>Beauregard</surname>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>2000</year>
+   <holder>Alan Cox</holder>
+   <holder>David Weinehall</holder>
+   <holder>Chris Beauregard</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation 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.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+  <chapter id="intro">
+      <title>Introduction</title>
+  <para>
+	The MCA bus functions provide a generalised interface to find MCA
+	bus cards, to claim them for a driver, and to read and manipulate POS 
+	registers without being aware of the motherboard internals or 
+	certain deep magic specific to onboard devices.
+  </para>
+  <para>
+	The basic interface to the MCA bus devices is the slot. Each slot
+	is numbered and virtual slot numbers are assigned to the internal
+	devices. Using a pci_dev as other busses do does not really make
+	sense in the MCA context as the MCA bus resources require card
+	specific interpretation.
+  </para>
+  <para>
+	Finally the MCA bus functions provide a parallel set of DMA
+	functions mimicing the ISA bus DMA functions as closely as possible,
+	although also supporting the additional DMA functionality on the
+	MCA bus controllers.
+  </para>
+  </chapter>
+  <chapter id="bugs">
+     <title>Known Bugs And Assumptions</title>
+  <para>
+	None.	
+  </para>
+  </chapter>
+
+  <chapter id="pubfunctions">
+     <title>Public Functions Provided</title>
+!Earch/i386/kernel/mca.c
+  </chapter>
+
+  <chapter id="dmafunctions">
+     <title>DMA Functions Provided</title>
+!Iinclude/asm-i386/mca_dma.h
+  </chapter>
+
+</book>
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/parport-multi.fig linux/Documentation/DocBook/parport-multi.fig
--- v2.3.51/linux/Documentation/DocBook/parport-multi.fig	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/parport-multi.fig	Tue Mar 14 17:54:42 2000
@@ -0,0 +1,59 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+6 1425 4350 5175 5475
+6 3450 5100 4425 5475
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+	 4425 5475 4425 5100 3450 5100 3450 5475 4425 5475
+4 0 0 50 0 0 12 0.0000 4 135 510 3600 5400 Printer\001
+-6
+6 3375 4350 5175 4725
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+	 5175 4725 5175 4350 3375 4350 3375 4725 5175 4725
+4 0 0 50 0 0 12 0.0000 4 180 870 3825 4650 Multiplexor\001
+-6
+6 1425 4650 2775 5475
+6 1425 4650 2775 5475
+2 4 0 1 0 7 50 0 -1 0.000 0 0 6 0 0 5
+	 2757 5475 2757 4650 1425 4650 1425 5475 2757 5475
+4 0 0 50 0 0 12 0.0000 4 180 735 1725 5100 Computer\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+	 2775 4875 2700 4875 2700 5025 2775 5025 2775 4875
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+	 2775 5175 2700 5175 2700 5325 2775 5325 2775 5175
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+	 2775 4950 3600 4725
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+	 2775 5250 3450 5325
+-6
+6 3150 2625 4125 3525
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+	 4125 3075 4125 2625 3150 2625 3150 3075 4125 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+	 3675 3075 3675 3525
+4 0 0 50 0 0 12 0.0000 4 135 510 3300 2925 Printer\001
+-6
+6 4275 3450 5250 4350
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+	 5250 3900 5250 3450 4275 3450 4275 3900 5250 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+	 4800 3900 4800 4350
+4 0 0 50 0 0 12 0.0000 4 135 510 4425 3750 Printer\001
+-6
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+	 3900 4050 3900 3525 3375 3525 3375 4050 3900 4050
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+	 3675 4050 3675 4350
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+	 3600 4350 3750 4350 3750 4425 3600 4425 3600 4350
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+	 4725 4350 4875 4350 4875 4425 4725 4425 4725 4350
+4 0 0 50 0 0 12 0.0000 4 135 285 3450 3900 ZIP\001
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/parport-share.fig linux/Documentation/DocBook/parport-share.fig
--- v2.3.51/linux/Documentation/DocBook/parport-share.fig	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/parport-share.fig	Tue Mar 14 17:54:42 2000
@@ -0,0 +1,154 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+0 32 #8e8e8e
+0 33 #8e8e8e
+0 34 #aeaaae
+0 35 #515551
+0 36 #414141
+0 37 #868286
+0 38 #8e8e8e
+0 39 #414141
+0 40 #868286
+0 41 #c7c3c7
+0 42 #e7e3e7
+0 43 #414141
+0 44 #868286
+0 45 #c7c3c7
+0 46 #e7e3e7
+0 47 #868286
+0 48 #c7c3c7
+0 49 #e7e3e7
+6 1200 3000 2250 4950
+6 1275 3150 2175 3675
+6 1312 3487 1837 3637
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 5
+	 1312 3562 1312 3524 1474 3524 1474 3487 1675 3487
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+	 1474 3637 1474 3562 1675 3562
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 2
+	 1675 3524 1837 3524
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 2
+	 1675 3487 1675 3524
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 2
+	 1312 3562 1474 3562
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 5
+	 1474 3637 1675 3637 1675 3562 1837 3562 1837 3524
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+	 1716 3637 1797 3637 1797 3600
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 3
+	 1716 3637 1716 3600 1797 3600
+-6
+6 1413 3345 2070 3397
+6 1994 3352 2070 3390
+2 1 0 1 7 40 19 0 -1 0.000 2 0 -1 0 0 3
+	 1994 3390 1994 3352 2070 3352
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+	 1994 3390 2070 3390 2070 3352
+-6
+6 1531 3353 1643 3389
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+	 1568 3353 1606 3353 1606 3389 1568 3389 1568 3353
+2 2 0 0 40 39 19 0 20 0.000 2 0 -1 0 0 5
+	 1606 3353 1643 3353 1643 3389 1606 3389 1606 3353
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+	 1568 3353 1531 3353 1531 3389 1568 3389 1568 3353
+-6
+6 1413 3345 1465 3397
+1 3 0 0 0 39 18 0 20 0.000 1 0.0000 1439 3371 26 26 1439 3371 1439 3397
+1 3 0 0 40 41 18 0 20 0.000 1 0.0000 1439 3371 15 15 1439 3371 1443 3385
+-6
+2 2 0 0 40 7 19 0 20 0.000 2 0 -1 0 0 3
+	 1950 3371 1875 3371 1950 3371
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+	 1945 3384 1896 3384 1896 3357 1945 3357 1945 3384
+-6
+6 1350 3183 2100 3300
+2 1 0 1 7 40 19 0 -1 0.000 2 0 -1 0 0 3
+	 1350 3300 1350 3183 2100 3183
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+	 1350 3300 2100 3300 2100 3183
+-6
+2 1 0 1 7 7 19 0 -1 0.000 2 0 -1 0 0 5
+	 1275 3675 1875 3675 1875 3450 2175 3450 2175 3150
+2 1 0 1 40 7 19 0 -1 0.000 2 0 -1 0 0 3
+	 1275 3675 1275 3150 2175 3150
+-6
+6 1950 3750 2175 3975
+5 1 0 1 7 7 19 0 -1 0.000 0 0 0 0 2038.000 3900.000 1985 3953 1985 3847 2091 3847
+5 1 0 1 40 7 19 0 -1 0.000 0 1 0 0 2038.000 3900.000 1985 3953 2091 3953 2091 3847
+-6
+6 1200 4050 1800 4800
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4125 1725 4125
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4200 1725 4200
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4275 1725 4275
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4350 1725 4350
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4425 1725 4425
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4500 1725 4500
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4575 1725 4575
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4650 1725 4650
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+	 1275 4725 1725 4725
+-6
+2 2 0 1 0 39 20 0 20 0.000 2 0 -1 0 0 5
+	 1200 4950 1425 4950 1425 4911 1200 4911 1200 4950
+2 2 0 1 0 39 20 0 20 0.000 2 0 -1 0 0 5
+	 2025 4950 2250 4950 2250 4911 2025 4911 2025 4950
+2 2 0 1 0 42 20 0 20 0.000 2 0 -1 0 0 5
+	 1200 4907 2250 4907 2250 3000 1200 3000 1200 4907
+-6
+6 2374 3225 3375 4050
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 3
+	 2374 3402 3139 3402 3257 4050
+	 0.000 -1.000 0.000
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 3
+	 2374 3461 3096 3437 3198 4050
+	 0.000 -1.000 0.000
+-6
+2 2 0 1 0 1 50 0 20 0.000 0 0 -1 0 0 5
+	 2925 4575 4050 4575 4050 4875 2925 4875 2925 4575
+2 3 0 1 0 32 50 0 20 0.000 0 0 -1 0 0 5
+	 1200 3000 1575 2475 2400 2475 2250 3000 1200 3000
+2 3 0 1 0 8 50 0 20 0.000 0 0 -1 0 0 5
+	 2925 4575 3000 4200 4050 4200 4050 4575 2925 4575
+2 2 0 1 0 0 50 0 20 0.000 0 0 -1 0 0 5
+	 3075 4725 3900 4725 3900 4800 3075 4800 3075 4725
+2 2 0 1 0 46 50 0 20 0.000 0 0 -1 0 0 5
+	 4800 3975 6450 3975 6450 4875 4800 4875 4800 3975
+2 2 0 1 0 36 50 0 20 0.000 0 0 -1 0 0 5
+	 5025 4575 6225 4575 6225 4725 5025 4725 5025 4575
+2 2 0 1 0 36 50 0 20 0.000 0 0 -1 0 0 5
+	 5025 3975 6225 3975 6225 3300 5025 3300 5025 3975
+2 3 0 1 0 37 50 0 20 0.000 0 0 -1 0 0 5
+	 4800 3975 4800 3825 5025 3825 5025 3975 4800 3975
+2 3 0 1 0 37 50 0 20 0.000 0 0 -1 0 0 5
+	 6225 3825 6375 3825 6450 3975 6225 3975 6225 3825
+2 3 0 1 0 32 50 0 20 0.000 0 0 -1 0 0 5
+	 2400 2475 2250 3000 2250 4875 2400 4350 2400 2475
+2 3 0 1 0 37 50 0 -1 0.000 0 0 -1 0 0 6
+	 3075 4200 3075 4050 3300 4050 3375 4050 3375 4200 3075 4200
+2 3 0 1 0 37 50 0 -1 0.000 0 0 -1 0 0 6
+	 3900 4200 3900 4050 3675 4050 3600 4050 3600 4200 3900 4200
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 5
+	 3705 4050 3825 3675 4185 3390 4590 3615 4800 4035
+	 0.000 -1.000 -1.000 -1.000 0.000
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 5
+	 3765 4050 3874 3708 4202 3449 4571 3654 4800 4185
+	 0.000 -1.000 -1.000 -1.000 0.000
+4 0 0 50 0 0 12 0.0000 4 180 735 1350 5400 Computer\001
+4 0 0 50 0 0 12 0.0000 4 180 675 3150 5400 Zip drive\001
+4 0 0 50 0 0 12 0.0000 4 135 510 5325 5400 Printer\001
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/parport-structure.fig linux/Documentation/DocBook/parport-structure.fig
--- v2.3.51/linux/Documentation/DocBook/parport-structure.fig	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/parport-structure.fig	Tue Mar 14 17:54:42 2000
@@ -0,0 +1,60 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+0 32 #414541
+0 33 #8e8e8e
+0 34 #414541
+0 35 #8e8e8e
+0 36 #414541
+0 37 #8e8e8e
+0 38 #414541
+0 39 #8e8e8e
+0 40 #414541
+0 41 #8e8e8e
+0 42 #414541
+0 43 #8e8e8e
+0 44 #414141
+0 45 #868286
+0 46 #c7c3c7
+0 47 #8e8e8e
+0 48 #414141
+0 49 #868286
+0 50 #c7c3c7
+0 51 #e7e3e7
+6 2025 1800 3075 2250
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+	 3045 2250 3045 1800 2025 1800 2025 2250 3045 2250
+4 0 0 50 0 14 12 0.0000 4 180 210 2400 2100 lp\001
+-6
+6 4125 1800 5175 2250
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+	 5145 2250 5145 1800 4125 1800 4125 2250 5145 2250
+4 0 0 50 0 14 12 0.0000 4 135 315 4425 2100 ppa\001
+-6
+6 3225 3075 4275 3525
+6 3375 3225 4125 3450
+4 0 0 50 0 14 12 0.0000 4 165 735 3375 3375 parport\001
+-6
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+	 4245 3525 4245 3075 3225 3075 3225 3525 4245 3525
+-6
+6 3000 4350 4500 4800
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+	 4500 4800 4500 4350 3000 4350 3000 4800 4500 4800
+4 0 0 50 0 14 12 0.0000 4 165 1050 3225 4650 parport_pc\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+	1 1 1.00 60.00 120.00
+	 2550 2250 3600 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+	1 1 1.00 60.00 120.00
+	 4650 2250 3825 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+	1 1 1.00 60.00 120.00
+	 3750 3525 3750 4350
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/parportbook.sgml linux/Documentation/DocBook/parportbook.sgml
--- v2.3.51/linux/Documentation/DocBook/parportbook.sgml	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/parportbook.sgml	Tue Mar 14 17:54:42 2000
@@ -0,0 +1,1747 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="ParportGuide">
+ <bookinfo>
+  <title>The Parallel Port Subsystem</title>
+
+  <authorgroup>
+   <author>
+    <firstname>Tim</firstname>
+    <surname>Waugh</surname>
+    <affiliation>
+     <address>
+      <email>twaugh@redhat.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>1999-2000</year>
+   <holder>Tim Waugh</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation 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.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+<chapter id="design">
+<title>Design goals</title>
+
+<sect1>
+<title>The problems</title>
+
+<!-- Short-comings -->
+<!-- How they are addressed -->
+
+<!-- Short-comings
+     - simplistic lp driver
+     - platform differences
+     - no support for Zip drive pass-through
+     - no support for readback? When did Carsten add it?
+     - more parallel port devices. Figures?
+     - IEEE 1284 transfer modes: no advanced modes
+  -->
+
+<para>The first parallel port support for Linux came with the line
+printer driver, <filename>lp</filename>.  The printer driver is a
+character special device, and (in Linux 2.0) had support for writing,
+via <function>write</function>, and configuration and statistics
+reporting via <function>ioctl</function>.</para>
+
+<para>The printer driver could be used on any computer that had an IBM
+PC-compatible parallel port.  Because some architectures have parallel
+ports that aren't really the same as PC-style ports, other variants of
+the printer driver were written in order to support Amiga and Atari
+parallel ports.</para>
+
+<para>When the Iomega Zip drive was released, and a driver written for
+it, a problem became apparent.  The Zip drive is a parallel port
+device that provides a parallel port of its own---it is designed to
+sit between a computer and an attached printer, with the printer
+plugged into the Zip drive, and the Zip drive plugged into the
+computer.</para>
+
+<para>The problem was that, although printers and Zip drives were both
+supported, for any given port only one could be used at a time.  Only
+one of the two drivers could be present in the kernel at once.  This
+was because of the fact that both drivers wanted to drive the same
+hardware---the parallel port.  When the printer driver initialised, it
+would call the <function>check_region</function> function to make sure
+that the IO region associated with the parallel port was free, and
+then it would call <function>request_region</function> to allocate it.
+The Zip drive used the same mechanism.  Whichever driver initialised
+first would gain exclusive control of the parallel port.</para>
+
+<para>The only way around this problem at the time was to make sure
+that both drivers were available as loadable kernel modules.  To use
+the printer, load the printer driver module; then for the Zip drive,
+unload the printer driver module and load the Zip driver
+module.</para>
+
+<para>The net effect was that printing a document that was stored on a Zip
+drive was a bit of an ordeal, at least if the Zip drive and printer
+shared a parallel port.  A better solution was needed.</para>
+
+<para>Zip drives are not the only devices that presented problems for
+Linux.  There are other devices with pass-through ports, for example
+parallel port CD-ROM drives.  There are also printers that report
+their status textually rather than using simple error pins: sending a
+command to the printer can cause it to report the number of pages that
+it has ever printed, or how much free memory it has, or whether it is
+running out of toner, and so on.  The printer driver didn't originally
+offer any facility for reading back this information (although Carsten
+Gross added nibble mode readback support for kernel 2.2).</para>
+
+<!-- IEEE 1284 transfer modes: no advanced modes --> 
+
+<para>The IEEE has issued a standards document called IEEE 1284, which
+documents existing practice for parallel port communications in a
+variety of modes.  Those modes are: <quote>compatibility</quote>,
+reverse nibble, reverse byte, ECP and EPP.  Newer devices often use
+the more advanced modes of transfer (ECP and EPP).  In Linux 2.0, the
+printer driver only supported <quote>compatibility mode</quote>
+(i.e. normal printer protocol) and reverse nibble mode.</para>
+
+</sect1>
+
+<sect1>
+<title>The solutions</title>
+
+<!-- How they are addressed
+     - sharing model
+     - overview of structure (i.e. port drivers) in 2.2 and 2.3.
+     - IEEE 1284 stuff
+     - whether or not 'platform independence' goal was met
+  -->
+
+<para>The <filename>parport</filename> code in Linux 2.2 was designed
+to meet these problems of architectural differences in parallel ports,
+of port-sharing between devices with pass-through ports, and of lack
+of support for IEEE 1284 transfer modes.</para>
+
+<!-- platform differences -->
+
+<para>There are two layers to the
+<filename>parport</filename> subsystem, only one of which deals
+directly with the hardware.  The other layer deals with sharing and
+IEEE 1284 transfer modes.  In this way, parallel support for a
+particular architecture comes in the form of a module which registers
+itself with the generic sharing layer.</para>
+
+<!-- sharing model -->
+
+<para>The sharing model provided by the <filename>parport</filename>
+subsystem is one of exclusive access.  A device driver, such as the
+printer driver, must ask the <filename>parport</filename> layer for
+access to the port, and can only use the port once access has been
+granted.  When it has finished a <quote>transaction</quote>, it can
+tell the <filename>parport</filename> layer that it may release the
+port for other device drivers to use.</para>
+
+<!-- talk a bit about how drivers can share devices on the same port -->
+
+<para>Devices with pass-through ports all manage to share a parallel
+port with other devices in generally the same way.  The device has a
+latch for each of the pins on its pass-through port.  The normal state
+of affairs is pass-through mode, with the device copying the signal
+lines between its host port and its pass-through port.  When the
+device sees a special signal from the host port, it latches the
+pass-through port so that devices further downstream don't get
+confused by the pass-through device's conversation with the host
+parallel port: the device connected to the pass-through port (and any
+devices connected in turn to it) are effectively cut off from the
+computer.  When the pass-through device has completed its transaction
+with the computer, it enables the pass-through port again.</para>
+
+<mediaobject>
+<imageobject>
+<imagedata Align=center scalefit=1 fileref="parport-share.eps">
+</imageobject>
+</mediaobject>
+
+<para>This technique relies on certain <quote>special signals</quote>
+being invisible to devices that aren't watching for them.  This tends
+to mean only changing the data signals and leaving the control signals
+alone.  IEEE 1284.3 documents a standard protocol for daisy-chaining
+devices together with parallel ports.</para>
+
+<!-- transfer modes -->
+
+<para>Support for standard transfer modes are provided as operations
+that can be performed on a port, along with operations for setting the
+data lines, or the control lines, or reading the status lines.  These
+operations appear to the device driver as function pointers; more
+later.</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="transfermodes">
+<title>Standard transfer modes</title>
+
+<!-- Defined by IEEE, but in common use (even though there are widely -->
+<!-- varying implementations). -->
+
+<para>The <quote>standard</quote> transfer modes in use over the
+parallel port are <quote>defined</quote> by a document called IEEE
+1284.  It really just codifies existing practice and documents
+protocols (and variations on protocols) that have been in common use
+for quite some time.</para>
+
+<para>The original definitions of which pin did what were set out by
+Centronics Data Computer Corporation, but only the printer-side
+interface signals were specified.</para>
+
+<para>By the early 1980s, IBM's host-side implementation had become
+the most widely used.  New printers emerged that claimed Centronics
+compatibility, but although compatible with Centronics they differed
+from one another in a number of ways.</para>
+
+<para>As a result of this, when IEEE 1284 was published in 1994, all
+that it could really do was document the various protocols that are
+used for printers (there are about six variations on a theme).</para>
+
+<para>In addition to the protocol used to talk to
+Centronics-compatible printers, IEEE 1284 defined other protocols that
+are used for unidirectional peripheral-to-host transfers (reverse
+nibble and reverse byte) and for fast bidirectional transfers (ECP and
+EPP).</para>
+
+</chapter>
+
+<chapter id="structure">
+<title>Structure</title>
+
+<!-- Main structure
+     - sharing core
+     - parports and their IEEE 1284 overrides
+       - IEEE 1284 transfer modes for generic ports
+       - maybe mention muxes here
+     - pardevices
+     - IEEE 1284.3 API
+  -->
+
+<!-- Diagram -->
+
+<mediaobject>
+<imageobject>
+<imagedata Align=Center ScaleFit=1 fileref="parport-structure.eps">
+</imageobject>
+</mediaobject>
+
+<sect1>
+<title>Sharing core</title>
+
+<!-- sharing core -->
+
+<para>At the core of the <filename>parport</filename> subsystem is the
+sharing mechanism (see <filename>drivers/parport/share.c</filename>).
+This module, <filename>parport</filename>, is responsible for
+keeping track of which ports there are in the system, which device
+drivers might be interested in new ports, and whether or not each port
+is available for use (or if not, which driver is currently using
+it).</para>
+
+</sect1>
+
+<sect1>
+<title>Parports and their overrides</title>
+<!-- parports and their overrides -->
+
+<para>The generic <filename>parport</filename> sharing code doesn't
+directly handle the parallel port hardware.  That is done instead by
+<quote>low-level</quote> <filename>parport</filename> drivers.  The
+function of a low-level <filename>parport</filename> driver is to
+detect parallel ports, register them with the sharing code, and
+provide a list of access functions for each port.</para>
+
+<para>The most basic access functions that must be provided are ones
+for examining the status lines, for setting the control lines, and for
+setting the data lines.  There are also access functions for setting
+the direction of the data lines; normally they are in the
+<quote>forward</quote> direction (that is, the computer drives them),
+but some ports allow switching to <quote>reverse</quote> mode (driven
+by the peripheral).  There is an access function for examining the
+data lines once in reverse mode.</para>
+
+</sect1>
+
+<sect1>
+<title>IEEE 1284 transfer modes</title>
+<!-- IEEE 1284 transfer modes -->
+
+<para>Stacked on top of the sharing mechanism, but still in the
+<filename>parport</filename> module, are functions for transferring
+data.  They are provided for the device drivers to use, and are very
+much like library routines.  Since these transfer functions are
+provided by the generic <filename>parport</filename> core they must
+use the <quote>lowest common denominator</quote> set of access
+functions: they can set the control lines, examine the status lines,
+and use the data lines.  With some parallel ports the data lines can
+only be set and not examined, and with other ports accessing the data
+register causes control line activity; with these types of situations,
+the IEEE 1284 transfer functions make a best effort attempt to do the
+right thing.  In some cases, it is not physically possible to use
+particular IEEE 1284 transfer modes.</para>
+
+<para>The low-level <filename>parport</filename> drivers also provide
+IEEE 1284 transfer functions, as names in the access function list.
+The low-level driver can just name the generic IEEE 1284 transfer
+functions for this.  Some parallel ports can do IEEE 1284 transfers in
+hardware; for those ports, the low-level driver can provide functions
+to utilise that feature.</para>
+
+</sect1>
+
+<!-- muxes? -->
+
+<!-- pardevices and pardrivers -->
+
+<sect1>
+<title>Pardevices and parport_drivers</title>
+
+<para>When a parallel port device driver (such as
+<filename>lp</filename>) initialises it tells the sharing layer about
+itself using <function>parport_register_driver</function>.  The
+information is put into a <structname>struct
+parport_driver</structname>, which is put into a linked list.  The
+information in a <structname>struct parport_driver</structname> really
+just amounts to some function pointers to callbacks in the parallel
+port device driver.</para>
+
+<para>During its initialisation, a low-level port driver tells the
+sharing layer about all the ports that it has found (using
+<function>parport_register_port</function>), and the sharing layer
+creates a <structname>struct parport</structname> for each of them.
+Each <structname>struct parport</structname> contains (among other
+things) a pointer to a <structname>struct
+parport_operations</structname>, which is a list of function pointers
+for the various operations that can be performed on a port.  You can
+think of a <structname>struct parport</structname> as a parallel port
+<quote>object</quote>, if <quote>object-orientated</quote> programming
+is your thing.  The <structname>parport</structname> structures are
+chained in a linked list, whose head is <varname>portlist</varname>
+(in <filename>drivers/parport/share.c</filename>).</para>
+
+<para>Once the port has been registered, the low-level port driver
+announces it.  The <function>parport_announce_port</function> function
+walks down the list of parallel port device drivers
+(<structname>struct parport_driver</structname>s) calling the
+<function>attach</function> function of each.</para>
+
+<para>Similarly, a low-level port driver can undo the effect of
+registering a port with the
+<function>parport_unregister_port</function> function, and device
+drivers are notified using the <function>detach</function>
+callback.</para>
+
+<para>Device drivers can undo the effect of registering themselves
+with the <function>parport_unregister_driver</function>
+function.</para>
+
+</sect1>
+
+<!-- IEEE 1284.3 API -->
+
+<sect1>
+<title>The IEEE 1284.3 API</title>
+
+<para>The ability to daisy-chain devices is very useful, but if every
+device does it in a different way it could lead to lots of
+complications for device driver writers.  Fortunately, the IEEE are
+standardising it in IEEE 1284.3, which covers daisy-chain devices and
+port multiplexors.</para>
+
+<para>At the time of writing, IEEE 1284.3 has not been published, but
+the draft specifies the on-the-wire protocol for daisy-chaining and
+multiplexing, and also suggests a programming interface for using it.
+That interface (or most of it) has been implemented in the
+<filename>parport</filename> code in Linux.</para>
+
+<para>At initialisation of the parallel port <quote>bus</quote>, daisy-chained
+devices are assigned addresses starting from zero.  There can only be
+four devices with daisy-chain addresses, plus one device on the end
+that doesn't know about daisy-chaining and thinks it's connected
+directly to a computer.</para>
+
+<para>Another way of connecting more parallel port devices is to use a
+multiplexor.  The idea is to have a device that is connected directly
+to a parallel port on a computer, but has a number of parallel ports
+on the other side for other peripherals to connect to (two or four
+ports are allowed).  The multiplexor switches control to different
+ports under software control---it is, in effect, a programmable
+printer switch.</para>
+
+<para>Combining the ability of daisy-chaining five devices together
+with the ability to multiplex one parallel port between four gives the
+potential to have twenty peripherals connected to the same parallel
+port!</para>
+
+<para>In addition, of course, a single computer can have multiple
+parallel ports.  So, each parallel port peripheral in the system can
+be identified with three numbers, or co-ordinates: the parallel port,
+the multiplexed port, and the daisy-chain address.</para>
+
+<mediaobject>
+<imageobject>
+<imagedata align=center scalefit=1 fileref="parport-multi.eps">
+</imageobject>
+</mediaobject>
+
+<!-- x parport_open -->
+<!-- x parport_close -->
+<!-- x parport_device_id -->
+<!-- x parport_device_num -->
+<!-- x parport_device_coords -->
+<!-- x parport_find_device -->
+<!-- x parport_find_class -->
+
+<para>Each device in the system is numbered at initialisation (by
+<function>parport_daisy_init</function>).  You can convert between
+this device number and its co-ordinates with
+<function>parport_device_num</function> and
+<function>parport_device_coords</function>.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_device_num</function></funcdef>
+  <paramdef>int <parameter>parport</parameter></paramdef>
+  <paramdef>int <parameter>mux</parameter></paramdef>
+  <paramdef>int <parameter>daisy</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_device_coords</function></funcdef>
+  <paramdef>int <parameter>devnum</parameter></paramdef>
+  <paramdef>int *<parameter>parport</parameter></paramdef>
+  <paramdef>int *<parameter>mux</parameter></paramdef>
+  <paramdef>int *<parameter>daisy</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>Any parallel port peripheral will be connected directly or
+indirectly to a parallel port on the system, but it won't have a
+daisy-chain address if it does not know about daisy-chaining, and it
+won't be connected through a multiplexor port if there is no
+multiplexor.  The special co-ordinate value <constant>-1</constant> is
+used to indicate these cases.</para>
+
+<para>Two functions are provided for finding devices based on their
+IEEE 1284 Device ID: <function>parport_find_device</function> and
+<function>parport_find_class</function>.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_find_device</function></funcdef>
+  <paramdef>const char *<parameter>mfg</parameter></paramdef>
+  <paramdef>const char *<parameter>mdl</parameter></paramdef>
+  <paramdef>int <parameter>from</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_find_class</function></funcdef>
+  <paramdef>parport_device_class <parameter>cls</parameter></paramdef>
+  <paramdef>int <parameter>from</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>These functions take a device number (in addition to some other
+things), and return another device number.  They walk through the list
+of detected devices until they find one that matches the requirements,
+and then return that device number (or <constant>-1</constant> if
+there are no more such devices).  They start their search at the
+device after the one in the list with the number given (at
+<parameter>from</parameter>+1, in other words).</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="drivers">
+<title>Device driver's view</title>
+
+<!-- Cover:
+     - sharing interface, preemption, interrupts, wakeups...
+     - IEEE 1284.3 interface
+     - port operations
+       - why can read data but ctr is faked, etc.
+  -->
+
+<!-- I should take a look at the kernel hackers' guide bit I wrote, -->
+<!-- as that deals with a lot of this.  The main complaint with it  -->
+<!-- was that there weren't enough examples, but 'The printer -->
+<!-- driver' should deal with that later; might be worth mentioning -->
+<!-- in the text. -->
+
+<para>This section is written from the point of view of the device
+driver programmer, who might be writing a driver for a printer or a
+scanner or else anything that plugs into the parallel port.  It
+explains how to use the <filename>parport</filename> interface to find
+parallel ports, use them, and share them with other device
+drivers.</para>
+
+<para>We'll start out with a description of the various functions that
+can be called, and then look at a reasonably simple example of their
+use: the printer driver.</para>
+
+<para>The interactions between the device driver and the
+<filename>parport</filename> layer are as follows.  First, the device
+driver registers its existence with <filename>parport</filename>, in
+order to get told about any parallel ports that have been (or will be)
+detected.  When it gets told about a parallel port, it then tells
+<filename>parport</filename> that it wants to drive a device on that
+port.  Thereafter it can claim exclusive access to the port in order
+to talk to its device.</para>
+
+<para>So, the first thing for the device driver to do is tell
+<filename>parport</filename> that it wants to know what parallel ports
+are on the system.  To do this, it uses the
+<function>parport_register_device</function> function:</para>
+
+<programlisting>
+<![CDATA[
+struct parport_driver {
+        const char *name;
+        void (*attach) (struct parport *);
+        void (*detach) (struct parport *);
+        struct parport_driver *next;
+};
+]]></programlisting>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_register_driver</function></funcdef>
+  <paramdef>struct parport_driver *<parameter>driver</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>In other words, the device driver passes pointers to a couple of
+functions to <filename>parport</filename>, and
+<filename>parport</filename> calls <function>attach</function> for
+each port that's detected (and <function>detach</function> for each
+port that disappears -- yes, this can happen).</para>
+
+<para>The next thing that happens is that the device driver tells
+<filename>parport</filename> that it thinks there's a device on the
+port that it can drive.  This typically will happen in the driver's
+<function>attach</function> function, and is done with
+<function>parport_register_device</function>:</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>struct pardevice *<function>parport_register_device</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>const char *<parameter>name</parameter></paramdef>
+  <paramdef>int <parameter>(*pf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>void <parameter>(*kf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>void <parameter>(*irq_func)</parameter>
+    <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+  <paramdef>int <parameter>flags</parameter></paramdef>
+  <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The <parameter>port</parameter> comes from the parameter supplied
+to the <function>attach</function> function when it is called, or
+alternatively can be found from the list of detected parallel ports
+directly with the (now deprecated)
+<function>parport_enumerate</function> function.</para>
+
+<para>The next three parameters, <parameter>pf</parameter>,
+<parameter>kf</parameter>, and <parameter>irq_func</parameter>, are
+more function pointers.  These callback functions get called under
+various circumstances, and are always given the
+<parameter>handle</parameter> as one of their parameters.</para>
+
+<para>The preemption callback, <parameter>pf</parameter>, is called
+when the driver has claimed access to the port but another device
+driver wants access.  If the driver is willing to let the port go, it
+should return zero and the port will be released on its behalf.  There
+is no need to call <function>parport_release</function>.  If
+<parameter>pf</parameter> gets called at a bad time for letting the
+port go, it should return non-zero and no action will be taken.  It is
+good manners for the driver to try to release the port at the earliest
+opportunity after its preemption callback is called.</para>
+
+<para>The <quote>kick</quote> callback, <parameter>kf</parameter>, is
+called when the port can be claimed for exclusive access; that is,
+<function>parport_claim</function> is guaranteed to succeed inside the
+<quote>kick</quote> callback.  If the driver wants to claim the port
+it should do so; otherwise, it need not take any action.</para>
+
+<para>The <parameter>irq_func</parameter> callback is called,
+predictably, when a parallel port interrupt is generated.  But it is
+not the only code that hooks on the interrupt.  The sequence is this:
+the lowlevel driver is the one that has done
+<function>request_irq</function>; it then does whatever
+hardware-specific things it needs to do to the parallel port hardware
+(for PC-style ports, there is nothing special to do); it then tells
+the IEEE 1284 code about the interrupt, which may involve reacting to
+an IEEE 1284 event, depending on the current IEEE 1284 phase; and
+finally the <parameter>irq_func</parameter> function is called.</para>
+
+<para>None of the callback functions are allowed to block.</para>
+
+<para>The <parameter>flags</parameter> are for telling
+<filename>parport</filename> any requirements or hints that are
+useful.  The only useful value here (other than
+<constant>0</constant>, which is the usual value) is
+<constant>PARPORT_DEV_EXCL</constant>.  The point of that flag is to
+request exclusive access at all times---once a driver has successfully
+called <function>parport_register_device</function> with that flag, no
+other device drivers will be able to register devices on that port
+(until the successful driver deregisters its device, of
+course).</para>
+
+<para>The <constant>PARPORT_DEV_EXCL</constant> flag is for preventing
+port sharing, and so should only be used when sharing the port with
+other device drivers is impossible and would lead to incorrect
+behaviour.  Use it sparingly!</para>
+
+<para>Devices can also be registered by device drivers based on their
+device numbers (the same device numbers as in the previous
+section).</para>
+
+<para>The <function>parport_open</function> function is similar to
+<function>parport_register_device</function>, and
+<function>parport_close</function> is the equivalent of
+<function>parport_unregister_device</function>.  The difference is
+that <function>parport_open</function> takes a device number rather
+than a pointer to a <structname>struct parport</structname>.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>struct pardevice *<function>parport_open</function></funcdef>
+  <paramdef>int <parameter>devnum</parameter></paramdef>
+  <paramdef>int <parameter>(*pf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>int <parameter>(*kf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>int <parameter>(*irqf)</parameter>
+    <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+  <paramdef>int <parameter>flags</parameter></paramdef>
+  <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>void <function>parport_close</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>struct pardevice *<function>parport_register_device</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>const char *<parameter>name</parameter></paramdef>
+  <paramdef>int <parameter>(*pf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>int <parameter>(*kf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>int <parameter>(*irqf)</parameter>
+    <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+  <paramdef>int <parameter>flags</parameter></paramdef>
+  <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>void <function>parport_unregister_device</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The intended use of these functions is during driver
+initialisation while the driver looks for devices that it supports, as
+demonstrated by the following code fragment:</para>
+
+<programlisting>
+<![CDATA[
+int devnum = -1;
+while ((devnum = parport_find_class (PARPORT_CLASS_DIGCAM,
+                                     devnum)) != -1) {
+    struct pardevice *dev = parport_open (devnum, ...);
+    ...
+}
+]]></programlisting>
+
+<para>Once your device driver has registered its device and been
+handed a pointer to a <structname>struct pardevice</structname>, the
+next thing you are likely to want to do is communicate with the device
+you think is there.  To do that you'll need to claim access to the
+port.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_claim</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_claim_or_block</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>void <function>parport_release</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>To claim access to the port, use
+<function>parport_claim</function> or
+<function>parport_claim_or_block</function>.  The first of these will
+not block, and so can be used from interrupt context.  If
+<function>parport_claim</function> succeeds it will return zero and
+the port is available to use.  It may fail (returning non-zero) if the
+port is in use by another driver and that driver is not willing to
+relinquish control of the port.</para>
+
+<para>The other function, <function>parport_claim_or_block</function>,
+will block if necessary to wait for the port to be free.  If it slept,
+it returns <constant>1</constant>; if it succeeded without needing to
+sleep it returns <constant>0</constant>.  If it fails it will return a
+negative error code.</para>
+
+<para>When you have finished communicating with the device, you can
+give up access to the port so that other drivers can communicate with
+their devices.  The <function>parport_release</function> function
+cannot fail, but it should not be called without the port claimed.
+Similarly, you should not try to claim the port if you already have it
+claimed.</para>
+
+<para>You may find that although there are convenient points for your
+driver to relinquish the parallel port and allow other drivers to talk
+to their devices, it would be preferable to keep hold of the port.
+The printer driver only needs the port when there is data to print,
+for example, but a network driver (such as PLIP) could be sent a
+remote packet at any time.  With PLIP, it is no huge catastrophe if a
+network packet is dropped, since it will likely be sent again, so it
+is possible for that kind of driver to share the port with other
+(pass-through) devices.</para>
+
+<para>The <function>parport_yield</function> and
+<function>parport_yield_blocking</function> functions are for marking
+points in the driver at which other drivers may claim the port and use
+their devices.  Yielding the port is similar to releasing it and
+reclaiming it, but it more efficient because nothing is done if there
+are no other devices needing the port.  In fact, nothing is done even
+if there are other devices waiting but the current device is still
+within its <quote>timeslice</quote>.  The default timeslice is half a
+second, but it can be adjusted via a <filename>/proc</filename>
+entry.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_yield</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_yield_blocking</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The first of these, <function>parport_yield</function>, will not
+block but as a result may fail.  The return value for
+<function>parport_yield</function> is the same as for
+<function>parport_claim</function>.  The blocking version,
+<function>parport_yield_blocking</function>, has the same return code
+as <function>parport_claim_or_block</function>.</para>
+
+<para>Once the port has been claimed, the device driver can use the
+functions in the <structname>struct parport_operations</structname>
+pointer in the <structname>struct parport</structname> it has a
+pointer to.  For example:</para>
+
+<programlisting>
+<![CDATA[
+port->ops->write_data (port, d);
+]]></programlisting>
+
+<para>Some of these operations have <quote>shortcuts</quote>.  For
+instance, <function>parport_write_data</function> is equivalent to the
+above, but may be a little bit faster (it's a macro that in some cases
+can avoid needing to indirect through <varname>port</varname> and
+<varname>ops</varname>).</para>
+
+</chapter>
+
+<chapter id="portdrivers">
+<title>Port drivers</title>
+
+<!-- What port drivers are for (i.e. implementing parport objects). -->
+
+<para>To recap, then:</para>
+
+<itemizedlist spacing=compact>
+
+<listitem>
+<para>
+The device driver registers itself with <filename>parport</filename>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+A low-level driver finds a parallel port and registers it with
+<filename>parport</filename> (these first two things can happen in
+either order).  This registration creates a <structname>struct
+parport</structname> which is linked onto a list of known ports.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<filename>parport</filename> calls the <function>attach</function>
+function of each registered device driver, passing it the pointer to
+the new <structname>struct parport</structname>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver gets a handle from <filename>parport</filename>, for
+use with
+<function>parport_claim</function>/<function>release</function>.  This
+handle takes the form of a pointer to a <structname>struct
+pardevice</structname>, representing a particular device on the
+parallel port, and is acquired using
+<function>parport_register_device</function>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver claims the port using
+<function>parport_claim</function> (or
+<function>function_claim_or_block</function>).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Then it goes ahead and uses the port.  When finished it releases the
+port.
+</para>
+</listitem>
+
+</itemizedlist>
+
+<para>The purpose of the low-level drivers, then, is to detect
+parallel ports and provide methods of accessing them
+(i.e. implementing the operations in <structname>struct
+parport_operations</structname>).</para>
+
+<!-- Interaction with sharing engine; port state -->
+<!-- What did I mean by that? -->
+
+<!-- Talk about parport_pc implementation, and contrast with e.g. amiga -->
+
+<para>A more complete description of which operation is supposed to do
+what is available in
+<filename>Documentation/parport-lowlevel.txt</filename>.</para>
+
+</chapter>
+
+<chapter id="lp">
+<title>The printer driver</title>
+
+<!-- Talk the reader through the printer driver. -->
+<!-- Could even talk about parallel port console here. -->
+
+<para>The printer driver, <filename>lp</filename> is a character
+special device driver and a <filename>parport</filename> client.  As a
+character special device driver it registers a <structname>struct
+file_operations</structname> using
+<function>register_chrdev</function>, with pointers filled in for
+<structfield>write</structfield>, <structfield>ioctl</structfield>,
+<structfield>open</structfield> and
+<structfield>release</structfield>.  As a client of
+<filename>parport</filename>, it registers a <structname>struct
+parport_driver</structname> using
+<function>parport_register_driver</function>, so that
+<filename>parport</filename> knows to call
+<function>lp_attach</function> when a new parallel port is discovered
+(and <function>lp_detach</function> when it goes away).</para>
+
+<para>The parallel port console functionality is also implemented in
+<filename>lp.c</filename>, but that won't be covered here (it's quite
+simple though).</para>
+
+<para>The initialisation of the driver is quite easy to understand
+(see <function>lp_init</function>).  The <varname>lp_table</varname>
+is an array of structures that contain information about a specific
+device (the <structname>struct pardevice</structname> associated with
+it, for example).  That array is initialised to sensible values first
+of all.</para>
+
+<para>Next, the printer driver calls
+<function>register_chrdev</function> passing it a pointer to
+<varname>lp_fops</varname>, which contains function pointers for the
+printer driver's implementation of <function>open</function>,
+<function>write</function>, and so on.  This part is the same as for
+any character special device driver.</para>
+
+<para>After successfully registering itself as a character special
+device driver, the printer driver registers itself as a
+<filename>parport</filename> client using
+<function>parport_register_driver</function>.  It passes a pointer to
+this structure:</para>
+
+<programlisting>
+<![CDATA[
+static struct parport_driver lp_driver = {
+        "lp",
+        lp_attach,
+        lp_detach,
+        NULL
+};
+]]></programlisting>
+
+<para>The <function>lp_detach</function> function is not very
+interesting (it does nothing); the interesting bit is
+<function>lp_attach</function>.  What goes on here depends on whether
+the user supplied any parameters.  The possibilities are: no
+parameters supplied, in which case the printer driver uses every port
+that is detected; the user supplied the parameter <quote>auto</quote>,
+in which case only ports on which the device ID string indicates a
+printer is present are used; or the user supplied a list of parallel
+port numbers to try, in which case only those are used.</para>
+
+<para>For each port that the printer driver wants to use (see
+<function>lp_register</function>), it calls
+<function>parport_register_device</function> and stores the resulting
+<structname>struct pardevice</structname> pointer in the
+<varname>lp_table</varname>.  If the user told it to do so, it then
+resets the printer.</para>
+
+<para>The other interesting piece of the printer driver, from the
+point of view of <filename>parport</filename>, is
+<function>lp_write</function>.  In this function, the user space
+process has data that it wants printed, and the printer driver hands
+it off to the <filename>parport</filename> code to deal with.</para>
+
+<para>The <filename>parport</filename> functions it uses that we have
+not seen yet are <function>parport_negotiate</function>,
+<function>parport_set_timeout</function>, and
+<function>parport_write</function>.  These functions are part of the
+IEEE 1284 implementation.</para>
+
+<para>The way the IEEE 1284 protocol works is that the host tells the
+peripheral what transfer mode it would like to use, and the peripheral
+either accepts that mode or rejects it; if the mode is rejected, the
+host can try again with a different mode.  This is the negotation
+phase.  Once the peripheral has accepted a particular transfer mode,
+data transfer can begin that mode.</para>
+
+<para>The particular transfer mode that the printer driver wants to
+use is named in IEEE 1284 as <quote>compatibility</quote> mode, and
+the function to request a particular mode is called
+<function>parport_negotiate</function>.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_negotiate</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>int <parameter>mode</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The <parameter>modes</parameter> parameter is a symbolic
+constant representing an IEEE 1284 mode; in this instance, it is
+<constant>IEEE1284_MODE_COMPAT</constant>. (Compatibility mode is
+slightly different to the other modes---rather than being specifically
+requested, it is the default until another mode is selected.)</para>
+
+<para>Back to <function>lp_write</function> then.  First, access to
+the parallel port is secured with
+<function>parport_claim_or_block</function>.  At this point the driver
+might sleep, waiting for another driver (perhaps a Zip drive driver,
+for instance) to let the port go.  Next, it goes to compatibility mode
+using <function>parport_negotiate</function>.</para>
+
+<para>The main work is done in the write-loop.  In particular, the
+line that hands the data over to <filename>parport</filename>
+reads:</para>
+
+<programlisting>
+<![CDATA[
+        written = parport_write (port, kbuf, copy_size);
+]]></programlisting>
+
+<para>The <function>parport_write</function> function writes data to
+the peripheral using the currently selected transfer mode
+(compatibility mode, in this case).  It returns the number of bytes
+successfully written:</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>ssize_t <function>parport_write</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>const void *<parameter>buf</parameter></paramdef>
+  <paramdef>size_t <parameter>len</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>ssize_t <function>parport_read</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>void *<parameter>buf</parameter></paramdef>
+  <paramdef>size_t <parameter>len</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>(<function>parport_read</function> does what it sounds like, but
+only works for modes in which reverse transfer is possible.  Of
+course, <function>parport_write</function> only works in modes in
+which forward transfer is possible, too.)</para>
+
+<para>The <parameter>buf</parameter> pointer should be to kernel space
+memory, and obviously the <parameter>len</parameter> parameter
+specifies the amount of data to transfer.</para>
+
+<para>In fact what <function>parport_write</function> does is call the
+appropriate block transfer function from the <structname>struct
+parport_operations</structname>:</para>
+
+<programlisting>
+<![CDATA[
+struct parport_operations {
+        [...]
+
+        /* Block read/write */
+        size_t (*epp_write_data) (struct parport *port, const void *buf,
+                                  size_t len, int flags);
+        size_t (*epp_read_data) (struct parport *port, void *buf, size_t len,
+                                 int flags);
+        size_t (*epp_write_addr) (struct parport *port, const void *buf,
+                                  size_t len, int flags);
+        size_t (*epp_read_addr) (struct parport *port, void *buf, size_t len,
+                                 int flags);
+
+        size_t (*ecp_write_data) (struct parport *port, const void *buf,
+                                  size_t len, int flags);
+        size_t (*ecp_read_data) (struct parport *port, void *buf, size_t len,
+                                 int flags);
+        size_t (*ecp_write_addr) (struct parport *port, const void *buf,
+                                  size_t len, int flags);
+
+        size_t (*compat_write_data) (struct parport *port, const void *buf,
+                                     size_t len, int flags);
+        size_t (*nibble_read_data) (struct parport *port, void *buf,
+                                    size_t len, int flags);
+        size_t (*byte_read_data) (struct parport *port, void *buf,
+                                  size_t len, int flags);
+};
+]]></programlisting>
+
+<para>The transfer code in <filename>parport</filename> will tolerate
+a data transfer stall only for so long, and this timeout can be
+specified with <function>parport_set_timeout</function>, which returns
+the previous timeout:</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>long <function>parport_set_timeout</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+  <paramdef>long <parameter>inactivity</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>This timeout is specific to the device, and is restored on
+<function>parport_claim</function>.</para>
+
+</chapter>
+
+<chapter id="ppdev">
+<title>User-level device drivers</title>
+
+<!-- ppdev -->
+<sect1>
+<title>Introduction to ppdev</title>
+
+<para>The printer is accessible through <filename>/dev/lp0</filename>;
+in the same way, the parallel port itself is accessible through
+<filename>/dev/parport0</filename>.  The difference is in the level of
+control that you have over the wires in the parallel port
+cable.</para>
+
+<para>With the printer driver, a user-space program (such as the
+printer spooler) can send bytes in <quote>printer protocol</quote>.
+Briefly, this means that for each byte, the eight data lines are set
+up, then a <quote>strobe</quote> line tells the printer to look at the
+data lines, and the printer sets an <quote>acknowledgement</quote>
+line to say that it got the byte.  The printer driver also allows the
+user-space program to read bytes in <quote>nibble mode</quote>, which
+is a way of transferring data from the peripheral to the computer half
+a byte at a time (and so it's quite slow).</para>
+
+<para>In contrast, the <filename>ppdev</filename> driver (accessed via
+<filename>/dev/parport0</filename>) allows you to:</para>
+
+<itemizedlist spacing=compact>
+
+<listitem>
+<para>
+examine status lines,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set control lines,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set/examine data lines (and control the direction of the data lines),
+</para>
+</listitem>
+
+<listitem>
+<para>
+wait for an interrupt (triggered by one of the status lines),
+</para>
+</listitem>
+
+<listitem>
+<para>
+find out how many new interrupts have occurred,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set up a response to an interrupt,
+</para>
+</listitem>
+
+<listitem>
+<para>
+use IEEE 1284 negotiation (for telling peripheral which transfer mode,
+to use)
+</para>
+</listitem>
+
+<listitem>
+<para>
+transfer data using a specified IEEE 1284 mode.
+</para>
+</listitem>
+
+</itemizedlist>
+
+</sect1>
+
+<sect1>
+<title>User-level or kernel-level driver?</title>
+
+<para>The decision of whether to choose to write a kernel-level device
+driver or a user-level device driver depends on several factors.  One
+of the main ones from a practical point of view is speed: kernel-level
+device drivers get to run faster because they are not preemptable,
+unlike user-level applications.</para>
+
+<para>Another factor is ease of development.  It is in general easier
+to write a user-level driver because (a) one wrong move does not
+result in a crashed machine, (b) you have access to user libraries
+(such as the C library), and (c) debugging is easier.</para>
+
+</sect1>
+
+<sect1>
+<title>Programming interface</title>
+
+<para>The <filename>ppdev</filename> interface is largely the same as
+that of other character special devices, in that it supports
+<function>open</function>, <function>close</function>,
+<function>read</function>, <function>write</function>, and
+<function>ioctl</function>.</para>
+
+<sect2>
+<title>Starting and stopping: <function>open</function> and
+<function>close</function></title>
+
+<para>The device node <filename>/dev/parport0</filename> represents
+any device that is connected to <filename>parport0</filename>, the
+first parallel port in the system.  Each time the device node is
+opened, it represents (to the process doing the opening) a different
+device.  It can be opened more than once, but only one instance can
+actually be in control of the parallel port at any time.  A process
+that has opened <filename>/dev/parport0</filename> shares the parallel
+port in the same way as any other device driver.  A user-land driver
+may be sharing the parallel port with in-kernel device drivers as well
+as other user-land drivers.</para>
+</sect2>
+
+<sect2>
+<title>Control: <function>ioctl</function></title>
+
+<para>Most of the control is done, naturally enough, via the
+<function>ioctl</function> call.  Using <function>ioctl</function>,
+the user-land driver can control both the <filename>ppdev</filename>
+driver in the kernel and the physical parallel port itself.  The
+<function>ioctl</function> call takes as parameters a file descriptor
+(the one returned from opening the device node), a command, and
+optionally (a pointer to) some data.</para>
+
+<variablelist>
+<varlistentry><term><constant>PPCLAIM</constant></term>
+<listitem>
+
+<para>Claims access to the port.  As a user-land device driver writer,
+you will need to do this before you are able to actually change the
+state of the parallel port in any way.  Note that some operations only
+affect the <filename>ppdev</filename> driver and not the port, such as
+<constant>PPSETMODE</constant>; they can be performed while access to
+the port is not claimed.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPEXCL</constant></term>
+<listitem>
+
+<para>Instructs the kernel driver to forbid any sharing of the port
+with other drivers, i.e. it requests exclusivity.  The
+<constant>PPEXCL</constant> command is only valid when the port is not
+already claimed for use, and it may mean that the next
+<constant>PPCLAIM</constant> <function>ioctl</function> will fail:
+some other driver may already have registered itself on that
+port.</para>
+
+<para>Most device drivers don't need exclusive access to the port.
+It's only provided in case it is really needed, for example for
+devices where access to the port is required for extensive periods of
+time (many seconds).</para>
+
+<para>Note that the <constant>PPEXCL</constant>
+<function>ioctl</function> doesn't actually claim the port there and
+then---action is deferred until the <constant>PPCLAIM</constant>
+<function>ioctl</function> is performed.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRELEASE</constant></term>
+<listitem>
+
+<para>Releases the port.  Releasing the port undoes the effect of
+claiming the port.  It allows other device drivers to talk to their
+devices (assuming that there are any).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPYIELD</constant></term>
+<listitem>
+
+<para>Yields the port to another driver.  This
+<function>ioctl</function> is a kind of short-hand for releasing the
+port and immediately reclaiming it.  It gives other drivers a chance
+to talk to their devices, but afterwards claims the port back.  An
+example of using this would be in a user-land printer driver: once a
+few characters have been written we could give the port to another
+device driver for a while, but if we still have characters to send to
+the printer we would want the port back as soon as possible.</para>
+
+<para>It is important not to claim the parallel port for too long, as
+other device drivers will have no time to service their devices.  If
+your device does not allow for parallel port sharing at all, it is
+better to claim the parallel port exclusively (see
+<constant>PPEXCL</constant>).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPNEGOT</constant></term>
+<listitem>
+
+<para>Performs IEEE 1284 negotiation into a particular mode.  Briefly,
+negotiation is the method by which the host and the peripheral decide
+on a protocol to use when transferring data.</para>
+
+<para>An IEEE 1284 compliant device will start out in compatibility
+mode, and then the host can negotiate to another mode (such as
+ECP).</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+an <type>int</type>; values for this are in
+<filename>parport.h</filename> and include:</para>
+
+<itemizedlist spacing=compact>
+<listitem><para><constant>IEEE1284_MODE_COMPAT</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_NIBBLE</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_BYTE</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_EPP</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_ECP</constant></para></listitem>
+</itemizedlist>
+
+<para>The <constant>PPNEGOT</constant> <function>ioctl</function>
+actually does two things: it performs the on-the-wire negotiation, and
+it sets the behaviour of subsequent
+<function>read</function>/<function>write</function> calls so that
+they use that mode (but see <constant>PPSETMODE</constant>).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPSETMODE</constant></term>
+<listitem>
+
+<para>Sets which IEEE 1284 protocol to use for the
+<function>read</function> and <function>write</function> calls.</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+an <type>int</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPGETTIME</constant></term>
+<listitem>
+
+<para>Retrieves the time-out value.  The <function>read</function> and
+<function>write</function> calls will time out if the peripheral
+doesn't respond quickly enough.  The <constant>PPGETTIME</constant>
+<function>ioctl</function> retrieves the length of time that the
+peripheral is allowed to have before giving up.</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+a <structname>struct timeval</structname>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPSETTIME</constant></term>
+<listitem>
+
+<para>Sets the time-out.  The <function>ioctl</function> parameter
+should be a pointer to a <structname>struct
+timeval</structname>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWCONTROL</constant></term>
+<listitem>
+
+<para>Sets the control lines.  The <function>ioctl</function>
+parameter is a pointer to an <type>unsigned char</type>, the bitwise
+OR of the control line values in
+<filename>parport.h</filename>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRCONTROL</constant></term>
+<listitem>
+
+<para>Returns the last value written to the control register, in the
+form of an <type>unsigned char</type>: each bit corresponds to a
+control line (although some are unused).  The
+<function>ioctl</function> parameter should be a pointer to an
+<type>unsigned char</type>.</para>
+
+<para>This doesn't actually touch the hardware; the last value written
+is remembered in software.  This is because some parallel port
+hardware does not offer read access to the control register.</para>
+
+<para>The control lines bits are defined in
+<filename>parport.h</filename>:</para>
+
+<itemizedlist spacing=compact>
+<listitem><para><constant>PARPORT_CONTROL_STROBE</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_AUTOFD</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_SELECT</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_INIT</constant></para></listitem>
+</itemizedlist>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPFCONTROL</constant></term>
+<listitem>
+
+<para>Frobs the control lines.  Since a common operation is to change
+one of the control signals while leaving the others alone, it would be
+quite inefficient for the user-land driver to have to use
+<constant>PPRCONTROL</constant>, make the change, and then use
+<constant>PPWCONTROL</constant>.  Of course, each driver could
+remember what state the control lines are supposed to be in (they are
+never changed by anything else), but in order to provide
+<constant>PPRCONTROL</constant>, <filename>ppdev</filename> must
+remember the state of the control lines anyway.</para>
+
+<para>The <constant>PPFCONTROL</constant> <function>ioctl</function>
+is for <quote>frobbing</quote> control lines, and is like
+<constant>PPWCONTROL</constant> but acts on a restricted set of
+control lines.  The <function>ioctl</function> parameter is a pointer
+to a <structname>struct ppdev_frob_struct</structname>:</para>
+
+<programlisting>
+<![CDATA[
+struct ppdev_frob_struct {
+        unsigned char mask;
+        unsigned char val;
+};
+]]>
+</programlisting>
+
+<para>The <structfield>mask</structfield> and
+<structfield>val</structfield> fields are bitwise ORs of control line
+names (such as in <constant>PPWCONTROL</constant>).  The operation
+performed by <constant>PPFCONTROL</constant> is:</para>
+
+<programlisting>
+<![CDATA[new_ctr = (old_ctr & ~mask) | val;]]>
+</programlisting>
+
+<para>In other words, the signals named in
+<structfield>mask</structfield> are set to the values in
+<structfield>val</structfield>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRSTATUS</constant></term>
+<listitem>
+
+<para>Returns an <type>unsigned char</type> containing bits set for
+each status line that is set (for instance,
+<constant>PARPORT_STATUS_BUSY</constant>).  The
+<function>ioctl</function> parameter should be a pointer to an
+<type>unsigned char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPDATADIR</constant></term>
+<listitem>
+
+<para>Controls the data line drivers.  Normally the computer's
+parallel port will drive the data lines, but for byte-wide transfers
+from the peripheral to the host it is useful to turn off those drivers
+and let the peripheral drive the signals. (If the drivers on the
+computer's parallel port are left on when this happens, the port might
+be damaged.)</para>
+
+<para>This is only needed in conjunction with
+<constant>PPWDATA</constant> or <constant>PPRDATA</constant>.</para>
+
+<para>The <function>ioctl</function> parameter is a pointer to an
+<type>int</type>.  If the <type>int</type> is zero, the drivers are
+turned on (forward direction); if non-zero, the drivers are turned off
+(reverse direction).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWDATA</constant></term>
+<listitem>
+
+<para>Sets the data lines (if in forward mode).  The
+<function>ioctl</function> parameter is a pointer to an <type>unsigned
+char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRDATA</constant></term>
+<listitem>
+
+<para>Reads the data lines (if in reverse mode).  The
+<function>ioctl</function> parameter is a pointer to an <type>unsigned
+char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPCLRIRQ</constant></term>
+<listitem>
+
+<para>Clears the interrupt count.  The <filename>ppdev</filename>
+driver keeps a count of interrupts as they are triggered.
+<constant>PPCLRIRQ</constant> stores this count in an
+<type>int</type>, a pointer to which is passed in as the
+<function>ioctl</function> parameter.</para>
+
+<para>In addition, the interrupt count is reset to zero.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWCTLONIRQ</constant></term>
+<listitem>
+
+<para>Set a trigger response.  Afterwards when an interrupt is
+triggered, the interrupt handler will set the control lines as
+requested.  The <function>ioctl</function> parameter is a pointer to
+an <type>unsigned char</type>, which is interpreted in the same way as
+for <constant>PPWCONTROL</constant>.</para>
+
+<para>The reason for this <function>ioctl</function> is simply speed.
+Without this <function>ioctl</function>, responding to an interrupt
+would start in the interrupt handler, switch context to the user-land
+driver via <function>poll</function> or <function>select</function>,
+and then switch context back to the kernel in order to handle
+<constant>PPWCONTROL</constant>.  Doing the whole lot in the interrupt
+handler is a lot faster.</para>
+
+</listitem></varlistentry>
+
+<!-- PPSETPHASE? -->
+
+</variablelist>
+
+</sect2>
+
+<sect2>
+<title>Transferring data: <function>read</function> and
+<function>write</function></title>
+
+<para>Transferring data using <function>read</function> and
+<function>write</function> is straightforward.  The data is
+transferring using the current IEEE 1284 mode (see the
+<constant>PPSETMODE</constant> <function>ioctl</function>).  For modes
+which can only transfer data in one direction, only the appropriate
+function will work, of course.</para>
+</sect2>
+
+<sect2>
+<title>Waiting for events: <function>poll</function> and
+<function>select</function></title>
+
+<para>The <filename>ppdev</filename> driver provides user-land device
+drivers with the ability to wait for interrupts, and this is done
+using <function>poll</function> (and <function>select</function>,
+which is implemented in terms of <function>poll</function>).</para>
+
+<para>When a user-land device driver wants to wait for an interrupt,
+it sleeps with <function>poll</function>.  When the interrupt arrives,
+<filename>ppdev</filename> wakes it up (with a <quote>read</quote>
+event, although strictly speaking there is nothing to actually
+<function>read</function>).</para>
+
+</sect2>
+
+</sect1>
+
+<sect1>
+<title>Examples</title>
+
+<para>Presented here are two demonstrations of how to write a simple
+printer driver for <filename>ppdev</filename>.  Firstly we will use
+the <function>write</function> function, and after that we will drive
+the control and data lines directly.</para>
+
+<para>The first thing to do is to actually open the device.</para>
+
+<programlisting><![CDATA[
+int drive_printer (const char *name)
+{
+    int fd;
+    int mode; /* We'll need this later. */
+
+    fd = open (name, O_RDWR);
+    if (fd == -1) {
+        perror ("open");
+        return 1;
+    }
+]]></programlisting>
+
+<para>Here <varname>name</varname> should be something along the lines
+of <filename>"/dev/parport0"</filename>. (If you don't have any
+<filename>/dev/parport</filename> files, you can make them with
+<command>mknod</command>; they are character special device nodes with
+major 99.)</para>
+
+<para>In order to do anything with the port we need to claim access to
+it.</para>
+
+<programlisting><![CDATA[
+    if (ioctl (fd, PPCLAIM)) {
+        perror ("PPCLAIM");
+        close (fd);
+        return 1;
+    }
+]]></programlisting>
+
+<para>Our printer driver will copy its input (from
+<varname>stdin</varname>) to the printer, and it can do that it one of
+two ways.  The first way is to hand it all off to the kernel driver,
+with the knowledge that the protocol that the printer speaks is IEEE
+1284's <quote>compatibility</quote> mode.</para>
+
+<programlisting><![CDATA[
+    /* Switch to compatibility mode.  (In fact we don't need
+     * to do this, since we start off in compatibility mode
+     * anyway, but this demonstrates PPNEGOT.)
+    mode = IEEE1284_MODE_COMPAT;
+    if (ioctl (fd, PPNEGOT, &mode)) {
+        perror ("PPNEGOT");
+        close (fd);
+        return 1;
+    }
+
+    for (;;) {
+        char buffer[1000];
+        char *ptr = buffer;
+        size_t got;
+
+        got = read (0 /* stdin */, buffer, 1000);
+        if (got < 0) {
+            perror ("read");
+            close (fd);
+            return 1;
+        }
+
+        if (got == 0)
+            /* End of input */
+            break;
+
+        while (got > 0) {
+            int written = write_printer (fd, ptr, got);
+
+            if (written < 0) {
+                perror ("write");
+                close (fd);
+                return 1;
+            }
+
+            ptr += written;
+            got -= written;
+        }
+    }
+]]></programlisting>
+
+<para>The <function>write_printer</function> function is not pictured
+above.  This is because the main loop that is shown can be used for
+both methods of driving the printer.  Here is one implementation of
+<function>write_printer</function>:</para>
+
+<programlisting><![CDATA[
+ssize_t write_printer (int fd, const void *ptr, size_t count)
+{
+    return write (fd, ptr, count);
+}
+]]></programlisting>
+
+<para>We hand the data to the kernel-level driver (using
+<function>write</function>) and it handles the printer
+protocol.</para>
+
+<para>Now let's do it the hard way!  In this particular example there
+is no practical reason to do anything other than just call
+<function>write</function>, because we know that the printer talks an
+IEEE 1284 protocol.  On the other hand, this particular example does
+not even need a user-land driver since there is already a kernel-level
+one; for the purpose of this discussion, try to imagine that the
+printer speaks a protocol that is not already implemented under
+Linux.</para>
+
+<para>So, here is the alternative implementation of
+<function>write_printer</function> (for brevity, error checking has
+been omitted):</para>
+
+<programlisting><![CDATA[
+ssize_t write_printer (int fd, const void *ptr, size_t count)
+{
+    ssize_t wrote = 0;
+
+    while (wrote < count) {
+        unsigned char status, control, data;
+        unsigned char mask = (PARPORT_STATUS_ERROR
+                              | PARPORT_STATUS_BUSY);
+        unsigned char val = (PARPORT_STATUS_ERROR
+                              | PARPORT_STATUS_BUSY);
+        struct parport_frob_struct frob;
+        struct timespec ts;
+
+        /* Wait for printer to be ready */
+        for (;;) {
+            ioctl (fd, PPRSTATUS, &status);
+
+            if ((status & mask) == val)
+                break;
+
+            ioctl (fd, PPRELEASE);
+            sleep (1);
+            ioctl (fd, PPCLAIM);
+        }
+
+        /* Set the data lines */
+        data = * ((char *) ptr)++;
+        ioctl (fd, PPWDATA, &data);
+
+        /* Delay for a bit */
+        ts.tv_sec = 0;
+        ts.tv_nsec = 1000;
+        nanosleep (&ts, NULL);
+
+        /* Pulse strobe */
+        frob.mask = PARPORT_CONTROL_STROBE;
+        frob.val = PARPORT_CONTROL_STROBE;
+        ioctl (fd, PPFCONTROL, &frob);
+        nanosleep (&ts, NULL);
+
+        /* End the pulse */
+        frob.val = 0;
+        ioctl (fd, PPFCONTROL, &frob);
+        nanosleep (&ts, NULL);
+
+        wrote++;
+    }
+
+    return wrote;
+}
+]]></programlisting>
+
+<para>To show a bit more of the <filename>ppdev</filename> interface,
+here is a small piece of code that is intended to mimic the printer's
+side of printer protocol.</para>
+
+<programlisting><![CDATA[
+  for (;;)
+    {
+      int irqc;
+      int busy = nAck | nFault;
+      int acking = nFault;
+      int ready = Busy | nAck | nFault;
+      char ch;
+
+      /* Set up the control lines when an interrupt happens. */
+      ioctl (fd, PPWCTLONIRQ, &busy);
+
+      /* Now we're ready. */
+      ioctl (fd, PPWCONTROL, &ready);
+
+      /* Wait for an interrupt. */
+      {
+        fd_set rfds;
+        FD_ZERO (&rfds);
+        FD_SET (fd, &rfds);
+        if (!select (fd + 1, &rfds, NULL, NULL, NULL))
+          /* Caught a signal? */
+          continue;
+      }
+
+      /* We are now marked as busy. */
+
+      /* Fetch the data. */
+      ioctl (fd, PPRDATA, &ch);
+
+      /* Clear the interrupt. */
+      ioctl (fd, PPCLRIRQ, &irqc);
+      if (irqc > 1)
+        fprintf (stderr, "Arghh! Missed %d interrupt%s!\n",
+         irqc - 1, irqc == 2 ? "s" : "");
+
+      /* Ack it. */
+      ioctl (fd, PPWCONTROL, &acking);
+      usleep (2);
+      ioctl (fd, PPWCONTROL, &busy);
+
+      putchar (ch);
+    }
+]]></programlisting>
+
+</sect1>
+
+</chapter>
+</book>
\ No newline at end of file
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/videobook.tmpl linux/Documentation/DocBook/videobook.tmpl
--- v2.3.51/linux/Documentation/DocBook/videobook.tmpl	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/videobook.tmpl	Mon Mar 13 22:19:09 2000
@@ -0,0 +1,1663 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="V4LGuide">
+ <bookinfo>
+  <title>Video4Linux Programming</title>
+  
+  <authorgroup>
+   <author>
+    <firstname>Alan</firstname>
+    <surname>Cox</surname>
+    <affiliation>
+     <address>
+      <email>alan@redhat.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>2000</year>
+   <holder>Alan Cox</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation 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.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+  <chapter id="intro">
+      <title>Introduction</title>
+  <para>
+        Parts of this document first appeared in Linux Magazine under a
+        ninety day exclusivity.
+  </para>
+  <para>
+        Video4Linux is intended to provide a common programming interface
+        for the many TV and capture cards now on the market, as well as
+        parallel port and USB video cameras. Radio, teletext decoders and
+        vertical blanking data interfaces are also provided.
+  </para>
+  </chapter>
+  <chapter>
+        <title>Radio Devices</title>
+  <para>
+        There are a wide variety of radio interfaces available for PC's, and these
+        are generally very simple to program. The biggest problem with supporting
+        such devices is normally extracting documentation from the vendor.
+  </para>
+  <para>
+        The radio interface supports a simple set of control ioctls standardised
+        across all radio and tv interfaces. It does not support read or write, which
+        are used for video streams. The reason radio cards do not allow you to read
+        the audio stream into an application is that without exception they provide
+        a connection on to a soundcard. Soundcards can be used to read the radio
+        data just fine. 
+  </para>
+  <sect1 id="registerradio">
+  <title>Registering Radio Devices</title>
+  <para>
+        The Video4linux core provides an interface for registering devices. The
+        first step in writing our radio card driver is to register it.
+  </para>
+  <programlisting>
+
+
+static struct video_device my_radio
+{
+        "My radio",
+        VID_TYPE_TUNER,
+        VID_HARDWARE_MYRADIO,
+        radio_open.
+        radio_close,
+        NULL,                /* no read */
+        NULL,                 /* no write */
+        NULL,                /* no poll */
+        radio_ioctl,
+        NULL,                /* no special init function */
+        NULL                /* no private data */
+};
+
+
+  </programlisting>
+  <para>
+        This declares our video4linux device driver interface. The VID_TYPE_ value
+        defines what kind of an interface we are, and defines basic capabilities.
+  </para>
+  <para>
+        The only defined value relevant for a radio card is VID_TYPE_TUNER which
+        indicates that the device can be tuned. Clearly our radio is going to have some
+        way to change channel so it is tuneable.
+  </para>
+  <para>
+        The VID_HARDWARE_ types are unique to each device. Numbers are assigned by
+        <email>alan@redhat.com</email> when device drivers are going to be released. Until then you
+        can pull a suitably large number out of your hat and use it. 10000 should be
+        safe for a very long time even allowing for the huge number of vendors
+        making new and different radio cards at the moment.
+  </para>
+  <para>
+        We declare an open and close routine, but we do not need read or write,
+        which are used to read and write video data to or from the card itself. As
+        we have no read or write there is no poll function.
+  </para>
+  <para>
+        The private initialise function is run when the device is registered. In
+        this driver we've already done all the work needed. The final pointer is a
+        private data pointer that can be used by the device driver to attach and
+        retrieve private data structures. We set this field "priv" to NULL for
+        the moment.
+  </para>
+  <para>
+        Having the structure defined is all very well but we now need to register it
+        with the kernel. 
+  </para>
+  <programlisting>
+
+
+static int io = 0x320;
+
+int __init myradio_init(struct video_init *v)
+{
+        if(check_region(io, MY_IO_SIZE))
+        {
+                printk(KERN_ERR 
+                    "myradio: port 0x%03X is in use.\n", io);
+                return -EBUSY;
+        }
+
+        if(video_device_register(&amp;my_radio, VFL_TYPE_RADIO)==-1)
+                return -EINVAL;
+        request_region(io, MY_IO_SIZE, "myradio");
+        return 0;
+}
+
+  </programlisting>
+  <para>
+        The first stage of the initialisation, as is normally the case, is to check 
+        that the I/O space we are about to fiddle with doesn't belong to some other 
+        driver. If it is we leave well alone. If the user gives the address of the 
+        wrong device then we will spot this. These policies will generally avoid 
+        crashing the machine.
+  </para>
+  <para>
+        Now we ask the Video4Linux layer to register the device for us. We hand it
+        our carefully designed video_device structure and also tell it which group
+        of devices we want it registered with. In this case VFL_TYPE_RADIO.
+  </para>
+  <para>
+        The types available are
+  </para>
+   <table frame=all><title>Device Types</title>
+   <tgroup cols=3 align=left>
+   <tbody>
+   <row>
+        <entry>VFL_TYPE_RADIO</><>/dev/radio{n}</><>
+
+        Radio devices are assigned in this block. As with all of these
+        selections the actual number assignment is done by the video layer
+        accordijng to what is free.</entry>
+	</row><row>
+        <entry>VFL_TYPE_GRABBER</><>/dev/video{n}</><>
+        Video capture devices and also -- counter-intuitively for the name --
+        hardware video playback devices such as MPEG2 cards.</entry>
+	</row><row>
+        <entry>VFL_TYPE_VBI</><>/dev/vbi{n}</><>
+        The VBI devices capture the hidden lines on a television picture
+        that carry further information like closed caption data, teletext
+        (primarily in Europe) and now Intercast and the ATVEC internet
+        television encodings.</entry>
+	</row><row>
+        <entry>VFL_TYPE_VTX</><>/dev/vtx[n}</><>
+        VTX is 'Videotext' also known as 'Teletext'. This is a system for
+        sending numbered, 40x25, mostly textual page images over the hidden
+        lines. Unlike the /dev/vbi interfaces, this is for 'smart' decoder 
+        chips. (The use of the word smart here has to be taken in context,
+        the smartest teletext chips are fairly dumb pieces of technology).
+	</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        We are most definitely a radio.
+  </para>
+  <para>
+        Finally we allocate our I/O space so that nobody treads on us and return 0
+        to signify general happiness with the state of the universe.
+  </para>
+  </sect1>
+  <sect1 id="openradio">
+  <title>Opening And Closing The Radio</title>
+
+  <para>
+        The functions we declared in our video_device are mostly very simple.
+        Firstly we can drop in what is basically standard code for open and close. 
+  </para>
+  <programlisting>
+
+
+static int users = 0;
+
+static int radio_open(stuct video_device *dev, int flags)
+{
+        if(users)
+                return -EBUSY;
+        users++;
+        MOD_INC_USE_COUNT;
+        return 0;
+}
+
+  </programlisting>
+  <para>
+        At open time we need to do nothing but check if someone else is also using
+        the radio card. If nobody is using it we make a note that we are using it,
+        then we ensure that nobody unloads our driver on us.
+  </para>
+  <programlisting>
+
+
+static int radio_close(struct video_device *dev)
+{
+        users--;
+        MOD_DEC_USE_COUNT;
+}
+
+  </programlisting>
+  <para>
+        At close time we simply need to reduce the user count and allow the module
+        to become unloadable.
+  </para>
+  <para>
+        If you are sharp you will have noticed neither the open nor the close
+        routines attempt to reset or change the radio settings. This is intentional.
+        It allows an application to set up the radio and exit. It avoids a user
+        having to leave an application running all the time just to listen to the
+        radio. 
+  </para>
+  </sect1>
+  <sect1 id="ioctlradio">
+  <title>The Ioctl Interface</title>
+  <para>
+        This leaves the ioctl routine, without which the driver will not be
+        terribly useful to anyone.
+  </para>
+  <programlisting>
+
+
+static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+        switch(cmd)
+        {
+                case VIDIOCGCAP:
+                {
+                        struct video_capability v;
+                        v.type = VID_TYPE_TUNER;
+                        v.channels = 1;
+                        v.audios = 1;
+                        v.maxwidth = 0;
+                        v.minwidth = 0;
+                        v.maxheight = 0;
+                        v.minheight = 0;
+                        strcpy(v.name, "My Radio");
+                        if(copy_to_user(arg, &amp;v, sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
+
+  </programlisting>
+  <para>
+        VIDIOCGCAP is the first ioctl all video4linux devices must support. It
+        allows the applications to find out what sort of a card they have found and
+        to figure out what they want to do about it. The fields in the structure are
+  </para>
+   <table frame=all><title>struct video_capability fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+        <entry>name</><>The device text name. This is intended for the user.</>
+	</row><row>
+        <entry>channels</><>The number of different channels you can tune on
+                        this card. It could even by zero for a card that has
+                        no tuning capability. For our simple FM radio it is 1. 
+                        An AM/FM radio would report 2.</entry>
+	</row><row>
+        <entry>audios</><>The number of audio inputs on this device. For our
+                        radio there is only one audio input.</entry>
+	</row><row>
+        <entry>minwidth,minheight</><>The smallest size the card is capable of capturing
+		        images in. We set these to zero. Radios do not
+                        capture pictures</entry>
+	</row><row>
+        <entry>maxwidth,maxheight</><>The largest image size the card is capable of
+                                      capturing. For our radio we report 0.
+				</entry>
+	</row><row>
+        <entry>type</><>This reports the capabilities of the device, and
+                        matches the field we filled in in the struct
+                        video_device when registering.</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        Having filled in the fields, we use copy_to_user to copy the structure into
+        the users buffer. If the copy fails we return an EFAULT to the application
+        so that it knows it tried to feed us garbage.
+  </para>
+  <para>
+        The next pair of ioctl operations select which tuner is to be used and let
+        the application find the tuner properties. We have only a single FM band
+        tuner in our example device.
+  </para>
+  <programlisting>
+
+
+                case VIDIOCGTUNER:
+                {
+                        struct video_tuner v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v))!=0)
+                                return -EFAULT;
+                        if(v.tuner)
+                                return -EINVAL;
+                        v.rangelow=(87*16000);
+                        v.rangehigh=(108*16000);
+                        v.flags = VIDEO_TUNER_LOW;
+                        v.mode = VIDEO_MODE_AUTO;
+                        v.signal = 0xFFFF;
+                        strcpy(v.name, "FM");
+                        if(copy_to_user(&amp;v, arg, sizeof(v))!=0)
+                                return -EFAULT;
+                        return 0;
+                }
+
+  </programlisting>
+  <para>
+        The VIDIOCGTUNER ioctl allows applications to query a tuner. The application
+        sets the tuner field to the tuner number it wishes to query. The query does
+        not change the tuner that is being used, it merely enquires about the tuner
+        in question.
+  </para>
+  <para>
+        We have exactly one tuner so after copying the user buffer to our temporary
+        structure we complain if they asked for a tuner other than tuner 0. 
+  </para>
+  <para>
+        The video_tuner structure has the following fields
+  </para>
+   <table frame=all><title>struct video_tuner fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+        <entry>int tuner</><entry>The number of the tuner in question</entry>
+   </row><row>
+        <entry>char name[32]</><entry>A text description of this tuner. "FM" will do fine.
+                        This is intended for the application.</entry>
+   </row><row>
+        <entry>u32 flags</>
+        <entry>Tuner capability flags</entry>
+   </row>
+   <row>
+        <entry>u16 mode</><entry>The current reception mode</entry>
+
+   </row><row>
+        <entry>u16 signal</><entry>The signal strength scaled between 0 and 65535. If
+                        a device cannot tell the signal strength it should
+                        report 65535. Many simple cards contain only a 
+                        signal/no signal bit. Such cards will report either
+                        0 or 65535.</entry>
+
+   </row><row>
+        <entry>u32 rangelow, rangehigh</><entry>
+                        The range of frequencies supported by the radio
+                        or TV. It is scaled according to the VIDEO_TUNER_LOW
+                        flag.</entry>
+
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+
+   <table frame=all><title>struct video_tuner flags</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+	<entry>VIDEO_TUNER_PAL</><entry>A PAL TV tuner</entry>
+	</row><row>
+        <entry>VIDEO_TUNER_NTSC</><entry>An NTSC (US) TV tuner</entry>
+	</row><row>
+        <entry>VIDEO_TUNER_SECAM</><entry>A SECAM (French) TV tuner</entry>
+	</row><row>
+        <entry>VIDEO_TUNER_LOW</><>
+             The tuner frequency is scaled in 1/16th of a KHz
+             steps. If not it is in 1/16th of a MHz steps
+	</entry>
+	</row><row>
+        <entry>VIDEO_TUNER_NORM</><entry>The tuner can set its format</entry>
+	</row><row>
+        <entry>VIDEO_TUNER_STEREO_ON</><entry>The tuner is currently receiving a stereo signal</entry>
+        </row>
+    </tbody>
+    </tgroup>
+    </table>
+
+   <table frame=all><title>struct video_tuner modes</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+                <entry>VIDEO_MODE_PAL</><>PAL Format</entry>
+   </row><row>
+                <entry>VIDEO_MODE_NTSC</><>NTSC Format (USA)</entry>
+   </row><row>
+                <entry>VIDEO_MODE_SECAM</><>French Format</entry>
+   </row><row>
+                <entry>VIDEO_MODE_AUTO</><>A device that does not need to do
+                                        TV format switching</entry>
+   </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        The settings for the radio card are thus fairly simple. We report that we
+        are a tuner called "FM" for FM radio. In order to get the best tuning
+        resolution we report VIDEO_TUNER_LOW and select tuning to 1/16th of KHz. Its
+        unlikely our card can do that resolution but it is a fair bet the card can
+        do better than 1/16th of a MHz. VIDEO_TUNER_LOW is appropriate to almost all
+        radio usage.
+  </para>
+  <para>
+        We report that the tuner automatically handles deciding what format it is
+        receiving - true enough as it only handles FM radio. Our example card is
+        also incapable of detecting stereo or signal strengths so it reports a
+        strength of 0xFFFF (maximum) and no stereo detected.
+  </para>
+  <para>
+        To finish off we set the range that can be tuned to be 87-108Mhz, the normal
+        FM broadcast radio range. It is important to find out what the card is
+        actually capable of tuning. It is easy enough to simply use the FM broadcast
+        range. Unfortunately if you do this you will discover the FM broadcast
+        ranges in the USA, Europe and Japan are all subtly different and some users
+        cannot receive all the stations they wish.
+  </para>
+  <para>
+        The application also needs to be able to set the tuner it wishes to use. In
+        our case, with a single tuner this is rather simple to arrange.
+  </para>
+  <programlisting>
+
+                case VIDIOCSTUNER:
+                {
+                        struct video_tuner v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.tuner != 0)
+                                return -EINVAL;
+                        return 0;
+                }
+
+  </programlisting>
+  <para>
+        We copy the user supplied structure into kernel memory so we can examine it. 
+        If the user has selected a tuner other than zero we reject the request. If 
+        they wanted tuner 0 then, suprisingly enough, that is the current tuner already.
+  </para>
+  <para>
+        The next two ioctls we need to provide are to get and set the frequency of
+        the radio. These both use an unsigned long argument which is the frequency.
+        The scale of the frequency depends on the VIDEO_TUNER_LOW flag as I
+        mentioned earlier on. Since we have VIDEO_TUNER_LOW set this will be in
+        1/16ths of a KHz.
+  </para>
+  <programlisting>
+
+static unsigned long current_freq;
+
+
+
+                case VIDIOCGFREQ:
+                        if(copy_to_user(arg, &amp;current_freq, 
+                                sizeof(unsigned long))
+                                return -EFAULT;
+                        return 0;
+
+  </programlisting>
+  <para>
+        Querying the frequency in our case is relatively simple. Our radio card is
+        too dumb to let us query the signal strength so we remember our setting if 
+        we know it. All we have to do is copy it to the user.
+  </para>
+  <programlisting>
+
+
+                case VIDIOCSFREQ:
+                {
+                        u32 freq;
+                        if(copy_from_user(arg, &amp;freq, 
+                                sizeof(unsigned long))!=0)
+                                return -EFAULT;
+                        if(hardware_set_freq(freq)<0)
+                                return -EINVAL;
+                        current_freq = freq;
+                        return 0;
+                }
+
+  </programlisting>
+  <para>
+        Setting the frequency is a little more complex. We begin by copying the
+        desired frequency into kernel space. Next we call a hardware specific routine
+        to set the radio up. This might be as simple as some scaling and a few
+        writes to an I/O port. For most radio cards it turns out a good deal more
+        complicated and may involve programming things like a phase locked loop on
+        the card. This is what documentation is for. 
+  </para>
+  <para>
+        The final set of operations we need to provide for our radio are the 
+        volume controls. Not all radio cards can even do volume control. After all
+        there is a perfectly good volume control on the sound card. We will assume
+        our radio card has a simple 4 step volume control.
+  </para>
+  <para>
+        There are two ioctls with audio we need to support
+  </para>
+  <programlisting>
+
+static int current_volume=0;
+
+                case VIDIOCGAUDIO:
+                {
+                        struct video_audio v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.audio != 0)
+                                return -EINVAL;
+                        v.volume = 16384*current_volume;
+                        v.step = 16384;
+                        strcpy(v.name, "Radio");
+                        v.mode = VIDEO_SOUND_MONO;
+                        v.balance = 0;
+                        v.base = 0;
+                        v.treble = 0;
+                        
+                        if(copy_to_user(arg. &amp;v, sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
+
+  </programlisting>
+  <para>
+        Much like the tuner we start by copying the user structure into kernel
+        space. Again we check if the user has asked for a valid audio input. We have
+        only input 0 and we punt if they ask for another input.
+  </para>
+  <para>
+        Then we fill in the video_audio structure. This has the following format
+  </para>
+   <table frame=all><title>struct video_audio fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+   <entry>audio</><>The input the user wishes to query</>
+   </row><row>
+   <entry>volume</><>The volume setting on a scale of 0-65535</>
+   </row><row>
+   <entry>base</><>The base level on a scale of 0-65535</>
+   </row><row>
+   <entry>treble</><>The treble level on a scale of 0-65535</>
+   </row><row>
+   <entry>flags</><>The features this audio device supports
+   </entry>
+   </row><row>
+   <entry>name</><>A text name to display to the user. We picked 
+                        "Radio" as it explains things quite nicely.</>
+   </row><row>
+   <entry>mode</><>The current reception mode for the audio
+
+                We report MONO because our card is too stupid to know if it is in
+                mono or stereo. 
+   </entry>
+   </row><row>
+   <entry>balance</><>The stereo balance on a scale of 0-65535, 32768 is
+                        middle.</>
+   </row><row>
+   <entry>step</><>The step by which the volume control jumps. This is
+                        used to help make it easy for applications to set 
+                        slider behaviour.</>   
+   </row>
+   </tbody>
+   </tgroup>
+   </table>
+
+   <table frame=all><title>struct video_audio flags</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+                <entry>VIDEO_AUDIO_MUTE</><>The audio is currently muted. We
+                                        could fake this in our driver but we
+                                        choose not to bother.</entry>
+   </row><row>
+                <entry>VIDEO_AUDIO_MUTABLE</><>The input has a mute option</entry>
+   </row><row>
+                <entry>VIDEO_AUDIO_TREBLE</><>The  input has a treble control</entry>
+   </row><row>
+                <entry>VIDEO_AUDIO_BASS</><>The input has a base control</entry>
+   </row>
+   </tbody>
+   </tgroup>
+   </table>
+
+   <table frame=all><title>struct video_audio modes</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+                <entry>VIDEO_SOUND_MONO</><>Mono sound</entry>
+   </row><row>
+                <entry>VIDEO_SOUND_STEREO</><>Stereo sound</entry>
+   </row><row>
+                <entry>VIDEO_SOUND_LANG1</><>Alternative language 1 (TV specific)</entry>
+   </row><row>
+                <entry>VIDEO_SOUND_LANG2</><>Alternative language 2 (TV specific)</entry>
+   </row>
+   </tbody>
+   </tgroup>
+   </table>
+  <para>
+        Having filled in the structure we copy it back to user space.
+  </para>
+  <para>
+        The VIDIOCSAUDIO ioctl allows the user to set the audio parameters in the
+        video_audio stucture. The driver does its best to honour the request.
+  </para>
+  <programlisting>
+
+                case VIDIOCSAUDIO:
+                {
+                        struct video_audio v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.audio)
+                                return -EINVAL;
+                        current_volume = v/16384;
+                        hardware_set_volume(current_volume);
+                        return 0;
+                }
+
+  </programlisting>
+  <para>
+        In our case there is very little that the user can set. The volume is
+        basically the limit. Note that we could pretend to have a mute feature
+        by rewriting this to 
+  </para>
+  <programlisting>
+
+                case VIDIOCSAUDIO:
+                {
+                        struct video_audio v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.audio)
+                                return -EINVAL;
+                        current_volume = v/16384;
+                        if(v.flags&amp;VIDEO_AUDIO_MUTE)
+                                hardware_set_volume(0);
+                        else
+                                hardware_set_volume(current_volume);
+                        current_muted = v.flags &amp; 
+                                              VIDEO_AUDIO_MUTE;
+                        return 0;
+                }
+
+  </programlisting>
+  <para>
+        This with the corresponding changes to the VIDIOCGAUDIO code to report the
+        state of the mute flag we save and to report the card has a mute function,
+        will allow applications to use a mute facility with this card. It is
+        questionable whether this is a good idea however. User applications can already
+        fake this themselves and kernel space is precious.
+  </para>
+  <para>
+        We now have a working radio ioctl handler. So we just wrap up the function
+  </para>
+  <programlisting>
+
+
+        }
+        return -ENOIOCTLCMD;
+}
+
+  </programlisting>
+  <para>
+        and pass the Video4Linux layer back an error so that it knows we did not
+        understand the request we got passed.
+  </para>
+  </sect1>
+  <sect1 id="modradio">
+  <title>Module Wrapper</title>
+  <para>
+        Finally we add in the usual module wrapping and the driver is done.
+  </para>
+  <programlisting>
+
+#ifndef MODULE
+
+static int io = 0x300;
+
+#else
+
+static int io = -1;
+
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("A driver for an imaginary radio card.");
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O address of the card.");
+
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+        if(io==-1)
+        {
+                printk(KERN_ERR 
+         "You must set an I/O address with io=0x???\n");
+                return -EINVAL;
+        }
+        return myradio_init(NULL);
+}
+
+void cleanup_module(void)
+{
+        video_unregister_device(&amp;my_radio);
+        release_region(io, MY_IO_SIZE);
+}
+
+#endif
+
+  </programlisting>
+  <para>
+        In this example we set the IO base by default if the driver is compiled into
+        the kernel where you cannot pass a parameter. For the module we require the
+        user sets the parameter. We set io to a nonsense port (-1) so that we can
+        tell if the user supplied an io parameter or not.
+  </para>
+  <para>
+        We use MODULE_ defines to give an author for the card driver and a
+        description. We also use them to declare that io is an integer and it is the
+        address of the card.
+  </para>
+  <para>
+        The clean-up routine unregisters the video_device we registered, and frees
+        up the I/O space. Note that the unregister takes the actual video_device
+        structure as its argument. Unlike the file operations structure which can be
+        shared by all instances of a device a video_device structure as an actual
+        instance of the device. If you are registering multiple radio devices you
+        need to fill in one structure per device (most likely by setting up a
+        template and copying it to each of the actual device structures).
+  </para>
+  </sect1>
+  </chapter>
+  <chapter>
+        <title>Video Capture Devices</title>
+  <sect1 id="introvid">
+  <title>Video Capture Device Types</title>
+  <para>
+        The video capture devices share the same interfaces as radio devices. In
+        order to explain the video capture interface I will use the example of a
+        camera that has no tuners or audio input. This keeps the example relatively
+        clean. To get both combine the two driver examples.
+  </para>
+  <para>
+        Video capture devices divide into four categories. A little technology
+        backgrounder. Full motion video even at television resolution (which is
+        actually fairly low) is pretty resource-intensive. You are continually
+        passing megabytes of data every second from the capture card to the display. 
+        several alternative approaches have emerged because copying this through the 
+        processor and the user program is a particularly bad idea .
+  </para>
+  <para>
+        The first is to add the television image onto the video output directly.
+        This is also how some 3D cards work. These basic cards can generally drop the
+        video into any chosen rectangle of the display. Cards like this, which
+        include most mpeg1 cards that used the feature connector,  aren't very
+        friendly in a windowing environment. They don't understand windows or
+        clipping. The video window is always on the top of the display.
+  </para>
+  <para>
+        Chroma keying is a technique used by cards to get around this. It is an old
+        television mixing trick where you mark all the areas you wish to replace
+        with a single clear colour that isn't used in the image - TV people use an
+        incredibly bright blue while computing people often use a paticularly
+        virulent purple. Bright blue occurs on the desktop. Anyone with virulent
+        purple windows has another problem besides their TV overlay.
+  </para>
+  <para>
+        The third approach is to copy the data from the capture card to the video
+        card, but to do it directly across the PCI bus. This relieves the processor
+        from doing the work but does require some smartness on the part of the video
+        capture chip, as well as a suitable video card. Programming this kind of
+        card and more so debugging it can be extremely tricky. There are some quite
+        complicated interactions with the display and you may also have to cope with
+        various chipset bugs that show up when PCI cards start talking to each
+        other. 
+  </para>
+  <para>
+        To keep our example fairly simple we will assume a card that supports
+        overlaying a flat rectangular image onto the frame buffer output, and which
+        can also capture stuff into processor memory.
+  </para>
+  </sect1>
+  <sect1 id="regvid">
+  <title>Registering Video Capture Devices</title>
+  <para>
+        This time we need to add more functions for our camera device.
+  </para>
+  <programlisting>
+static struct video_device my_camera
+{
+        "My Camera",
+        VID_TYPE_OVERLAY|VID_TYPE_SCALES|\
+        VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY,
+        VID_HARDWARE_MYCAMERA,
+        camera_open.
+        camera_close,
+        camera_read,      /* no read */
+        NULL,             /* no write */
+        camera_poll,      /* no poll */
+        camera_ioctl,
+        NULL,             /* no special init function */
+        NULL              /* no private data */
+};
+  </programlisting>
+  <para>
+        We need a read() function which is used for capturing data from
+        the card, and we need a poll function so that a driver can wait for the next
+        frame to be captured.
+  </para>
+  <para>
+        We use the extra video capability flags that did not apply to the
+        radio interface. The video related flags are
+  </para>
+   <table frame=all><title>Capture Capabilities</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+<entry>VID_TYPE_CAPTURE</><>We support image capture</>
+</row><row>
+<entry>VID_TYPE_TELETEXT</><>A teletext capture device (vbi{n])</>
+</row><row>
+<entry>VID_TYPE_OVERLAY</><>The image can be directly overlaid onto the
+                                frame buffer</>
+</row><row>
+<entry>VID_TYPE_CHROMAKEY</><>Chromakey can be used to select which parts
+                                of the image to display</>
+</row><row>
+<entry>VID_TYPE_CLIPPING</><>It is possible to give the board a list of
+                                rectangles to draw around. </>
+</row><row>
+<entry>VID_TYPE_FRAMERAM</><>The video capture goes into the video memory
+                                and actually changes it. Applications need
+                                to know this so they can clean up after the
+                                card</>
+</row><row>
+<entry>VID_TYPE_SCALES</><>The image can be scaled to various sizes,
+                                rather than being a single fixed size.</>
+</row><row>
+<entry>VID_TYPE_MONOCHROME</><>The capture will be monochrome. This isn't a 
+                                complete answer to the question since a mono
+                                camera on a colour capture card will still
+                                produce mono output.</>
+</row><row>
+<entry>VID_TYPE_SUBCAPTURE</><>The card allows only part of its field of
+                                view to be captured. This enables
+                                applications to avoid copying all of a large
+                                image into memory when only some section is
+                                relevant.</>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        We set VID_TYPE_CAPTURE so that we are seen as a capture card,
+        VID_TYPE_CHROMAKEY so the application knows it is time to draw in virulent
+        purple, and VID_TYPE_SCALES because we can be resized.
+  </para>
+  <para>
+        Our setup is fairly similar. This time we also want an interrupt line
+        for the 'frame captured' signal. Not all cards have this so some of them
+        cannot handle poll().
+  </para>
+  <programlisting>
+
+
+static int io = 0x320;
+static int irq = 11;
+
+int __init mycamera_init(struct video_init *v)
+{
+        if(check_region(io, MY_IO_SIZE))
+        {
+                printk(KERN_ERR 
+                      "mycamera: port 0x%03X is in use.\n", io);
+                return -EBUSY;
+        }
+
+        if(video_device_register(&amp;my_camera, 
+            VFL_TYPE_GRABBER)==-1)
+                return -EINVAL;
+        request_region(io, MY_IO_SIZE, "mycamera");
+        return 0;
+}
+
+  </programlisting>
+  <para>
+        This is little changed from the needs of the radio card. We specify
+        VFL_TYPE_GRABBER this time as we want to be allocated a /dev/video name.
+  </para>
+  </sect1>
+  <sect1 id="opvid">
+  <title>Opening And Closing The Capture Device</title>
+  <programlisting>
+
+
+static int users = 0;
+
+static int camera_open(stuct video_device *dev, int flags)
+{
+        if(users)
+                return -EBUSY;
+        if(request_irq(irq, camera_irq, 0, "camera", dev)&lt;0)
+                return -EBUSY;
+        users++;
+        MOD_INC_USE_COUNT;
+        return 0;
+}
+
+
+static int camera_close(struct video_device *dev)
+{
+        users--;
+        free_irq(irq, dev);
+        MOD_DEC_USE_COUNT;
+}
+  </programlisting>
+  <para>
+        The open and close routines are also quite similar. The only real change is
+        that we now request an interrupt for the camera device interrupt line. If we
+        cannot get the interrupt we report EBUSY to the application and give up.
+  </para>
+  </sect1>
+  <sect1 id="irqvid">
+  <title>Interrupt Handling</title>
+  <para>
+        Our example handler is for an ISA bus device. If it was PCI you would be
+        able to share the interrupt and would have set SA_SHIRQ to indicate a 
+        shared IRQ. We pass the device pointer as the interrupt routine argument. We
+        don't need to since we only support one card but doing this will make it
+        easier to upgrade the driver for multiple devices in the future.
+  </para>
+  <para>
+        Our interrupt routine needs to do little if we assume the card can simply
+        queue one frame to be read after it captures it. 
+  </para>
+  <programlisting>
+
+
+static struct wait_queue *capture_wait;
+static int capture_ready = 0;
+
+static void camera_irq(int irq, void *dev_id, 
+                          struct pt_regs *regs)
+{
+        capture_ready=1;
+        wake_up_interruptible(&amp;capture_wait);
+}
+  </programlisting>
+  <para>
+        The interrupt handler is nice and simple for this card as we are assuming
+        the card is buffering the frame for us. This means we have little to do but
+        wake up        anybody interested. We also set a capture_ready flag, as we may
+        capture a frame before an application needs it. In this case we need to know
+        that a frame is ready. If we had to collect the frame on the interrupt life
+        would be more complex.
+  </para>
+  <para>
+        The two new routines we need to supply are camera_read which returns a
+        frame, and camera_poll which waits for a frame to become ready.
+  </para>
+  <programlisting>
+
+
+static int camera_poll(struct video_device *dev, 
+	struct file *file, struct poll_table *wait)
+{
+        poll_wait(file, &amp;capture_wait, wait);
+        if(capture_read)
+                return POLLIN|POLLRDNORM;
+        return 0;
+}
+
+  </programlisting>
+  <para>
+        Our wait queue for polling is the capture_wait queue. This will cause the
+        task to be woken up by our camera_irq routine. We check capture_read to see
+        if there is an image present and if so report that it is readable.
+  </para>
+  </sect1>
+  <sect1 id="rdvid">
+  <title>Reading The Video Image</title>
+  <programlisting>
+
+
+static long camera_read(struct video_device *dev, char *buf,
+                                unsigned long count)
+{
+        struct wait_queue wait = { current, NULL };
+        u8 *ptr;
+        int len;
+        int i;
+
+        add_wait_queue(&amp;capture_wait, &amp;wait);
+
+        while(!capture_ready)
+        {
+                if(file->flags&amp;O_NDELAY)
+                {
+                        remove_wait_queue(&amp;capture_wait, &amp;wait);
+                        current->state = TASK_RUNNING;
+                        return -EWOULDBLOCK;
+                }
+                if(signal_pending(current))
+                {
+                        remove_wait_queue(&amp;capture_wait, &amp;wait);
+                        current->state = TASK_RUNNING;
+                        return -ERESTARTSYS;
+                }
+                schedule();
+                current->state = TASK_INTERRUPTIBLE;
+        }
+        remove_wait_queue(&amp;capture_wait, &amp;wait);
+        current->state = TASK_RUNNING;
+
+  </programlisting>
+  <para>
+        The first thing we have to do is to ensure that the application waits until
+        the next frame is ready. The code here is almost identical to the mouse code
+        we used earlier in this chapter. It is one of the common building blocks of
+        Linux device driver code and probably one which you will find occurs in any
+        drivers you write.
+  </para>
+  <para>
+        We wait for a frame to be ready, or for a signal to interrupt our waiting. If a
+        signal occurs we need to return from the system call so that the signal can
+        be sent to the application itself. We also check to see if the user actually
+        wanted to avoid waiting - ie  if they are using non-blocking I/O and have other things 
+        to get on with.
+  </para>
+  <para>
+        Next we copy the data from the card to the user application. This is rarely
+        as easy as our example makes out. We will add capture_w, and capture_h here
+        to hold the width and height of the captured image. We assume the card only
+        supports 24bit RGB for now.
+  </para>
+  <programlisting>
+
+
+
+        capture_ready = 0;
+
+        ptr=(u8 *)buf;
+        len = capture_w * 3 * capture_h; /* 24bit RGB */
+
+        if(len>count)
+                len=count;  /* Doesn't all fit */
+
+        for(i=0; i&lt;len; i++)
+        {
+                put_user(inb(io+IMAGE_DATA), ptr);
+                ptr++;
+        }
+
+        hardware_restart_capture();
+                
+        return i;
+}
+
+  </programlisting>
+  <para>
+        For a real hardware device you would try to avoid the loop with put_user().
+        Each call to put_user() has a time overhead checking whether the accesses to user
+        space are allowed. It would be better to read a line into a temporary buffer
+        then copy this to user space in one go.
+  </para>
+  <para>
+        Having captured the image and put it into user space we can kick the card to
+        get the next frame acquired.
+  </para>
+  </sect1>
+  <sect1 id="iocvid">
+  <title>Video Ioctl Handling</title>
+  <para>
+        As with the radio driver the major control interface is via the ioctl()
+        function. Video capture devices support the same tuner calls as a radio
+        device and also support additional calls to control how the video functions
+        are handled. In this simple example the card has no tuners to avoid making
+        the code complex. 
+  </para>
+  <programlisting>
+
+
+
+static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+        switch(cmd)
+        {
+                case VIDIOCGCAP:
+                {
+                        struct video_capability v;
+                        v.type = VID_TYPE_CAPTURE|\
+                                 VID_TYPE_CHROMAKEY|\
+                                 VID_TYPE_SCALES|\
+                                 VID_TYPE_OVERLAY;
+                        v.channels = 1;
+                        v.audios = 0;
+                        v.maxwidth = 640;
+                        v.minwidth = 16;
+                        v.maxheight = 480;
+                        v.minheight = 16;
+                        strcpy(v.name, "My Camera");
+                        if(copy_to_user(arg, &amp;v, sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
+
+
+  </programlisting>
+  <para>
+        The first ioctl we must support and which all video capture and radio
+        devices are required to support is VIDIOCGCAP. This behaves exactly the same
+        as with a radio device. This time, however, we report the extra capabilities
+        we outlined earlier on when defining our video_dev structure.
+  </para>
+  <para>
+        We now set the video flags saying that we support overlay, capture,
+        scaling and chromakey. We also report size limits - our smallest image is
+        16x16 pixels, our largest is 640x480. 
+  </para>
+  <para>
+        To keep things simple we report no audio and no tuning capabilities at all.
+  </para>
+  <programlisting>        
+
+                case VIDIOCGCHAN:
+                {
+                        struct video_channel v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.channel != 0)
+                                return -EINVAL;
+                        v.flags = 0;
+                        v.tuners = 0;
+                        v.type = VIDEO_TYPE_CAMERA;
+                        v.norm = VIDEO_MODE_AUTO;
+                        strcpy(v.name, "Camera Input");break;
+                        if(copy_to_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
+
+
+  </programlisting>
+  <para>
+        This follows what is very much the standard way an ioctl handler looks
+        in Linux. We copy the data into a kernel space variable and we check that the
+        request is valid (in this case that the input is 0). Finally we copy the
+        camera info back to the user.
+  </para>
+  <para>
+        The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is
+        inputs to the video card). Our example card has a single camera input. The
+        fields in the structure are
+  </para>
+   <table frame=all><title>struct video_channel fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+
+   <entry>channel</><>The channel number we are selecting</entry>
+   </row><row>
+   <entry>name</><>The name for this channel. This is intended
+                   to describe the port to the user.
+                   Appropriate names are therefore things like
+                   "Camera" "SCART input"</entry>
+   </row><row>
+   <entry>flags</><>Channel properties</entry>
+   </row><row>
+   <entry>type</><>Input type</entry>
+   </row><row>
+   <entry>norm</><>The current television encoding being used
+                   if relevant for this channel.
+    </entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <table frame=all><title>struct video_channel flags</title>
+    <tgroup cols=2 align=left>
+    <tbody>
+    <row>
+        <entry>VIDEO_VC_TUNER</><>Channel has a tuner.</entry>
+   </row><row>
+        <entry>VIDEO_VC_AUDIO</><>Channel has audio.</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <table frame=all><title>struct video_channel types</title>
+    <tgroup cols=2 align=left>
+    <tbody>
+    <row>
+        <entry>VIDEO_TYPE_TV</><>Television input.</entry>
+   </row><row>
+        <entry>VIDEO_TYPE_CAMERA</><>Fixed camera input.</entry>
+   </row><row>
+	<entry>0</><>Type is unknown.</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <table frame=all><title>struct video_channel norms</title>
+    <tgroup cols=2 align=left>
+    <tbody>
+    <row>
+        <entry>VIDEO_MODE_PAL</><>PAL encoded Television</entry>
+   </row><row>
+        <entry>VIDEO_MODE_NTSC</><>NTSC (US) encoded Television</entry>
+   </row><row>
+        <entry>VIDEO_MODE_SECAM</><>SECAM (French) Televison </entry>
+   </row><row>
+        <entry>VIDEO_MODE_AUTO</><>Automatic switching, or format does not
+                                matter</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <para>
+        The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to
+        request the norm is changed - for exaple to switch between a PAL or an NTSC
+        format camera.
+  </para>
+  <programlisting>
+
+
+                case VIDIOCSCHAN:
+                {
+                        struct video_channel v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.channel != 0)
+                                return -EINVAL;
+                        if(v.norm != VIDEO_MODE_AUTO)
+                                return -EINVAL;
+                        return 0;
+                }
+
+
+  </programlisting>
+  <para>
+        The implementation of this call in our driver is remarkably easy. Because we
+        are assuming fixed format hardware we need only check that the user has not
+        tried to change anything. 
+  </para>
+  <para>
+        The user also needs to be able to configure and adjust the picture they are
+        seeing. This is much like adjusting a television set. A user application
+        also needs to know the palette being used so that it knows how to display
+        the image that has been captured. The VIDIOCGPICT and VIDIOCSPICT ioctl
+        calls provide this information.
+  </para>
+  <programlisting>
+
+
+                case VIDIOCGPICT
+                {
+                        struct video_picture v;
+                        v.brightness = hardware_brightness();
+                        v.hue = hardware_hue();
+                        v.colour = hardware_saturation();
+                        v.contrast = hardware_brightness();
+                        /* Not settable */
+                        v.whiteness = 32768;
+                        v.depth = 24;           /* 24bit */
+                        v.palette = VIDEO_PALETTE_RGB24;
+                        if(copy_to_user(&amp;v, arg, 
+                             sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
+
+
+  </programlisting>
+  <para>
+        The brightness, hue, color, and contrast provide the picture controls that
+        are akin to a conventional television. Whiteness provides additional
+        control for greyscale images. All of these values are scaled between 0-65535
+        and have 32768 as the mid point setting. The scaling means that applications
+        do not have to worry about the capability range of the hardware but can let
+        it make a best effort attempt.
+  </para>
+  <para>
+        Our depth is 24, as this is in bits. We will be returing RGB24 format. This
+        has one byte of red, then one of green, then one of blue. This then repeats
+        for every other pixel in the image. The other common formats the interface 
+        defines are
+  </para>
+   <table frame=all><title>Framebuffer Encodings</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+   <entry>GREY</><>Linear greyscale. This is for simple cameras and the
+                        like</>
+   </row><row>
+   <entry>RGB565</><>The top 5 bits hold 32 red levels, the next six bits 
+                        hold green and the low 5 bits hold blue. </>
+   </row><row>
+   <entry>RGB555</><>The top bit is clear. The red green and blue levels
+                        each occupy five bits.</>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        Additional modes are support for YUV capture formats. These are common for
+        TV and video conferencing applications.
+  </para>
+  <para>
+        The VIDIOCSPICT ioctl allows a user to set some of the picture parameters.
+        Exactly which ones are supported depends heavily on the card itself. It is
+        possible to support many modes and effects in software. In general doing
+        this in the kernel is a bad idea. Video capture is a performance-sensitive
+        application and the programs can often do better if they aren't being
+        'helped' by an overkeen driver writer. Thus for our device we will report
+        RGB24 only and refuse to allow a change.
+  </para>
+  <programlisting>
+
+
+                case VIDIOCSPICT:
+                {
+                        struct video_picture v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.depth!=24 || 
+                           v.palette != VIDEO_PALETTE_RGB24)
+                                return -EINVAL;
+                        set_hardware_brightness(v.brightness);
+                        set_hardware_hue(v.hue);
+                        set_hardware_saturation(v.colour);
+                        set_hardware_brightness(v.contrast);
+                        return 0;
+                }
+
+
+  </programlisting>
+  <para>
+        We check the user has not tried to change the palette or the depth. We do
+        not want to carry out some of the changes and then return an error. This may
+        confuse the application which will be assuming no change occurred.
+  </para>
+  <para>
+        In much the same way as you need to be able to set the picture controls to
+        get the right capture images, many cards need to know what they are
+        displaying onto when generating overlay output. In some cases getting this
+        wrong even makes a nasty mess or may crash the computer. For that reason
+        the VIDIOCSBUF ioctl used to set up the frame buffer information may well
+        only be usable by root.
+  </para>
+  <para>
+        We will assume our card is one of the old ISA devices with feature connector
+        and only supports a couple of standard video modes. Very common for older
+        cards although the PCI devices are way smarter than this.
+  </para>
+  <programlisting>
+
+
+static struct video_buffer capture_fb;
+
+                case VIDIOCGFBUF:
+                {
+                        if(copy_to_user(arg, &amp;capture_fb, 
+                             sizeof(capture_fb)))
+                                return -EFAULT;
+                        return 0;
+                        
+                }
+
+
+  </programlisting>
+  <para>
+        We keep the frame buffer information in the format the ioctl uses. This
+        makes it nice and easy to work with in the ioctl calls.
+  </para>
+  <programlisting>
+
+                case VIDIOCSFBUF:
+                {
+                        struct video_buffer v;
+
+                        if(!capable(CAP_SYS_ADMIN))
+                                return -EPERM;
+
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.width!=320 &amp;&amp; v.width!=640)
+                                return -EINVAL;
+                        if(v.height!=200 &amp;&amp; v.height!=240 
+                                &amp;&amp; v.height!=400
+                                &amp;&amp; v.height !=480)
+                                return -EINVAL;
+                        memcpy(&amp;capture_fb, &amp;v, sizeof(v));
+                        hardware_set_fb(&amp;v);
+                        return 0;
+                }
+
+
+
+  </programlisting>
+  <para>
+        The capable() function checks a user has the required capability. The Linux
+        operating system has a set of about 30 capabilities indicating privileged
+        access to services. The default set up gives the superuser (uid 0) all of
+        them and nobody else has any.
+  </para>
+  <para>
+        We check that the user has the SYS_ADMIN capability, that is they are
+        allowed to operate as the machine administrator. We don't want anyone but
+        the administrator making a mess of the display.
+  </para>
+  <para>
+        Next we check for standard PC video modes (320 or 640 wide with either
+        EGA or VGA depths). If the mode is not a standard video mode we reject it as
+        not supported by our card. If the mode is acceptable we save it so that
+        VIDIOCFBUF will give the right answer next time it is called.  The
+        hardware_set_fb() function is some undescribed card specific function to
+        program the card for the desired mode.
+  </para>
+  <para>
+        Before the driver can display an overlay window it needs to know where the
+        window should be placed, and also how large it should be. If the card
+        supports clipping it needs to know which rectangles to omit from the
+        display. The video_window structure is used to describe the way the image 
+        should be displayed. 
+   </para>
+   <table frame=all><title>struct video_window fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+        <entry>width</><>The width in pixels of the desired image. The card
+                        may use a smaller size if this size is not available</>
+	</row><row>
+        <entry>height</><>The height of the image. The card may use a smaller
+                        size if this size is not available.</>
+	</row><row>
+        <entry>x</><>   The X position of the top left of the window. This
+                        is in pixels relative to the left hand edge of the
+                        picture. Not all cards can display images aligned on
+                        any pixel boundary. If the position is unsuitable
+                        the card adjusts the image right and reduces the
+                        width.</>
+	</row><row>
+        <entry>y</><>   The Y position of the top left of the window. This
+                        is counted in pixels relative to the top edge of the
+                        picture. As with the width if the card cannot
+                        display  starting on this line it will adjust the
+                        values.</>
+	</row><row>
+        <entry>chromakey</><>The colour (expressed in RGB32 format) for the
+                        chromakey colour if chroma keying is being used. </>
+	</row><row>
+        <entry>clips</><>An array of rectangles that must not be drawn
+			over.</>
+	</row><row>
+        <entry>clipcount</><>The number of clips in this array.</>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <para>
+        Each clip is a struct video_clip which has the following fields
+   </para>
+   <table frame=all><title>video_clip fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+        <entry>x, y</><>Co-ordinates relative to the display</>
+	</row><row>
+        <entry>width, height</><>Width and height in pixels</>
+	</row><row>
+        <entry>next</><>A spare field for the application to use</>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <para>
+        The driver is required to ensure it always draws in the area requested or a        smaller area, and that it never draws in any of the areas that are clipped.
+        This may well mean it has to leave alone. small areas the application wished to be
+        drawn.
+  </para>
+  <para>
+        Our example card uses chromakey so does not have to address most of the
+        clipping.  We will add a video_window structure to our global variables to
+        remember our parameters, as we did with the frame buffer.
+  </para>
+  <programlisting>
+
+
+                case VIDIOCGWIN:
+                {
+                        if(copy_to_user(arg, &amp;capture_win, 
+                            sizeof(capture_win)))
+                                return -EFAULT;
+                        return 0;
+                }
+
+
+                case VIDIOCSWIN:
+                {
+                        struct video_window v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.width > 640 || v.height > 480)
+                                return -EINVAL;
+                        if(v.width < 16 || v.height < 16)
+                                return -EINVAL;
+                        hardware_set_key(v.chromakey);
+                        hardware_set_window(v);
+                        memcpy(&amp;capture_win, &amp;v, sizeof(v));
+                        capture_w = v.width;
+                        capture_h = v.height;
+                        return 0;
+                }
+
+
+  </programlisting>
+  <para>
+        Because we are using Chromakey our setup is fairly simple. Mostly we have to
+        check the values are sane and load them into the capture card.
+  </para>
+  <para>
+        With all the setup done we can now turn on the actual capture/overlay. This
+        is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument
+        where 0 is on and 1 is off.
+  </para>
+  <programlisting>
+
+
+                case VIDIOCCAPTURE:
+                {
+                        int v;
+                        if(get_user(v, (int *)arg))
+                                return -EFAULT;
+                        if(v==0)
+                                hardware_capture_off();
+                        else
+                        {
+                                if(capture_fb.width == 0 
+                                    || capture_w == 0)
+                                        return -EINVAL;
+                                hardware_capture_on();
+                        }
+                        return 0;
+                }
+
+
+  </programlisting>
+  <para>
+        We grab the flag from user space and either enable or disable according to
+        its value. There is one small corner case we have to consider here. Suppose
+        that the capture was requested before the video window or the frame buffer
+        had been set up. In those cases there will be unconfigured fields in our
+        card data, as well as unconfigured hardware settings. We check for this case and
+        return an error if the frame buffer or the capture window width is zero.
+  </para>
+  <programlisting>
+
+
+                default:
+                        return -ENOIOCTLCMD;
+        }
+}
+  </programlisting>
+  <para>
+
+        We don't need to support any other ioctls, so if we get this far, it is time
+        to tell the video layer that we don't now what the user is talking about.
+  </para>
+  </sect1>
+  <sect1 id="endvid">
+  <title>Other Functionality</title>
+  <para>
+        The Video4Linux layer supports additional features, including a high
+        performance mmap() based capture mode and capturing part of the image. 
+        These features are out of the scope of the book.  You should however have enough 
+        example code to implement most simple video4linux devices for radio and TV
+        cards.
+  </para>
+  </sect1>
+  </chapter>
+  <chapter id="bugs">
+     <title>Known Bugs And Assumptions</title>
+  <para>
+  <variablelist>
+    <varlistentry><term>Multiple Opens</term>
+    <listitem>
+    <para>
+        The driver assumes multiple opens should not be allowed. A driver
+        can work around this but not cleanly.
+    </para>
+    </listitem></varlistentry>
+
+    <varlistentry><term>API Deficiences</term>
+    <listitem>
+    <para>
+        The existing API poorly reflects compression capable devices. There
+        are plans afoot to merge V4L, V4L2 and some other ideas into a
+        better interface.
+    </para>
+    </listitem></varlistentry>
+  </variablelist>
+
+  </para>
+  </chapter>
+
+  <chapter id="pubfunctions">
+     <title>Public Functions Provided</title>
+!Edrivers/char/videodev.c
+  </chapter>
+
+</book>
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/wanbook.tmpl linux/Documentation/DocBook/wanbook.tmpl
--- v2.3.51/linux/Documentation/DocBook/wanbook.tmpl	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/wanbook.tmpl	Sun Mar 12 19:39:47 2000
@@ -0,0 +1,97 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="WANGuide">
+ <bookinfo>
+  <title>Synchronous PPP and Cisco HDLC Programming Guide</title>
+  
+  <authorgroup>
+   <author>
+    <firstname>Alan</firstname>
+    <surname>Cox</surname>
+    <affiliation>
+     <address>
+      <email>alan@redhat.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>2000</year>
+   <holder>Alan Cox</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation 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.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+  <chapter id="intro">
+      <title>Introduction</title>
+  <para>
+	The syncppp drivers in Linux provide a fairly complete 
+	implementation of Cisco HDLC and a minimal implementation of
+	PPP. The longer term goal is to switch the PPP layer to the
+	generic PPP interface that is new in Linux 2.3.x. The API should
+	remain unchanged when this is done, but support will then be
+	available for IPX, compression and other PPP features
+  </para>
+  </chapter>
+  <chapter id="bugs">
+     <title>Known Bugs And Assumptions</title>
+  <para>
+  <variablelist>
+    <varlistentry><term>PPP is minimal</term>
+    <listitem>
+    <para>
+	The current PPP implementation is very basic, although sufficient
+	for most wan usages.
+    </para>
+    </listitem></varlistentry>
+
+    <varlistentry><term>Cisco HDLC Quirks</term>
+    <listitem>
+    <para>
+	Currently we do not end all packets with the correct Cisco multicast
+	or unicast flags. Nothing appears to mind too much but this should
+	be corrected.
+    </para>
+    </listitem></varlistentry>
+  </variablelist>
+	
+  </para>
+  </chapter>
+
+  <chapter id="pubfunctions">
+     <title>Public Functions Provided</title>
+!Edrivers/net/wan/syncppp.c
+  </chapter>
+
+</book>
diff -u --recursive --new-file v2.3.51/linux/Documentation/DocBook/z8530book.tmpl linux/Documentation/DocBook/z8530book.tmpl
--- v2.3.51/linux/Documentation/DocBook/z8530book.tmpl	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/DocBook/z8530book.tmpl	Sun Mar 12 19:39:47 2000
@@ -0,0 +1,383 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="Z85230Guide">
+ <bookinfo>
+  <title>Z8530 Programming Guide</title>
+  
+  <authorgroup>
+   <author>
+    <firstname>Alan</firstname>
+    <surname>Cox</surname>
+    <affiliation>
+     <address>
+      <email>alan@redhat.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>2000</year>
+   <holder>Alan Cox</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation 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.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+  <chapter id="intro">
+      <title>Introduction</title>
+  <para>
+	The Z85x30 family synchronous/asynchronous controller chips are
+	used on a larg number of cheap network interface cards. The
+	kernel provides a core interface layer that is designed to make
+	it easy to provide WAN services using this chip.
+  </para>
+  <para>
+	The current driver only support synchronous operation. Merging the
+	asynchronous driver support into this code to allow any Z85x30
+	device to be used as both a tty interface and as a synchronous 
+	controller is a project for Linux post the 2.4 release
+  </para>
+  <para>
+	The support code handles most common card configurations and
+	supports running both Cisco HDLC and Synchronous PPP. With extra
+	glue the frame relay and X.25 protocols can also be used with this
+	driver.
+  </para>
+  </chapter>
+  
+  <chapter>
+ 	<title>Driver Modes</title>
+  <para>
+	The Z85230 driver layer can drive Z8530, Z85C30 and Z85230 devices
+	in three different modes. Each mode can be applied to an individual
+	channel on the chip (each chip has two channels).
+  </para>
+  <para>
+	The PIO synchronous mode supports the most common Z8530 wiring. Here
+	the chip is interface to the I/O and interrupt facilities of the
+	host machine but not to the DMA subsystem. When running PIO the
+	Z8530 has extremely tight timing requirements. Doing high speeds,
+	even with a Z85230 will be tricky. Typically you should expect to
+	achieve at best 9600 baud with a Z8C530 and 64Kbits with a Z85230.
+  </para>
+  <para>
+	The DMA mode supports the chip when it is configured to use dual DMA
+	channels on an ISA bus. The better cards tend to support this mode
+	of operation for a single channel. With DMA running the Z85230 tops
+	out when it starts to hit ISA DMA constraints at about 512Kbits. It
+	is worth noting here that many PC machines hang or crash when the
+	chip is driven fast enough to hold the ISA bus solid.
+  </para>
+  <para>
+	Transmit DMA mode uses a single DMA channel. The DMA channel is used
+	for transmission as the transmit FIFO is smaller than the receive
+	FIFO. it gives better performance than pure PIO mode but is nowhere
+	near as ideal as pure DMA mode. 
+  </para>
+  </chapter>
+
+  <chapter>
+ 	<title>Using the Z85230 driver</title>
+  <para>
+	The Z85230 driver provides the back end interface to your board. To
+	configure a Z8530 interface you need to detect the board and to 
+	identify its ports and interrupt resources. It is also your problem
+	to verify the resources are available.
+  </para>
+  <para>
+	Having identified the chip you need to fill in a struct z8530_dev,
+	which describes each chip. This object must exist until you finally
+	shutdown the board. Firstly zero the active field. This ensures 
+	nothing goes off without you intending it. The irq field should
+	be set to the interrupt number of the chip. (Each chip has a single
+	interrupt source rather than each channel). You are responsible
+	for allocating the interrupt line. The interrupt handler should be
+	set to <function>z8530_interrupt</function>. The device id should
+	be set to the z8530_dev structure pointer. Whether the interrupt can
+	be shared or not is board dependant, and up to you to initialise.
+  </para>
+  <para>
+	The structure holds two channel structures. 
+	Initialise chanA.ctrlio and chanA.dataio with the address of the
+	control and data ports. You can or this with Z8530_PORT_SLEEP to
+	indicate your interface needs the 5uS delay for chip settling done
+	in software. The PORT_SLEEP option is architecture specific. Other
+	flags may become available on future platforms, eg for MMIO.
+	Initialise the chanA.irqs to &amp;z8530_nop to start the chip up
+	as disabled and discarding interrupt events. This ensures that
+	stray interrupts will be mopped up and not hang the bus. Set
+	chanA.dev to point to the device structure itself. The
+	private and name field you may use as you wish. The private field
+	is unused by the Z85230 layer. The name is used for error reporting
+	and it may thus make sense to make it match the network name.
+  </para>
+  <para>
+	Repeat the same operation with the B channel if your chip has
+	both channels wired to something useful. This isnt always the
+	case. If it is not wired then the I/O values do not matter, but
+	you must initialise chanB.dev.
+  </para>
+  <para>
+	If your board has DMA facilities then initialise the txdma and
+	rxdma fields for the relevant channels. You must also allocate the
+	ISA DMA channels and do any neccessary board level initialisation
+	to configure them. The low level driver will do the Z8530 and
+	DMA controller programming but not board specific magic.
+  </para>
+  <para>
+	Having intialised the device you can then call
+	<function>z8530_init</function>. This will probe the chip and 
+	reset it into a known state. An identification sequence is then
+	run to identify the chip type. If the checks fail to pass the
+	function returns a non zero error code. Typically this indicates
+	that the port given is not valid. After this call the
+	type field of the z8530_dev structure is initialised to either
+	Z8530, Z85C30 or Z85230 according to the chip found.
+  </para>
+  <para>
+	Once you have called z8530_init you can also make use of the utility
+	function <function>z8530_describe</function>. This provides a 
+	consistant reporting format for the Z8530 devices, and allows all
+	the drivers to provide consistent reporting.
+  </para>
+  </chapter>
+
+  <chapter>
+ 	<title>Attaching Network Interfaces</title>
+  <para>
+	If you wish to use the network interface facilities of the driver,
+	then you need to attach a network device to each channel that is
+	present and in use. In addition to use the SyncPPP and Cisco HDLC
+	you need to follow some additional plumbing rules. They may seem 
+	complex but a look at the example hostess_sv11 driver should
+	reassure you.
+  </para>
+  <para>
+	The network device used for each channel should be pointed to by
+	the netdevice field of each channel. The dev-&gt; priv field of the
+	network device points to your private data - you will need to be
+	able to find your ppp device from this. In addition to use the
+	sync ppp layer the private data must start with a void * pointer
+	to the syncppp structures.
+  </para>
+  <para>
+	The way most drivers approach this paticular problem is to
+	create a structure holding the Z8530 device definition and
+	put that and the syncppp pointer into the private field of
+	the network device. The network device fields of the channels
+	then point back to the network devices. The ppp_device can also
+	be put in the private structure conveniently.
+  </para>
+  <para>
+	If you wish to use the synchronous ppp then you need to attach
+	the syncppp layer to the network device. You should do this before
+	you register the network device. The
+	<function>sppp_attach</function> requires that the first void *
+	pointer in your private data is pointing to an empty struct
+	ppp_device. The function fills in the initial data for the
+	ppp/hdlc layer.
+  </para>
+  <para>
+	Before you register your network device you will also need to
+	provide suitable handlers for most of the network device callbacks. 
+	See the network device documentation for more details on this.
+  </para>
+  </chapter>
+
+  <chapter>
+ 	<title>Configuring And Activating The Port</title>
+  <para>
+	The Z85230 driver provides helper functions and tables to load the
+	port registers on the Z8530 chips. When programming the register
+	settings for a channel be aware that the documentation recommends
+	initialisation orders. Strange things happen when these are not
+	followed. 
+  </para>
+  <para>
+	<function>z8530_channel_load</function> takes an array of
+	pairs of initialisation values in an array of u8 type. The first
+	value is the Z8530 register number. Add 16 to indicate the alternate
+	register bank on the later chips. The array is terminated by a 255.
+  </para>
+  <para>
+	The driver provides a pair of public tables. The
+	z8530_hdlc_kilostream table is for the UK 'Kilostream' service and
+	also happens to cover most other end host configurations. The
+	z8530_hdlc_kilostream_85230 table is the same configuration using
+	the enhancements of the 85230 chip. The configuration loaded is
+	standard NRZ encoded synchronous data with HDLC bitstuffing. All
+	of the timing is taken from the other end of the link.
+  </para>
+  <para>
+	When writing your own tables be aware that the driver internally
+	tracks register values. It may need to reload values. You should
+	therefore be sure to set registers 1-7, 9-11, 14 and 15 in all
+	configurations. Where the register settings depend on DMA selection
+	the driver will update the bits itself when you open or close.
+	Loading a new table with the interface open is not recommended.
+  </para>
+  <para>
+	There are three standard configurations supported by the core
+	code. In PIO mode the interface is programmed up to use
+	interrupt driven PIO. This places high demands on the host processor
+	to avoid latency. The driver is written to take account of latency
+	issues but it cannot avoid latencies caused by other drivers,
+	notably IDE in PIO mode. Because the drivers allocate buffers you
+	must also prevent MTU changes while the port is open.
+  </para>
+  <para>
+	Once the port is open it will call the rx_function of each channel
+	whenever a completed packet arrived. This is invoked from
+	interrupt context and passes you the channel and a network	
+	buffer (struct sk_buff) holding the data. The data includes
+	the CRC bytes so most users will want to trim the last two
+	bytes before processing the data. This function is very timing
+	critical. When you wish to simply discard data the support
+	code provides the function <function>z8530_null_rx</function>
+	to discard the data.
+  </para>
+  <para>
+	To active PIO mode sending and receiving the <function>
+	z8530_sync_open</function> is called. This expects to be passed
+	the network device and the channel. Typically this is called from
+	your network device open callback. On a failure a non zero error
+	status is returned. The <function>z8530_sync_close</function> 
+	function shuts down a PIO channel. This must be done before the 
+	channel is opened again	and before the driver shuts down 
+	and unloads.
+  </para>
+  <para>
+	The ideal mode of operation is dual channel DMA mode. Here the
+	kernel driver will configure the board for DMA in both directions.
+	The driver also handles ISA DMA issues such as controller
+	programming and the memory range limit for you. This mode is
+	activated by calling the <function>z8530_sync_dma_open</function>
+	function. On failure a non zero error value is returned.
+	Once this mode is activated it can be shut down by calling the
+	<function>z8530_sync_dma_close</function>. You must call the close
+	function matching the open mode you used.
+  </para>
+  <para>
+	The final supported mode uses a single DMA channel to drive the
+	transmit side. As the Z85C30 has a larger FIFO on the receive
+	channel	this tends to increase the maximum speed a little. 
+	This is activated by calling the <function>z8530_sync_txdma_open
+	</function>. This returns a non zero error code on failure. The
+	<function>z8530_sync_txdma_close</function> function closes down
+	the Z8530 interface from this mode.
+  </para>
+  </chapter>
+
+  <chapter>
+ 	<title>Network Layer Functions</title>
+  <para>
+	The Z8530 layer provides functions to queue packets for
+	transmission. The driver internally buffers the frame currently
+	being transmitted and one further frame (in order to keep back
+	to back transmission running). Any further buffering is up to
+	the caller.
+  </para>
+  <para>
+	The function <function>z8530_queue_xmit</function> takes a network
+	buffer in sk_buff format and queues it for transmission. The
+	caller must provide the entire packet with the exception of the
+	bitstuffing and CRC. This is normally done by the caller via
+	the syncppp interface layer. It returns 0 if the buffer has been 
+        queued and non zero values  for queue full. If the function accepts 
+	the buffer it becomes property of the Z8530 layer and the caller 
+	should not free it. 
+  </para>
+  <para>
+	The function <function>z8530_get_stats</function> returns a pointer
+	to an internally maintained per interface statistics block. This
+	provides most of the interface code needed to implement the network
+	layer get_stats callback.
+  </para>
+  </chapter>
+
+  <chapter>
+     <title>Porting The Z8530 Driver</title>
+  <para>
+	The Z8530 driver is written to be portable. In DMA mode it makes
+	assumptions about the use of ISA DMA. These are probably warranted
+	in most cases as the Z85230 in paticular was designed to glue to PC
+	type machines. The PIO mode makes no real assumptions.
+  </para>
+  <para>
+	Should you need to retarget the Z8530 driver to another architecture
+	the only code that should need changing are the port I/O functions.
+	At the moment these assume PC I/O port accesses. This may not be
+	appropriate for all platforms. Replacing 
+	<function>z8530_read_port</function> and <function>z8530_write_port
+	</function> is intended to be all that is required to port this
+	driver layer.
+  </para>
+  </chapter>
+
+  <chapter id="bugs">
+     <title>Known Bugs And Assumptions</title>
+  <para>
+  <variablelist>
+    <varlistentry><term>Interrupt Locking</term>
+    <listitem>
+    <para>
+	The locking in the driver is done via the global cli/sti lock. This
+	makes for relatively poor SMP performance. Switching this to use a
+	per device spin lock would probably materially improve performance.
+    </para>
+    </listitem></varlistentry>
+
+    <varlistentry><term>Occasional Failures</term>
+    <listitem>
+    <para>
+	We have reports of occasional failures when run for very long
+	periods of time and the driver starts to receive junk frames. At
+	the moment the cause of this is not clear.
+    </para>
+    </listitem></varlistentry>
+  </variablelist>
+	
+  </para>
+  </chapter>
+
+  <chapter id="pubfunctions">
+     <title>Public Functions Provided</title>
+!Edrivers/net/wan/z85230.c
+  </chapter>
+
+  <chapter id="intfunctions">
+     <title>Internal Functions</title>
+!Idrivers/net/wan/z85230.c
+  </chapter>
+
+</book>
diff -u --recursive --new-file v2.3.51/linux/Documentation/networking/comx.txt linux/Documentation/networking/comx.txt
--- v2.3.51/linux/Documentation/networking/comx.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/comx.txt	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,248 @@
+
+		COMX drivers for the 2.2 kernel
+
+Originally written by: Tivadar Szemethy, <tiv@itc.hu>
+Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+
+Last change: 21/06/1999.
+
+INTRODUCTION
+
+This document describes the software drivers and their use for the 
+COMX line of synchronous serial adapters for Linux version 2.2.0 and
+above.
+The cards are produced and sold by ITC-Pro Ltd. Budapest, Hungary
+For further info contact <info@itc.hu> 
+or http://www.itc.hu (mostly in Hungarian).
+The firmware files and software are available from ftp://ftp.itc.hu
+
+Currently, the drivers support the following cards and protocols:
+
+COMX (2x64 kbps intelligent board)
+CMX (1x256 + 1x128 kbps intelligent board)
+HiCOMX (2x2Mbps intelligent board)
+LoCOMX (1x512 kbps passive board)
+MixCOM (1x512 or 2x512kbps passive board with a hardware watchdog an
+	optional BRI interface and optional flashROM (1-32M))
+
+At the moment of writing this document, the (Cisco)-HDLC, LAPB, SyncPPP and
+Frame Relay (DTE, rfc1294 IP encapsulation with partially implemented Q933a 
+LMI) protocols are available as link-level protocol. 
+X.25 support is being worked on.
+
+USAGE
+
+Load the comx.o module and the hardware-specific and protocol-specific 
+modules you'll need into the running kernel using the insmod utility.
+This creates the /proc/comx directory.
+See the example scripts in the 'etc' directory.
+
+/proc INTERFACE INTRO
+
+The COMX driver set has a new type of user interface based on the /proc 
+filesystem which eliminates the need for external user-land software doing 
+IOCTL calls. 
+Each network interface or device (i.e. those ones you configure with 'ifconfig'
+and 'route' etc.) has a corresponding directory under /proc/comx. You can
+dynamically create a new interface by saying 'mkdir /proc/comx/comx0' (or you
+can name it whatever you want up to 8 characters long, comx[n] is just a 
+convention).
+Generally the files contained in these directories are text files, which can
+be viewed by 'cat filename' and you can write a string to such a file by
+saying 'echo _string_ >filename'. This is very similar to the sysctl interface.
+Don't use a text editor to edit these files, always use 'echo' (or 'cat'
+where appropriate).
+When you've created the comx[n] directory, two files are created automagically
+in it: 'boardtype' and 'protocol'. You have to fill in these files correctly
+for your board and protocol you intend to use (see the board and protocol 
+descriptions in this file below or the example scripts in the 'etc' directory).
+After filling in these files, other files will appear in the directory for 
+setting the various hardware- and protocol-related informations (for example
+irq and io addresses, keepalive values etc.) These files are set to default 
+values upon creation, so you don't necessarily have to change all of them.
+
+When you're ready with filling in the files in the comx[n] directory, you can
+configure the corresponding network interface with the standard network 
+configuration utilites. If you're unble to bring the interfaces up, look up
+the various kernel log files on your system, and consult the messages for
+a probable reason.
+
+EXAMPLE
+
+To create the interface 'comx0' which is the first channel of a COMX card:
+
+insmod comx 
+# insmod comx-hw-comx ; insmod comx-proto-hdlc  (these are usually
+autoloaded if you use the kernel module loader)
+
+mkdir /proc/comx/comx0
+echo comx >/proc/comx/comx0/boardtype
+echo 0x360 >/proc/comx/comx0/io		<- jumper-selectable I/O port 
+echo 0x0a >/proc/comx/comx0/irq		<- jumper-selectable IRQ line
+echo 0xd000 >/proc/comx/comx0/memaddr	<- software-configurable memory
+					   address. COMX uses 64 KB, and this
+					   can be: 0xa000, 0xb000, 0xc000, 
+					   0xd000, 0xe000. Avoid conflicts
+					   with other hardware.
+cat </etc/siol1.rom >/proc/comx/comx0/firmware <- the firmware for the card
+echo HDLC >/proc/comx/comx0/protocol	<- the data-link protocol
+echo 10 >/proc/comx/comx0/keepalive	<- the keepalive for the protocol
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255 <-
+					   finally configure it with ifconfig
+Check its status:
+cat /proc/comx/comx0/status
+
+If you want to use the second channel of this board:
+
+mkdir /proc/comx/comx1
+echo comx >/proc/comx/comx1/boardtype
+echo 0x360 >/proc/comx/comx1/io	
+echo 10 >/proc/comx/comx1/irq		
+echo 0xd000 >/proc/comx/comx1/memaddr
+echo 1 >/proc/comx/comx1/channel	<- channels are numbered 
+					   as 0 (default) and 1
+
+Now, check if the driver recognized that you're going to use the other
+channel of the same adapter:
+
+cat /proc/comx/comx0/twin
+comx1
+cat /proc/comx/comx1/twin
+comx0
+
+You don't have to load the firmware twice, if you use both channels of
+an adapter, just write it into the channel 0's /proc firmware file.
+
+Default values: io 0x360 for COMX, 0x320 (HICOMX), irq 10, memaddr 0xd0000
+
+THE LOCOMX HARDWARE DRIVER
+
+The LoCOMX driver doesn't require firmware, and it doesn't use memory either,
+but it uses DMA channels 1 and 3. You can set the clock rate (if enabled by
+jumpers on the board) by writing the kbps value into the file named 'clock'.
+Set it to 'external' (it is the default) if you have external clock source.
+
+(Note: currently the LoCOMX driver does not support the internal clock)
+
+THE COMX, CMX AND HICOMX DRIVERS
+
+On the HICOMX, COMX and CMX, you have to load the firmware (it is different for
+the three cards!). All these adapters can share the same memory
+address (we usually use 0xd0000). On the CMX you can set the internal
+clock rate (if enabled by jumpers on the small adapter boards) by writing
+the kbps value into the 'clock' file. You have to do this before initializing
+the card. If you use both HICOMX and CMX/COMX cards, initialize the HICOMX
+first. The I/O address of the HICOMX board is not configurable by any
+method available to the user: it is hardwired to 0x320, and if you have to 
+change it, consult ITC-Pro Ltd.
+
+THE MIXCOM DRIVER
+
+The MixCOM board doesn't require firmware, the driver communicates with
+it through I/O ports. You can have three of these cards in one machine.
+
+THE HDLC LINE PROTOCOL DRIVER
+
+There's only one configurable parameter with this protocol: the 'keepalive'
+value. You can set this in seconds or set to 'off'. Agree with the administrator
+of your peer router on this setting. The default is 10 (seconds).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo hdlc >/proc/comx/comx0/protocol
+echo 10 >/proc/comx/comx0/keepalive	<- not necessary, 10 is the default
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255
+
+
+THE PPP LINE PROTOCOL DRIVER
+
+To use this driver, you have to have ppp-2.3.4, and have a modified version of
+pppd (this pppd will work as async pppd to, the modifiactions merely relax 
+some restricions in order to be able to use non-async lines too.
+If configured, this driver can use Van Jacobson TCP header compression (you'll
+need the slhc.o module for this).
+Additionaly to use this protocol, enable async ppp in your kernel config, and
+create the comx device special files in /dev. They're character special files
+with major 88, and their names must be the same as their network interface
+counterparts (i.e /dev/comx0 with minor 0 corresponds interface comx0 and so
+on).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo ppp >/proc/comx/comx0/protocol
+ifconfig comx0 up
+pppd comx0 1.2.3.4:5.6.7.8 persist	<- with this option pppd won't exit
+					   when the line goes down
+
+THE LAPB LINE PROTOCOL DRIVER
+
+For this, you'll need to configure LAPB support (See 'LAPB Data Link Driver' in
+'Network options' section) into your kernel (thanks to Jonathan Naylor for his 
+excellent implementation). 
+comxlapb.o provides the following files in the appropriate directory
+(the default values in parens): t1 (5), t2 (1), n2 (20), mode (DTE, STD) and
+window (7). Agree with the administrator of your peer router on these
+settings (most people use defaults, but you have to know if you are DTE or
+DCE).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo lapb >/proc/comx/comx0/protocol
+echo dce >/proc/comx/comx0/mode		<- DCE interface in this example
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255
+
+
+THE FRAME RELAY PROTOCOL DRIVER
+
+You DON'T need any other frame relay related modules from the kernel to use
+COMX-Frame Relay. This protocol is a bit more complicated than the others, 
+because it allows to use 'subinterfaces' or DLCIs within one physical device.
+First you have to create the 'master' device (the actual physical interface)
+as you would do for other protocols. Specify 'frad' as protocol type.
+Now you can bring this interface up by saying 'ifconfig comx0 up' (or whatever
+you've named the interface). Do not assign any IP address to this interface
+and do not set any routes through it.
+Then, set up your DLCIs the following way: create a comx interface for each
+DLCI you intend to use (with mkdir), and write 'dlci' to the 'boardtype' file, 
+and 'ietf-ip' to the 'protocol' file. Currently, the only supported 
+encapsulation type is this (also called as RFC1294/1490 IP encapsulation).
+Write the DLCI number to the 'dlci' file, and write the name of the physical
+COMX device to the file called 'master'. 
+Now you can assign an IP address to this interface and set routes using it.
+See the example file for further info and example config script.
+Notes: this driver implements a DTE interface with partially implemented
+Q933a LMI.
+You can find an extensively commented example in the 'etc' directory.
+
+FURTHER /proc FILES
+
+boardtype:
+Type of the hardware. Valid values are:
+ 'comx', 'hicomx', 'locomx', 'cmx'.
+
+protocol:
+Data-link protocol on this channel. Can be: HDLC, LAPB, PPP, FRAD
+
+status:
+You can read the channel's actual status from the 'status' file, for example
+'cat /proc/comx/comx3/status'.
+
+lineup_delay:
+Interpreted in seconds (default is 1). Used to avoid line jitter: the system
+will consider the line status 'UP' only if it is up for at least this number
+of seconds.
+
+debug: 
+You can set various debug options through this file. Valid options are:
+'comx_events', 'comx_tx', 'comx_rx', 'hw_events', 'hw_tx', 'hw_rx'.
+You can enable a debug options by writing its name prepended by a '+' into
+the debug file, for example 'echo +comx_rx >comx0/debug'. 
+Disabling an option happens similarly, use the '-' prefix 
+(e.g. 'echo -hw_rx >debug').
+Debug results can be read from the debug file, for example: 
+tail -f /proc/comx/comx2/debug
+
+
diff -u --recursive --new-file v2.3.51/linux/Documentation/networking/dmfe.txt linux/Documentation/networking/dmfe.txt
--- v2.3.51/linux/Documentation/networking/dmfe.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/dmfe.txt	Sun Mar 12 19:27:22 2000
@@ -0,0 +1,63 @@
+  dmfe.c: Version 1.28        01/18/2000
+
+        A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. 
+        Copyright (C) 1997  Sten Wang
+
+        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.
+
+
+  A. Compiler command:
+
+     A-1: For normal single processor kernel
+          "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall 
+            -Wstrict-prototypes -O6 -c dmfe.c"
+
+     A-2: For single processor and enable kernel module version function
+          "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -I/usr/src/linux/net/inet 
+            -Wall -Wstrict-prototypes -O6 -c dmfe.c"
+
+     A-3: For multiple processors(SMP) and enable the module version function
+          "gcc -D__SMP__ -DMODULE -DMODVERSIONS -D__KERNEL__ -I/usr/src/linux
+           /net/inet -Wall -Wstrict-prototypes -O6 -c dmfe.c"
+
+
+  B. The following steps teach you how to active DM9102 board:
+
+        1. Used the upper compiler command to compile dmfe.c
+
+        2. Insert dmfe module into kernel
+           "insmod dmfe"        ;;Auto Detection Mode (Suggest)
+           "insmod dmfe mode=0" ;;Force 10M Half Duplex
+           "insmod dmfe mode=1" ;;Force 100M Half Duplex
+           "insmod dmfe mode=4" ;;Force 10M Full Duplex
+           "insmod dmfe mode=5" ;;Force 100M Full Duplex
+
+        3. Config a dm9102 network interface
+           "ifconfig eth0 172.22.3.18"
+                          ^^^^^^^^^^^ Your IP address
+
+        4. Active the IP routing table. For some distributions, it is not
+           necessary. You can type "route" to check.
+
+           "route add default eth0"
+
+
+        5. Well done. Your DM9102 adapter actived now.
+
+
+   C. Object files description:
+        1. dmfe_rh61.o:       	For Redhat 6.1
+
+        If you can make sure your kernel version, you can rename
+        to dmfe.o and directly use it without re-compiling.
+
+
+  Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
diff -u --recursive --new-file v2.3.51/linux/Documentation/sound/ALS linux/Documentation/sound/ALS
--- v2.3.51/linux/Documentation/sound/ALS	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/ALS	Sun Mar 12 19:39:47 2000
@@ -0,0 +1,43 @@
+ALS-007/ALS-100/ALS-200 based sound cards
+=========================================
+
+Support for sound cards based around the Avance Logic
+ALS-007/ALS-100/ALS-200 chip is included.  These chips are a single
+chip PnP sound solution which is mostly hardware compatible with the
+Sound Blaster 16 card, with most differences occurring in the use of
+the mixer registers.  For this reason the ALS code is integrated
+as part of the Sound Blaster 16 driver (adding only 800 bytes to the
+SB16 driver).
+
+To use an ALS sound card under Linux, enable the following options in the
+sound configuration section of the kernel config:
+  - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support
+  - FM synthesizer (YM3812/OPL-3) support 
+Since the ALS-007/100/200 is a PnP card, the sound driver probably should be
+compiled as a module, with the isapnptools used to wake up the sound card.
+Set the "I/O base for SB", "Sound Blaster IRQ" and "Sound Blaster DMA" (8 bit -
+either 0, 1 or 3) to the values used in your particular installation (they
+should match the values used to configure the card using isapnp).  The
+ALS-007 does NOT implement 16 bit DMA, so the "Sound Blaster 16 bit DMA"
+should be set to -1.  If you wish to use the external MPU-401 interface on
+the card, "MPU401 I/O base of SB16" and "SB MPU401 IRQ" should be set to
+the appropriate values for your installation.  (Note that the ALS-007
+requires a separate IRQ for the MPU-401, so don't specify -1 here).  (Note
+that the base port of the internal FM synth is fixed at 0x388 on the ALS007; 
+in any case the FM synth location cannot be set in the kernel configuration).
+
+The resulting sound driver will provide the following capabilities:
+  - 8 and 16 bit audio playback
+  - 8 and 16 bit audio recording
+  - Software selection of record source (line in, CD, FM, mic, master)
+  - Record and playback of midi data via the external MPU-401
+  - Playback of midi data using inbuilt FM synthesizer
+  - Control of the ALS-007 mixer via any OSS-compatible mixer programs. 
+    Controls available are Master (L&R), Line in (L&R), CD (L&R), 
+    DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono).
+
+Jonathan Woithe
+jwoithe@physics.adelaide.edu.au
+30 March 1998
+
+Modified 2000-02-26 by Dave Forrest, drf5n@virginia.edu to add ALS100/ALS200
diff -u --recursive --new-file v2.3.51/linux/Documentation/sound/ALS007 linux/Documentation/sound/ALS007
--- v2.3.51/linux/Documentation/sound/ALS007	Fri Jul 10 14:03:35 1998
+++ linux/Documentation/sound/ALS007	Wed Dec 31 16:00:00 1969
@@ -1,40 +0,0 @@
-ALS-007 based sound cards
-=========================
-
-Support for sound cards based around the Avance Logic ALS-007 chip is
-included.  The ALS-007 is a single chip PnP sound solution which is mostly
-hardware compatible with the Sound Blaster 16 card, with most differences
-occurring in the use of the mixer registers.  For this reason the ALS-007 
-code is integrated as part of the Sound Blaster 16 driver (adding only 800
-bytes to the SB16 driver).
-
-To use an ALS-007 sound card under Linux, enable the following options in the
-sound configuration section of the kernel config:
-  - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support
-  - FM synthesizer (YM3812/OPL-3) support 
-Since the ALS-007 is a PnP card, the sound driver probably should be
-compiled as a module, with the isapnptools used to wake up the sound card.
-Set the "I/O base for SB", "Sound Blaster IRQ" and "Sound Blaster DMA" (8 bit -
-either 0, 1 or 3) to the values used in your particular installation (they
-should match the values used to configure the card using isapnp).  The
-ALS-007 does NOT implement 16 bit DMA, so the "Sound Blaster 16 bit DMA"
-should be set to -1.  If you wish to use the external MPU-401 interface on
-the card, "MPU401 I/O base of SB16" and "SB MPU401 IRQ" should be set to
-the appropriate values for your installation.  (Note that the ALS-007
-requires a separate IRQ for the MPU-401, so don't specify -1 here).  (Note
-that the base port of the internal FM synth is fixed at 0x388 on the ALS007; 
-in any case the FM synth location cannot be set in the kernel configuration).
-
-The resulting sound driver will provide the following capabilities:
-  - 8 and 16 bit audio playback
-  - 8 and 16 bit audio recording
-  - Software selection of record source (line in, CD, FM, mic, master)
-  - Record and playback of midi data via the external MPU-401
-  - Playback of midi data using inbuilt FM synthesizer
-  - Control of the ALS-007 mixer via any OSS-compatible mixer programs. 
-    Controls available are Master (L&R), Line in (L&R), CD (L&R), 
-    DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono).
-
-Jonathan Woithe
-jwoithe@physics.adelaide.edu.au
-30 March 1998
diff -u --recursive --new-file v2.3.51/linux/Documentation/sound/Maestro linux/Documentation/sound/Maestro
--- v2.3.51/linux/Documentation/sound/Maestro	Fri Mar 10 16:40:38 2000
+++ linux/Documentation/sound/Maestro	Sun Mar 12 19:39:47 2000
@@ -24,7 +24,7 @@
 
 The various families of Maestro are mostly identical as far as this 
 driver is concerned.  It doesn't touch the DSP parts that differ (though
-it could for FM synthesis)
+it could for FM synthesis).
 
 Driver OSS Behavior
 --------------------
@@ -90,8 +90,33 @@
 ( dsps_order=2 ).  These devices act as fully distinct units and use
 separate channels in the maestro.
 
+Power Management
+----------------
+
+As of version 0.14, this driver has a minimal understanding of PCI
+Power Management.  If it finds a valid power management capability
+on the PCI device it will attempt to use the power management
+functions of the maestro.  It will only do this on Maestro 2Es and
+only on machines that are known to function well.  You can
+force the use of power management by setting the 'use_pm' module
+option to 1, or can disable it entirely by setting it to 0.
+
+When using power management, the driver does a few things
+differently.  It will keep the chip in a lower power mode
+when the module is inserted but /dev/dsp is not open.  This
+allows the mixer to function but turns off the clocks
+on other parts of the chip.  When /dev/dsp is opened the chip
+is brought into full power mode, and brought back down
+when it is closed.  It also powers down the chip entirely
+when the module is removed or the machine is shutdown.  This
+can have nonobvious consequences.  CD audio may not work
+after a power managing driver is removed.  Also, software that
+doesn't understand power management may not be able to talk
+to the powered down chip until the machine goes through a hard
+reboot to bring it back.
+
 .. more details ..
------------------
+------------------
 
 drivers/sound/maestro.c contains comments that hopefully explain
 the maestro implementation.
diff -u --recursive --new-file v2.3.51/linux/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- v2.3.51/linux/Documentation/usb/ov511.txt	Thu Feb 10 17:11:02 2000
+++ linux/Documentation/usb/ov511.txt	Sun Mar 12 19:18:54 2000
@@ -3,7 +3,7 @@
 -------------------------------------------------------------------------------
 
 Author: Mark McClelland
-Homepage: http://people.delphi.com/mmcclelland/linux/
+Homepage: http://alpha.dyndns.org/ov511
 
 INTRODUCTION:
 
@@ -11,25 +11,41 @@
 grab a frame in color (YUV420) at 640x480 or 320x240 using either vidcat or
 xawtv. Other utilities may work but have not yet been tested.
 
+NEW IN THIS VERSION:
+ o Preliminary snapshot support
+ o Experimental red-blue misalignment fixes
+ o Better YUV420 color conversion
+ o Module options
+ o Finer-grained debug message control
+ o Support for new cameras (4, 36)
+ o Uses initcalls
+
 SUPPORTED CAMERAS:
-________________________________________________________
-Manufacturer     | Model          | Custom ID | Status
------------------+----------------+-----------+---------
-MediaForte       | MV300          | 0         | Working
-D-Link           | DSB-C300       | 3         | Working
-Puretek          | PT-6007        | 5         | Untested
-Creative Labs    | WebCam 3       | 21        | Working
-Lifeview         | RoboCam        | 100       | Untested
-AverMedia        | InterCam Elite | 102       | Working
-MediaForte       | MV300          | 112       | Working
---------------------------------------------------------
+_________________________________________________________
+Manufacturer     | Model           | Custom ID | Status
+-----------------+-----------------+-----------+---------
+MediaForte       | MV300           | 0         | Working
+Aiptek           | HyperVCam ?     | 0         | Working
+NetView          | NV300M          | 0         | Working
+D-Link           | DSB-C300        | 3         | Working
+Hawking Tech.    | ???             | 3         | Working
+???              | Generic         | 4         | Untested
+Puretek          | PT-6007         | 5         | Working
+Creative Labs    | WebCam 3        | 21        | Working
+???              | Koala-Cam       | 36        | Untested
+Lifeview         | RoboCam         | 100       | Untested
+AverMedia        | InterCam Elite  | 102       | Working
+MediaForte       | MV300           | 112       | Working
+Omnivision       | OV7110 EV board | 112       | Working*
+---------------------------------------------------------
+(*) uses OV7110 (monochrome)
 
 Any camera using the OV511 and the OV7610 CCD should work with this driver. The
 driver only detects known cameras though, based on their custom id number. If
 you have a currently unsupported camera, the ID number should be reported to you
-in the kernel logs. If you have an unsupported camera, please send me the model,
-manufacturer and ID number and I will add it to the detection code. In the
-meantime, you can add to the code yourself in the function ov511_probe()
+in the kernel logs. Please send me the model, manufacturer and ID number and I 
+will add it to the detection code. In the meantime, you can add to the code
+yourself in the function ov511_probe().
 
 WHAT YOU NEED:
 
@@ -44,7 +60,7 @@
 
 You must have first compiled USB support, support for your specific USB host
 controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend
-making them modules.) 
+making them modules.)
 
 Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX):
 
@@ -56,6 +72,14 @@
 If it is not already there (it usually is), create the video device:
 
 	mknod /dev/video c 81 0
+
+Sometimes /dev/video is a symlink to /dev/video0
+
+You will have to set permissions on this device to allow you to read/write
+from it:
+
+	chmod 666 /dev/video
+	chmod 666 /dev/video0 (if necessary)
 	
 Now you are ready to run a video app! Both vidcat and xawtv work well for me
 at 640x480.
@@ -83,20 +107,103 @@
 you get a scrambled image it is likely that you made a mistake in Xawtv.ad.
 Try setting the size to 320x240 if all else fails.
 
+FAQ:
+Q: "Why does the picture have noise and look grainy"
+A: This is a problem at low light levels, and may be also due to subtle bugs in
+   the code. The cause is most likely the OV7610 settings we are currently
+   using. I am looking into this problem.
+
+Q: "The driver sometimes says `Failed to read OV7610 ID.' What is the deal?"
+A: The I2C code that allows the OV511 to communicate with the camera chip is a
+   bit flaky right now. This message means that the I2C bus never got
+   initialized properly, and the camera will most likely not work even if you
+   disable this warning. Try unloading/reloading the driver or unplugging/re-
+   plugging the camera if this happens.
+
+Q: "Why do you bother with this phony camera detection crap? It doesn't do
+    anything useful!"
+A: The main purpose of only supporting known camera models is to force people
+   with new camera models to tell me about them, so I can assemble the list
+   above, and so the code can know what CCD chip you have. Right now, nearly all
+   of the cameras use the OV7610 and consequently I have not put support for
+   other ones in, so the value of the detection code is questionable. Eventually
+   though, new CCDs might appear and we will be fortunate to have the detection.
+
+MODULE PARAMETERS:
+
+  You can set these with:  insmod ov511 NAME=VALUE
+  There is currently no way to set these on a per-camera basis.
+
+  NAME: autoadjust
+  TYPE: integer (boolean)
+  DEFAULT: 1
+  DESC: The camera normally adjusts exposure, gain, and hue automatically. This
+        can be set to 0 to disable this automatic adjustment. Note that there is
+        currently no way to set these parameters manually once autoadjust is
+        disabled. (This feature is not working yet)
+
+  NAME: debug
+  TYPE: integer (0-6)
+  DEFAULT: 3
+  DESC: Sets the threshold for printing debug messages. The higher the value,
+        the more is printed. The levels are cumulative, and are as follows:
+          0=no debug messages
+          1=init/detection/unload and other significant messages
+          2=some warning messages
+          3=config/control function calls
+          4=most function calls and data parsing messages
+          5=highly repetitive mesgs
+
+  NAME: fix_rgb_offset
+  TYPE: integer (boolean)
+  DEFAULT: 0
+  DESC: Some people have reported that the blue component of the image is one
+        or so lines higher than the red component. This is only apparent in 
+        images with white objects on black backgrounds at 640x480. Setting this
+        to 1 will realign the color planes correctly. NOTE: This is still
+        experimental and very buggy.
+
+  NAME: snapshot
+  TYPE: integer (boolean)
+  DEFAULT: 0
+  DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot
+        button is pressed. Note that this does not yet work with most apps,
+        including xawtv and vidcat. NOTE: See the section "TODO" for more info.
+
 WORKING FEATURES:
  o Color streaming/capture at 640x480 and 320x240
  o YUV420 color
+ o Monochrome
  o Setting/getting of saturation, contrast and brightness (no color yet)
 
-WHAT NEEDS TO BE DONE:
-
-The rest of the work will involve implementing support for all the different
-resolutions, color depths, etc. Also, while support for the OV511's proprietary
-lossy compression is apparently not necessary (the code currently disables it,)
-it would be a nice addition as it improves performance quite a bit. OmniVision
-wouldn't tell me how the algorithm works, so we can't really work on that yet.
-Please kindly inform OmniVision that you would like them to release their
-specifications to the Linux community.
+EXPERIMENTAL FEATURES:
+ o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
+   corrupted frames.
+ o Snapshot mode (only works with some read() based apps; see below for more)
+
+TODO:
+ o Fix the noise / grainy image problem.
+ o Get compression working. It would be a nice addition as it improves
+   frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works,
+   so we can't really work on that yet. Please kindly inform OmniVision that you
+   would like them to release their specifications to the Linux community.
+ o Get 160x120 working
+ o YUV422 (and other color modes)
+ o Fix read(). It only works right now if you run an mmap() based app like xawtv
+   or vidcat after loading the module and before using read(). Apparently there
+   are some initialization issues.
+ o Get snapshot mode working with mmap().
+ o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
+ o Get hue (red/blue channel balance) adjustment working (in ov511_get_picture()
+   and ov511_set_picture())
+ o Get autoadjust disable working
+ o Devise some clean way to support different types of CCDs (based on Custom ID)
+ o OV511A support
+ o V4L2 support (Probably not until it goes into the kernel)
+ o Fix I2C initialization. Some people are reporting problems with reading the
+   7610 registers. This could be due to timing differences, an excessive I2C
+   clock rate, or a problem with ov511_i2c_read().
+ o Get rid of the memory management functions (put them in videodev.c??)
 
 HOW TO CONTACT ME:
 
@@ -108,4 +215,5 @@
 The code is based in no small part on the CPiA driver by Johannes Erdfelt,
 Randy Dunlap, and others. Big thanks to them for their pioneering work on that
 and the USB stack. Thanks to Bret Wallach for getting camera reg IO , ISOC, and
-image capture working.
+image capture working. Thanks to Orion Sky Lawlor and Kevin Moore for their
+work as well.
diff -u --recursive --new-file v2.3.51/linux/Documentation/video4linux/CQcam.txt linux/Documentation/video4linux/CQcam.txt
--- v2.3.51/linux/Documentation/video4linux/CQcam.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/video4linux/CQcam.txt	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,414 @@
+c-qcam - Connectix Color QuickCam video4linux kernel driver
+
+Copyright (C) 1999  Dave Forrest  <drf5n@virginia.edu>
+                    released under GNU GPL.
+
+1999-12-08 Dave Forrest, written with kernel version 2.2.12 in mind
+
+
+Table of Contents
+
+1.0 Introduction
+2.0 Compilation, Installation, and Configuration
+3.0 Troubleshooting
+4.0 Future Work / current work arounds
+9.0 Sample Program, v4lgrab
+10.0 Other Information
+
+
+1.0 Introduction
+
+  The file ../drivers/char/c-qcam.c is a device driver for the
+Logitech (nee Connectix) parallel port interface color CCD camera.
+This is a fairly inexpensive device for capturing images.  Logitech
+does not currently provide information for developers, but many people
+have engineered several solutions for non-Microsoft use of the Color
+Quickcam.
+
+1.1 Motivation
+
+  I spent a number of hours trying to get my camera to work, and I
+hope this document saves you some time.  My camera will not work with
+the 2.2.13 kernel as distributed, but with a few patches to the
+module, I was able to grab some frames. See 4.0, Future Work.
+
+
+
+2.0 Compilation, Installation, and Configuration
+
+  The c-qcam depends on parallel port support, video4linux, and the
+Color Quickcam.  It is also nice to have the parallel port readback
+support enabled. I enabled these as modules during the kernel
+configuration.  The appropriate flags are:
+
+    CONFIG_PRINTER       M    for lp.o, parport.o parport_pc.o modules
+    CONFIG_PNP_PARPORT   M for autoprobe.o IEEE1284 readback module
+    CONFIG_PRINTER_READBACK M for parport_probe.o IEEE1284 readback module
+    CONFIG_VIDEO_DEV     M    for videodev.o video4linux module
+    CONFIG_VIDEO_CQCAM   M    for c-qcam.o  Color Quickcam module 
+
+  With these flags, the kernel should compile and install the modules.
+To record and monitor the compilation, I use:
+
+ (make dep; \
+  make zlilo ; \
+  make modules; \
+  make modules_install ; 
+  depmod -a ) &>log &
+ less log  # then a capital 'F' to watch the progress
+  
+But that is my personal preference.
+
+2.2 Configuration
+ 
+  The configuration requires module configuration and device
+configuration.  I like kmod or kerneld process with the
+/etc/modules.conf file so the modules can automatically load/unload as
+they are used.  The video devices could already exist, be generated
+using MAKEDEV, or need to be created.  The following sections detail
+these procedures.
+
+
+2.1 Module Configuration  
+
+  Using modules requires a bit of work to install and pass the
+parameters.  Do read ../modules.txt, and understand that entries
+in /etc/modules.conf of:
+
+   alias parport_lowlevel parport_pc
+   options parport_pc io=0x378 irq=none
+   alias char-major-81 videodev
+   alias char-major-81-0 c-qcam
+
+will cause the kmod/kerneld/modprobe to do certain things.  If you are
+using kmod or kerneld, then a request for a 'char-major-81-0' will cause
+the 'c-qcam' module to load.  If you have other video sources with
+modules, you might want to assign the different minor numbers to
+different modules.
+
+2.2 Device Configuration
+
+  At this point, we need to ensure that the device files exist.
+Video4linux used the /dev/video* files, and we want to attach the
+Quickcam to one of these.
+
+   ls -lad /dev/video*  # should produce a list of the video devices
+
+If the video devices do not exist, you can create them with:
+
+  su
+  cd /dev
+  for ii in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
+    mknod video$ii c 81 $ii   # char-major-81-[0-16]
+    chown root.root video$ii  # owned by root
+    chmod 600 video$ii        # read/writable by root only
+  done
+
+  Lots of people connect video0 to video and bttv, but you might want
+your c-qcam to mean something more:
+
+   ln -s video0 c-qcam  # make /dev/c-qcam a working file
+   ln -s c-qcam video   # make /dev/c-qcam your default video source
+
+  But these are conveniences.  The important part is to make the proper
+special character files with the right major and minor numbers.  All
+of the special device files are listed in ../devices.txt.  If you
+would like the c-qcam readable by non-root users, you will need to
+change the permissions.
+
+3.0 Troubleshooting
+
+  If the sample program below, v4lgrab, gives you output then
+everything is working.
+
+    v4lgrab | wc # should give you a count of characters
+
+  Otherwise, you have some problem.
+
+  The c-qcam is IEEE1284 compatible, so if you are using the proc file
+system (CONFIG_PROC_FS), the parallel printer support
+(CONFIG_PRINTER), the IEEE 1284 sytem,(CONFIG_PRINTER_READBACK), you
+should be able to read some identification from your quickcam with
+
+         modprobe -v parport
+         modprobe -v parport_probe
+         cat /proc/parport/PORTNUMBER/autoprobe
+Returns:
+  CLASS:MEDIA;
+  MODEL:Color QuickCam 2.0;
+  MANUFACTURER:Connectix;
+
+  A good response to this indicates that your color quickcam is alive
+and well.  A common problem is that the current driver does not
+reliably detect a c-qcam, even though one is attached.  In this case,
+
+     modprobe -v c-qcam     
+or
+     insmod -v c-qcam
+
+  Returns a message saying "Device or resource busy"  Development is
+currently underway, but a workaround is to patch the module to skip
+the detection code and attach to a defined port.  Check the
+video4linux mailing list and archive for more current information.
+
+3.1 Checklist:
+
+  Can you get an image?
+            v4lgrab >qcam.ppm ; wc qcam.ppm ; xv qcam.ppm
+
+  Is a working c-qcam connected to the port? 
+            grep ^ /proc/parport/?/autoprobe
+
+  Do the /dev/video* files exist?  
+            ls -lad /dev/video
+
+  Is the c-qcam module loaded?     
+            modprobe -v c-qcam ; lsmod
+
+  Does the camera work with alternate programs? cqcam, etc?
+
+
+
+
+4.0 Future Work / current workarounds
+
+  It is hoped that this section will soon become obsolete, but if it
+isn't, you might try patching the c-qcam module to add a parport=xxx
+option as in the bw-qcam module so you can specify the parallel port:
+
+       insmod -v c-qcam parport=0  
+
+And bypass the detection code, see ../../drivers/char/c-qcam.c and
+look for the 'qc_detect' code and call.
+
+  Note that there is work in progress to change the video4linux API,
+this work is documented at the video4linux2 site listed below.
+
+
+9.0 --- A sample program using v4lgrabber, 
+
+This program is a simple image grabber that will copy a frame from the
+first video device, /dev/video0 to standard output in portable pixmap
+format (.ppm)  Using this like: 'v4lgrab | convert - c-qcam.jpg'
+produced this picture of me at 
+    http://mug.sys.virginia.edu/~drf5n/extras/c-qcam.jpg
+
+-------------------- 8< ---------------- 8< -----------------------------
+
+/* Simple Video4Linux image grabber. */
+/*
+ *	Video4Linux Driver Test/Example Framegrabbing Program
+ *
+ *	Compile with:
+ *		gcc -s -Wall -Wstrict-prototypes v4lgrab.c -o v4lgrab
+ *      Use as:
+ *              v4lgrab >image.ppm
+ *
+ *	Copyright (C) 1998-05-03, Phil Blundell <philb@gnu.org>  
+ *      Copied from http://www.tazenda.demon.co.uk/phil/vgrabber.c 
+ *      with minor modifications (Dave Forrest, drf5n@virginia.edu).
+ *
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+
+#include <linux/types.h>
+#include <linux/videodev.h>
+
+#define FILE "/dev/video0"
+
+/* Stole this from tvset.c */
+
+#define READ_VIDEO_PIXEL(buf, format, depth, r, g, b)                   \
+{                                                                       \
+        switch (format)                                                 \
+        {                                                               \
+                case VIDEO_PALETTE_GREY:                                \
+                        switch (depth)                                  \
+                        {                                               \
+                                case 4:                                 \
+                                case 6:                                 \
+                                case 8:                                 \
+                                        (r) = (g) = (b) = (*buf++ << 8);\
+                                        break;                          \
+                                                                        \
+                                case 16:                                \
+                                        (r) = (g) = (b) =               \
+                                                *((unsigned short *) buf);      \
+                                        buf += 2;                       \
+                                        break;                          \
+                        }                                               \
+                        break;                                          \
+                                                                        \
+                                                                        \
+                case VIDEO_PALETTE_RGB565:                              \
+                {                                                       \
+                        unsigned short tmp = *(unsigned short *)buf;    \
+                        (r) = tmp&0xF800;                               \
+                        (g) = (tmp<<5)&0xFC00;                          \
+                        (b) = (tmp<<11)&0xF800;                         \
+                        buf += 2;                                       \
+                }                                                       \
+                break;                                                  \
+                                                                        \
+                case VIDEO_PALETTE_RGB555:                              \
+                        (r) = (buf[0]&0xF8)<<8;                         \
+                        (g) = ((buf[0] << 5 | buf[1] >> 3)&0xF8)<<8;    \
+                        (b) = ((buf[1] << 2 ) & 0xF8)<<8;               \
+                        buf += 2;                                       \
+                        break;                                          \
+                                                                        \
+                case VIDEO_PALETTE_RGB24:                               \
+                        (r) = buf[0] << 8; (g) = buf[1] << 8;           \
+                        (b) = buf[2] << 8;                              \
+                        buf += 3;                                       \
+                        break;                                          \
+                                                                        \
+                default:                                                \
+                        fprintf(stderr,                                 \
+                                "Format %d not yet supported\n",        \
+                                format);                                \
+        }                                                               \
+}                                               
+
+int get_brightness_adj(unsigned char *image, long size, int *brightness) {
+  long i, tot = 0;
+  for (i=0;i<size*3;i++)
+    tot += image[i];
+  *brightness = (128 - tot/(size*3))/3;
+  return !((tot/(size*3)) >= 126 && (tot/(size*3)) <= 130);
+}
+
+int main(int argc, char ** argv)
+{
+  int fd = open(FILE, O_RDONLY), f;
+  struct video_capability cap;
+  struct video_window win;
+  struct video_picture vpic;
+
+  unsigned char *buffer, *src;
+  int bpp = 24, r, g, b;
+  unsigned int i, src_depth;
+
+  if (fd < 0) {
+    perror(FILE);
+    exit(1);
+  }
+
+  if (ioctl(fd, VIDIOCGCAP, &cap) < 0) {
+    perror("VIDIOGCAP");
+    fprintf(stderr, "(" FILE " not a video4linux device?)\n");
+    close(fd);
+    exit(1);
+  }
+
+  if (ioctl(fd, VIDIOCGWIN, &win) < 0) {
+    perror("VIDIOCGWIN");
+    close(fd);
+    exit(1);
+  }
+
+  if (ioctl(fd, VIDIOCGPICT, &vpic) < 0) {
+    perror("VIDIOCGPICT");
+    close(fd);
+    exit(1);
+  }
+
+  if (cap.type & VID_TYPE_MONOCHROME) {
+    vpic.depth=8;
+    vpic.palette=VIDEO_PALETTE_GREY;    /* 8bit grey */
+    if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
+      vpic.depth=6;
+      if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
+        vpic.depth=4;
+        if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
+          fprintf(stderr, "Unable to find a supported capture format.\n");
+          close(fd);
+          exit(1);
+        }
+      }
+    }
+  } else {
+    vpic.depth=24;
+    vpic.palette=VIDEO_PALETTE_RGB24;
+    
+    if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
+      vpic.palette=VIDEO_PALETTE_RGB565;
+      vpic.depth=16;
+      
+      if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
+        vpic.palette=VIDEO_PALETTE_RGB555;
+        vpic.depth=15;
+        
+        if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
+          fprintf(stderr, "Unable to find a supported capture format.\n");
+          return -1;
+        }
+      }
+    }
+  }
+  
+  buffer = malloc(win.width * win.height * bpp);
+  if (!buffer) {
+    fprintf(stderr, "Out of memory.\n");
+    exit(1);
+  }
+  
+  do {
+    int newbright;
+    read(fd, buffer, win.width * win.height * bpp);
+    f = get_brightness_adj(buffer, win.width * win.height, &newbright);
+    if (f) {
+      vpic.brightness += (newbright << 8);
+      if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
+        perror("VIDIOSPICT");
+        break;
+      }
+    }
+  } while (f);
+
+  fprintf(stdout, "P6\n%d %d 255\n", win.width, win.height);
+
+  src = buffer;
+
+  for (i = 0; i < win.width * win.height; i++) {
+    READ_VIDEO_PIXEL(src, vpic.palette, src_depth, r, g, b);
+    fputc(r>>8, stdout);
+    fputc(g>>8, stdout);
+    fputc(b>>8, stdout);
+  }
+    
+  close(fd);
+  return 0;
+}
+-------------------- 8< ---------------- 8< -----------------------------
+
+
+10.0 --- Other Information
+
+Use the ../../Maintainers file, particularly the  VIDEO FOR LINUX and PARALLEL
+PORT SUPPORT sections
+
+The video4linux page:
+  http://roadrunner.swansea.linux.org.uk/v4l.shtml
+
+The video4linux2 page:
+  http://millennium.diads.com/bdirks/v4l2.htm
+
+Some web pages about the quickcams:
+   http://www.dkfz-heidelberg.de/Macromol/wedemann/mini-HOWTO-cqcam.html
+
+   http://www.crynwr.com/qcpc/            QuickCam Third-Party Drivers
+   http://www.crynwr.com/qcpc/re.html     Some Reverse Engineering
+   http://cse.unl.edu/~cluening/gqcam/    v4l client
+   http://phobos.illtel.denver.co.us/pub/qcread/ doesn't use v4l
+   ftp://ftp.cs.unm.edu/pub/chris/quickcam/   Has lots of drivers
+   http://www.cs.duke.edu/~reynolds/quickcam/ Has lots of information
+
+
diff -u --recursive --new-file v2.3.51/linux/Documentation/video4linux/bttv/README linux/Documentation/video4linux/bttv/README
--- v2.3.51/linux/Documentation/video4linux/bttv/README	Tue Mar  7 14:32:25 2000
+++ linux/Documentation/video4linux/bttv/README	Sun Mar 12 19:12:09 2000
@@ -17,7 +17,7 @@
 CONFIG_I2C_ALGOBIT=m
 
 The latest bttv version is available here:
-	http://www.in-berlin.de/User/kraxel/v4l/
+	http://me.in-berlin.de/~kraxel/bttv.html
 
 You'll find Ralphs original (mostly outdated) documentation in the
 ralphs-doc subdirectory.
diff -u --recursive --new-file v2.3.51/linux/MAINTAINERS linux/MAINTAINERS
--- v2.3.51/linux/MAINTAINERS	Thu Mar  2 14:36:22 2000
+++ linux/MAINTAINERS	Tue Mar 14 18:05:12 2000
@@ -212,6 +212,11 @@
 L:     linux-computone@lazuli.wittsend.com
 S:     Supported
 
+COMX/MULTIGATE SYNC SERIAL DRIVERS
+P:	Gergely Madarasz
+M:	Gergely Madarasz <gorgo@itc.hu>
+S:	Supported
+
 CONFIGURE, MENUCONFIG, XCONFIG
 P:	Michael Elizabeth Chastain
 M:	mec@shout.net
@@ -287,7 +292,7 @@
 S:	Maintained
 
 DIGI INTL. EPCA DRIVER
-P:	 Chad Schwartz
+P:	Chad Schwartz
 M:      support@dgii.com
 M:      chads@dgii.com
 L:      digilnux@dgii.com
@@ -330,6 +335,11 @@
 L:	linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu
 S:	Maintained
 
+EEPRO100 NETWORK DRIVER
+P:	Andrey V. Savochkin
+M:	saw@saw.sw.com.sg
+S:	Maintained
+
 ETHEREXPRESS-16 NETWORK DRIVER
 P:	Philip Blundell
 M:	Philip.Blundell@pobox.com
@@ -750,7 +760,7 @@
 
 OPL3-SA2, SA3, and SAx DRIVER
 P:	Scott Murray
-M:	scottm@interlog.com
+M:	scott@spiteful.org
 L:	linux-sound@vger.rutgers.edu
 S:	Maintained
 
@@ -825,12 +835,17 @@
 W:	http://www.pnd-pc.demon.co.uk/promise/
 S:	Maintained
 
+QNX4 FILESYSTEM
+P:	Anders Larsen
+M:	al@alarsen.net
+L:	linux-kernel@vger.rutgers.edu
+W:	http://www.alarsen.net/linux/qnx4fs/
+S:	Maintained
+
 RAGE128 FRAMEBUFFER DISPLAY DRIVER
 P:	Brad Douglas
 M:	brad@neruo.com
-P:	Anthony Tong
-M:	atong@uiuc.edu
-L:	linux-fbdev@vcuser.vc.union.edu
+L:	linux-fbdev@vuser.vc.union.edu
 S:	Maintained
 
 RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
@@ -839,13 +854,6 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
-QNX4 FILESYSTEM
-P:	Anders Larsen
-M:	al@alarsen.net
-L:	linux-kernel@vger.rutgers.edu
-W:	http://www.alarsen.net/linux/qnx4fs/
-S:	Maintained
-
 REAL TIME CLOCK DRIVER
 P:	Paul Gortmaker
 M:	p_gortmaker@yahoo.com
@@ -853,8 +861,8 @@
 S:	Maintained
 
 ROSE NETWORK LAYER
-P:	Frederic Rible
-M:	frible@teaser.fr
+P:	Jean-Paul Roubelat
+M:	jpr@f6fbb.org
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
@@ -864,6 +872,13 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+RTLINUX  REALTIME  LINUX
+P:	Victor Yodaiken
+M:	yodaiken@fsmlabs.com
+L:	rtl@rtlinux.org
+W:	www.rtlinux.org
+S:	Maintained
+
 SA1100 SUPPORT
 P:	Nicolas Pitre
 M:	nico@cam.org
@@ -1100,6 +1115,12 @@
 L:	linux-usb@suse.com
 S:	Maintained
 
+USB PEGASUS DRIVER
+P:	Petko Manolov
+M:	petkan@spct.net
+L:	linux-usb@suse.com
+S:	Maintained
+
 USB PRINTER DRIVER
 P:	Vojtech Pavlik
 M:	vojtech@suse.cz
@@ -1169,13 +1190,6 @@
 P:	Henner Eisen
 M:	eis@baty.hanse.de
 L:	linux-x25@vger.rutgers.edu
-S:	Maintained
-
-RTLINUX  REALTIME  LINUX
-P:	Victor Yodaiken
-M:	yodaiken@fsmlabs.com
-L:	rtl@rtlinux.org
-W:	www.rtlinux.org
 S:	Maintained
 
 Z85230 SYNCHRONOUS DRIVER
diff -u --recursive --new-file v2.3.51/linux/Makefile linux/Makefile
--- v2.3.51/linux/Makefile	Fri Mar 10 16:40:38 2000
+++ linux/Makefile	Tue Mar 14 18:35:29 2000
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 3
-SUBLEVEL = 51
-EXTRAVERSION =
+SUBLEVEL = 99
+EXTRAVERSION = -pre1
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
@@ -116,145 +116,53 @@
 LIBS		=$(TOPDIR)/lib/lib.a
 SUBDIRS		=kernel drivers mm fs net ipc lib
 
-ifdef CONFIG_DRM
-DRIVERS += drivers/char/drm/drm.o
-endif
-
-ifeq ($(CONFIG_AGP),y)
-DRIVERS += drivers/char/agp/agp.o
-endif
-
-ifdef CONFIG_NUBUS
-DRIVERS := $(DRIVERS) drivers/nubus/nubus.a
-endif
-
-ifeq ($(CONFIG_ISDN),y)
-DRIVERS := $(DRIVERS) drivers/isdn/isdn.a
-endif
-
-ifdef CONFIG_NET_FC
-DRIVERS := $(DRIVERS) drivers/net/fc/fc.a
-endif
-
-ifdef CONFIG_ATALK
-DRIVERS := $(DRIVERS) drivers/net/appletalk/appletalk.a
-endif
-
-ifdef CONFIG_TR
-DRIVERS := $(DRIVERS) drivers/net/tokenring/tr.a
-endif
-
-ifdef CONFIG_WAN
-DRIVERS := $(DRIVERS) drivers/net/wan/wan.a
-endif
-
-ifeq ($(CONFIG_ARCNET),y)
-DRIVERS := $(DRIVERS) drivers/net/arcnet/arcnet.a
-endif
-
-ifdef CONFIG_ATM
-DRIVERS := $(DRIVERS) drivers/atm/atm.a
-endif
-
-ifeq ($(CONFIG_SCSI),y)
-DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
-endif
-
-ifeq ($(CONFIG_IEEE1394),y)
-DRIVERS := $(DRIVERS) drivers/ieee1394/ieee1394.a
-endif
+DRIVERS-n :=
+DRIVERS-y :=
+DRIVERS-m :=
+DRIVERS-  :=
+
+DRIVERS-$(CONFIG_DRM) += drivers/char/drm/drm.o
+DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o
+DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
+DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a
+DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.a
+DRIVERS-$(CONFIG_APPLETALK) += drivers/net/appletalk/appletalk.a
+DRIVERS-$(CONFIG_TR) += drivers/net/tokenring/tr.a
+DRIVERS-$(CONFIG_WAN) += drivers/net/wan/wan.a
+DRIVERS-$(CONFIG_ARCNET) += drivers/net/arcnet/arcnet.a
+DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.a
+DRIVERS-$(CONFIG_IDE) += drivers/ide/ide.a
+DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsi.a
+DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394.a
 
 ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
-DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
-endif
-
-ifeq ($(CONFIG_SOUND),y)
-DRIVERS := $(DRIVERS) drivers/sound/sounddrivers.o
-endif
-
-ifdef CONFIG_PCI
-DRIVERS := $(DRIVERS) drivers/pci/pci.a
-endif
-
-ifeq ($(CONFIG_PCMCIA),y)
-DRIVERS := $(DRIVERS) drivers/pcmcia/pcmcia.o
-endif
-
-ifeq ($(CONFIG_PCMCIA_NETCARD),y)
-DRIVERS := $(DRIVERS) drivers/net/pcmcia/pcmcia_net.o
-endif
-
-ifeq ($(CONFIG_PCMCIA_CHRDEV),y)
-DRIVERS := $(DRIVERS) drivers/char/pcmcia/pcmcia_char.o
+DRIVERS-y += drivers/cdrom/cdrom.a
 endif
 
-ifdef CONFIG_DIO
-DRIVERS := $(DRIVERS) drivers/dio/dio.a
-endif
+DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
+DRIVERS-$(CONFIG_PCI) += drivers/pci/pci.a
+DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o
+DRIVERS-$(CONFIG_PCMCIA_NETCARD) += drivers/net/pcmcia/pcmcia_net.o
+DRIVERS-$(CONFIG_PCMCIA_CHRDEV) += drivers/char/pcmcia/pcmcia_char.o
+DRIVERS-$(CONFIG_DIO) += drivers/dio/dio.a
+DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus.a
+DRIVERS-$(CONFIG_ZORRO) += drivers/zorro/zorro.a
+DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a
+DRIVERS-$(CONFIG_PPC) += drivers/macintosh/macintosh.a
+DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.a
+DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o
+DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a
+DRIVERS-$(CONFIG_VT) += drivers/video/video.o
+DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a
+DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
+DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
+DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
+DRIVERS-$(CONFIG_I2O) += drivers/i2o/i2o.a
+DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda_drivers.a
+DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.a
+DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.a
 
-ifdef CONFIG_SBUS
-DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
-endif
-
-ifdef CONFIG_ZORRO
-DRIVERS := $(DRIVERS) drivers/zorro/zorro.a
-endif
-
-ifeq ($(CONFIG_FC4),y)
-DRIVERS := $(DRIVERS) drivers/fc4/fc4.a
-endif
-
-ifdef CONFIG_PPC
-DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
-endif
-
-ifdef CONFIG_MAC
-DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
-endif
-
-ifeq ($(CONFIG_ISAPNP),y)
-DRIVERS := $(DRIVERS) drivers/pnp/pnp.o
-endif
-
-ifdef CONFIG_SGI_IP22
-DRIVERS := $(DRIVERS) drivers/sgi/sgi.a
-endif
-
-ifdef CONFIG_VT
-DRIVERS := $(DRIVERS) drivers/video/video.o
-endif
-
-ifeq ($(CONFIG_PARIDE),y)
-DRIVERS := $(DRIVERS) drivers/block/paride/paride.a
-endif
-
-ifdef CONFIG_HAMRADIO
-DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.o
-endif
-
-ifeq ($(CONFIG_TC),y)
-DRIVERS := $(DRIVERS) drivers/tc/tc.a
-endif
-
-ifeq ($(CONFIG_USB),y)
-DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o
-endif
-
-ifeq ($(CONFIG_I2O),y)
-DRIVERS := $(DRIVERS) drivers/i2o/i2o.a
-endif
-
-ifeq ($(CONFIG_IRDA),y)
-DRIVERS := $(DRIVERS) drivers/net/irda/irda_drivers.a
-endif
-
-ifeq ($(CONFIG_I2C),y)
-DRIVERS := $(DRIVERS) drivers/i2c/i2c.a
-endif
-
-ifeq ($(CONFIG_PHONE),y)
-DRIVERS := $(DRIVERS) drivers/telephony/telephony.a
-endif
+DRIVERS += $(DRIVERS-y)
 
 include arch/$(ARCH)/Makefile
 
@@ -399,6 +307,7 @@
 	if [ -f IPV4_MODULES  ]; then inst_mod IPV4_MODULES  ipv4;  fi; \
 	if [ -f IPV6_MODULES  ]; then inst_mod IPV6_MODULES  ipv6;  fi; \
 	if [ -f ATM_MODULES   ]; then inst_mod ATM_MODULES   atm;   fi; \
+	if [ -f IDE_MODULES   ]; then inst_mod IDE_MODULES   ide;   fi; \
 	if [ -f SCSI_MODULES  ]; then inst_mod SCSI_MODULES  scsi;  fi; \
 	if [ -f FS_MODULES    ]; then inst_mod FS_MODULES    fs;    fi; \
 	if [ -f NLS_MODULES   ]; then inst_mod NLS_MODULES   fs;    fi; \
@@ -483,6 +392,9 @@
 backup: mrproper
 	cd .. && tar cf - linux/ | gzip -9 > backup.gz
 	sync
+
+sgmldocs: 
+	$(MAKE) -C $(TOPDIR)/Documentation/DocBook books
 
 sums:
 	find . -type f -print | sort | xargs sum > .SUMS
diff -u --recursive --new-file v2.3.51/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.3.51/linux/arch/alpha/config.in	Fri Mar 10 16:40:38 2000
+++ linux/arch/alpha/config.in	Sun Mar 12 19:49:24 2000
@@ -223,6 +223,19 @@
 fi
 
 mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support' CONFIG_SCSI
diff -u --recursive --new-file v2.3.51/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v2.3.51/linux/arch/alpha/defconfig	Fri Mar 10 16:40:39 2000
+++ linux/arch/alpha/defconfig	Sun Mar 12 19:49:24 2000
@@ -70,12 +70,6 @@
 # Block devices
 #
 CONFIG_BLK_DEV_FD=y
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
 # CONFIG_BLK_CPQ_DA is not set
 
 #
@@ -89,8 +83,6 @@
 # CONFIG_BLK_DEV_DAC960 is not set
 CONFIG_PARIDE_PARPORT=y
 # CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # Networking options
@@ -121,6 +113,11 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
 
 #
 # SCSI support
diff -u --recursive --new-file v2.3.51/linux/arch/arm/Makefile linux/arch/arm/Makefile
--- v2.3.51/linux/arch/arm/Makefile	Fri Mar 10 16:40:39 2000
+++ linux/arch/arm/Makefile	Sun Mar 12 19:39:39 2000
@@ -121,6 +121,11 @@
 ARCHDIR		 = ebsa110
 endif
 
+ifeq ($(CONFIG_ARCH_CLPS7500),y)
+MACHINE		 = clps7500
+ARCHDIR		 = cl7500
+endif
+
 ifeq ($(CONFIG_FOOTBRIDGE),y)
 MACHINE		 = footbridge
 ARCHDIR		 = ebsa285
@@ -135,6 +140,11 @@
 ARCHDIR		 = nexuspci
 endif
 
+ifeq ($(CONFIG_ARCH_SHARK),y)
+MACHINE		 = shark
+ARCHDIR		 = shark
+endif
+
 ifeq ($(CONFIG_ARCH_SA1100),y)
 MACHINE		 = sa1100
 ARCHDIR		 = sa1100
@@ -158,6 +168,11 @@
 DRIVERS		+= drivers/acorn/char/acorn-char.o
 DRIVERS		+= drivers/acorn/net/acorn-net.a
 DRIVERS		+= drivers/acorn/scsi/acorn-scsi.a
+endif
+
+ifeq ($(CONFIG_ARCH_CLPS7500),y)
+SUBDIRS		+= drivers/acorn/char
+DRIVERS		+= drivers/acorn/char/acorn-char.o
 endif
 
 MAKEBOOT	 = $(MAKE) -C arch/$(ARCH)/boot
diff -u --recursive --new-file v2.3.51/linux/arch/arm/boot/compressed/Makefile linux/arch/arm/boot/compressed/Makefile
--- v2.3.51/linux/arch/arm/boot/compressed/Makefile	Tue Mar  7 14:32:25 2000
+++ linux/arch/arm/boot/compressed/Makefile	Sun Mar 12 19:39:39 2000
@@ -25,6 +25,10 @@
 ZTEXTADDR	 = 0x10008000
 endif
 
+ifeq ($(CONFIG_ARCH_CLPS7500),y)
+ZTEXTADDR	 = 0x10008000
+endif
+
 ifeq ($(CONFIG_ARCH_EBSA110),y)
 ZTEXTADDR	 = 0x00008000
 endif
@@ -39,7 +43,6 @@
 
 ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
 HEAD		 = head-nexuspci.o
-OBJS		+= $(TOPDIR)/arch/arm/lib/ll_char_wr_scc.o
 ZTEXTADDR	 = 0x40200000
 ZRELADDR	 = 0x40008000
 endif
diff -u --recursive --new-file v2.3.51/linux/arch/arm/config.in linux/arch/arm/config.in
--- v2.3.51/linux/arch/arm/config.in	Fri Mar 10 16:40:39 2000
+++ linux/arch/arm/config.in	Sun Mar 12 19:49:24 2000
@@ -37,6 +37,7 @@
    bool '  Include support for EBSA285' CONFIG_ARCH_EBSA285
    bool '  Include support for CATS' CONFIG_CATS
    bool '  Include support for NetWinder' CONFIG_ARCH_NETWINDER
+   bool '  Include support for Compaq Personal Server' CONFIG_PERSONAL_SERVER
 fi
 
 if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then
@@ -170,6 +171,7 @@
 if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
      "$CONFIG_ARCH_SA1100" = "y" -o \
      "$CONFIG_ARCH_NETWINDER" = "y" -o \
+     "$CONFIG_PERSONAL_SERVER" = "y" -o \
      "$CONFIG_CATS" = "y" ]; then
    string 'Initial kernel command string' CONFIG_CMDLINE
 fi
@@ -252,6 +254,19 @@
 #    source drivers/isdn/Config.in
 # fi
 # endmenu
+
+mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
 
 mainmenu_option next_comment
 comment 'SCSI support'
diff -u --recursive --new-file v2.3.51/linux/arch/arm/defconfig linux/arch/arm/defconfig
--- v2.3.51/linux/arch/arm/defconfig	Thu Feb 10 17:11:03 2000
+++ linux/arch/arm/defconfig	Sun Mar 12 19:49:24 2000
@@ -87,46 +87,6 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
-CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_IDEDISK_MULTI_MODE=y
-# CONFIG_BLK_DEV_IDECD is not set
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-
-#
-# IDE chipset support/bugfixes
-#
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA_PCI=y
-CONFIG_IDEDMA_PCI_AUTO=y
-CONFIG_IDEDMA_NEW_DRIVE_LISTINGS=y
-CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
-CONFIG_BLK_DEV_OFFBOARD=y
-# CONFIG_BLK_DEV_AEC6210 is not set
-# CONFIG_BLK_DEV_ALI15X3 is not set
-# CONFIG_BLK_DEV_CMD646 is not set
-CONFIG_BLK_DEV_CY82C693=y
-# CONFIG_BLK_DEV_HPT34X is not set
-# CONFIG_BLK_DEV_HPT366 is not set
-# CONFIG_BLK_DEV_NS87415 is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-CONFIG_BLK_DEV_PDC202XX=y
-# CONFIG_PDC202XX_FORCE_BURST_BIT is not set
-# CONFIG_PDC202XX_FORCE_MASTER_MODE is not set
-# CONFIG_BLK_DEV_TRM290 is not set
-CONFIG_BLK_DEV_SL82C105=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-# CONFIG_IDE_CHIPSETS is not set
 # CONFIG_BLK_CPQ_DA is not set
 
 #
@@ -172,8 +132,6 @@
 CONFIG_PARIDE_KTTI=m
 CONFIG_PARIDE_ON20=m
 CONFIG_PARIDE_ON26=m
-CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # Character devices
@@ -507,6 +465,54 @@
 # PCMCIA network device support
 #
 # CONFIG_NET_PCMCIA is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+CONFIG_IDEDMA_NEW_DRIVE_LISTINGS=y
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+CONFIG_BLK_DEV_OFFBOARD=y
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+CONFIG_BLK_DEV_CY82C693=y
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_PDC202XX=y
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+CONFIG_BLK_DEV_SL82C105=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
diff -u --recursive --new-file v2.3.51/linux/arch/arm/kernel/Makefile linux/arch/arm/kernel/Makefile
--- v2.3.51/linux/arch/arm/kernel/Makefile	Thu Feb 10 17:11:03 2000
+++ linux/arch/arm/kernel/Makefile	Sun Mar 12 19:39:39 2000
@@ -8,71 +8,55 @@
 HEAD_OBJ  = head-$(PROCESSOR).o
 ENTRY_OBJ = entry-$(PROCESSOR).o
 
-O_TARGET := kernel.o
-O_OBJS   := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o \
-	    semaphore.o setup.o signal.o sys_arm.o time.o traps.o
-
 ifeq ($(CONFIG_ISA_DMA),y)
   ISA_DMA_OBJS += dma-isa.o
 endif
 
-O_OBJS_arc        = dma-arc.o iic.o fiq.o time-acorn.o oldlatches.o
-O_OBJS_a5k        = dma-a5k.o iic.o fiq.o time-acorn.o
-O_OBJS_rpc        = dma-rpc.o iic.o fiq.o time-acorn.o
+O_OBJS_arc        = dma-arc.o oldlatches.o
+O_OBJS_a5k        = dma-a5k.o
+O_OBJS_rpc        = dma-rpc.o
 O_OBJS_ebsa110    = dma-dummy.o
-O_OBJS_footbridge = dma-footbridge.o $(ISA_DMA_OBJS) isa.o
+O_OBJS_footbridge = dma.o dma-footbridge.o $(ISA_DMA_OBJS) hw-footbridge.o isa.o
 O_OBJS_nexuspci   = dma-dummy.o
 O_OBJS_sa1100     = dma-dummy.o fiq.o
 
-OX_OBJS_arc	  = dma.o
-OX_OBJS_a5k	  = dma.o
-OX_OBJS_rpc	  = dma.o
-OX_OBJS_ebsa110	  = 
-OX_OBJS_footbridge= dma.o hw-footbridge.o
-OX_OBJS_nexuspci  =
-OX_OBJS_sa1100    =
+O_TARGET := kernel.o
 
-all: kernel.o $(HEAD_OBJ) init_task.o
+# Object file lists.
 
-O_OBJS += $(O_OBJS_$(MACHINE))
+obj-y			:= arch.o $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o \
+			   semaphore.o setup.o signal.o sys_arm.o time.o traps.o \
+			   $(O_OBJS_$(MACHINE))
+obj-m			:=
+obj-n			:=
+obj-			:=
+
+export-objs		:= armksyms.o dma.o ecard.o hw-footbridge.o leds-$(MACHINE).o
+
+obj-$(CONFIG_ARCH_ACORN) += dma.o ecard.o iic.o fiq.o time-acorn.o
+obj-$(CONFIG_DEBUG_LL)	+= debug-$(PROCESSOR).o
+obj-$(CONFIG_MODULES)	+= armksyms.o
+obj-$(CONFIG_LEDS)	+= leds-$(MACHINE).o
+obj-$(CONFIG_ARTHUR)	+= arthur.o
 
-ifeq ($(CONFIG_DEBUG_LL),y)
-  O_OBJS += debug-$(PROCESSOR).o
+ifeq ($(MACHINE),nexuspci)
+  obj-$(CONFIG_PCI)	+= plx9080.o
+else
+  obj-$(CONFIG_PCI)	+= bios32.o dec21285.o
 endif
 
-ifeq ($(CONFIG_MODULES),y)
-  OX_OBJS = armksyms.o
-endif
+# Files that are both resident and modular; remove from modular.
 
-ifeq ($(CONFIG_ARCH_ACORN),y)
-  OX_OBJS += ecard.o
-endif
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
 
-ifeq ($(CONFIG_PCI),y)
-  ifeq ($(MACHINE),nexuspci)
-    O_OBJS += plx9080.o
-  else
-    O_OBJS += bios32.o dec21285.o
-  endif
-endif
+# Translate to Rules.make lists.
 
-ifeq ($(CONFIG_LEDS),y)
-  OX_OBJS += leds-$(MACHINE).o
-endif
-
-ifeq ($(CONFIG_MODULES),y)
-  OX_OBJS += $(OX_OBJS_$(MACHINE))
-else
-  O_OBJS += $(OX_OBJS_$(MACHINE))
-endif
+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)))
 
-ifeq ($(CONFIG_ARTHUR),y)
-  O_OBJS += arthur.o
-else
-  ifeq ($(CONFIG_ARTHUR),m)
-    M_OBJS += arthur.o
-  endif
-endif
+all: kernel.o $(HEAD_OBJ) init_task.o
 
 $(HEAD_OBJ): $(HEAD_OBJ:.o=.S)
 	$(CC) -D__ASSEMBLY__ $(AFLAGS) -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@
diff -u --recursive --new-file v2.3.51/linux/arch/arm/kernel/arch.c linux/arch/arm/kernel/arch.c
--- v2.3.51/linux/arch/arm/kernel/arch.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/arch.c	Mon Mar 13 13:55:09 2000
@@ -0,0 +1,322 @@
+/*
+ * linux/arch/arm/kernel/arch.c
+ *
+ * Architecture specifics
+ */
+#include <linux/config.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+#include <asm/elf.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+
+#include "arch.h"
+
+extern unsigned int system_rev;
+extern unsigned int system_serial_low;
+extern unsigned int system_serial_high;
+
+unsigned int vram_size;
+#ifdef CONFIG_ARCH_ACORN
+unsigned int memc_ctrl_reg;
+unsigned int number_mfm_drives;
+#endif
+
+/*
+ * 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.
+ */
+static void __init
+fixup_acorn(struct machine_desc *desc, struct param_struct *params,
+	    char **cmdline, struct meminfo *mi)
+{
+#ifdef CONFIG_ARCH_ACORN
+	int i;
+
+	if (machine_is_riscpc()) {
+		/*
+		 * RiscPC can't handle half-word loads and stores
+		 */
+		elf_hwcap &= ~HWCAP_HALF;
+
+		switch (params->u1.s.pages_in_vram) {
+		case 512:
+			vram_size += PAGE_SIZE * 256;
+		case 256:
+			vram_size += PAGE_SIZE * 256;
+		default:
+			break;
+		}
+
+		if (vram_size) {
+			desc->video_start = 0x02000000;
+			desc->video_end   = 0x02000000 + vram_size;
+		}
+
+		for (i = 0; i < 4; i++) {
+			mi->bank[i].start = PHYS_OFFSET + (i << 26);
+			mi->bank[i].size  =
+				params->u1.s.pages_in_bank[i] *
+				params->u1.s.page_size;
+		}
+		mi->nr_banks = 4;
+	}
+	memc_ctrl_reg	  = params->u1.s.memc_control_reg;
+	number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
+#endif
+}
+
+static void __init
+fixup_ebsa285(struct machine_desc *desc, struct param_struct *params,
+	      char **cmdline, struct meminfo *mi)
+{
+	ORIG_X		 = params->u1.s.video_x;
+	ORIG_Y		 = params->u1.s.video_y;
+	ORIG_VIDEO_COLS  = params->u1.s.video_num_cols;
+	ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
+}
+
+/*
+ * Older NeTTroms either do not provide a parameters
+ * page, or they don't supply correct information in
+ * the parameter page.
+ */
+static void __init
+fixup_netwinder(struct machine_desc *desc, struct param_struct *params,
+		char **cmdline, struct meminfo *mi)
+{
+	if (params->u1.s.nr_pages != 0x2000 &&
+	    params->u1.s.nr_pages != 0x4000) {
+		printk(KERN_WARNING "Warning: bad NeTTrom parameters "
+		       "detected, using defaults\n");
+
+		params->u1.s.nr_pages = 0x2000;	/* 32MB */
+		params->u1.s.ramdisk_size = 0;
+		params->u1.s.flags = FLAG_READONLY;
+		params->u1.s.initrd_start = 0;
+		params->u1.s.initrd_size = 0;
+		params->u1.s.rd_start = 0;
+	}
+}
+
+/*
+ * CATS uses soft-reboot by default, since
+ * hard reboots fail on early boards.
+ */
+static void __init
+fixup_cats(struct machine_desc *desc, struct param_struct *params,
+	   char **cmdline, struct meminfo *mi)
+{
+	ORIG_VIDEO_LINES  = 25;
+	ORIG_VIDEO_POINTS = 16;
+	ORIG_Y = 24;
+}
+
+static void __init
+fixup_coebsa285(struct machine_desc *desc, struct param_struct *params,
+		char **cmdline, struct meminfo *mi)
+{
+#if 0
+	extern unsigned long boot_memory_end;
+	extern char boot_command_line[];
+
+	mi->nr_banks      = 1;
+	mi->bank[0].start = PHYS_OFFSET;
+	mi->bank[0].size  = boot_memory_end;
+
+	*cmdline = boot_command_line;
+#endif
+}
+
+static void __init
+fixup_sa1100(struct machine_desc *desc, struct param_struct *params,
+	     char **cmdline, struct meminfo *mi)
+{
+#ifdef CONFIG_ARCH_SA1100
+	int i;
+	extern struct mem_desc {
+		unsigned long phys_start;
+		unsigned long length;
+	} mem_desc[];
+	extern unsigned int mem_desc_size;
+
+	for( i = 0; i < mem_desc_size; i++ ) {
+		if( i >= NR_BANKS ) {
+			printk( __FUNCTION__ 
+				": mem_desc too large for meminfo structure\n");
+			break;
+		}
+		mi->bank[i].start = mem_desc[i].phys_start;
+		mi->bank[i].size = mem_desc[i].length;
+	}
+	mi->nr_banks = i;
+
+#if defined(CONFIG_SA1100_BRUTUS)
+	ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
+	setup_ramdisk( 1, 0, 0, 8192 );
+	setup_initrd( __phys_to_virt(0xd8000000), 0x00400000 );
+#elif defined(CONFIG_SA1100_EMPEG)
+	ROOT_DEV = MKDEV( 3, 1 );  /* /dev/hda1 */
+	setup_ramdisk( 1, 0, 0, 4096 );
+	setup_initrd( 0xd0000000+((1024-320)*1024), (320*1024) );
+#elif defined(CONFIG_SA1100_TIFON)
+	ROOT_DEV = MKDEV(UNNAMED_MAJOR, 0);
+	setup_ramdisk(1, 0, 0, 4096);
+	setup_initrd( 0xd0000000 + 0x1100004, 0x140000 );
+#elif defined(CONFIG_SA1100_VICTOR)
+	ROOT_DEV = MKDEV( 60, 2 );
+
+	/* Get command line parameters passed from the loader (if any) */
+	if( *((char*)0xc0000000) )
+		strcpy( default_command_line, ((char *)0xc0000000) );
+
+	/* power off if any problem */
+	strcat( default_command_line, " panic=1" );
+#elif defined(CONFIG_SA1100_LART)
+	ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
+	setup_ramdisk(1, 0, 0, 8192);
+	setup_initrd(0xc0400000, 0x00400000);
+#endif
+#endif
+}
+
+#define NO_PARAMS	0
+#define NO_VIDEO	0, 0
+
+/*
+ * This is the list of all architectures supported by
+ * this kernel.  This should be integrated with the list
+ * in head-armv.S.
+ */
+static struct machine_desc machine_desc[] __attribute__ ((__section__ (".arch.info"))) = {
+	{
+		MACH_TYPE_EBSA110,
+		"EBSA110",	/* RMK			*/
+		0x00000400,
+		NO_VIDEO,
+		1, 0, 1, 1, 1,
+		NULL
+	}, {
+		MACH_TYPE_RISCPC,
+		"Acorn-RiscPC",	/* RMK			*/
+		0x10000100,
+		NO_VIDEO,
+		1, 1, 0, 0, 0,
+		fixup_acorn
+	}, {
+		2,
+		"unknown",
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, {
+		MACH_TYPE_NEXUSPCI,
+		"FTV/PCI",	/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, {
+		MACH_TYPE_EBSA285,
+		"EBSA285",		/* RMK			*/
+		0x00000100,
+		0x000a0000, 0x000bffff,
+		0, 0, 0, 0, 0,
+		fixup_ebsa285
+	}, {
+		MACH_TYPE_NETWINDER,
+		"Rebel-NetWinder",	/* RMK			*/
+		0x00000100,
+		0x000a0000, 0x000bffff,
+		1, 0, 1, 0, 0,
+		fixup_netwinder
+	}, {
+		MACH_TYPE_CATS,
+		"Chalice-CATS",	/* Philip Blundell	*/
+		NO_PARAMS,
+		0x000a0000, 0x000bffff,
+		0, 0, 0, 0, 1,
+		fixup_cats
+	}, {
+		MACH_TYPE_TBOX,
+		"unknown-TBOX",	/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, {
+		MACH_TYPE_CO285,
+		"co-EBSA285",	/* Mark van Doesburg	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		fixup_coebsa285
+	}, {
+		MACH_TYPE_CLPS7110,
+		"CL-PS7110",	/* Werner Almesberger	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, {
+		MACH_TYPE_ARCHIMEDES,
+		"Acorn-Archimedes",/* RMK/DAG		*/
+		0x0207c000,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		fixup_acorn
+	}, {
+		MACH_TYPE_A5K,
+		"Acorn-A5000",	/* RMK/PB		*/
+		0x0207c000,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		fixup_acorn
+	}, {
+		MACH_TYPE_ETOILE,
+		"Etoile",		/* Alex de Vries	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, {
+		MACH_TYPE_LACIE_NAS,
+		"LaCie_NAS",	/* Benjamin Herrenschmidt */
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, {
+		MACH_TYPE_CLPS7500,
+		"CL-PS7500",	/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, {
+		MACH_TYPE_SHARK,
+		"Shark",		/* Alexander Schulz	*/
+		NO_PARAMS,
+		0x06000000, 0x06000000+0x001fffff,
+		0, 0, 0, 0, 0,
+		NULL
+	}, {
+		MACH_TYPE_SA1100,
+		"SA1100-based",	/* Nicolas Pitre	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		fixup_sa1100
+	}, {
+		MACH_TYPE_PERSONAL_SERVER,
+		"Compaq Personal Server",
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}
+};
diff -u --recursive --new-file v2.3.51/linux/arch/arm/kernel/arch.h linux/arch/arm/kernel/arch.h
--- v2.3.51/linux/arch/arm/kernel/arch.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/arch.h	Sun Mar 12 19:39:39 2000
@@ -0,0 +1,15 @@
+struct machine_desc {
+	unsigned int	nr;		/* architecture number	*/
+	const char	*name;		/* architecture name	*/
+	unsigned int	param_offset;	/* parameter page	*/
+	unsigned int	video_start;	/* start of video RAM	*/
+	unsigned int	video_end;	/* end of video RAM	*/
+	unsigned int	reserve_lp0 :1;	/* never has lp0	*/
+	unsigned int	reserve_lp1 :1;	/* never has lp1	*/
+	unsigned int	reserve_lp2 :1;	/* never has lp2	*/
+	unsigned int	broken_hlt  :1;	/* hlt is broken	*/
+	unsigned int	soft_reboot :1;	/* soft reboot		*/
+	void		(*fixup)(struct machine_desc *,
+				 struct param_struct *, char **,
+				 struct meminfo *);
+};
diff -u --recursive --new-file v2.3.51/linux/arch/arm/kernel/armksyms.c linux/arch/arm/kernel/armksyms.c
--- v2.3.51/linux/arch/arm/kernel/armksyms.c	Sat Feb 26 22:31:38 2000
+++ linux/arch/arm/kernel/armksyms.c	Sun Mar 12 19:39:39 2000
@@ -8,6 +8,7 @@
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/in6.h>
+#include <linux/vt_kern.h>
 
 #include <asm/byteorder.h>
 #include <asm/elf.h>
@@ -82,6 +83,8 @@
 EXPORT_SYMBOL(fpundefinstr);
 EXPORT_SYMBOL(ret_from_exception);
 #endif
+
+EXPORT_SYMBOL(kd_mksound);
 
 	/* platform dependent support */
 EXPORT_SYMBOL(dump_thread);
diff -u --recursive --new-file v2.3.51/linux/arch/arm/kernel/bios32.c linux/arch/arm/kernel/bios32.c
--- v2.3.51/linux/arch/arm/kernel/bios32.c	Thu Feb 10 17:11:03 2000
+++ linux/arch/arm/kernel/bios32.c	Sun Mar 12 19:39:39 2000
@@ -222,8 +222,9 @@
 	return 0;
 }
 
-#ifdef CONFIG_FOOTBRIDGE
 /* ebsa285 host-specific stuff */
+
+#ifdef CONFIG_ARCH_EBSA285
 static int irqmap_ebsa285[] __initdata = { IRQ_IN1, IRQ_IN0, IRQ_PCI, IRQ_IN3 };
 
 static u8 __init ebsa285_swizzle(struct pci_dev *dev, u8 *pin)
@@ -251,7 +252,9 @@
 	ebsa285_swizzle,
 	ebsa285_map_irq
 };
+#endif
 
+#ifdef CONFIG_CATS
 /* cats host-specific stuff */
 static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };
 
@@ -277,7 +280,9 @@
 	no_swizzle,
 	cats_map_irq
 };
+#endif
 
+#ifdef CONFIG_ARCH_NETWINDER
 /* netwinder host-specific stuff */
 static int __init netwinder_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
 {
@@ -318,6 +323,41 @@
 };
 #endif
 
+#ifdef CONFIG_PERSONAL_SERVER
+static int irqmap_personal_server[] __initdata = {
+	IRQ_IN0, IRQ_IN1, IRQ_IN2, IRQ_IN3, 0, 0, 0,
+	IRQ_DOORBELLHOST, IRQ_DMA1, IRQ_DMA2, IRQ_PCI
+};
+
+static int __init personal_server_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	unsigned char line;
+
+	pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line);
+
+	if (line > 0x40 && line <= 0x5f) {
+		/* line corresponds to the bit controlling this interrupt
+		 * in the footbridge.  Ignore the first 8 interrupt bits,
+		 * look up the rest in the map.  IN0 is bit number 8
+		 */
+		return irqmap_personal_server[(line & 0x1f) - 8];
+	} else if (line == 0) {
+		/* no interrupt */
+		return 0;
+	} else
+		return irqmap_personal_server[(line - 1) & 3];
+}
+
+static struct hw_pci personal_server_pci __initdata = {
+	dc21285_init,
+	0x9000,
+	0x00100000,
+	no_swizzle,
+	personal_server_map_irq
+};
+
+#endif
+
 #ifdef CONFIG_ARCH_NEXUSPCI
 /*
  * Owing to a PCB cockup, issue A backplanes are wired thus:
@@ -352,17 +392,38 @@
 {
 	struct hw_pci *hw_pci = NULL;
 
-#ifdef CONFIG_FOOTBRIDGE
-	if (machine_is_ebsa285())
-		hw_pci = &ebsa285_pci;
-	else if (machine_is_cats())
-		hw_pci = &cats_pci;
-	else if (machine_is_netwinder())
-		hw_pci = &netwinder_pci;
+	do {
+#ifdef CONFIG_ARCH_EBSA285
+		if (machine_is_ebsa285()) {
+			hw_pci = &ebsa285_pci;
+			break;
+		}
+#endif
+#ifdef CONFIG_CATS
+		if (machine_is_cats()) {
+			hw_pci = &cats_pci;
+			break;
+		}
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+		if (machine_is_netwinder()) {
+			hw_pci = &netwinder_pci;
+			break;
+		}
+#endif
+#ifdef CONFIG_PERSONAL_SERVER
+		if (machine_is_personal_server()) {
+			hw_pci = &personal_server_pci;
+			break;
+		}
 #endif
 #ifdef CONFIG_ARCH_NEXUSPCI
-	hw_pci = &ftv_pci;
+		if (machine_is_nexuspci()) {
+			hw_pci = &ftv_pci;
+			break;
+		}
 #endif
+	} while (0);			
 
 	if (hw_pci == NULL)
 		return;
diff -u --recursive --new-file v2.3.51/linux/arch/arm/kernel/head-armv.S linux/arch/arm/kernel/head-armv.S
--- v2.3.51/linux/arch/arm/kernel/head-armv.S	Fri Jan 21 18:19:15 2000
+++ linux/arch/arm/kernel/head-armv.S	Sun Mar 12 19:39:39 2000
@@ -22,6 +22,7 @@
 		.equ	SYMBOL_NAME(swapper_pg_dir),	TEXTADDR - 0x8000 + SWAPPER_PGDIR_OFFSET
 
 		.section ".text.init",#alloc,#execinstr
+		.type	stext, #function
 ENTRY(stext)
 ENTRY(_stext)
 
@@ -311,6 +312,7 @@
  * required for debugging information to be shown to the user.
  * paging_init() does the real page table initialisation.
  */
+		.type	__arch_types_start, #object
 		@ 0x00 - DEC EBSA110
 __arch_types_start:
 		.long	0
@@ -398,15 +400,15 @@
 
 		@ 0x0e - CL-PS7500
 		.long	0
-		.long	0
-		.long	0
-		.long	0
+		.long	0x10000000
+		.long	0x03000000
+		.long	0xe0000000
 
-		@ 0x0f - Shark
-		.long	0
-		.long	0
-		.long	0
+		@ 0x0f - Digital Shark (DNARD)
 		.long	0
+		.long	0x08000000
+		.long	0x40000000
+		.long	0xe0000000
 
 		@ 0x10 - SA1100
 		.long	0
diff -u --recursive --new-file v2.3.51/linux/arch/arm/kernel/ptrace.c linux/arch/arm/kernel/ptrace.c
--- v2.3.51/linux/arch/arm/kernel/ptrace.c	Fri Oct 22 13:21:44 1999
+++ linux/arch/arm/kernel/ptrace.c	Sun Mar 12 19:39:39 2000
@@ -521,6 +521,48 @@
 			/* give it a chance to run. */
 			ret = 0;
 			goto out;
+			
+		case PTRACE_GETREGS:
+		{	/* Get all gp regs from the child. */
+			unsigned char *stack;
+
+			ret = 0;
+			stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs));
+			if (copy_to_user((void *)data, stack,
+					 sizeof(struct pt_regs)))
+				ret = -EFAULT;
+
+			goto out;
+		};
+
+		case PTRACE_SETREGS:
+		{
+			/* Set all gp regs in the child. */
+			unsigned char *stack;
+
+			ret = 0;
+			stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs));
+			if (copy_from_user(stack, (void *)data,
+					   sizeof(struct pt_regs)))
+				ret = -EFAULT;
+			goto out;
+		  };
+
+		case PTRACE_GETFPREGS: 
+			/* Get the child FPU state. */
+			ret = 0;
+			if (copy_to_user((void *)data, &child->thread.fpstate,
+					 sizeof(struct user_fp)))
+				ret = -EFAULT;
+			goto out;
+		
+		case PTRACE_SETFPREGS:
+			/* Set the child FPU state. */
+			ret = 0;
+			if (copy_from_user(&child->thread.fpstate, (void *)data,
+					   sizeof(struct user_fp)))
+				ret = -EFAULT;
+			goto out;
 
 		case PTRACE_DETACH:				/* detach a process that was attached. */
 			ret = -EIO;
diff -u --recursive --new-file v2.3.51/linux/arch/arm/kernel/setup.c linux/arch/arm/kernel/setup.c
--- v2.3.51/linux/arch/arm/kernel/setup.c	Thu Mar  2 14:36:22 2000
+++ linux/arch/arm/kernel/setup.c	Sun Mar 12 19:39:39 2000
@@ -1,13 +1,12 @@
 /*
  *  linux/arch/arm/kernel/setup.c
  *
- *  Copyright (C) 1995-1999 Russell King
+ *  Copyright (C) 1995-2000 Russell King
  */
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/stddef.h>
-#include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/utsname.h>
@@ -23,6 +22,8 @@
 #include <asm/setup.h>
 #include <asm/system.h>
 
+#include "arch.h"
+
 #ifndef MEM_SIZE
 #define MEM_SIZE	(16*1024*1024)
 #endif
@@ -31,6 +32,7 @@
 #define CONFIG_CMDLINE ""
 #endif
 
+extern void paging_init(struct meminfo *);
 extern void reboot_setup(char *str);
 extern void disable_hlt(void);
 extern int root_mountflags;
@@ -38,33 +40,11 @@
 
 unsigned int processor_id;
 unsigned int __machine_arch_type;
-unsigned int vram_size;
 unsigned int system_rev;
 unsigned int system_serial_low;
 unsigned int system_serial_high;
 unsigned int elf_hwcap;
 
-#ifdef CONFIG_ARCH_ACORN
-unsigned int memc_ctrl_reg;
-unsigned int number_mfm_drives;
-#endif
-
-struct meminfo meminfo;
-
-struct machine_desc {
-	const char	*name;		/* architecture name	*/
-	unsigned int	param_offset;	/* parameter page	*/
-	unsigned int	video_start;	/* start of video RAM	*/
-	unsigned int	video_end;	/* end of video RAM	*/
-	unsigned int	reserve_lp0 :1;	/* never has lp0	*/
-	unsigned int	reserve_lp1 :1;	/* never has lp1	*/
-	unsigned int	reserve_lp2 :1;	/* never has lp2	*/
-	unsigned int	broken_hlt  :1;	/* hlt is broken	*/
-	unsigned int	soft_reboot :1;	/* soft reboot		*/
-	void		(*fixup)(struct machine_desc *,
-				 struct param_struct *, char **);
-};
-
 #ifdef MULTI_CPU
 struct processor processor;
 #endif
@@ -156,6 +136,33 @@
 	cpu_proc_init();
 }
 
+static struct machine_desc * __init setup_architecture(unsigned int nr)
+{
+	extern struct machine_desc __arch_info_begin, __arch_info_end;
+	struct machine_desc *list;
+
+	/*
+	 * locate architecture in the list of supported architectures.
+	 */
+	for (list = &__arch_info_begin; list < &__arch_info_end; list++)
+		if (list->nr == nr)
+			break;
+
+	/*
+	 * If the architecture type is not recognised, then we
+	 * can co nothing...
+	 */
+	if (list >= &__arch_info_end) {
+		printk("Architecture configuration botched (nr %d), unable "
+		       "to continue.\n", nr);
+		while (1);
+	}
+
+	printk("Architecture: %s\n", list->name);
+
+	return list;
+}
+
 static unsigned long __init memparse(char *ptr, char **retptr)
 {
 	unsigned long ret = simple_strtoul(ptr, retptr, 0);
@@ -180,7 +187,7 @@
  * are "size[KkMm]"
  */
 static void __init
-parse_cmdline(char **cmdline_p, char *from)
+parse_cmdline(struct meminfo *mi, char **cmdline_p, char *from)
 {
 	char c = ' ', *to = command_line;
 	int usermem = 0, len = 0;
@@ -198,7 +205,7 @@
 			 */
 			if (usermem == 0) {
 				usermem = 1;
-				meminfo.nr_banks = 0;
+				mi->nr_banks = 0;
 			}
 
 			start = PHYS_OFFSET;
@@ -206,9 +213,9 @@
 			if (*from == '@')
 				start = memparse(from + 1, &from);
 
-			meminfo.bank[meminfo.nr_banks].start = start;
-			meminfo.bank[meminfo.nr_banks].size  = size;
-			meminfo.nr_banks += 1;
+			mi->bank[mi->nr_banks].start = start;
+			mi->bank[mi->nr_banks].size  = size;
+			mi->nr_banks += 1;
 		}
 		c = *from++;
 		if (!c)
@@ -265,7 +272,8 @@
 #define free_bootmem(s,sz)	free_bootmem((s)<<PAGE_SHIFT, (sz)<<PAGE_SHIFT)
 #define reserve_bootmem(s,sz)	reserve_bootmem((s)<<PAGE_SHIFT, (sz)<<PAGE_SHIFT)
 
-static unsigned int __init find_bootmap_pfn(unsigned int bootmap_pages)
+static unsigned int __init
+find_bootmap_pfn(struct meminfo *mi, unsigned int bootmap_pages)
 {
 	unsigned int start_pfn, bank, bootmap_pfn;
 
@@ -278,25 +286,25 @@
 	 */
 #ifdef CONFIG_BLK_DEV_INITRD
 	if (initrd_start) {
-		if (__pa(initrd_end) > meminfo.end) {
+		if (__pa(initrd_end) > mi->end) {
 			printk ("initrd extends beyond end of memory "
 				"(0x%08lx > 0x%08lx) - disabling initrd\n",
-				__pa(initrd_end), meminfo.end);
+				__pa(initrd_end), mi->end);
 			initrd_start = 0;
 			initrd_end   = 0;
 		}
 	}
 #endif
 
-	for (bank = 0; bank < meminfo.nr_banks; bank ++) {
+	for (bank = 0; bank < mi->nr_banks; bank ++) {
 		unsigned int start, end;
 
-		if (meminfo.bank[bank].size == 0)
+		if (mi->bank[bank].size == 0)
 			continue;
 
-		start = O_PFN_UP(meminfo.bank[bank].start);
-		end   = O_PFN_DOWN(meminfo.bank[bank].size +
-				   meminfo.bank[bank].start);
+		start = O_PFN_UP(mi->bank[bank].start);
+		end   = O_PFN_DOWN(mi->bank[bank].size +
+				   mi->bank[bank].start);
 
 		if (end < start_pfn)
 			continue;
@@ -322,7 +330,7 @@
 /*
  * Initialise the bootmem allocator.
  */
-static void __init setup_bootmem(void)
+static void __init setup_bootmem(struct meminfo *mi)
 {
 	unsigned int end_pfn, start_pfn, bootmap_pages, bootmap_pfn;
 	unsigned int i;
@@ -330,21 +338,21 @@
 	/*
 	 * Calculate the  physical address of the top of memory.
 	 */
-	meminfo.end = 0;
-	for (i = 0; i < meminfo.nr_banks; i++) {
+	mi->end = 0;
+	for (i = 0; i < mi->nr_banks; i++) {
 		unsigned long end;
 
-		if (meminfo.bank[i].size != 0) {
-			end = meminfo.bank[i].start + meminfo.bank[i].size;
-			if (meminfo.end < end)
-				meminfo.end = end;
+		if (mi->bank[i].size != 0) {
+			end = mi->bank[i].start + mi->bank[i].size;
+			if (mi->end < end)
+				mi->end = end;
 		}
 	}
 
 	start_pfn     = O_PFN_UP(PHYS_OFFSET);
-	end_pfn	      = O_PFN_DOWN(meminfo.end);
+	end_pfn	      = O_PFN_DOWN(mi->end);
 	bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
-	bootmap_pfn   = find_bootmap_pfn(bootmap_pages);
+	bootmap_pfn   = find_bootmap_pfn(mi, bootmap_pages);
 
 	/*
 	 * Initialise the boot-time allocator
@@ -354,10 +362,10 @@
 	/*
 	 * Register all available RAM with the bootmem allocator.
 	 */
-	for (i = 0; i < meminfo.nr_banks; i++)
-		if (meminfo.bank[i].size)
-			free_bootmem(O_PFN_UP(meminfo.bank[i].start),
-				     PFN_SIZE(meminfo.bank[i].size));
+	for (i = 0; i < mi->nr_banks; i++)
+		if (mi->bank[i].size)
+			free_bootmem(O_PFN_UP(mi->bank[i].start),
+				     PFN_SIZE(mi->bank[i].size));
 
 	/*
 	 * Register the reserved regions with bootmem
@@ -379,7 +387,8 @@
 #endif
 }
 
-static void __init request_standard_resources(struct machine_desc *mdesc)
+static void __init
+request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc)
 {
 	struct resource *res;
 	int i;
@@ -389,14 +398,14 @@
 	kernel_data.start  = __virt_to_bus(init_mm.end_code);
 	kernel_data.end    = __virt_to_bus(init_mm.brk - 1);
 
-	for (i = 0; i < meminfo.nr_banks; i++) {
+	for (i = 0; i < mi->nr_banks; i++) {
 		unsigned long virt_start, virt_end;
 
-		if (meminfo.bank[i].size == 0)
+		if (mi->bank[i].size == 0)
 			continue;
 
-		virt_start = __phys_to_virt(meminfo.bank[i].start);
-		virt_end   = virt_start + meminfo.bank[i].size - 1;
+		virt_start = __phys_to_virt(mi->bank[i].start);
+		virt_end   = virt_start + mi->bank[i].size - 1;
 
 		res = alloc_bootmem_low(sizeof(*res));
 		res->name  = "System RAM";
@@ -432,270 +441,15 @@
 		request_resource(&ioport_resource, &lp2);
 }
 
-/*
- * 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.
- */
-static void __init
-fixup_acorn(struct machine_desc *desc, struct param_struct *params,
-	    char **cmdline)
-{
-#ifdef CONFIG_ARCH_ACORN
-	int i;
-
-	if (machine_is_riscpc()) {
-		/*
-		 * RiscPC can't handle half-word loads and stores
-		 */
-		elf_hwcap &= ~HWCAP_HALF;
-
-		switch (params->u1.s.pages_in_vram) {
-		case 512:
-			vram_size += PAGE_SIZE * 256;
-		case 256:
-			vram_size += PAGE_SIZE * 256;
-		default:
-			break;
-		}
-
-		if (vram_size) {
-			desc->video_start = 0x02000000;
-			desc->video_end   = 0x02000000 + vram_size;
-		}
-
-		for (i = 0; i < 4; i++) {
-			meminfo.bank[i].start = PHYS_OFFSET + (i << 26);
-			meminfo.bank[i].size  =
-				params->u1.s.pages_in_bank[i] *
-				params->u1.s.page_size;
-		}
-		meminfo.nr_banks = 4;
-	}
-	memc_ctrl_reg	  = params->u1.s.memc_control_reg;
-	number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
-#endif
-}
-
-static void __init
-fixup_ebsa285(struct machine_desc *desc, struct param_struct *params,
-	      char **cmdline)
-{
-	ORIG_X		 = params->u1.s.video_x;
-	ORIG_Y		 = params->u1.s.video_y;
-	ORIG_VIDEO_COLS  = params->u1.s.video_num_cols;
-	ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
-}
-
-/*
- * Older NeTTroms either do not provide a parameters
- * page, or they don't supply correct information in
- * the parameter page.
- */
-static void __init
-fixup_netwinder(struct machine_desc *desc, struct param_struct *params,
-		char **cmdline)
-{
-	if (params->u1.s.nr_pages != 0x2000 &&
-	    params->u1.s.nr_pages != 0x4000) {
-		printk(KERN_WARNING "Warning: bad NeTTrom parameters "
-		       "detected, using defaults\n");
-
-		params->u1.s.nr_pages = 0x2000;	/* 32MB */
-		params->u1.s.ramdisk_size = 0;
-		params->u1.s.flags = FLAG_READONLY;
-		params->u1.s.initrd_start = 0;
-		params->u1.s.initrd_size = 0;
-		params->u1.s.rd_start = 0;
-	}
-}
-
-/*
- * CATS uses soft-reboot by default, since
- * hard reboots fail on early boards.
- */
-static void __init
-fixup_cats(struct machine_desc *desc, struct param_struct *params,
-	   char **cmdline)
-{
-	ORIG_VIDEO_LINES  = 25;
-	ORIG_VIDEO_POINTS = 16;
-	ORIG_Y = 24;
-}
-
-static void __init
-fixup_coebsa285(struct machine_desc *desc, struct param_struct *params,
-		char **cmdline)
-{
-#if 0
-	extern unsigned long boot_memory_end;
-	extern char boot_command_line[];
-
-	meminfo.nr_banks      = 1;
-	meminfo.bank[0].start = PHYS_OFFSET;
-	meminfo.bank[0].size  = boot_memory_end;
-
-	*cmdline = boot_command_line;
-#endif
-}
-
-static void __init
-fixup_sa1100(struct machine_desc *desc, struct param_struct *params,
-		char **cmdline)
-{
-#ifdef CONFIG_ARCH_SA1100
-	int i;
-	extern struct mem_desc {
-		unsigned long phys_start;
-		unsigned long length;
-	} mem_desc[];
-	extern unsigned int mem_desc_size;
-
-	for( i = 0; i < mem_desc_size; i++ ) {
-		if( i >= NR_BANKS ) {
-			printk( __FUNCTION__ 
-				": mem_desc too large for meminfo structure\n");
-			break;
-		}
-		meminfo.bank[i].start = mem_desc[i].phys_start;
-		meminfo.bank[i].size = mem_desc[i].length;
-	}
-	meminfo.nr_banks = i;
-
-#if defined(CONFIG_SA1100_BRUTUS)
-	ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
-	setup_ramdisk( 1, 0, 0, 8192 );
-	setup_initrd( __phys_to_virt(0xd8000000), 0x00400000 );
-#elif defined(CONFIG_SA1100_EMPEG)
-	ROOT_DEV = MKDEV( 3, 1 );  /* /dev/hda1 */
-	setup_ramdisk( 1, 0, 0, 4096 );
-	setup_initrd( 0xd0000000+((1024-320)*1024), (320*1024) );
-#elif defined(CONFIG_SA1100_TIFON)
-	ROOT_DEV = MKDEV(UNNAMED_MAJOR, 0);
-	setup_ramdisk(1, 0, 0, 4096);
-	setup_initrd( 0xd0000000 + 0x1100004, 0x140000 );
-#elif defined(CONFIG_SA1100_VICTOR)
-	ROOT_DEV = MKDEV( 60, 2 );
-
-	/* Get command line parameters passed from the loader (if any) */
-	if( *((char*)0xc0000000) )
-		strcpy( default_command_line, ((char *)0xc0000000) );
-
-	/* power off if any problem */
-	strcat( default_command_line, " panic=1" );
-#elif defined(CONFIG_SA1100_LART)
-	ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
-	setup_ramdisk(1, 0, 0, 8192);
-	setup_initrd(0xc0400000, 0x00400000);
-#endif
-#endif
-}
-
-#define NO_PARAMS	0
-#define NO_VIDEO	0, 0
-
-/*
- * This is the list of all architectures supported by
- * this kernel.  This should be integrated with the list
- * in head-armv.S.
- */
-static struct machine_desc machine_desc[] __initdata = {
-	{ "EBSA110",		/* RMK			*/
-		0x00000400,
-		NO_VIDEO,
-		1, 0, 1, 1, 1,
-		NULL
-	}, { "Acorn-RiscPC",	/* RMK			*/
-		0x10000100,
-		NO_VIDEO,
-		1, 1, 0, 0, 0,
-		fixup_acorn
-	}, { "unknown",
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		NULL
-	}, { "FTV/PCI",		/* Philip Blundell	*/
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		NULL
-	}, { "EBSA285",		/* RMK			*/
-		0x00000100,
-		0x000a0000, 0x000bffff,
-		0, 0, 0, 0, 0,
-		fixup_ebsa285
-	}, { "Rebel-NetWinder",	/* RMK			*/
-		0x00000100,
-		0x000a0000, 0x000bffff,
-		1, 0, 1, 0, 0,
-		fixup_netwinder
-	}, { "Chalice-CATS",	/* Philip Blundell	*/
-		NO_PARAMS,
-		0x000a0000, 0x000bffff,
-		0, 0, 0, 0, 1,
-		fixup_cats
-	}, { "unknown-TBOX",	/* Philip Blundell	*/
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		NULL
-	}, { "co-EBSA285",	/* Mark van Doesburg	*/
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		fixup_coebsa285
-	}, { "CL-PS7110",	/* Werner Almesberger	*/
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		NULL
-	}, { "Acorn-Archimedes",/* RMK/DAG		*/
-		0x0207c000,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		fixup_acorn
-	}, { "Acorn-A5000",	/* RMK/PB		*/
-		0x0207c000,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		fixup_acorn
-	}, { "Etoile",		/* Alex de Vries	*/
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		NULL
-	}, { "LaCie_NAS",	/* Benjamin Herrenschmidt */
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		NULL
-	}, { "CL-PS7500",	/* Philip Blundell	*/
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		NULL
-	}, { "Shark",		/* Alexander Schulz	*/
-		NO_PARAMS,
-		/* do you really mean 0x200000? */
-		0x06000000, 0x06000000+0x00200000,
-		0, 0, 0, 0, 0,
-		NULL
-	}, { "SA1100-based",	/* Nicolas Pitre	*/
-		NO_PARAMS,
-		NO_VIDEO,
-		0, 0, 0, 0, 0,
-		fixup_sa1100
-	}
-};
-
 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));
+
 #if defined(CONFIG_ARCH_ARC)
 	__machine_arch_type = MACH_TYPE_ARCHIMEDES;
 #elif defined(CONFIG_ARCH_A5K)
@@ -706,7 +460,7 @@
 
 	ROOT_DEV = MKDEV(0, 255);
 
-	mdesc = machine_desc + machine_arch_type;
+	mdesc = setup_architecture(machine_arch_type);
 	machine_name = mdesc->name;
 
 	if (mdesc->broken_hlt)
@@ -719,7 +473,7 @@
 		params = phys_to_virt(mdesc->param_offset);
 
 	if (mdesc->fixup)
-		mdesc->fixup(mdesc, params, &from);
+		mdesc->fixup(mdesc, params, &from, &meminfo);
 
 	if (params && params->u1.s.page_size != PAGE_SIZE) {
 		printk(KERN_WARNING "Warning: bad configuration page, "
@@ -763,11 +517,11 @@
 
 	memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
 	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
-	parse_cmdline(cmdline_p, from);
-	setup_bootmem();
-	request_standard_resources(mdesc);
+	parse_cmdline(&meminfo, cmdline_p, from);
+	setup_bootmem(&meminfo);
+	request_standard_resources(&meminfo, mdesc);
 
-	paging_init();
+	paging_init(&meminfo);
 
 #ifdef CONFIG_VT
 #if defined(CONFIG_VGA_CONSOLE)
diff -u --recursive --new-file v2.3.51/linux/arch/arm/lib/Makefile linux/arch/arm/lib/Makefile
--- v2.3.51/linux/arch/arm/lib/Makefile	Fri Jan 21 18:19:15 2000
+++ linux/arch/arm/lib/Makefile	Sun Mar 12 19:39:39 2000
@@ -14,37 +14,25 @@
 O_TARGET := lib.o
 O_OBJS   := backtrace.o delay.o
 
+L_OBJS_arc		:= io-acorn.o
+L_OBJS_a5k		:= io-acorn.o floppydma.o
+L_OBJS_rpc		:= io-acorn.o floppydma.o
+L_OBJS_clps7500		:= io-acorn.o
+L_OBJS_ebsa110		:= io-ebsa110.o
+L_OBJS_footbridge	:= io-footbridge.o
+L_OBJS_nexuspci		:= io-footbridge.o
+L_OBJS_sa1100		:= io-footbridge.o
+L_OBJS_shark		:= io-shark.o
+
 ifeq ($(PROCESSOR),armo)
   L_OBJS += uaccess-armo.o
 endif
 
-ifdef CONFIG_ARCH_ACORN
-  L_OBJS += io-acorn.o
-  ifdef CONFIG_ARCH_A5K
-    L_OBJS += floppydma.o
-  endif
-  ifdef CONFIG_ARCH_RPC
-    L_OBJS += floppydma.o
-  endif
-endif
-
-ifeq ($(MACHINE),ebsa110)
-  L_OBJS += io-ebsa110.o
-else
+ifneq ($(MACHINE),ebsa110)
   OX_OBJS += io.o
 endif
 
-ifeq ($(MACHINE),footbridge)
-  L_OBJS += io-footbridge.o
-endif
-
-#
-# SA1100 IO routines happen to be the
-# same as the footbridge routines
-#
-ifeq ($(MACHINE),sa1100)
-  L_OBJS += io-footbridge.o
-endif
+L_OBJS	+= $(L_OBJS_$(MACHINE))
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.3.51/linux/arch/arm/lib/io-shark.c linux/arch/arm/lib/io-shark.c
--- v2.3.51/linux/arch/arm/lib/io-shark.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/lib/io-shark.c	Sun Mar 12 19:39:39 2000
@@ -0,0 +1,79 @@
+/*
+ * linux/arch/arm/lib/io-shark.c
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ *
+ * derived from:
+ * linux/arch/arm/lib/io-ebsa.S
+ * Copyright (C) 1995, 1996 Russell King
+ */
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+
+void print_warning(void)
+{
+	printk(KERN_WARNING "ins?/outs? not implemented on this architecture\n");
+}
+
+void insl(unsigned int port, void *to, int len)
+{
+	print_warning();
+}
+
+void insb(unsigned int port, void *to, int len)
+{
+	print_warning();
+}
+
+void outsl(unsigned int port, const void *from, int len)
+{
+	print_warning();
+}
+
+void outsb(unsigned int port, const void *from, int len)
+{
+	print_warning();
+}
+
+/* these should be in assembler again */
+
+/*
+ * Purpose: read a block of data from a hardware register to memory.
+ * Proto  : insw(int from_port, void *to, int len_in_words);
+ * Proto  : inswb(int from_port, void *to, int len_in_bytes);
+ * Notes  : increment to
+ */
+
+void insw(unsigned int port, void *to, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		((unsigned short *) to)[i] = inw(port);
+}
+
+void inswb(unsigned int port, void *to, int len)
+{
+	insw(port, to, len >> 2);
+}
+
+/*
+ * Purpose: write a block of data from memory to a hardware register.
+ * Proto  : outsw(int to_reg, void *from, int len_in_words);
+ * Proto  : outswb(int to_reg, void *from, int len_in_bytes);
+ * Notes  : increments from
+ */
+
+void outsw(unsigned int port, const void *from, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		outw(((unsigned short *) from)[i], port);
+}
+
+void outswb(unsigned int port, const void *from, int len)
+{
+	outsw(port, from, len >> 2);
+}
diff -u --recursive --new-file v2.3.51/linux/arch/arm/mm/init.c linux/arch/arm/mm/init.c
--- v2.3.51/linux/arch/arm/mm/init.c	Sun Feb 20 21:12:38 2000
+++ linux/arch/arm/mm/init.c	Sun Mar 12 19:39:39 2000
@@ -33,6 +33,7 @@
 #include "map.h"
 
 static unsigned long totalram_pages;
+struct meminfo meminfo;
 pgd_t swapper_pg_dir[PTRS_PER_PGD];
 
 /*
@@ -160,12 +161,14 @@
 /*
  * paging_init() sets up the page tables...
  */
-void __init paging_init(void)
+void __init paging_init(struct meminfo *mi)
 {
 	void *zero_page, *bad_page, *bad_table;
 	unsigned long zone_size[MAX_NR_ZONES];
 	int i;
 
+	memcpy(&meminfo, mi, sizeof(meminfo));
+
 #ifdef CONFIG_CPU_32
 #define TABLE_OFFSET	(PTRS_PER_PTE)
 #else
@@ -197,12 +200,12 @@
 	 * any problems with DMA or highmem, so all memory is
 	 * allocated to the DMA zone.
 	 */
-	for (i = 0; i < meminfo.nr_banks; i++) {
-		if (meminfo.bank[i].size) {
+	for (i = 0; i < mi->nr_banks; i++) {
+		if (mi->bank[i].size) {
 			unsigned int end;
 
-			end = (meminfo.bank[i].start - PHYS_OFFSET +
-			       meminfo.bank[i].size) >> PAGE_SHIFT;
+			end = (mi->bank[i].start - PHYS_OFFSET +
+			       mi->bank[i].size) >> PAGE_SHIFT;
 			if (zone_size[0] < end)
 				zone_size[0] = end;
 		}
@@ -327,24 +330,6 @@
 	free_area((unsigned long)(&__init_begin),
 		  (unsigned long)(&__init_end),
 		  "init");
-
-#ifdef CONFIG_FOOTBRIDGE
-	{
-	extern int __netwinder_begin, __netwinder_end,
-		   __ebsa285_begin, __ebsa285_end;
-
-	if (!machine_is_netwinder())
-		free_area((unsigned long)(&__netwinder_begin),
-			  (unsigned long)(&__netwinder_end),
-			  "netwinder");
-
-	if (!machine_is_ebsa285() && !machine_is_cats() &&
-	    !machine_is_co285())
-		free_area((unsigned long)(&__ebsa285_begin),
-			  (unsigned long)(&__ebsa285_end),
-			  "ebsa285/cats");
-	}
-#endif
 
 	printk("\n");
 }
diff -u --recursive --new-file v2.3.51/linux/arch/arm/mm/proc-arm6,7.S linux/arch/arm/mm/proc-arm6,7.S
--- v2.3.51/linux/arch/arm/mm/proc-arm6,7.S	Sun Feb 20 21:12:38 2000
+++ linux/arch/arm/mm/proc-arm6,7.S	Sun Mar 12 19:39:39 2000
@@ -107,19 +107,45 @@
 ENTRY(cpu_arm6_data_abort)
 		ldr	r4, [r0]			@ read instruction causing problem
 		mov	r2, r4, lsr #19			@ r2 b1 = L
-Ldata_simple:
+		and	r1, r4, #14 << 24
 		and	r2, r2, #2			@ check read/write bit
-		mrc	p15, 0, r0, c6, c0, 0		@ get FAR
+		teq	r1, #4 << 23
+		bne	Ldata_simple
+
+
+Ldata_ldmstm:	tst	r4, #1 << 21			@ check writeback bit
+		beq	Ldata_simple
+		mov	r7, #0x11
+		orr	r7, r7, r7, lsl #8
+		and	r0, r4, r7
+		and	r1, r4, r7, lsl #1
+		add	r0, r0, r1, lsr #1
+		and	r1, r4, r7, lsl #2
+		add	r0, r0, r1, lsr #2
+		and	r1, r4, r7, lsl #3
+		add	r0, r0, r1, lsr #3
+		add	r0, r0, r0, lsr #8
+		add	r0, r0, r0, lsr #4
+		and	r7, r0, #15			@ r7 = no. of registers to transfer.
+		and	r5, r4, #15 << 16		@ Get Rn
+		ldr	r0, [sp, r5, lsr #14]		@ Get register
+		tst	r4, #1 << 23			@ U bit
+		subne	r7, r0, r7, lsl #2
+		addeq	r7, r0, r7, lsl #2		@ Do correction (signed)
+Ldata_saver7:	str	r7, [sp, r5, lsr #14]		@ Put register
+Ldata_simple:	mrc	p15, 0, r0, c6, c0, 0		@ get FAR
 		mrc	p15, 0, r1, c5, c0, 0		@ get FSR
-		and	r1, r1, #15
+		and	r1, r1, #255
 		mov	pc, lr
 
 ENTRY(cpu_arm7_data_abort)
 		ldr	r4, [r0]			@ read instruction causing problem
 		mov	r2, r4, lsr #19			@ r2 b1 = L
 		and	r1, r4, #15 << 24
+		and	r2, r2, #2			@ check read/write bit
 		add	pc, pc, r1, lsr #22		@ Now branch to the relevent processing routine
 		movs	pc, lr
+
 		b	Ldata_unknown
 		b	Ldata_unknown
 		b	Ldata_unknown
@@ -141,106 +167,24 @@
 		mov	r2, r3
 		b	baddataabort
 
-Ldata_ldmstm:	tst	r4, #1 << 21			@ check writeback bit
-		beq	Ldata_simple
-
-		mov	r7, #0x11
-		orr	r7, r7, r7, lsl #8
-		and	r0, r4, r7
-		and	r1, r4, r7, lsl #1
-		add	r0, r0, r1, lsr #1
-		and	r1, r4, r7, lsl #2
-		add	r0, r0, r1, lsr #2
-		and	r1, r4, r7, lsl #3
-		add	r0, r0, r1, lsr #3
-		add	r0, r0, r0, lsr #8
-		add	r0, r0, r0, lsr #4
-		and	r7, r0, #15			@ r7 = no. of registers to transfer.
-		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]		@ Get register
-		eor	r6, r4, r4, lsl #2
-		tst	r6, #1 << 23			@ Check inc/dec ^ writeback
-		rsbeq	r7, r7, #0
-		add	r7, r0, r7, lsl #2		@ Do correction (signed)
-		str	r7, [sp, r5, lsr #14]		@ Put register
-
-Ldata_lateldrpostconst:
-		movs	r1, r4, lsl #20			@ Get offset
-		beq	Ldata_simple			@ if offset is zero, no effect
-		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]
-		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1, lsr #20
-		addeq	r0, r0, r1, lsr #20
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_simple
 
 Ldata_lateldrpreconst:
 		tst	r4, #1 << 21			@ check writeback bit
-		movnes	r1, r4, lsl #20			@ Get offset
+		beq	Ldata_simple
+Ldata_lateldrpostconst:
+		movs	r1, r4, lsl #20			@ Get offset
 		beq	Ldata_simple
 		and	r5, r4, #15 << 16		@ Get Rn
 		ldr	r0, [sp, r5, lsr #14]
 		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1, lsr #20
-		addeq	r0, r0, r1, lsr #20
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_simple
-
-Ldata_lateldrpostreg:
-		and	r5, r4, #15
-		ldr	r1, [sp, r5, lsl #2]		@ Get Rm
-		mov	r3, r4, lsr #7
-		ands	r3, r3, #31
-		and	r6, r4, #0x70
-		orreq	r6, r6, #8
-		add	pc, pc, r6
-		mov	r0, r0
-
-		mov	r1, r1, lsl r3			@ 0: LSL #!0
-		b	1f
-		b	1f				@ 1: LSL #0
-		mov	r0, r0
-		b	1f				@ 2: MUL?
-		mov	r0, r0
-		b	1f				@ 3: MUL?
-		mov	r0, r0
-		mov	r1, r1, lsr r3			@ 4: LSR #!0
-		b	1f
-		mov	r1, r1, lsr #32			@ 5: LSR #32
-		b	1f
-		b	1f				@ 6: MUL?
-		mov	r0, r0
-		b	1f				@ 7: MUL?
-		mov	r0, r0
-		mov	r1, r1, asr r3			@ 8: ASR #!0
-		b	1f
-		mov	r1, r1, asr #32			@ 9: ASR #32
-		b	1f
-		b	1f				@ A: MUL?
-		mov	r0, r0
-		b	1f				@ B: MUL?
-		mov	r0, r0
-		mov	r1, r1, ror r3			@ C: ROR #!0
-		b	1f
-		mov	r1, r1, rrx			@ D: RRX
-		b	1f
-		mov	r0, r0				@ E: MUL?
-		mov	r0, r0
-		mov	r0, r0				@ F: MUL?
-
-
-1:		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]
-		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1
-		addeq	r0, r0, r1
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_simple
+		subne	r7, r0, r1, lsr #20
+		addeq	r7, r0, r1, lsr #20
+		b	Ldata_saver7
 
 Ldata_lateldrprereg:
 		tst	r4, #1 << 21			@ check writeback bit
 		beq	Ldata_simple
+Ldata_lateldrpostreg:
 		and	r5, r4, #15
 		ldr	r1, [sp, r5, lsl #2]		@ Get Rm
 		mov	r3, r4, lsr #7
@@ -286,10 +230,9 @@
 1:		and	r5, r4, #15 << 16		@ Get Rn
 		ldr	r0, [sp, r5, lsr #14]
 		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1
-		addeq	r0, r0, r1
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_simple
+		subne	r7, r0, r1
+		addeq	r7, r0, r1
+		b	Ldata_saver7
 
 /*
  * Function: arm6_7_check_bugs (void)
diff -u --recursive --new-file v2.3.51/linux/arch/arm/vmlinux-armo.lds.in linux/arch/arm/vmlinux-armo.lds.in
--- v2.3.51/linux/arch/arm/vmlinux-armo.lds.in	Thu Mar  2 14:36:22 2000
+++ linux/arch/arm/vmlinux-armo.lds.in	Sun Mar 12 19:39:39 2000
@@ -6,76 +6,89 @@
 ENTRY(stext)
 SECTIONS
 {
-  . = TEXTADDR;
+	. = TEXTADDR;
 
-  __init_begin = .;		/* Init code and data		*/
-  .text.init : { *(.text.init) }
-  __proc_info_begin = .;
-  .proc.info : { *(.proc.info) }
-  __proc_info_end = .;
-  .data.init : { *(.data.init) }
-  . = ALIGN(16);
-  __setup_start = .;
-  .setup.init : { *(.setup.init) }
-  __setup_end = .;
-  __initcall_start = .;
-  .initcall.init : { *(.initcall.init) }
-  __initcall_end = .;
-  . = ALIGN(32768);
-  __init_end = .;
-
-  .init.task : {
-	*(.init.task)
-  }
-
-  /DISCARD/ : {			/* Exit code and data		*/
-	*(.text.exit)
-	*(.data.exit)
-  }
-
-  _text = .;			/* Text and read-only data	*/
-  .text : {
-	*(.text)
-	*(.fixup)
-	*(.gnu.warning)
-	}
-
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
-  .rodata : { *(.rodata) }
-  .kstrtab : { *(.kstrtab) }
-
-  . = ALIGN(16);		/* Exception table		*/
-  __start___ex_table = .;
-  __ex_table : { *(__ex_table) }
-  __stop___ex_table = .;
-
-  __start___ksymtab = .;	/* Kernel symbol table		*/
-  __ksymtab : { *(__ksymtab) }
-  __stop___ksymtab = .;
-
-  .got : { *(.got) }		/* Global offset table		*/
-
-  _etext = .;			/* End of text section		*/
-
-  .data : {			/* Data				*/
-	*(.data)
-	CONSTRUCTORS
-	}
-
-  _edata = .;			/* End of data section		*/
-
-  __bss_start = .;		/* BSS				*/
-  .bss : {
-	*(.bss)
-	}
-  _end = . ;
-
-				/* Stabs debugging sections.	*/
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
+	.init : {
+		__init_begin = .;	/* Init code and data		*/
+			*(.text.init)
+		__proc_info_begin = .;
+			*(.proc.info)
+		__proc_info_end = .;
+		__arch_info_begin = .;
+			*(.arch.info)
+		__arch_info_end = .;
+			*(.data.init)
+		. = ALIGN(16);
+		__setup_start = .;
+			*(.setup.init)
+		__setup_end = .;
+		__initcall_start = .;
+			*(.initcall.init)
+		__initcall_end = .;
+		. = ALIGN(32768);
+		__init_end = .;
+	}
+
+	.init.task : {
+		*(.init.task)
+	}
+
+	/DISCARD/ : {			/* Exit code and data		*/
+		*(.text.exit)
+		*(.data.exit)
+	}
+
+	.text : {
+		_text = .;		/* Text and read-only data	*/
+			*(.text)
+			*(.fixup)
+			*(.gnu.warning)
+			*(.text.lock)	/* out-of-line lock text */
+			*(.rodata)
+			*(.kstrtab)
+		. = ALIGN(16);		/* Exception table		*/
+		__start___ex_table = .;
+			*(__ex_table)
+		__stop___ex_table = .;
+
+		__start___ksymtab = .;	/* Kernel symbol table		*/
+			*(__ksymtab)
+		__stop___ksymtab = .;
+
+		*(.got)			/* Global offset table		*/
+
+		_etext = .;		/* End of text section		*/
+	}
+
+	.data : {
+		/*
+		 * The cacheline aligned data
+		 */
+		. = ALIGN(32);
+		*(.data.cacheline_aligned)
+
+		/*
+		 * and the usual data section
+		 */
+		*(.data)
+		CONSTRUCTORS
+
+		_edata = .;
+	}
+
+
+	.bss : {
+		  __bss_start = .;	/* BSS				*/
+		*(.bss)
+		  _end = . ;
+	}
+
+					/* Stabs debugging sections.	*/
+	.stab 0 : { *(.stab) }
+	.stabstr 0 : { *(.stabstr) }
+	.stab.excl 0 : { *(.stab.excl) }
+	.stab.exclstr 0 : { *(.stab.exclstr) }
+	.stab.index 0 : { *(.stab.index) }
+	.stab.indexstr 0 : { *(.stab.indexstr) }
+	.comment 0 : { *(.comment) }
 }
diff -u --recursive --new-file v2.3.51/linux/arch/arm/vmlinux-armv.lds.in linux/arch/arm/vmlinux-armv.lds.in
--- v2.3.51/linux/arch/arm/vmlinux-armv.lds.in	Thu Mar  2 14:36:22 2000
+++ linux/arch/arm/vmlinux-armv.lds.in	Sun Mar 12 19:39:39 2000
@@ -13,6 +13,9 @@
 		__proc_info_begin = .;
 			*(.proc.info)
 		__proc_info_end = .;
+		__arch_info_begin = .;
+			*(.arch.info)
+		__arch_info_end = .;
 			*(.data.init)
 		. = ALIGN(16);
 		__setup_start = .;
@@ -23,6 +26,11 @@
 		__initcall_end = .;
 		. = ALIGN(4096);
 		__init_end = .;
+	}
+
+	/DISCARD/ : {			/* Exit code and data		*/
+			*(.text.exit)
+			*(.data.exit)
 	}
 
 	.text : {			/* Real text segment		*/
diff -u --recursive --new-file v2.3.51/linux/arch/i386/boot/tools/build.c linux/arch/i386/boot/tools/build.c
--- v2.3.51/linux/arch/i386/boot/tools/build.c	Mon Oct 11 15:38:14 1999
+++ linux/arch/i386/boot/tools/build.c	Mon Mar 13 09:35:06 2000
@@ -150,9 +150,13 @@
 	sz = sb.st_size;
 	fprintf (stderr, "System is %d kB\n", sz/1024);
 	sys_size = (sz + 15) / 16;
-	if (sys_size > (is_big_kernel ? 0xffff : DEF_SYSSIZE))
+	/* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */
+	if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE))
 		die("System is too big. Try using %smodules.",
 			is_big_kernel ? "" : "bzImage or ");
+	if (sys_size > 0xffff)
+		fprintf(stderr,"warning: kernel is too big for standalone boot "
+		    "from floppy\n");
 	while (sz > 0) {
 		int l, n;
 
diff -u --recursive --new-file v2.3.51/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.3.51/linux/arch/i386/config.in	Fri Mar 10 16:40:39 2000
+++ linux/arch/i386/config.in	Sun Mar 12 19:49:24 2000
@@ -175,6 +175,19 @@
 source drivers/telephony/Config.in
 
 mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support' CONFIG_SCSI
diff -u --recursive --new-file v2.3.51/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.3.51/linux/arch/i386/defconfig	Fri Mar 10 16:40:39 2000
+++ linux/arch/i386/defconfig	Sun Mar 12 20:15:19 2000
@@ -93,36 +93,10 @@
 # Block devices
 #
 CONFIG_BLK_DEV_FD=y
-CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_BLK_DEV_IDECS is not set
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-
-#
-# IDE chipset support/bugfixes
-#
-CONFIG_BLK_DEV_CMD640=y
-# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
-# CONFIG_BLK_DEV_ISAPNP is not set
-CONFIG_BLK_DEV_RZ1000=y
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_IDEPCI_SHARE_IRQ=y
-# CONFIG_BLK_DEV_IDEDMA_PCI is not set
-# CONFIG_BLK_DEV_OFFBOARD is not set
-# CONFIG_BLK_DEV_AEC6210 is not set
-# CONFIG_BLK_DEV_CMD64X is not set
-# CONFIG_BLK_DEV_CS5530 is not set
-# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
 
 #
 # Additional Block Devices
@@ -131,11 +105,6 @@
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 # CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-# CONFIG_PARIDE is not set
-CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # Networking options
@@ -175,6 +144,73 @@
 # CONFIG_PHONE_IXJ is not set
 
 #
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_BLK_DEV_CMD640=y
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_RZ1000=y
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_IDEDMA_PCI_AUTO is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+# CONFIG_IDEDMA_AUTO is not set
+# CONFIG_IDEDMA_PCI_EXPERIMENTAL is not set
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_AEC6210_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD7409 is not set
+# CONFIG_AMD7409_OVERRIDE is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_CMD64X_RAID is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_HPT366_FIP is not set
+# CONFIG_HPT366_MODE3 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_PIIX_TUNING is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+
+#
 # SCSI support
 #
 CONFIG_SCSI=y
@@ -339,7 +375,6 @@
 # CONFIG_PCMCIA_XIRC2PS is not set
 # CONFIG_ARCNET_COM20020_CS is not set
 # CONFIG_PCMCIA_3C575 is not set
-# CONFIG_PCMCIA_TULIP is not set
 CONFIG_NET_PCMCIA_RADIO=y
 CONFIG_PCMCIA_RAYCS=y
 # CONFIG_PCMCIA_NETWAVE is not set
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
--- v2.3.51/linux/arch/i386/kernel/Makefile	Thu Mar  2 14:36:22 2000
+++ linux/arch/i386/kernel/Makefile	Sun Mar 12 19:18:54 2000
@@ -29,7 +29,7 @@
 endif
 
 ifdef CONFIG_MCA
-O_OBJS += mca.o
+OX_OBJS += mca.o
 endif
 
 ifeq ($(CONFIG_MTRR),y)
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/acpi.c linux/arch/i386/kernel/acpi.c
--- v2.3.51/linux/arch/i386/kernel/acpi.c	Fri Mar 10 16:40:39 2000
+++ linux/arch/i386/kernel/acpi.c	Sun Mar 12 19:45:17 2000
@@ -1,7 +1,7 @@
 /*
  *  acpi.c - Linux ACPI driver
  *
- *  Copyright (C) 1999 Andrew Henroid
+ *  Copyright (C) 1999-2000 Andrew Henroid
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -55,6 +55,11 @@
 			 struct file *file,
 			 void *buffer,
 			 size_t *len);
+static int acpi_do_table(ctl_table *ctl,
+			 int write,
+			 struct file *file,
+			 void *buffer,
+			 size_t *len);
 static int acpi_do_event_reg(ctl_table *ctl,
 			     int write,
 			     struct file *file,
@@ -73,12 +78,6 @@
 
 static struct ctl_table_header *acpi_sysctl = NULL;
 
-static struct acpi_facp *acpi_facp = NULL;
-static int acpi_fake_facp = 0;
-static struct acpi_facs *acpi_facs = NULL;
-static unsigned long acpi_facp_addr = 0;
-static unsigned long acpi_dsdt_addr = 0;
-
 // current system sleep state (S0 - S4)
 static acpi_sstate_t acpi_sleep_state = ACPI_S0;
 // time sleep began
@@ -123,15 +122,28 @@
 	ACPI_SLP_TYP_DISABLED  /* S5 */
 };
 
+struct acpi_table_info
+{
+	u32 expected_signature;
+	u32 expected_size;
+
+	struct acpi_table *table;
+	size_t size;
+	int mapped;
+};
+
+static struct acpi_table_info acpi_facp
+	= {ACPI_FACP_SIG, sizeof(struct acpi_facp), NULL, 0, 0};
+static struct acpi_table_info acpi_dsdt = {ACPI_DSDT_SIG, 0, NULL, 0, 0};
+static struct acpi_table_info acpi_facs
+	= {ACPI_FACS_SIG, sizeof(struct acpi_facs), NULL, 0, 0};
+static rwlock_t acpi_do_table_lock = RW_LOCK_UNLOCKED;
+
 static struct ctl_table acpi_table[] =
 {
-	{ACPI_FACP, "facp",
-	 &acpi_facp_addr, sizeof(acpi_facp_addr),
-	 0400, NULL, &acpi_do_ulong},
-
-	{ACPI_DSDT, "dsdt",
-	 &acpi_dsdt_addr, sizeof(acpi_dsdt_addr),
-	 0400, NULL, &acpi_do_ulong},
+	{ACPI_FACP, "facp", &acpi_facp, 0, 0644, NULL, &acpi_do_table},
+
+	{ACPI_DSDT, "dsdt", &acpi_dsdt, 0, 0644, NULL, &acpi_do_table},
 
 	{ACPI_PM1_ENABLE, "pm1_enable",
 	 NULL, 0,
@@ -409,6 +421,33 @@
 }
 
 /*
+ * Initialize an ACPI table
+ */
+static void acpi_init_table(struct acpi_table_info *info,
+			    void *data,
+			    int mapped)
+{
+	struct acpi_table *table = (struct acpi_table*) data;
+	info->table = table;
+	info->size = (size_t)(table ? table->length:0);
+	info->mapped = mapped;
+}
+
+/*
+ * Destroy an ACPI table
+ */
+static void acpi_destroy_table(struct acpi_table_info *info)
+{
+	if (info->table) {
+		if (info->mapped)
+			acpi_unmap_table(info->table);
+		else
+			kfree(info->table);
+		info->table = NULL;
+	}
+}
+
+/*
  * Locate and map ACPI tables
  */
 static int __init acpi_find_tables(void)
@@ -458,37 +497,40 @@
 		return -ENODEV;
 	}
 	// search RSDT for FACP
-	acpi_facp = NULL;
+	acpi_facp.table = NULL;
 	rsdt_entry = (u32 *) (rsdt + 1);
 	rsdt_entry_count = (int) ((rsdt->length - sizeof(*rsdt)) >> 2);
 	while (rsdt_entry_count) {
 		struct acpi_table *dt = acpi_map_table(*rsdt_entry);
 		if (dt && dt->signature == ACPI_FACP_SIG) {
-			acpi_facp = (struct acpi_facp*) dt;
-			acpi_facp_addr = *rsdt_entry;
-			acpi_dsdt_addr = acpi_facp->dsdt;
+			struct acpi_facp *facp = (struct acpi_facp*) dt;
+			acpi_init_table(&acpi_facp, dt, 1);
+
+			// map DSDT if it exists
+			dt = acpi_map_table(facp->dsdt);
+			if (dt && dt->signature == ACPI_DSDT_SIG)
+				acpi_init_table(&acpi_dsdt, dt, 1);
+			else
+				acpi_unmap_table(dt);
 
 			// map FACS if it exists
-			if (acpi_facp->facs) {
-				dt = acpi_map_table(acpi_facp->facs);
-				if (dt && dt->signature == ACPI_FACS_SIG) {
-					acpi_facs = (struct acpi_facs*) dt;
-				}
-				else {
-					acpi_unmap_table(dt);
-				}
-			}
+			dt = acpi_map_table(facp->facs);
+			if (dt && dt->signature == ACPI_FACS_SIG)
+				acpi_init_table(&acpi_facs, dt, 1);
+			else
+				acpi_unmap_table(dt);
 		}
 		else {
 			acpi_unmap_table(dt);
 		}
+
 		rsdt_entry++;
 		rsdt_entry_count--;
 	}
 
 	acpi_unmap_table(rsdt);
 
-	if (!acpi_facp) {
+	if (!acpi_facp.table) {
 		printk(KERN_ERR "ACPI: missing FACP\n");
 		return -ENODEV;
 	}
@@ -500,11 +542,9 @@
  */
 static void acpi_destroy_tables(void)
 {
-	if (!acpi_fake_facp)
-		acpi_unmap_table((struct acpi_table*) acpi_facp);
-	else
-		kfree(acpi_facp);
-	acpi_unmap_table((struct acpi_table*) acpi_facs);
+	acpi_destroy_table(&acpi_facs);
+	acpi_destroy_table(&acpi_dsdt);
+	acpi_destroy_table(&acpi_facp);
 }
 
 /*
@@ -512,6 +552,7 @@
  */
 static int __init acpi_init_piix4(struct pci_dev *dev)
 {
+	struct acpi_facp *facp;
 	u32 base;
 	u16 cmd;
 	u8 pmregmisc;
@@ -534,33 +575,34 @@
 
 	printk(KERN_INFO "ACPI: found PIIX4 at 0x%04x\n", base);
 
-	acpi_facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
-	if (!acpi_facp)
+	facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
+	if (!facp)
 		return -ENOMEM;
 
-	acpi_fake_facp = 1;
-	memset(acpi_facp, 0, sizeof(struct acpi_facp));
-	acpi_facp->int_model = ACPI_PIIX4_INT_MODEL;
-	acpi_facp->sci_int = ACPI_PIIX4_SCI_INT;
-	acpi_facp->smi_cmd = ACPI_PIIX4_SMI_CMD;
-	acpi_facp->acpi_enable = ACPI_PIIX4_ACPI_ENABLE;
-	acpi_facp->acpi_disable = ACPI_PIIX4_ACPI_DISABLE;
-	acpi_facp->s4bios_req = ACPI_PIIX4_S4BIOS_REQ;
-	acpi_facp->pm1a_evt = base + ACPI_PIIX4_PM1_EVT;
-	acpi_facp->pm1a_cnt = base + ACPI_PIIX4_PM1_CNT;
-	acpi_facp->pm2_cnt = ACPI_PIIX4_PM2_CNT;
-	acpi_facp->pm_tmr = base + ACPI_PIIX4_PM_TMR;
-	acpi_facp->gpe0 = base + ACPI_PIIX4_GPE0;
-	acpi_facp->pm1_evt_len = ACPI_PIIX4_PM1_EVT_LEN;
-	acpi_facp->pm1_cnt_len = ACPI_PIIX4_PM1_CNT_LEN;
-	acpi_facp->pm2_cnt_len = ACPI_PIIX4_PM2_CNT_LEN;
-	acpi_facp->pm_tm_len = ACPI_PIIX4_PM_TM_LEN;
-	acpi_facp->gpe0_len = ACPI_PIIX4_GPE0_LEN;
-	acpi_facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT;
-	acpi_facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT;
+	memset(facp, 0, sizeof(struct acpi_facp));
+	facp->hdr.signature = ACPI_FACP_SIG;
+	facp->hdr.length = sizeof(struct acpi_facp);
+	facp->int_model = ACPI_PIIX4_INT_MODEL;
+	facp->sci_int = ACPI_PIIX4_SCI_INT;
+	facp->smi_cmd = ACPI_PIIX4_SMI_CMD;
+	facp->acpi_enable = ACPI_PIIX4_ACPI_ENABLE;
+	facp->acpi_disable = ACPI_PIIX4_ACPI_DISABLE;
+	facp->s4bios_req = ACPI_PIIX4_S4BIOS_REQ;
+	facp->pm1a_evt = base + ACPI_PIIX4_PM1_EVT;
+	facp->pm1a_cnt = base + ACPI_PIIX4_PM1_CNT;
+	facp->pm2_cnt = ACPI_PIIX4_PM2_CNT;
+	facp->pm_tmr = base + ACPI_PIIX4_PM_TMR;
+	facp->gpe0 = base + ACPI_PIIX4_GPE0;
+	facp->pm1_evt_len = ACPI_PIIX4_PM1_EVT_LEN;
+	facp->pm1_cnt_len = ACPI_PIIX4_PM1_CNT_LEN;
+	facp->pm2_cnt_len = ACPI_PIIX4_PM2_CNT_LEN;
+	facp->pm_tm_len = ACPI_PIIX4_PM_TM_LEN;
+	facp->gpe0_len = ACPI_PIIX4_GPE0_LEN;
+	facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT;
+	facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT;
 
-	acpi_facp_addr = virt_to_phys(acpi_facp);
-	acpi_dsdt_addr = 0;
+	acpi_init_table(&acpi_facp, facp, 0);
+	acpi_init_table(&acpi_dsdt, NULL, 0);
 
 	acpi_p_blk = base + ACPI_PIIX4_P_BLK;
 
@@ -572,6 +614,7 @@
  */
 static int __init acpi_init_via(struct pci_dev *dev)
 {
+	struct acpi_facp *facp;
 	u32 base;
 	u8 tmp, irq;
 
@@ -594,39 +637,39 @@
 
 	printk(KERN_INFO "ACPI: found %s at 0x%04x\n", dev->name, base);
 
-	acpi_facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
-	if (!acpi_facp)
+	facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
+	if (!facp)
 		return -ENOMEM;
 
-	acpi_fake_facp = 1;
-	memset(acpi_facp, 0, sizeof(struct acpi_facp));
+	memset(facp, 0, sizeof(struct acpi_facp));
+	facp->hdr.signature = ACPI_FACP_SIG;
+	facp->hdr.length = sizeof(struct acpi_facp);
+	facp->int_model = ACPI_VIA_INT_MODEL;
+	facp->sci_int = irq;
+	facp->smi_cmd = base + ACPI_VIA_SMI_CMD;
+	facp->acpi_enable = ACPI_VIA_ACPI_ENABLE;
+	facp->acpi_disable = ACPI_VIA_ACPI_DISABLE;
+	facp->pm1a_evt = base + ACPI_VIA_PM1_EVT;
+	facp->pm1a_cnt = base + ACPI_VIA_PM1_CNT;
+	facp->pm_tmr = base + ACPI_VIA_PM_TMR;
+	facp->gpe0 = base + ACPI_VIA_GPE0;
+
+	facp->pm1_evt_len = ACPI_VIA_PM1_EVT_LEN;
+	facp->pm1_cnt_len = ACPI_VIA_PM1_CNT_LEN;
+	facp->pm_tm_len = ACPI_VIA_PM_TM_LEN;
+	facp->gpe0_len = ACPI_VIA_GPE0_LEN;
+	facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT;
+	facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT;
+
+	facp->duty_offset = ACPI_VIA_DUTY_OFFSET;
+	facp->duty_width = ACPI_VIA_DUTY_WIDTH;
+
+	facp->day_alarm = ACPI_VIA_DAY_ALARM;
+	facp->mon_alarm = ACPI_VIA_MON_ALARM;
+	facp->century = ACPI_VIA_CENTURY;
 
-	acpi_facp->int_model = ACPI_VIA_INT_MODEL;
-	acpi_facp->sci_int = irq;
-	acpi_facp->smi_cmd = base + ACPI_VIA_SMI_CMD;
-	acpi_facp->acpi_enable = ACPI_VIA_ACPI_ENABLE;
-	acpi_facp->acpi_disable = ACPI_VIA_ACPI_DISABLE;
-	acpi_facp->pm1a_evt = base + ACPI_VIA_PM1_EVT;
-	acpi_facp->pm1a_cnt = base + ACPI_VIA_PM1_CNT;
-	acpi_facp->pm_tmr = base + ACPI_VIA_PM_TMR;
-	acpi_facp->gpe0 = base + ACPI_VIA_GPE0;
-
-	acpi_facp->pm1_evt_len = ACPI_VIA_PM1_EVT_LEN;
-	acpi_facp->pm1_cnt_len = ACPI_VIA_PM1_CNT_LEN;
-	acpi_facp->pm_tm_len = ACPI_VIA_PM_TM_LEN;
-	acpi_facp->gpe0_len = ACPI_VIA_GPE0_LEN;
-	acpi_facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT;
-	acpi_facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT;
-
-	acpi_facp->duty_offset = ACPI_VIA_DUTY_OFFSET;
-	acpi_facp->duty_width = ACPI_VIA_DUTY_WIDTH;
-
-	acpi_facp->day_alarm = ACPI_VIA_DAY_ALARM;
-	acpi_facp->mon_alarm = ACPI_VIA_MON_ALARM;
-	acpi_facp->century = ACPI_VIA_CENTURY;
-
-	acpi_facp_addr = virt_to_phys(acpi_facp);
-	acpi_dsdt_addr = 0;
+	acpi_init_table(&acpi_facp, facp, 0);
+	acpi_init_table(&acpi_dsdt, NULL, 0);
 
 	acpi_p_blk = base + ACPI_VIA_P_BLK;
 
@@ -693,29 +736,27 @@
  */
 static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
 {
+	struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
 	u32 pm1_status, gpe_status, gpe_level, gpe_edge;
 	unsigned long flags;
 
 	// detect and clear fixed events
-	pm1_status = (acpi_read_pm1_status(acpi_facp)
-		      & acpi_read_pm1_enable(acpi_facp));
-	acpi_write_pm1_status(acpi_facp, pm1_status);
+	pm1_status = (acpi_read_pm1_status(facp) & acpi_read_pm1_enable(facp));
+	acpi_write_pm1_status(facp, pm1_status);
 	
 	// detect and handle general-purpose events
-	gpe_status = (acpi_read_gpe_status(acpi_facp)
-		      & acpi_read_gpe_enable(acpi_facp));
+	gpe_status = (acpi_read_gpe_status(facp) & acpi_read_gpe_enable(facp));
 	gpe_level = gpe_status & acpi_gpe_level;
 	if (gpe_level) {
 		// disable level-triggered events (re-enabled after handling)
-		acpi_write_gpe_enable(
-			acpi_facp,
-			acpi_read_gpe_enable(acpi_facp) & ~gpe_level);
+		acpi_write_gpe_enable(facp,
+				      acpi_read_gpe_enable(facp) & ~gpe_level);
 	}
 	gpe_edge = gpe_status & ~gpe_level;
 	if (gpe_edge) {
 		// clear edge-triggered events
-		while (acpi_read_gpe_status(acpi_facp) & gpe_edge)
-			acpi_write_gpe_status(acpi_facp, gpe_edge);
+		while (acpi_read_gpe_status(facp) & gpe_edge)
+			acpi_write_gpe_status(facp, gpe_edge);
 	}
 
 	// notify process waiting on /dev/acpi
@@ -804,7 +845,7 @@
 static void acpi_idle(void)
 {
 	static int sleep_level = 1;
-	struct acpi_facp *facp = acpi_facp;
+	struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
 
 	if (!facp || !facp->pm_tmr || !acpi_p_blk)
 		goto not_initialized;
@@ -973,6 +1014,7 @@
 {
 	unsigned long slp_typ = acpi_slp_typ[(int) state];
 	if (slp_typ != ACPI_SLP_TYP_DISABLED) {
+		struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
 		u16 typa, typb, value;
 
 		// bits 8-15 are SLP_TYPa, bits 0-7 are SLP_TYPb
@@ -987,20 +1029,20 @@
 		acpi_sleep_state = state;
 
 		// clear wake status
-		acpi_write_pm1_status(acpi_facp, ACPI_WAK);
+		acpi_write_pm1_status(facp, ACPI_WAK);
 
 		// set SLP_TYPa/b and SLP_EN
-		if (acpi_facp->pm1a_cnt) {
-			value = inw(acpi_facp->pm1a_cnt) & ~ACPI_SLP_TYP_MASK;
-			outw(value | typa | ACPI_SLP_EN, acpi_facp->pm1a_cnt);
-		}
-		if (acpi_facp->pm1b_cnt) {
-			value = inw(acpi_facp->pm1b_cnt) & ~ACPI_SLP_TYP_MASK;
-			outw(value | typb | ACPI_SLP_EN, acpi_facp->pm1b_cnt);
+		if (facp->pm1a_cnt) {
+			value = inw(facp->pm1a_cnt) & ~ACPI_SLP_TYP_MASK;
+			outw(value | typa | ACPI_SLP_EN, facp->pm1a_cnt);
+		}
+		if (facp->pm1b_cnt) {
+			value = inw(facp->pm1b_cnt) & ~ACPI_SLP_TYP_MASK;
+			outw(value | typb | ACPI_SLP_EN, facp->pm1b_cnt);
 		}
 
 		// wait until S1 is entered
-		while (!(acpi_read_pm1_status(acpi_facp) & ACPI_WAK)) ;
+		while (!(acpi_read_pm1_status(facp) & ACPI_WAK)) ;
 		// finished sleeping, update system time
 		acpi_update_clock();
 		acpi_enter_dx(ACPI_D0);
@@ -1115,6 +1157,120 @@
 }
 
 /*
+ * Determine if user buffer contains a valid table
+ */
+static int acpi_verify_table(void *buffer,
+			     size_t size,
+			     struct acpi_table_info *info)
+{
+	if (size < sizeof(struct acpi_table))
+		return -EINVAL;
+	else if (verify_area(VERIFY_READ, buffer, size))
+		return -EFAULT;
+	else {
+		struct acpi_table hdr;
+		size_t table_size;
+
+		copy_from_user(&hdr, buffer, sizeof(hdr));
+		table_size = (size_t) hdr.length;
+		if (hdr.signature != info->expected_signature
+		    || table_size < size
+		    || (info->expected_size
+			&& table_size != info->expected_size))
+			return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * Examine/replace an ACPI table
+ */
+static int acpi_do_table(ctl_table *ctl,
+			 int write,
+			 struct file *file,
+			 void *buffer,
+			 size_t *len)
+{
+	struct acpi_table_info *info = (struct acpi_table_info *) ctl->data;
+	u8 *data = NULL;
+	size_t size = 0;
+	int error = 0;
+
+	if (!info) {
+		*len = 0;
+		return 0;
+	}
+
+	if (!write) {
+		// table read
+		read_lock(&acpi_do_table_lock);
+		if (info->table && file->f_pos < info->size) {
+			data = (u8*) info->table + file->f_pos;
+			size = info->size - file->f_pos;
+			if (size > *len)
+				size = *len;
+			if (copy_to_user(buffer, data, size))
+				error = -EFAULT;
+		}
+		read_unlock(&acpi_do_table_lock);
+	}
+	else if (file->f_pos) {
+		// table body replacement
+		write_lock(&acpi_do_table_lock);
+		if (info->table && file->f_pos < info->size) {
+			data = (u8*) info->table + file->f_pos;
+			size = info->size - file->f_pos;
+			if (size > *len)
+				size = *len;
+			if (copy_from_user(data, buffer, size))
+				error = -EFAULT;
+		}
+		write_unlock(&acpi_do_table_lock);
+	}
+	else {
+		// table header/body replacement
+		struct acpi_table hdr;
+		size_t table_size;
+
+		// make sure we are being given a valid table
+		error = acpi_verify_table(buffer, *len, info);
+		if (error)
+			return error;
+		copy_from_user(&hdr, buffer, sizeof(hdr));
+		table_size = (size_t) hdr.length;
+		
+		write_lock(&acpi_do_table_lock);
+		
+		data = (u8*) info->table;
+		size = *len;
+		
+		if (!data || info->mapped || table_size != info->size) {
+			// allocate a (different sized) table
+			data = kmalloc(table_size, GFP_KERNEL);
+			if (data) {
+				memset(data, 0, table_size);
+				memcpy(data, &hdr, sizeof(hdr));
+				acpi_destroy_table(info);
+				acpi_init_table(info, data, 0);
+			}
+			else
+				error = -ENOMEM;
+		}
+		if (data)
+			copy_from_user(data, buffer, size);
+		
+		write_unlock(&acpi_do_table_lock);
+	}
+
+	if (error)
+		return error;
+
+	*len = size;
+	file->f_pos += size;
+	return 0;
+}
+
+/*
  * Examine/modify event register
  */
 static int acpi_do_event_reg(ctl_table *ctl,
@@ -1123,6 +1279,7 @@
 			     void *buffer,
 			     size_t *len)
 {
+	struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
 	char str[2 * sizeof(u32) + 4], *strend;
 	u32 val, enabling;
 	int size;
@@ -1136,10 +1293,10 @@
 		val = 0;
 		switch (ctl->ctl_name) {
 		case ACPI_PM1_ENABLE:
-			val = acpi_read_pm1_enable(acpi_facp);
+			val = acpi_read_pm1_enable(facp);
 			break;
 		case ACPI_GPE_ENABLE:
-			val = acpi_read_gpe_enable(acpi_facp);
+			val = acpi_read_gpe_enable(facp);
 			break;
 		case ACPI_GPE_LEVEL:
 			val = acpi_gpe_level;
@@ -1170,42 +1327,41 @@
 		switch (ctl->ctl_name) {
 		case ACPI_PM1_ENABLE:
 			// clear previously disabled events
-			enabling = (val
-				    & ~acpi_read_pm1_enable(acpi_facp));
-			acpi_write_pm1_status(acpi_facp, enabling);
+			enabling = (val & ~acpi_read_pm1_enable(facp));
+			acpi_write_pm1_status(facp, enabling);
 			
 			if (val) {
 				// enable ACPI unless it is already
-				if (!acpi_is_enabled(acpi_facp))
-					acpi_enable(acpi_facp);
+				if (!acpi_is_enabled(facp))
+					acpi_enable(facp);
 			}
-			else if (!acpi_read_gpe_enable(acpi_facp)) {
+			else if (!acpi_read_gpe_enable(facp)) {
 				// disable ACPI unless it is already
-				if (acpi_is_enabled(acpi_facp))
-					acpi_disable(acpi_facp);
+				if (acpi_is_enabled(facp))
+					acpi_disable(facp);
 			}
 			
-			acpi_write_pm1_enable(acpi_facp, val);
+			acpi_write_pm1_enable(facp, val);
 			break;
 		case ACPI_GPE_ENABLE:
 			// clear previously disabled events
 			enabling = (val
-				    & ~acpi_read_gpe_enable(acpi_facp));
-			while (acpi_read_gpe_status(acpi_facp) & enabling)
-				acpi_write_gpe_status(acpi_facp, enabling);
+				    & ~acpi_read_gpe_enable(facp));
+			while (acpi_read_gpe_status(facp) & enabling)
+				acpi_write_gpe_status(facp, enabling);
 			
 			if (val) {
 				// enable ACPI unless it is already
-				if (!acpi_is_enabled(acpi_facp))
-					acpi_enable(acpi_facp);
+				if (!acpi_is_enabled(facp))
+					acpi_enable(facp);
 			}
-			else if (!acpi_read_pm1_enable(acpi_facp)) {
+			else if (!acpi_read_pm1_enable(facp)) {
 				// disable ACPI unless it is already
-				if (acpi_is_enabled(acpi_facp))
-					acpi_disable(acpi_facp);
+				if (acpi_is_enabled(facp))
+					acpi_disable(facp);
 			}
 			
-			acpi_write_gpe_enable(acpi_facp, val);
+			acpi_write_gpe_enable(facp, val);
 			break;
 		case ACPI_GPE_LEVEL:
 			acpi_gpe_level = val;
@@ -1301,8 +1457,9 @@
  */
 static int __init acpi_init(void)
 {
-	switch(acpi_enabled)
-	{
+	struct acpi_facp *facp = NULL;
+
+	switch (acpi_enabled) {
 	case ACPI_ENABLED:
 		if (acpi_find_tables() && acpi_find_chipset())
 			return -ENODEV;
@@ -1319,37 +1476,39 @@
 		return -ENODEV;
 	}
 
+	facp = (struct acpi_facp*) acpi_facp.table;
+
 	/*
 	 * Internally we always keep latencies in timer
 	 * ticks, which is simpler and more consistent (what is
 	 * an uS to us?). Besides, that gives people more
 	 * control in the /proc interfaces.
 	 */
-	if (acpi_facp->p_lvl2_lat
-	    && acpi_facp->p_lvl2_lat <= ACPI_MAX_P_LVL2_LAT) {
-		acpi_p_lvl2_lat = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl2_lat);
+	if (facp->p_lvl2_lat
+	    && facp->p_lvl2_lat <= ACPI_MAX_P_LVL2_LAT) {
+		acpi_p_lvl2_lat = ACPI_uS_TO_TMR_TICKS(facp->p_lvl2_lat);
 		acpi_enter_lvl2_lat = ACPI_uS_TO_TMR_TICKS(ACPI_TMR_HZ / 1000);
 	}
-	if (acpi_facp->p_lvl3_lat
-	    && acpi_facp->p_lvl3_lat <= ACPI_MAX_P_LVL3_LAT) {
-		acpi_p_lvl3_lat = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat);
+	if (facp->p_lvl3_lat
+	    && facp->p_lvl3_lat <= ACPI_MAX_P_LVL3_LAT) {
+		acpi_p_lvl3_lat = ACPI_uS_TO_TMR_TICKS(facp->p_lvl3_lat);
 		acpi_enter_lvl3_lat
-			= ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat * 5);
+			= ACPI_uS_TO_TMR_TICKS(facp->p_lvl3_lat * 5);
 	}
 
-	if (acpi_claim_ioports(acpi_facp)) {
+	if (acpi_claim_ioports(facp)) {
 		printk(KERN_ERR "ACPI: I/O port allocation failed\n");
 		goto err_out;
 	}
 
-	if (acpi_facp->sci_int
-	    && request_irq(acpi_facp->sci_int,
+	if (facp->sci_int
+	    && request_irq(facp->sci_int,
 			   acpi_irq,
 			   SA_INTERRUPT | SA_SHIRQ,
 			   "acpi",
-			   acpi_facp)) {
+			   &acpi_facp)) {
 		printk(KERN_ERR "ACPI: SCI (IRQ%d) allocation failed\n",
-		       acpi_facp->sci_int);
+		       facp->sci_int);
 		goto err_out;
 	}
 
@@ -1369,7 +1528,7 @@
 		return 0;
 #endif
 
-	if (acpi_facp->pm_tmr)
+	if (facp->pm_tmr)
 		pm_idle = acpi_idle;
 
 	return 0;
@@ -1387,16 +1546,18 @@
  */
 static void __exit acpi_exit(void)
 {
+	struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
+
 	pm_idle = NULL;
 	pm_active = 0;
 	pm_power_off = NULL;
 
 	unregister_sysctl_table(acpi_sysctl);
-	acpi_disable(acpi_facp);
-	acpi_release_ioports(acpi_facp);
+	acpi_disable(facp);
+	acpi_release_ioports(facp);
 
-	if (acpi_facp->sci_int)
-		free_irq(acpi_facp->sci_int, acpi_facp);
+	if (facp->sci_int)
+		free_irq(facp->sci_int, &acpi_facp);
 
 	acpi_destroy_tables();
 
@@ -1429,3 +1590,4 @@
 
 module_init(acpi_init);
 module_exit(acpi_exit);
+
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.3.51/linux/arch/i386/kernel/entry.S	Sat Feb 26 22:31:38 2000
+++ linux/arch/i386/kernel/entry.S	Tue Mar 14 17:45:20 2000
@@ -638,6 +638,8 @@
 	.long SYMBOL_NAME(sys_setfsuid)		/* 215 */
 	.long SYMBOL_NAME(sys_setfsgid)
 	.long SYMBOL_NAME(sys_pivot_root)
+	.long SYMBOL_NAME(sys_mincore)
+	.long SYMBOL_NAME(sys_madvise)
 
 
 	/*
@@ -646,6 +648,6 @@
 	 * entries. Don't panic if you notice that this hasn't
 	 * been shrunk every time we add a new system call.
 	 */
-	.rept NR_syscalls-217
+	.rept NR_syscalls-219
 		.long SYMBOL_NAME(sys_ni_syscall)
 	.endr
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.3.51/linux/arch/i386/kernel/head.S	Thu Feb 10 17:11:03 2000
+++ linux/arch/i386/kernel/head.S	Mon Mar 13 09:35:06 2000
@@ -67,10 +67,17 @@
 	movl %eax,%cr4
 #endif
 /*
- * Setup paging (the tables are already set up, just switch them on)
+ * Setup paging (intialize tables, then switch them on)
  */
 1:
-	movl $0x101000,%eax
+	movl $pg0-__PAGE_OFFSET,%edi /* initialize page tables */
+	movl $007,%eax		/* "007" doesn't mean with right to kill, but
+				   PRESENT+RW+USER */
+1:	stosl
+	add $0x1000,%eax
+	cmp $empty_zero_page-__PAGE_OFFSET,%edi
+	jne 1b
+	movl $swapper_pg_dir-__PAGE_OFFSET,%eax
 	movl %eax,%cr3		/* set the page table pointer.. */
 	movl %cr0,%eax
 	orl $0x80000000,%eax
@@ -350,8 +357,8 @@
 	.long SYMBOL_NAME(gdt_table)
 
 /*
- * This is initialized to create a identity-mapping at 0-4M (for bootup
- * purposes) and another mapping of the 0-4M area at virtual address
+ * This is initialized to create an identity-mapping at 0-8M (for bootup
+ * purposes) and another mapping of the 0-8M area at virtual address
  * PAGE_OFFSET.
  */
 .org 0x1000
@@ -366,270 +373,20 @@
 	.fill BOOT_KERNEL_PGD_PTRS-2,4,0
 
 /*
- * The page tables are initialized to only 4MB here - the final page
- * tables are set up later depending on memory size. The "007" at the
- * end doesn't mean with right to kill, but PRESENT+RW+USER
+ * The page tables are initialized to only 8MB here - the final page
+ * tables are set up later depending on memory size.
  */
 .org 0x2000
 ENTRY(pg0)
-	.long 0x000007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
-	.long 0x008007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
-	.long 0x010007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
-	.long 0x018007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
-	.long 0x020007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
-	.long 0x028007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
-	.long 0x030007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
-	.long 0x038007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
-	.long 0x040007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
-	.long 0x048007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
-	.long 0x050007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
-	.long 0x058007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
-	.long 0x060007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
-	.long 0x068007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
-	.long 0x070007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
-	.long 0x078007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
-	.long 0x080007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
-	.long 0x088007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
-	.long 0x090007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
-	.long 0x098007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
-	.long 0x0a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
-	.long 0x0a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
-	.long 0x0b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
-	.long 0x0b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
-	.long 0x0c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
-	.long 0x0c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
-	.long 0x0d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
-	.long 0x0d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
-	.long 0x0e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
-	.long 0x0e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
-	.long 0x0f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
-	.long 0x0f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
-	.long 0x100007,0x101007,0x102007,0x103007,0x104007,0x105007,0x106007,0x107007
-	.long 0x108007,0x109007,0x10a007,0x10b007,0x10c007,0x10d007,0x10e007,0x10f007
-	.long 0x110007,0x111007,0x112007,0x113007,0x114007,0x115007,0x116007,0x117007
-	.long 0x118007,0x119007,0x11a007,0x11b007,0x11c007,0x11d007,0x11e007,0x11f007
-	.long 0x120007,0x121007,0x122007,0x123007,0x124007,0x125007,0x126007,0x127007
-	.long 0x128007,0x129007,0x12a007,0x12b007,0x12c007,0x12d007,0x12e007,0x12f007
-	.long 0x130007,0x131007,0x132007,0x133007,0x134007,0x135007,0x136007,0x137007
-	.long 0x138007,0x139007,0x13a007,0x13b007,0x13c007,0x13d007,0x13e007,0x13f007
-	.long 0x140007,0x141007,0x142007,0x143007,0x144007,0x145007,0x146007,0x147007
-	.long 0x148007,0x149007,0x14a007,0x14b007,0x14c007,0x14d007,0x14e007,0x14f007
-	.long 0x150007,0x151007,0x152007,0x153007,0x154007,0x155007,0x156007,0x157007
-	.long 0x158007,0x159007,0x15a007,0x15b007,0x15c007,0x15d007,0x15e007,0x15f007
-	.long 0x160007,0x161007,0x162007,0x163007,0x164007,0x165007,0x166007,0x167007
-	.long 0x168007,0x169007,0x16a007,0x16b007,0x16c007,0x16d007,0x16e007,0x16f007
-	.long 0x170007,0x171007,0x172007,0x173007,0x174007,0x175007,0x176007,0x177007
-	.long 0x178007,0x179007,0x17a007,0x17b007,0x17c007,0x17d007,0x17e007,0x17f007
-	.long 0x180007,0x181007,0x182007,0x183007,0x184007,0x185007,0x186007,0x187007
-	.long 0x188007,0x189007,0x18a007,0x18b007,0x18c007,0x18d007,0x18e007,0x18f007
-	.long 0x190007,0x191007,0x192007,0x193007,0x194007,0x195007,0x196007,0x197007
-	.long 0x198007,0x199007,0x19a007,0x19b007,0x19c007,0x19d007,0x19e007,0x19f007
-	.long 0x1a0007,0x1a1007,0x1a2007,0x1a3007,0x1a4007,0x1a5007,0x1a6007,0x1a7007
-	.long 0x1a8007,0x1a9007,0x1aa007,0x1ab007,0x1ac007,0x1ad007,0x1ae007,0x1af007
-	.long 0x1b0007,0x1b1007,0x1b2007,0x1b3007,0x1b4007,0x1b5007,0x1b6007,0x1b7007
-	.long 0x1b8007,0x1b9007,0x1ba007,0x1bb007,0x1bc007,0x1bd007,0x1be007,0x1bf007
-	.long 0x1c0007,0x1c1007,0x1c2007,0x1c3007,0x1c4007,0x1c5007,0x1c6007,0x1c7007
-	.long 0x1c8007,0x1c9007,0x1ca007,0x1cb007,0x1cc007,0x1cd007,0x1ce007,0x1cf007
-	.long 0x1d0007,0x1d1007,0x1d2007,0x1d3007,0x1d4007,0x1d5007,0x1d6007,0x1d7007
-	.long 0x1d8007,0x1d9007,0x1da007,0x1db007,0x1dc007,0x1dd007,0x1de007,0x1df007
-	.long 0x1e0007,0x1e1007,0x1e2007,0x1e3007,0x1e4007,0x1e5007,0x1e6007,0x1e7007
-	.long 0x1e8007,0x1e9007,0x1ea007,0x1eb007,0x1ec007,0x1ed007,0x1ee007,0x1ef007
-	.long 0x1f0007,0x1f1007,0x1f2007,0x1f3007,0x1f4007,0x1f5007,0x1f6007,0x1f7007
-	.long 0x1f8007,0x1f9007,0x1fa007,0x1fb007,0x1fc007,0x1fd007,0x1fe007,0x1ff007
-	.long 0x200007,0x201007,0x202007,0x203007,0x204007,0x205007,0x206007,0x207007
-	.long 0x208007,0x209007,0x20a007,0x20b007,0x20c007,0x20d007,0x20e007,0x20f007
-	.long 0x210007,0x211007,0x212007,0x213007,0x214007,0x215007,0x216007,0x217007
-	.long 0x218007,0x219007,0x21a007,0x21b007,0x21c007,0x21d007,0x21e007,0x21f007
-	.long 0x220007,0x221007,0x222007,0x223007,0x224007,0x225007,0x226007,0x227007
-	.long 0x228007,0x229007,0x22a007,0x22b007,0x22c007,0x22d007,0x22e007,0x22f007
-	.long 0x230007,0x231007,0x232007,0x233007,0x234007,0x235007,0x236007,0x237007
-	.long 0x238007,0x239007,0x23a007,0x23b007,0x23c007,0x23d007,0x23e007,0x23f007
-	.long 0x240007,0x241007,0x242007,0x243007,0x244007,0x245007,0x246007,0x247007
-	.long 0x248007,0x249007,0x24a007,0x24b007,0x24c007,0x24d007,0x24e007,0x24f007
-	.long 0x250007,0x251007,0x252007,0x253007,0x254007,0x255007,0x256007,0x257007
-	.long 0x258007,0x259007,0x25a007,0x25b007,0x25c007,0x25d007,0x25e007,0x25f007
-	.long 0x260007,0x261007,0x262007,0x263007,0x264007,0x265007,0x266007,0x267007
-	.long 0x268007,0x269007,0x26a007,0x26b007,0x26c007,0x26d007,0x26e007,0x26f007
-	.long 0x270007,0x271007,0x272007,0x273007,0x274007,0x275007,0x276007,0x277007
-	.long 0x278007,0x279007,0x27a007,0x27b007,0x27c007,0x27d007,0x27e007,0x27f007
-	.long 0x280007,0x281007,0x282007,0x283007,0x284007,0x285007,0x286007,0x287007
-	.long 0x288007,0x289007,0x28a007,0x28b007,0x28c007,0x28d007,0x28e007,0x28f007
-	.long 0x290007,0x291007,0x292007,0x293007,0x294007,0x295007,0x296007,0x297007
-	.long 0x298007,0x299007,0x29a007,0x29b007,0x29c007,0x29d007,0x29e007,0x29f007
-	.long 0x2a0007,0x2a1007,0x2a2007,0x2a3007,0x2a4007,0x2a5007,0x2a6007,0x2a7007
-	.long 0x2a8007,0x2a9007,0x2aa007,0x2ab007,0x2ac007,0x2ad007,0x2ae007,0x2af007
-	.long 0x2b0007,0x2b1007,0x2b2007,0x2b3007,0x2b4007,0x2b5007,0x2b6007,0x2b7007
-	.long 0x2b8007,0x2b9007,0x2ba007,0x2bb007,0x2bc007,0x2bd007,0x2be007,0x2bf007
-	.long 0x2c0007,0x2c1007,0x2c2007,0x2c3007,0x2c4007,0x2c5007,0x2c6007,0x2c7007
-	.long 0x2c8007,0x2c9007,0x2ca007,0x2cb007,0x2cc007,0x2cd007,0x2ce007,0x2cf007
-	.long 0x2d0007,0x2d1007,0x2d2007,0x2d3007,0x2d4007,0x2d5007,0x2d6007,0x2d7007
-	.long 0x2d8007,0x2d9007,0x2da007,0x2db007,0x2dc007,0x2dd007,0x2de007,0x2df007
-	.long 0x2e0007,0x2e1007,0x2e2007,0x2e3007,0x2e4007,0x2e5007,0x2e6007,0x2e7007
-	.long 0x2e8007,0x2e9007,0x2ea007,0x2eb007,0x2ec007,0x2ed007,0x2ee007,0x2ef007
-	.long 0x2f0007,0x2f1007,0x2f2007,0x2f3007,0x2f4007,0x2f5007,0x2f6007,0x2f7007
-	.long 0x2f8007,0x2f9007,0x2fa007,0x2fb007,0x2fc007,0x2fd007,0x2fe007,0x2ff007
-	.long 0x300007,0x301007,0x302007,0x303007,0x304007,0x305007,0x306007,0x307007
-	.long 0x308007,0x309007,0x30a007,0x30b007,0x30c007,0x30d007,0x30e007,0x30f007
-	.long 0x310007,0x311007,0x312007,0x313007,0x314007,0x315007,0x316007,0x317007
-	.long 0x318007,0x319007,0x31a007,0x31b007,0x31c007,0x31d007,0x31e007,0x31f007
-	.long 0x320007,0x321007,0x322007,0x323007,0x324007,0x325007,0x326007,0x327007
-	.long 0x328007,0x329007,0x32a007,0x32b007,0x32c007,0x32d007,0x32e007,0x32f007
-	.long 0x330007,0x331007,0x332007,0x333007,0x334007,0x335007,0x336007,0x337007
-	.long 0x338007,0x339007,0x33a007,0x33b007,0x33c007,0x33d007,0x33e007,0x33f007
-	.long 0x340007,0x341007,0x342007,0x343007,0x344007,0x345007,0x346007,0x347007
-	.long 0x348007,0x349007,0x34a007,0x34b007,0x34c007,0x34d007,0x34e007,0x34f007
-	.long 0x350007,0x351007,0x352007,0x353007,0x354007,0x355007,0x356007,0x357007
-	.long 0x358007,0x359007,0x35a007,0x35b007,0x35c007,0x35d007,0x35e007,0x35f007
-	.long 0x360007,0x361007,0x362007,0x363007,0x364007,0x365007,0x366007,0x367007
-	.long 0x368007,0x369007,0x36a007,0x36b007,0x36c007,0x36d007,0x36e007,0x36f007
-	.long 0x370007,0x371007,0x372007,0x373007,0x374007,0x375007,0x376007,0x377007
-	.long 0x378007,0x379007,0x37a007,0x37b007,0x37c007,0x37d007,0x37e007,0x37f007
-	.long 0x380007,0x381007,0x382007,0x383007,0x384007,0x385007,0x386007,0x387007
-	.long 0x388007,0x389007,0x38a007,0x38b007,0x38c007,0x38d007,0x38e007,0x38f007
-	.long 0x390007,0x391007,0x392007,0x393007,0x394007,0x395007,0x396007,0x397007
-	.long 0x398007,0x399007,0x39a007,0x39b007,0x39c007,0x39d007,0x39e007,0x39f007
-	.long 0x3a0007,0x3a1007,0x3a2007,0x3a3007,0x3a4007,0x3a5007,0x3a6007,0x3a7007
-	.long 0x3a8007,0x3a9007,0x3aa007,0x3ab007,0x3ac007,0x3ad007,0x3ae007,0x3af007
-	.long 0x3b0007,0x3b1007,0x3b2007,0x3b3007,0x3b4007,0x3b5007,0x3b6007,0x3b7007
-	.long 0x3b8007,0x3b9007,0x3ba007,0x3bb007,0x3bc007,0x3bd007,0x3be007,0x3bf007
-	.long 0x3c0007,0x3c1007,0x3c2007,0x3c3007,0x3c4007,0x3c5007,0x3c6007,0x3c7007
-	.long 0x3c8007,0x3c9007,0x3ca007,0x3cb007,0x3cc007,0x3cd007,0x3ce007,0x3cf007
-	.long 0x3d0007,0x3d1007,0x3d2007,0x3d3007,0x3d4007,0x3d5007,0x3d6007,0x3d7007
-	.long 0x3d8007,0x3d9007,0x3da007,0x3db007,0x3dc007,0x3dd007,0x3de007,0x3df007
-	.long 0x3e0007,0x3e1007,0x3e2007,0x3e3007,0x3e4007,0x3e5007,0x3e6007,0x3e7007
-	.long 0x3e8007,0x3e9007,0x3ea007,0x3eb007,0x3ec007,0x3ed007,0x3ee007,0x3ef007
-	.long 0x3f0007,0x3f1007,0x3f2007,0x3f3007,0x3f4007,0x3f5007,0x3f6007,0x3f7007
-	.long 0x3f8007,0x3f9007,0x3fa007,0x3fb007,0x3fc007,0x3fd007,0x3fe007,0x3ff007
 
+.org 0x3000
 ENTRY(pg1)
-	.long 0x400007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
-	.long 0x408007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
-	.long 0x410007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
-	.long 0x418007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
-	.long 0x420007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
-	.long 0x428007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
-	.long 0x430007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
-	.long 0x438007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
-	.long 0x440007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
-	.long 0x448007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
-	.long 0x450007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
-	.long 0x458007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
-	.long 0x460007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
-	.long 0x468007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
-	.long 0x470007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
-	.long 0x478007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
-	.long 0x480007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
-	.long 0x488007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
-	.long 0x490007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
-	.long 0x498007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
-	.long 0x4a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
-	.long 0x4a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
-	.long 0x4b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
-	.long 0x4b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
-	.long 0x4c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
-	.long 0x4c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
-	.long 0x4d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
-	.long 0x4d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
-	.long 0x4e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
-	.long 0x4e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
-	.long 0x4f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
-	.long 0x4f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
-	.long 0x500007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
-	.long 0x508007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
-	.long 0x510007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
-	.long 0x518007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
-	.long 0x520007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
-	.long 0x528007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
-	.long 0x530007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
-	.long 0x538007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
-	.long 0x540007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
-	.long 0x548007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
-	.long 0x550007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
-	.long 0x558007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
-	.long 0x560007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
-	.long 0x568007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
-	.long 0x570007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
-	.long 0x578007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
-	.long 0x580007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
-	.long 0x588007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
-	.long 0x590007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
-	.long 0x598007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
-	.long 0x5a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
-	.long 0x5a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
-	.long 0x5b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
-	.long 0x5b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
-	.long 0x5c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
-	.long 0x5c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
-	.long 0x5d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
-	.long 0x5d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
-	.long 0x5e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
-	.long 0x5e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
-	.long 0x5f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
-	.long 0x5f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
-	.long 0x600007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
-	.long 0x608007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
-	.long 0x610007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
-	.long 0x618007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
-	.long 0x620007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
-	.long 0x628007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
-	.long 0x630007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
-	.long 0x638007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
-	.long 0x640007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
-	.long 0x648007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
-	.long 0x650007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
-	.long 0x658007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
-	.long 0x660007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
-	.long 0x668007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
-	.long 0x670007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
-	.long 0x678007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
-	.long 0x680007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
-	.long 0x688007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
-	.long 0x690007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
-	.long 0x698007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
-	.long 0x6a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
-	.long 0x6a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
-	.long 0x6b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
-	.long 0x6b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
-	.long 0x6c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
-	.long 0x6c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
-	.long 0x6d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
-	.long 0x6d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
-	.long 0x6e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
-	.long 0x6e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
-	.long 0x6f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
-	.long 0x6f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
-	.long 0x700007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
-	.long 0x708007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
-	.long 0x710007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
-	.long 0x718007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
-	.long 0x720007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
-	.long 0x728007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
-	.long 0x730007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
-	.long 0x738007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
-	.long 0x740007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
-	.long 0x748007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
-	.long 0x750007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
-	.long 0x758007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
-	.long 0x760007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
-	.long 0x768007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
-	.long 0x770007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
-	.long 0x778007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
-	.long 0x780007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
-	.long 0x788007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
-	.long 0x790007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
-	.long 0x798007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
-	.long 0x7a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
-	.long 0x7a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
-	.long 0x7b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
-	.long 0x7b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
-	.long 0x7c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
-	.long 0x7c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
-	.long 0x7d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
-	.long 0x7d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
-	.long 0x7e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
-	.long 0x7e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
-	.long 0x7f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
-	.long 0x7f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
+
+/*
+ * empty_zero_page must immediately follow the page tables ! (The
+ * initialization loop counts until empty_zero_page)
+ */
+
 .org 0x4000
 ENTRY(empty_zero_page)
 
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.3.51/linux/arch/i386/kernel/i386_ksyms.c	Sat Feb 26 22:31:38 2000
+++ linux/arch/i386/kernel/i386_ksyms.c	Sun Mar 12 19:18:54 2000
@@ -121,21 +121,7 @@
 #endif
 
 #ifdef CONFIG_MCA
-/* Adapter probing and info methods. */
 EXPORT_SYMBOL(machine_id);
-EXPORT_SYMBOL(mca_find_adapter);
-EXPORT_SYMBOL(mca_write_pos);
-EXPORT_SYMBOL(mca_read_pos);
-EXPORT_SYMBOL(mca_read_stored_pos);
-EXPORT_SYMBOL(mca_set_adapter_name);
-EXPORT_SYMBOL(mca_get_adapter_name);
-EXPORT_SYMBOL(mca_set_adapter_procfn);
-EXPORT_SYMBOL(mca_isenabled);
-EXPORT_SYMBOL(mca_isadapter);
-EXPORT_SYMBOL(mca_mark_as_used);
-EXPORT_SYMBOL(mca_mark_as_unused);
-EXPORT_SYMBOL(mca_find_unused_adapter);
-EXPORT_SYMBOL(mca_is_adapter_used);
 #endif
 
 #ifdef CONFIG_VT
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.3.51/linux/arch/i386/kernel/irq.c	Tue Mar  7 14:32:25 2000
+++ linux/arch/i386/kernel/irq.c	Tue Mar 14 17:54:42 2000
@@ -456,6 +456,18 @@
  * hardware disable after having gotten the irq
  * controller lock. 
  */
+ 
+/**
+ *	disable_irq_nosync - disable an irq without waiting
+ *	@irq: Interrupt to disable
+ *
+ *	Disable the selected interrupt line. Disables of an interrupt
+ *	stack. Unlike disable_irq, this function does not ensure existing
+ *	instances of the irq handler have completed before returning.
+ *
+ *	This function may be called from IRQ context.
+ */
+ 
 void inline disable_irq_nosync(unsigned int irq)
 {
 	irq_desc_t *desc = irq_desc + irq;
@@ -469,10 +481,19 @@
 	spin_unlock_irqrestore(&desc->lock, flags);
 }
 
-/*
- * Synchronous version of the above, making sure the IRQ is
- * no longer running on any other IRQ..
+/**
+ *	disable_irq - disable an irq and wait for completion
+ *	@irq: Interrupt to disable
+ *
+ *	Disable the selected interrupt line. Disables of an interrupt
+ *	stack. That is for two disables you need two enables. This
+ *	function waits for any pending IRQ handlers for this interrupt
+ *	to complete before returning. If you use this function while
+ *	holding a resource the IRQ handler may need you will deadlock.
+ *
+ *	This function may be called - with care - from IRQ context.
  */
+ 
 void disable_irq(unsigned int irq)
 {
 	disable_irq_nosync(irq);
@@ -484,6 +505,16 @@
 	}
 }
 
+/**
+ *	enable_irq - enable interrupt handling on an irq
+ *	@irq: Interrupt to enable
+ *
+ *	Re-enables the processing of interrupts on this IRQ line
+ *	providing no disable_irq calls are now in effect.
+ *
+ *	This function may be called from IRQ context.
+ */
+ 
 void enable_irq(unsigned int irq)
 {
 	irq_desc_t *desc = irq_desc + irq;
@@ -598,6 +629,38 @@
 	return 1;
 }
 
+/**
+ *	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 irqflags, 
@@ -642,7 +705,25 @@
 		kfree(action);
 	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. The function
+ *	does not return until any executing interrupts for this IRQ
+ *	have completed.
+ *
+ *	This function may be called from interrupt context. 
+ *
+ *	Bugs: Attempting to free an irq in a handler for the same irq hangs
+ *	      the machine.
+ */
+ 
 void free_irq(unsigned int irq, void *dev_id)
 {
 	irq_desc_t *desc;
@@ -693,6 +774,15 @@
  * with "IRQ_WAITING" cleared and the interrupt
  * disabled.
  */
+
+/**
+ *	probe_irq_on	- begin an interrupt autodetect
+ *
+ *	Commence probing for an interrupt. The interrupts are scanned
+ *	and a mask of potential interrupt lines is returned.
+ *
+ */
+ 
 unsigned long probe_irq_on(void)
 {
 	unsigned int i;
@@ -770,6 +860,16 @@
  * Return a mask of triggered interrupts (this
  * can handle only legacy ISA interrupts).
  */
+ 
+/**
+ *	probe_irq_mask
+ *	@val:	mask of interrupts to consider
+ *
+ *	Scan the ISA bus interrupt lines and return a bitmap of
+ *	active interrupts. The interrupt probe logic state is then
+ *	returned to its previous value.
+ */
+ 
 unsigned int probe_irq_mask(unsigned long val)
 {
 	int i;
@@ -798,8 +898,27 @@
 
 /*
  * Return the one interrupt that triggered (this can
- * handle any interrupt source)
+ * handle any interrupt source).
+ */
+
+/**
+ *	probe_irq_off	- end an interrupt autodetect
+ *	@val: mask of potential interrupts (unused)
+ *
+ *	Scans the unused interrupt lines and returns the line which
+ *	appears to have triggered the interrupt. If no interrupt was
+ *	found then zero is returned. If more than one interrupt is
+ *	found then minus the first candidate is returned to indicate
+ *	their is doubt.
+ *
+ *	The interrupt probe logic state is returned to its previous
+ *	value.
+ *
+ *	BUGS: When used in a module (which arguably shouldnt happen)
+ *	nothing prevents two IRQ probe callers from overlapping. The
+ *	results of this are non-optimal.
  */
+ 
 int probe_irq_off(unsigned long val)
 {
 	int i, irq_found, nr_irqs;
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/mca.c linux/arch/i386/kernel/mca.c
--- v2.3.51/linux/arch/i386/kernel/mca.c	Tue Nov 23 22:42:20 1999
+++ linux/arch/i386/kernel/mca.c	Sun Mar 12 19:18:54 2000
@@ -34,6 +34,7 @@
  *	- switched to regular procfs methods.
  */
 
+#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
@@ -362,6 +363,19 @@
 
 /*--------------------------------------------------------------------*/
 
+/**
+ *	mca_find_adapter - scan for adapters
+ *	@id:	MCA identification to search for
+ *	@start:	Starting slot
+ *
+ *	Search the MCA configuration for adapters matching the 16bit
+ *	ID given. The first time it should be called with start as zero
+ *	and then further calls made passing the return value of the
+ *	previous call until MCA_NOTFOUND is returned.
+ *
+ *	Disabled adapters are not reported.
+ */
+ 
 int mca_find_adapter(int id, int start)
 {
 	if(mca_info == NULL || id == 0xffff) {
@@ -390,8 +404,25 @@
 	return MCA_NOTFOUND;
 } /* mca_find_adapter() */
 
+EXPORT_SYMBOL(mca_find_adapter);
+
 /*--------------------------------------------------------------------*/
 
+/**
+ *	mca_find_unused_adapter - scan for unused adapters
+ *	@id:	MCA identification to search for
+ *	@start:	Starting slot
+ *
+ *	Search the MCA configuration for adapters matching the 16bit
+ *	ID given. The first time it should be called with start as zero
+ *	and then further calls made passing the return value of the
+ *	previous call until MCA_NOTFOUND is returned.
+ *
+ *	Adapters that have been claimed by drivers and those that
+ *	are disabled are not reported. This function thus allows a driver
+ *	to scan for further cards when some may already be driven.
+ */
+
 int mca_find_unused_adapter(int id, int start) 
 {
 	if(mca_info == NULL || id == 0xffff) {
@@ -421,8 +452,20 @@
 	return MCA_NOTFOUND;
 } /* mca_find_unused_adapter() */	
 
+EXPORT_SYMBOL(mca_find_unused_adapter);
+
 /*--------------------------------------------------------------------*/
 
+/**
+ *	mca_read_stored_pos - read POS register from boot data
+ *	@slot: slot number to read from
+ *	@reg:  register to read from
+ *
+ *	Fetch a POS value that was stored at boot time by the kernel
+ *	when it scanned the MCA space. The register value is returned.
+ *	Missing or invalid registers report 0.
+ */
+ 
 unsigned char mca_read_stored_pos(int slot, int reg) 
 {
 	if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0;
@@ -430,8 +473,22 @@
 	return mca_info->slot[slot].pos[reg];
 } /* mca_read_stored_pos() */
 
+EXPORT_SYMBOL(mca_read_stored_pos);
+
 /*--------------------------------------------------------------------*/
 
+/**
+ *	mca_read_pos - read POS register from card
+ *	@slot: slot number to read from
+ *	@reg:  register to read from
+ *
+ *	Fetch a POS value directly from the hardware to obtain the
+ *	current value. This is much slower than mca_read_stored_pos and
+ *	may not be invoked from interrupt context. It handles the
+ *	deep magic required for onboard devices transparently.
+ */
+ 
+
 unsigned char mca_read_pos(int slot, int reg) 
 {
 	unsigned int byte = 0;
@@ -489,16 +546,32 @@
 	return byte;
 } /* mca_read_pos() */
 
+EXPORT_SYMBOL(mca_read_pos);
+
 /*--------------------------------------------------------------------*/
 
-/* Note that this a technically a Bad Thing, as IBM tech stuff says
- * you should only set POS values through their utilities.
- * However, some devices such as the 3c523 recommend that you write
- * back some data to make sure the configuration is consistent.
- * I'd say that IBM is right, but I like my drivers to work.
- * This function can't do checks to see if multiple devices end up
- * with the same resources, so you might see magic smoke if someone
- * screws up.
+/**
+ *	mca_write_pos - read POS register from card
+ *	@slot: slot number to read from
+ *	@reg:  register to read from
+ *	@byte: byte to write to the POS registers
+ *
+ *	Store a POS value directly from the hardware. You should not 
+ *	normally need to use this function and should have a very good
+ *	knowledge of MCA bus before you do so. Doing this wrongly can
+ *	damage the hardware.
+ *
+ *	This function may not be used from interrupt context.
+ *
+ *	Note that this a technically a Bad Thing, as IBM tech stuff says
+ *	you should only set POS values through their utilities.
+ *	However, some devices such as the 3c523 recommend that you write
+ *	back some data to make sure the configuration is consistent.
+ *	I'd say that IBM is right, but I like my drivers to work.
+ *
+ *	This function can't do checks to see if multiple devices end up
+ *	with the same resources, so you might see magic smoke if someone
+ *	screws up.
  */
 
 void mca_write_pos(int slot, int reg, unsigned char byte) 
@@ -532,8 +605,20 @@
 	mca_info->slot[slot].pos[reg] = byte;
 } /* mca_write_pos() */
 
+EXPORT_SYMBOL(mca_write_pos);
+
 /*--------------------------------------------------------------------*/
 
+/**
+ *	mca_set_adapter_name - Set the description of the card
+ *	@slot: slot to name
+ *	@name: text string for the namen
+ *
+ *	This function sets the name reported via /proc for this
+ *	adapter slot. This is for user information only. Setting a 
+ *	name deletes any previous name.
+ */
+ 
 void mca_set_adapter_name(int slot, char* name) 
 {
 	if(mca_info == NULL) return;
@@ -550,6 +635,26 @@
 	}
 }
 
+EXPORT_SYMBOL(mca_set_adapter_name);
+
+/**
+ *	mca_set_adapter_procfn - Set the /proc callback
+ *	@slot: slot to configure
+ *	@procfn: callback function to call for /proc 
+ *	@dev: device information passed to the callback
+ *
+ *	This sets up an information callback for /proc/mca/slot?.  The
+ *	function is called with the buffer, slot, and device pointer (or
+ *	some equally informative context information, or nothing, if you
+ *	prefer), and is expected to put useful information into the
+ *	buffer.  The adapter name, id, and POS registers get printed
+ * 	before this is called though, so don't do it again.
+ *
+ *	This should be called with a NULL procfn when a module
+ *	unregisters, thus preventing kernel crashes and other such
+ *	nastiness.
+ */
+
 void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* dev)
 {
 	if(mca_info == NULL) return;
@@ -560,11 +665,33 @@
 	}
 }
 
+EXPORT_SYMBOL(mca_set_adapter_procfn);
+
+/**
+ *	mca_is_adapter_used - check if claimed by driver
+ *	@slot:	slot to check
+ *
+ *	Returns 1 if the slot has been claimed by a driver
+ */
+
 int mca_is_adapter_used(int slot)
 {
 	return mca_info->slot[slot].driver_loaded;
 }
 
+EXPORT_SYMBOL(mca_is_adapter_used);
+
+/**
+ *	mca_mark_as_used - claim an MCA device
+ *	@slot:	slot to claim
+ *	FIXME:  should we make this threadsafe
+ *
+ *	Claim an MCA slot for a device driver. If the
+ *	slot is already taken the function returns 1,
+ *	if it is not taken it is claimed and 0 is
+ *	returned.
+ */
+
 int mca_mark_as_used(int slot)
 {
 	if(mca_info->slot[slot].driver_loaded) return 1;
@@ -572,10 +699,29 @@
 	return 0;
 }
 
+EXPORT_SYMBOL(mca_mark_as_used);
+
+/**
+ *	mca_mark_as_unused - release an MCA device
+ *	@slot:	slot to claim
+ *
+ *	Release the slot for other drives to use.
+ */
+
 void mca_mark_as_unused(int slot)
 {
 	mca_info->slot[slot].driver_loaded = 0;
 }
+
+EXPORT_SYMBOL(mca_mark_as_unused);
+
+/**
+ *	mca_get_adapter_name - get the adapter description
+ *	@slot:	slot to query
+ *
+ *	Return the adapter description if set. If it has not been
+ *	set or the slot is out range then return NULL.
+ */
  
 char *mca_get_adapter_name(int slot) 
 {
@@ -588,6 +734,16 @@
 	return 0;
 }
 
+EXPORT_SYMBOL(mca_get_adapter_name);
+
+/**
+ *	mca_isadapter - check if the slot holds an adapter
+ *	@slot:	slot to query
+ *
+ *	Returns zero if the slot does not hold an adapter, non zero if
+ *	it does.
+ */
+
 int mca_isadapter(int slot)
 {
 	if(mca_info == NULL) return 0;
@@ -600,6 +756,17 @@
 	return 0;
 }
 
+EXPORT_SYMBOL(mca_isadapter);
+
+
+/**
+ *	mca_isadapter - check if the slot holds an adapter
+ *	@slot:	slot to query
+ *
+ *	Returns a non zero value if the slot holds an enabled adapter
+ *	and zero for any other case.
+ */
+
 int mca_isenabled(int slot)
 {
 	if(mca_info == NULL) return 0;
@@ -610,6 +777,8 @@
 
 	return 0;
 }
+
+EXPORT_SYMBOL(mca_isenabled);
 
 /*--------------------------------------------------------------------*/
 
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
--- v2.3.51/linux/arch/i386/kernel/mtrr.c	Sat Feb 26 22:31:39 2000
+++ linux/arch/i386/kernel/mtrr.c	Tue Mar 14 17:54:42 2000
@@ -1101,8 +1101,44 @@
 static int (*get_free_region) (unsigned long base,
 			       unsigned long size) = generic_get_free_region;
 
-int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
-	      char increment)
+/**
+ *	mtrr_add - Add a memory type region
+ *	@base: Physical base address of region
+ *	@size: Physical size of region
+ *	@type: Type of MTRR desired
+ *	@increment: If this is true do usage counting on the region
+ *
+ *	Memory type region registers control the caching on newer Intel and
+ *	non Intel processors. This function allows drivers to request an
+ *	MTRR is added. The details and hardware specifics of each processors
+ *	implementation are hidden from the caller, but nevertheless the 
+ *	caller should expect to need to provide a power of two size on an
+ *	equivalent power of two boundary.
+ *
+ *	If the region cannot be added either because all regions are in use
+ *	or the CPU cannot support it a negative value is returned. On success
+ *	the register number for this entry is returned, but should be treated
+ *	as a cookie only.
+ *
+ *	On a multiprocessor machine the changes are made to all processors.
+ *	This is required on x86 by the Intel processors.
+ *
+ *	The available types are
+ *
+ *	MTRR_TYPE_UNCACHEABLE	-	No caching
+ *
+ *	MTRR_TYPE_WRITEBACK	-	Write data back in bursts whenever
+ *
+ *	MTRR_TYPE_WRCOMB	-	Write data back soon but allow bursts
+ *
+ *	MTRR_TYPE_WRTHROUGH	-	Cache reads but not writes
+ *
+ *	BUGS: Needs a quiet flag for the cases where drivers do not mind
+ *	failures and do not wish system log messages to be sent.
+ */
+
+int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment)
+{
 /*  [SUMMARY] Add an MTRR entry.
     <base> The starting (base) address of the region.
     <size> The size (in bytes) of the region.
@@ -1113,7 +1149,6 @@
     the error code.
     [NOTE] This routine uses a spinlock.
 */
-{
     int i, max;
     mtrr_type ltype;
     unsigned long lbase, lsize, last;
@@ -1145,7 +1180,7 @@
 	    if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
 		 (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) -1 ) ) )
 	    {
-		printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
+		printk (KERN_WARNING "mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
 		return -EINVAL;
 	    }
 	}
@@ -1162,13 +1197,13 @@
 	{
 	    if (type != MTRR_TYPE_WRCOMB)
 	    {
-		printk ("mtrr: only write-combining is supported\n");
+		printk (KERN_WARNING "mtrr: only write-combining is supported\n");
 		return -EINVAL;
 	    }
 	}
 	else if (base + size < 0x100000)
 	{
-	    printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
+	    printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
 		    base, size);
 	    return -EINVAL;
 	}
@@ -1179,7 +1214,7 @@
 	     lbase = lbase >> 1, last = last >> 1);
 	if (lbase != last)
 	{
-	    printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
+	    printk (KERN_WARNING "mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
 		    base, size);
 	    return -EINVAL;
 	}
@@ -1196,7 +1231,7 @@
     /*  If the type is WC, check that this processor supports it  */
     if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () )
     {
-        printk ("mtrr: your processor doesn't support write-combining\n");
+        printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n");
         return -ENOSYS;
     }
     increment = increment ? 1 : 0;
@@ -1212,7 +1247,7 @@
 	if ( (base < lbase) || (base + size > lbase + lsize) )
 	{
 	    up(&main_lock);
-	    printk ("mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
+	    printk (KERN_WARNING "mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
 		    base, size, lbase, lsize);
 	    return -EINVAL;
 	}
@@ -1245,6 +1280,21 @@
     return i;
 }   /*  End Function mtrr_add  */
 
+/**
+ *	mtrr_del
+ *	@reg: Register returned by mtrr_add
+ *	@base: Physical base address
+ *	@size: Size of region
+ *
+ *	If register is supplied then base and size are ignored. This is
+ *	how drivers should call it.
+ *
+ *	Releases an MTRR region. If the usage count drops to zero the 
+ *	register is freed and the region returns to default state.
+ *	On success the register is returned, on failure a negative error
+ *	code.
+ */
+ 
 int mtrr_del (int reg, unsigned long base, unsigned long size)
 /*  [SUMMARY] Delete MTRR/decrement usage count.
     <reg> The register. If this is less than 0 then <<base>> and <<size>> must
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.3.51/linux/arch/i386/kernel/setup.c	Sat Feb 26 22:31:39 2000
+++ linux/arch/i386/kernel/setup.c	Sun Mar 12 19:39:47 2000
@@ -790,14 +790,14 @@
 
 static int __init get_model_name(struct cpuinfo_x86 *c)
 {
-	unsigned int n, dummy, *v;
+	unsigned int n, dummy, *v, ecx, edx;
 
 	/* Actually we must have cpuid or we could never have
 	 * figured out that this was AMD from the vendor info :-).
 	 */
 
 	cpuid(0x80000000, &n, &dummy, &dummy, &dummy);
-	if (n < 4)
+	if (n < 0x80000004)
 		return 0;
 	cpuid(0x80000001, &dummy, &dummy, &dummy, &(c->x86_capability));
 	v = (unsigned int *) c->x86_model_id;
@@ -806,13 +806,24 @@
 	cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
 	c->x86_model_id[48] = 0;
 	/*  Set MTRR capability flag if appropriate  */
-	if(boot_cpu_data.x86 !=5)
-		return 1;
-	if((boot_cpu_data.x86_model == 9) ||
-	   ((boot_cpu_data.x86_model == 8) && 
-	    (boot_cpu_data.x86_mask >= 8)))
-		c->x86_capability |= X86_FEATURE_MTRR;
+	if(boot_cpu_data.x86 == 5) {
+		if((boot_cpu_data.x86_model == 9) ||
+		   ((boot_cpu_data.x86_model == 8) && 
+		    (boot_cpu_data.x86_mask >= 8)))
+			c->x86_capability |= X86_FEATURE_MTRR;
+	}
 
+	if (n >= 0x80000005){
+		cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
+		printk("CPU: L1 I Cache: %dK  L1 D Cache: %dK\n",
+			ecx>>24, edx>>24);
+		c->x86_cache_size=(ecx>>24)+(edx>>24);
+	}
+	if (n >= 0x80000006){
+		cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
+		printk("CPU: L2 Cache: %dK\n", ecx>>16);
+		c->x86_cache_size=(ecx>>16);
+	}
 	return 1;
 }
 
@@ -882,18 +893,7 @@
 			}
 			break;
 		case 6:	/* An Athlon. We can trust the BIOS probably */
-		{
-			
-			u32 ecx, edx, dummy;
-			cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
-			printk("L1 I Cache: %dK  L1 D Cache: %dK\n",
-				ecx>>24, edx>>24);
-			cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
-			printk("L2 Cache: %dK\n", ecx>>16);
-			c->x86_cache_size = ecx>>16;
-			break;
-		}
-		
+			break;		
 	}
 	return r;
 }
@@ -1021,10 +1021,18 @@
 		/* It isnt really a PCI quirk directly, but the cure is the
 		   same. The MediaGX has deep magic SMM stuff that handles the
 		   SB emulation. It thows away the fifo on disable_dma() which
-		   is wrong and ruins the audio. */
+                   is wrong and ruins the audio. 
+                   
+                   Bug2: VSA1 has a wrap bug so that using maximum sized DMA 
+                   causes bad things. According to NatSemi VSA2 has another
+                   bug to do with 'hlt'. I've not seen any boards using VSA2
+                   and X doesn't seem to support it either so who cares 8).
+                   VSA1 we work around however.
+                   
+		*/
 		
-		printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bug.\n");
-		isa_dma_bridge_buggy = 1;
+		printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bugs.\n");
+		isa_dma_bridge_buggy = 2;
 #endif		
 		/* GXm supports extended cpuid levels 'ala' AMD */
 		if (c->cpuid_level == 2) {
diff -u --recursive --new-file v2.3.51/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.3.51/linux/arch/i386/kernel/time.c	Fri Jan 21 18:19:16 2000
+++ linux/arch/i386/kernel/time.c	Sun Mar 12 19:39:47 2000
@@ -615,6 +615,8 @@
 
 void __init time_init(void)
 {
+	extern int x86_udelay_tsc;
+	
 	xtime.tv_sec = get_cmos_time();
 	xtime.tv_usec = 0;
 
@@ -650,6 +652,11 @@
 		if (tsc_quotient) {
 			fast_gettimeoffset_quotient = tsc_quotient;
 			use_tsc = 1;
+			/*
+			 *	We could be more selective here I suspect
+			 *	and just enable this for the next intel chips ?
+			 */
+			x86_udelay_tsc = 1;
 #ifndef do_gettimeoffset
 			do_gettimeoffset = do_fast_gettimeoffset;
 #endif
diff -u --recursive --new-file v2.3.51/linux/arch/i386/lib/delay.c linux/arch/i386/lib/delay.c
--- v2.3.51/linux/arch/i386/lib/delay.c	Sun Dec 27 10:36:38 1998
+++ linux/arch/i386/lib/delay.c	Sun Mar 12 19:39:47 2000
@@ -12,12 +12,38 @@
 
 #include <linux/sched.h>
 #include <linux/delay.h>
+#include <asm/delay.h>
 
 #ifdef __SMP__
 #include <asm/smp.h>
 #endif
 
-void __delay(unsigned long loops)
+int x86_udelay_tsc = 0;		/* Delay via TSC */
+
+	
+/*
+ *	Do a udelay using the TSC for any CPU that happens
+ *	to have one that we trust. This could be optimised to avoid
+ *	the multiply per loop but its a delay loop so who are we kidding...
+ */
+
+static void __rdtsc_delay(unsigned long loops)
+{
+	unsigned long bclock, now;
+	
+	rdtscl(bclock);
+	do
+	{
+		rdtscl(now);
+	}
+	while((now-bclock) < loops);
+}
+
+/*
+ *	Non TSC based delay loop for 386, 486, MediaGX
+ */
+ 
+static void __loop_delay(unsigned long loops)
 {
 	int d0;
 	__asm__ __volatile__(
@@ -28,6 +54,14 @@
 		"2:\tdecl %0\n\tjns 2b"
 		:"=&a" (d0)
 		:"0" (loops));
+}
+
+void __delay(unsigned long loops)
+{
+	if(x86_udelay_tsc)
+		__rdtsc_delay(loops);
+	else
+		__loop_delay(loops);
 }
 
 inline void __const_udelay(unsigned long xloops)
diff -u --recursive --new-file v2.3.51/linux/arch/ia64/config.in linux/arch/ia64/config.in
--- v2.3.51/linux/arch/ia64/config.in	Fri Mar 10 16:40:39 2000
+++ linux/arch/ia64/config.in	Sun Mar 12 19:49:24 2000
@@ -80,6 +80,19 @@
 fi
 
 mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support' CONFIG_SCSI
diff -u --recursive --new-file v2.3.51/linux/arch/ia64/defconfig linux/arch/ia64/defconfig
--- v2.3.51/linux/arch/ia64/defconfig	Thu Feb 10 17:11:03 2000
+++ linux/arch/ia64/defconfig	Sun Mar 12 19:49:24 2000
@@ -38,12 +38,6 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
 
 #
 # Additional Block Devices
@@ -55,6 +49,45 @@
 # CONFIG_BLK_DEV_XD is not set
 CONFIG_PARIDE_PARPORT=y
 # CONFIG_PARIDE is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_IDEDMA_PCI_AUTO=y
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_PIIX=y
+CONFIG_PIIX_TUNING=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
 # CONFIG_BLK_DEV_HD is not set
 
 #
diff -u --recursive --new-file v2.3.51/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.3.51/linux/arch/m68k/config.in	Sun Feb 13 19:29:03 2000
+++ linux/arch/m68k/config.in	Sun Mar 12 19:49:24 2000
@@ -152,6 +152,19 @@
 fi
 
 mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support' CONFIG_SCSI
diff -u --recursive --new-file v2.3.51/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
--- v2.3.51/linux/arch/m68k/defconfig	Fri Jan 28 15:09:07 2000
+++ linux/arch/m68k/defconfig	Sun Mar 12 19:49:24 2000
@@ -56,13 +56,6 @@
 CONFIG_AMIGA_FLOPPY=y
 CONFIG_ATARI_FLOPPY=y
 # CONFIG_BLK_DEV_LOOP is not set
-# CONFIG_BLK_DEV_IDE is not set
-# CONFIG_BLK_DEV_IDEDISK is not set
-# CONFIG_BLK_DEV_IDECD is not set
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-# CONFIG_BLK_DEV_IDEDOUBLER is not set
 # CONFIG_AMIGA_Z2RAM is not set
 # CONFIG_ATARI_ACSI is not set
 # CONFIG_ACSI_MULTI_LUN is not set
@@ -119,6 +112,11 @@
 # QoS and/or fair queueing
 #
 # CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
 
 #
 # SCSI support
diff -u --recursive --new-file v2.3.51/linux/arch/mips/config.in linux/arch/mips/config.in
--- v2.3.51/linux/arch/mips/config.in	Fri Mar 10 16:40:40 2000
+++ linux/arch/mips/config.in	Sun Mar 12 19:49:24 2000
@@ -166,6 +166,19 @@
 fi
 
 mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support' CONFIG_SCSI
diff -u --recursive --new-file v2.3.51/linux/arch/mips/defconfig linux/arch/mips/defconfig
--- v2.3.51/linux/arch/mips/defconfig	Fri Mar 10 16:40:40 2000
+++ linux/arch/mips/defconfig	Sun Mar 12 19:49:24 2000
@@ -73,12 +73,6 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
 
 #
 # Additional Block Devices
@@ -89,8 +83,6 @@
 # CONFIG_BLK_DEV_RAM is not set
 # CONFIG_BLK_DEV_XD is not set
 # CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # Networking options
@@ -146,6 +138,11 @@
 #
 # CONFIG_PHONE is not set
 # CONFIG_PHONE_IXJ is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
 
 #
 # SCSI support
diff -u --recursive --new-file v2.3.51/linux/arch/mips/kernel/irixelf.c linux/arch/mips/kernel/irixelf.c
--- v2.3.51/linux/arch/mips/kernel/irixelf.c	Sat Feb 26 22:31:40 2000
+++ linux/arch/mips/kernel/irixelf.c	Mon Mar 13 22:18:11 2000
@@ -35,8 +35,6 @@
 #include <asm/mipsregs.h>
 #include <asm/prctl.h>
 
-#include <linux/config.h>
-
 #define DLINFO_ITEMS 12
 
 #include <linux/elf.h>
@@ -602,8 +600,7 @@
 /* These are the functions used to load ELF style executables and shared
  * libraries.  There is no binary dependent code anywhere else.
  */
-static inline int do_load_irix_binary(struct linux_binprm * bprm,
-				      struct pt_regs * regs)
+static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
 	struct elfhdr elf_ex, interp_elf_ex;
 	struct dentry *interpreter_dentry;
@@ -748,14 +745,11 @@
 	sys_close(elf_exec_fileno);
 	current->personality = PER_IRIX32;
 
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_DEC_USE_COUNT(current->exec_domain->module);
+	put_exec_domain(current->exec_domain);
 	if (current->binfmt && current->binfmt->module)
 		__MOD_DEC_USE_COUNT(current->binfmt->module);
 	current->exec_domain = lookup_exec_domain(current->personality);
 	current->binfmt = &irix_format;
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_INC_USE_COUNT(current->exec_domain->module);
 	if (current->binfmt && current->binfmt->module)
 		__MOD_INC_USE_COUNT(current->binfmt->module);
 
@@ -823,16 +817,6 @@
 	goto out;
 }
 
-static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_load_irix_binary(bprm, regs);
-	MOD_DEC_USE_COUNT;
-	return retval;
-}
-
 /* This is really simpleminded and specialized - we are loading an
  * a.out library that is given an ELF header.
  */
@@ -934,13 +918,11 @@
 	int retval = -EACCES;
 	struct file *file;
 
-	MOD_INC_USE_COUNT;
 	file = fget(fd);
 	if (file) {
 		retval = do_load_irix_library(file);
 		fput(file);
 	}
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 	
@@ -1143,10 +1125,6 @@
 	elf_fpregset_t fpu;		/* NT_PRFPREG */
 	struct elf_prpsinfo psinfo;	/* NT_PRPSINFO */
 
-#ifndef CONFIG_BINFMT_IRIX
-	MOD_INC_USE_COUNT;
-#endif
-
 	/* Count what's needed to dump, up to the limit of coredump size. */
 	segs = 0;
 	size = 0;
@@ -1356,9 +1334,6 @@
 
 end_coredump:
 	set_fs(fs);
-#ifndef CONFIG_BINFMT_IRIX
-	MOD_DEC_USE_COUNT;
-#endif
 	return has_dumped;
 }
 
diff -u --recursive --new-file v2.3.51/linux/arch/mips64/config.in linux/arch/mips64/config.in
--- v2.3.51/linux/arch/mips64/config.in	Fri Mar 10 16:40:40 2000
+++ linux/arch/mips64/config.in	Sun Mar 12 19:49:24 2000
@@ -114,6 +114,19 @@
 source drivers/telephony/Config.in
 
 mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support' CONFIG_SCSI
diff -u --recursive --new-file v2.3.51/linux/arch/mips64/defconfig linux/arch/mips64/defconfig
--- v2.3.51/linux/arch/mips64/defconfig	Sat Feb 26 22:31:41 2000
+++ linux/arch/mips64/defconfig	Sun Mar 12 19:49:24 2000
@@ -60,12 +60,6 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
 # CONFIG_BLK_CPQ_DA is not set
 
 #
@@ -78,8 +72,6 @@
 # CONFIG_BLK_DEV_XD is not set
 # CONFIG_BLK_DEV_DAC960 is not set
 # CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # Networking options
@@ -117,6 +109,11 @@
 #
 # CONFIG_PHONE is not set
 # CONFIG_PHONE_IXJ is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
 
 #
 # SCSI support
diff -u --recursive --new-file v2.3.51/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.3.51/linux/arch/ppc/config.in	Sat Feb 26 22:31:41 2000
+++ linux/arch/ppc/config.in	Sun Mar 12 19:49:24 2000
@@ -187,6 +187,19 @@
 fi
 
 mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 tristate 'SCSI support' CONFIG_SCSI
 if [ "$CONFIG_SCSI" != "n" ]; then
diff -u --recursive --new-file v2.3.51/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.3.51/linux/arch/ppc/defconfig	Fri Mar 10 16:40:40 2000
+++ linux/arch/ppc/defconfig	Sun Mar 12 19:49:24 2000
@@ -79,32 +79,6 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
-CONFIG_BLK_DEV_IDE=y
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_BLK_DEV_IDECS is not set
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-CONFIG_BLK_DEV_IDEFLOPPY=y
-CONFIG_BLK_DEV_IDESCSI=y
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEPCI=y
-# CONFIG_IDEPCI_SHARE_IRQ is not set
-# CONFIG_BLK_DEV_IDEDMA_PCI is not set
-# CONFIG_BLK_DEV_OFFBOARD is not set
-# CONFIG_BLK_DEV_AEC6210 is not set
-# CONFIG_BLK_DEV_CMD64X is not set
-# CONFIG_BLK_DEV_CS5530 is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-CONFIG_BLK_DEV_SL82C105=y
-CONFIG_BLK_DEV_IDE_PMAC=y
-CONFIG_BLK_DEV_IDEDMA_PMAC=y
-CONFIG_IDEDMA_PMAC_AUTO=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-# CONFIG_IDE_CHIPSETS is not set
 # CONFIG_BLK_CPQ_DA is not set
 CONFIG_BLK_DEV_LOOP=y
 # CONFIG_BLK_DEV_NBD is not set
@@ -114,8 +88,6 @@
 # CONFIG_BLK_DEV_XD is not set
 # CONFIG_BLK_DEV_DAC960 is not set
 # CONFIG_PARIDE is not set
-CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
 
 #
 # Networking options
@@ -158,6 +130,40 @@
 # QoS and/or fair queueing
 #
 # CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_BLK_DEV_IDEFLOPPY=y
+CONFIG_BLK_DEV_IDESCSI=y
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_SL82C105=y
+CONFIG_BLK_DEV_IDE_PMAC=y
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_IDEDMA_PMAC_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+# CONFIG_BLK_DEV_HD is not set
 
 #
 # SCSI support
diff -u --recursive --new-file v2.3.51/linux/arch/sh/config.in linux/arch/sh/config.in
--- v2.3.51/linux/arch/sh/config.in	Fri Mar 10 16:40:40 2000
+++ linux/arch/sh/config.in	Sun Mar 12 19:49:24 2000
@@ -95,6 +95,19 @@
 fi
 
 mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support' CONFIG_SCSI
diff -u --recursive --new-file v2.3.51/linux/arch/sh/defconfig linux/arch/sh/defconfig
--- v2.3.51/linux/arch/sh/defconfig	Fri Mar 10 16:40:40 2000
+++ linux/arch/sh/defconfig	Sun Mar 12 19:49:24 2000
@@ -45,6 +45,21 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
+
+#
+# Additional Block Devices
+#
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
 CONFIG_BLK_DEV_IDE=y
 
 #
@@ -63,16 +78,6 @@
 #
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_IDE_CHIPSETS is not set
-
-#
-# Additional Block Devices
-#
-# CONFIG_BLK_DEV_LOOP is not set
-# CONFIG_BLK_DEV_MD is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_PARIDE is not set
 # CONFIG_BLK_DEV_IDE_MODES is not set
 # CONFIG_BLK_DEV_HD is not set
 
diff -u --recursive --new-file v2.3.51/linux/arch/sparc/boot/piggyback.c linux/arch/sparc/boot/piggyback.c
--- v2.3.51/linux/arch/sparc/boot/piggyback.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/boot/piggyback.c	Sun Mar 12 19:11:16 2000
@@ -1,4 +1,4 @@
-/* $Id: piggyback.c,v 1.2 1998/12/15 12:24:43 jj Exp $
+/* $Id: piggyback.c,v 1.3 2000/03/11 00:22:26 zaitcev Exp $
    Simple utility to make a single-image install kernel with initial ramdisk
    for Sparc tftpbooting without need to set up nfs.
    
@@ -29,8 +29,18 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
-/* Note: run this on an a.out kernel (use elftoaout for it), as PROM looks for a.out image onlly
-   usage: piggyback vmlinux System.map tail, where tail is gzipped fs of the initial ramdisk */
+/*
+ * Note: run this on an a.out kernel (use elftoaout for it),
+ * as PROM looks for a.out image only.
+ */
+
+void usage(void)
+{
+	/* fs_img.gz is an image of initial ramdisk. */
+	fprintf(stderr, "Usage: piggyback vmlinux.aout System.map fs_img.gz\n");
+	fprintf(stderr, "\tKernel image will be modified in place.\n");
+	exit(1);
+}
 
 void die(char *str)
 {
@@ -45,7 +55,8 @@
 	FILE *map;
 	struct stat s;
 	int image, tail;
-	
+
+	if (argc != 4) usage();
 	start = end = 0;
 	if (stat (argv[3], &s) < 0) die (argv[3]);
 	map = fopen (argv[2], "r");
diff -u --recursive --new-file v2.3.51/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.3.51/linux/arch/sparc/config.in	Thu Mar  2 14:36:22 2000
+++ linux/arch/sparc/config.in	Mon Mar 13 09:35:06 2000
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.87 2000/02/27 19:34:12 davem Exp $
+# $Id: config.in,v 1.88 2000/03/13 03:40:27 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -74,10 +74,9 @@
 endmenu
 
 mainmenu_option next_comment
-comment 'Floppy, IDE, and other block devices'
+comment 'Floppy and other block devices'
 
 bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD
-define_bool CONFIG_BLK_DEV_IDE n
 bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
    tristate '  Linear (append) mode' CONFIG_MD_LINEAR
@@ -108,6 +107,24 @@
    source drivers/isdn/Config.in
 fi
 endmenu
+
+
+define_bool CONFIG_IDE n
+define_bool CONFIG_BLK_DEV_IDE_MODES n
+define_bool CONFIG_BLK_DEV_HD n
+
+# mainmenu_option next_comment
+# comment 'ATA/IDE/MFM/RLL support'
+#
+# tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+#
+# if [ "$CONFIG_IDE" != "n" ]; then
+#   source drivers/ide/Config.in
+# else
+#   define_bool CONFIG_BLK_DEV_IDE_MODES n
+#   define_bool CONFIG_BLK_DEV_HD n
+# fi
+# endmenu
 
 mainmenu_option next_comment
 comment 'SCSI support'
diff -u --recursive --new-file v2.3.51/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.3.51/linux/arch/sparc/defconfig	Fri Mar 10 16:40:40 2000
+++ linux/arch/sparc/defconfig	Sun Mar 12 19:49:24 2000
@@ -101,7 +101,6 @@
 # Floppy, IDE, and other block devices
 #
 CONFIG_BLK_DEV_FD=y
-# CONFIG_BLK_DEV_IDE is not set
 CONFIG_BLK_DEV_MD=y
 CONFIG_MD_LINEAR=m
 CONFIG_MD_STRIPED=m
@@ -168,6 +167,11 @@
 # ISDN subsystem
 #
 # CONFIG_ISDN is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
 
 #
 # SCSI support
diff -u --recursive --new-file v2.3.51/linux/arch/sparc/kernel/sys_solaris.c linux/arch/sparc/kernel/sys_solaris.c
--- v2.3.51/linux/arch/sparc/kernel/sys_solaris.c	Wed Dec 29 13:13:13 1999
+++ linux/arch/sparc/kernel/sys_solaris.c	Mon Mar 13 12:35:39 2000
@@ -15,12 +15,14 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 
+/* CHECKME: this stuff looks rather bogus */
 asmlinkage int
 do_solaris_syscall (struct pt_regs *regs)
 {
 	int ret;
 
 	lock_kernel();
+	put_exec_domain(current->exec_domain);
 	current->personality = PER_SVR4;
 	current->exec_domain = lookup_exec_domain(PER_SVR4);
 
diff -u --recursive --new-file v2.3.51/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.3.51/linux/arch/sparc/kernel/sys_sunos.c	Fri Mar 10 16:40:41 2000
+++ linux/arch/sparc/kernel/sys_sunos.c	Mon Mar 13 22:18:10 2000
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.114 2000/03/07 22:27:27 davem Exp $
+/* $Id: sys_sunos.c,v 1.115 2000/03/13 21:57:23 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -294,57 +294,6 @@
 	       strategy <= 4 ? mstrings[strategy] : "BOGUS",
 	       address, len);
 	unlock_kernel();
-}
-
-/* Places into character array, the status of all the pages in the passed
- * range from 'addr' to 'addr + len'.  -1 on failure, 0 on success...
- * The encoding in each character is:
- * low-bit is zero == Page is not in physical ram right now
- * low-bit is one  == Page is currently residing in core
- * All other bits are undefined within the character so there...
- * Also, if you try to get stats on an area outside of the user vm area
- * *or* the passed base address is not aligned on a page boundary you
- * get an error.
- */
-asmlinkage int sunos_mincore(unsigned long addr, unsigned long len, char *array)
-{
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	pte_t *ptep;
-	unsigned long limit;
-	int num_pages, pnum, retval = -EINVAL;
-
-	lock_kernel();
-	if(addr & ~(PAGE_MASK))
-		goto out;
-
-	num_pages = (len / PAGE_SIZE);
-	retval = -EFAULT;
-	if(verify_area(VERIFY_WRITE, array, num_pages))
-		goto out;
-	retval = -ENOMEM;
-	if((addr >= PAGE_OFFSET) || ((addr + len) > PAGE_OFFSET))
-		goto out; /* I'm sure you're curious about kernel mappings.. */
-
-	/* Wheee, go through pte's */
-	pnum = 0;
-	for(limit = addr + len; addr < limit; addr += PAGE_SIZE, pnum++) {
-		pgdp = pgd_offset(current->mm, addr);
-		if(pgd_none(*pgdp))
-			goto out; /* As per SunOS manpage */
-		pmdp = pmd_offset(pgdp, addr);
-		if(pmd_none(*pmdp))
-			goto out; /* As per SunOS manpage */
-		ptep = pte_offset(pmdp, addr);
-		if(pte_none(*ptep))
-			goto out; /* As per SunOS manpage */
-		/* Page in core or Swapped page? */
-		__put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
-	}
-	retval = 0; /* Success... I think... */
-out:
-	unlock_kernel();
-	return retval;
 }
 
 /* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
diff -u --recursive --new-file v2.3.51/linux/arch/sparc/kernel/systbls.S linux/arch/sparc/kernel/systbls.S
--- v2.3.51/linux/arch/sparc/kernel/systbls.S	Sun Feb 20 21:12:38 2000
+++ linux/arch/sparc/kernel/systbls.S	Mon Mar 13 22:18:10 2000
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.94 2000/02/16 07:31:30 davem Exp $
+/* $Id: systbls.S,v 1.95 2000/03/13 21:57:23 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -33,7 +33,7 @@
 /*60*/	.long sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize
 /*65*/	.long sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_geteuid
 /*70*/	.long sys_getegid, sys_mmap, sys_setreuid, sys_munmap, sys_mprotect
-/*75*/	.long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_nis_syscall, sys_getgroups16
+/*75*/	.long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_mincore, sys_getgroups16
 /*80*/	.long sys_setgroups16, sys_getpgrp, sys_setgroups, sys_setitimer, sys_ftruncate64
 /*85*/	.long sys_swapon, sys_getitimer, sys_setuid, sys_sethostname, sys_setgid
 /*90*/	.long sys_dup2, sys_setfsuid, sys_fcntl, sys_select, sys_setfsgid
@@ -104,7 +104,7 @@
 	.long sunos_nosys, sunos_sbrk, sunos_sstk
 	.long sunos_mmap, sunos_vadvise, sys_munmap
 	.long sys_mprotect, sunos_madvise, sys_vhangup
-	.long sunos_nosys, sunos_mincore, sys_getgroups16
+	.long sunos_nosys, sys_mincore, sys_getgroups16
 	.long sys_setgroups16, sys_getpgrp, sunos_setpgrp
 	.long sys_setitimer, sunos_nosys, sys_swapon
 	.long sys_getitimer, sys_gethostname, sys_sethostname
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.3.51/linux/arch/sparc64/config.in	Thu Mar  2 14:36:22 2000
+++ linux/arch/sparc64/config.in	Mon Mar 13 09:35:13 2000
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.99 2000/02/27 19:34:17 davem Exp $
+# $Id: config.in,v 1.101 2000/03/13 05:49:55 jj Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -98,29 +98,22 @@
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
 tristate 'Network block device support' CONFIG_BLK_DEV_NBD
 
-if [ "$CONFIG_PCI" = "y" ]; then
-   tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
-   if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
-      dep_tristate '  Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
-      dep_tristate '  Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
-      dep_tristate '  Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
-      dep_tristate '  Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
-      dep_tristate '  SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
-      define_bool CONFIG_BLK_DEV_IDEPCI y
-      define_bool CONFIG_BLK_DEV_IDEDMA y
-      define_bool CONFIG_IDEDMA_AUTO y
-      define_bool CONFIG_IDEDMA_NEW_DRIVE_LISTINGS y
-      define_bool CONFIG_BLK_DEV_NS87415 y
-      define_bool CONFIG_BLK_DEV_CMD64X y
-      define_bool CONFIG_BLK_DEV_IDE_MODES y
-   fi
+if [ "$CONFIG_NET" = "y" ]; then
+   source net/Config.in
 fi
 
-endmenu
+mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
 
-if [ "$CONFIG_NET" = "y" ]; then
-   source net/Config.in
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
+  define_bool CONFIG_BLK_DEV_IDE_MODES n
+  define_bool CONFIG_BLK_DEV_HD n
 fi
+endmenu
 
 mainmenu_option next_comment
 comment 'SCSI support'
@@ -172,12 +165,16 @@
   	 bool '  Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
 	 int  '  Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
       fi
+      dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
       dep_tristate 'SYM53C8XX SCSI support' CONFIG_SCSI_SYM53C8XX $CONFIG_SCSI
-      if [ "$CONFIG_SCSI_SYM53C8XX" != "n" ]; then
+      if [ "$CONFIG_SCSI_NCR53C8XX" != "n" -o "$CONFIG_SCSI_SYM53C8XX" != "n" ]; then
 	 int 'default tagged command queue depth' CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS 8
 	 int 'maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 32
 	 int 'synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 10
 	 bool '  enable profiling' CONFIG_SCSI_NCR53C8XX_PROFILE
+         if [ "$CONFIG_SCSI_SYM53C8XX" != "n" ]; then
+	    bool '  include support for the NCR PQS/PDS SCSI card' CONFIG_SCSI_NCR53C8XX_PQS_PDS
+         fi
 	 if [ "$CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS" = "0" ]; then
 	    bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
 	 fi
@@ -219,7 +216,7 @@
       mainmenu_option next_comment
       comment 'Ethernet (10 or 100Mbit)'
 
-      bool 'Sun LANCE support' CONFIG_SUNLANCE
+      tristate 'Sun LANCE support' CONFIG_SUNLANCE
       tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 	 tristate 'Sun BigMAC 10/100baseT support (EXPERIMENTAL)' CONFIG_SUNBMAC
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.3.51/linux/arch/sparc64/defconfig	Fri Mar 10 16:40:41 2000
+++ linux/arch/sparc64/defconfig	Mon Mar 13 09:35:06 2000
@@ -128,19 +128,6 @@
 # CONFIG_BLK_DEV_RAM is not set
 CONFIG_BLK_DEV_LOOP=m
 CONFIG_BLK_DEV_NBD=m
-CONFIG_BLK_DEV_IDE=y
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_BLK_DEV_IDECD=y
-CONFIG_BLK_DEV_IDETAPE=m
-CONFIG_BLK_DEV_IDEFLOPPY=m
-# CONFIG_BLK_DEV_IDESCSI is not set
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-CONFIG_IDEDMA_NEW_DRIVE_LISTINGS=y
-CONFIG_BLK_DEV_NS87415=y
-CONFIG_BLK_DEV_CMD64X=y
-CONFIG_BLK_DEV_IDE_MODES=y
 
 #
 # Networking options
@@ -195,6 +182,71 @@
 # CONFIG_NET_SCHED is not set
 
 #
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=m
+CONFIG_BLK_DEV_IDEFLOPPY=m
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_AUTO=y
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_AEC6210_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD7409 is not set
+# CONFIG_AMD7409_OVERRIDE is not set
+CONFIG_BLK_DEV_CMD64X=y
+# CONFIG_CMD64X_RAID is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_HPT366_FIP is not set
+# CONFIG_HPT366_MODE3 is not set
+CONFIG_BLK_DEV_NS87415=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+
+#
 # SCSI support
 #
 CONFIG_SCSI=y
@@ -228,11 +280,13 @@
 CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
 CONFIG_AIC7XXX_PROC_STATS=y
 CONFIG_AIC7XXX_RESET_DELAY=5
+CONFIG_SCSI_NCR53C8XX=y
 CONFIG_SCSI_SYM53C8XX=y
 CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=4
 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
 CONFIG_SCSI_NCR53C8XX_SYNC=10
 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set
+# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
 # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 CONFIG_SCSI_QLOGIC_ISP=y
 
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/kernel/binfmt_aout32.c linux/arch/sparc64/kernel/binfmt_aout32.c
--- v2.3.51/linux/arch/sparc64/kernel/binfmt_aout32.c	Fri Jan 28 15:09:07 2000
+++ linux/arch/sparc64/kernel/binfmt_aout32.c	Mon Mar 13 12:35:39 2000
@@ -82,8 +82,7 @@
  * dumping of the process results in another error..
  */
 
-static inline int
-do_aout32_core_dump(long signr, struct pt_regs * regs, struct file *file)
+static int aout32_core_dump(long signr, struct pt_regs *regs, struct file *file)
 {
 	mm_segment_t fs;
 	int has_dumped = 0;
@@ -143,17 +142,6 @@
 	return has_dumped;
 }
 
-static int
-aout32_core_dump(long signr, struct pt_regs * regs, struct file * file)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_aout32_core_dump(signr, regs, file);
-	MOD_DEC_USE_COUNT;
-	return retval;
-}
-
 /*
  * create_aout32_tables() parses the env- and arg-strings in new user
  * memory and creates the pointer tables from them, and puts their
@@ -207,8 +195,7 @@
  * libraries.  There is no binary dependent code anywhere else.
  */
 
-static inline int do_load_aout32_binary(struct linux_binprm * bprm,
-					struct pt_regs * regs)
+static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
 	struct exec ex;
 	struct file * file;
@@ -320,14 +307,11 @@
 		}
 	}
 beyond_if:
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_DEC_USE_COUNT(current->exec_domain->module);
+	put_exec_domain(current->exec_domain);
 	if (current->binfmt && current->binfmt->module)
 		__MOD_DEC_USE_COUNT(current->binfmt->module);
 	current->exec_domain = lookup_exec_domain(current->personality);
 	current->binfmt = &aout32_format;
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_INC_USE_COUNT(current->exec_domain->module);
 	if (current->binfmt && current->binfmt->module)
 		__MOD_INC_USE_COUNT(current->binfmt->module);
 
@@ -358,21 +342,8 @@
 	return 0;
 }
 
-
-static int
-load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_load_aout32_binary(bprm, regs);
-	MOD_DEC_USE_COUNT;
-	return retval;
-}
-
 /* N.B. Move to .h file and use code in fs/binfmt_aout.c? */
-static inline int
-do_load_aout32_library(int fd)
+static int load_aout32_library(int fd)
 {
         struct file * file;
 	struct inode * inode;
@@ -441,17 +412,6 @@
 out_putf:
 	fput(file);
 out:
-	return retval;
-}
-
-static int
-load_aout32_library(int fd)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_load_aout32_library(fd);
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- v2.3.51/linux/arch/sparc64/kernel/ioctl32.c	Sun Feb 20 21:12:38 2000
+++ linux/arch/sparc64/kernel/ioctl32.c	Mon Mar 13 22:18:10 2000
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.80 2000/02/17 06:45:09 jj Exp $
+/* $Id: ioctl32.c,v 1.82 2000/03/13 21:57:27 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
@@ -34,6 +34,7 @@
 #include <linux/cdrom.h>
 #include <linux/loop.h>
 #include <linux/auto_fs.h>
+#include <linux/devfs_fs.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>
 #include <linux/fb.h>
@@ -2334,6 +2335,10 @@
 COMPATIBLE_IOCTL(PPPIOCNEWUNIT)
 COMPATIBLE_IOCTL(PPPIOCATTACH)
 COMPATIBLE_IOCTL(PPPIOCDETACH)
+COMPATIBLE_IOCTL(PPPIOCSMRRU)
+COMPATIBLE_IOCTL(PPPIOCCONNECT)
+COMPATIBLE_IOCTL(PPPIOCDISCONN)
+COMPATIBLE_IOCTL(PPPIOCATTCHAN)
 /* CDROM stuff */
 COMPATIBLE_IOCTL(CDROMPAUSE)
 COMPATIBLE_IOCTL(CDROMRESUME)
@@ -2517,6 +2522,11 @@
 COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC)
 COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER)
 COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE)
+/* DEVFS */
+COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV)
+COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK)
+COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE)
+COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK)
 /* Raw devices */
 COMPATIBLE_IOCTL(RAW_SETBIND)
 COMPATIBLE_IOCTL(RAW_GETBIND)
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.3.51/linux/arch/sparc64/kernel/sys_sparc32.c	Fri Mar 10 16:40:41 2000
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Mon Mar 13 22:18:10 2000
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.134 2000/03/07 22:27:30 davem Exp $
+/* $Id: sys_sparc32.c,v 1.136 2000/03/13 21:57:29 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -3645,7 +3645,7 @@
 };
 
 union nfsctl_res32 {
-	struct knfs_fh		cr32_getfh;
+	__u8			cr32_getfh[NFS_FHSIZE];
 	u32			cr32_debug;
 };
 
@@ -4217,4 +4217,13 @@
 	up(&current->mm->mmap_sem);
 out:
 	return ret;       
+}
+
+extern asmlinkage long sys_mincore(unsigned long start, size_t len, unsigned char *vec);
+
+asmlinkage long sys32_mincore(unsigned long start, u32 __len, unsigned char *vec)
+{
+	size_t len = (size_t) __len;
+
+	return sys_mincore(start, len, vec);
 }
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c
--- v2.3.51/linux/arch/sparc64/kernel/sys_sunos32.c	Fri Mar 10 16:40:41 2000
+++ linux/arch/sparc64/kernel/sys_sunos32.c	Mon Mar 13 22:18:10 2000
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.40 2000/03/07 22:27:31 davem Exp $
+/* $Id: sys_sunos32.c,v 1.41 2000/03/13 21:57:31 davem Exp $
  * sys_sunos32.c: SunOS binary compatability layer on sparc64.
  *
  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -250,56 +250,6 @@
 	       current->comm, strategy <= 4 ? mstrings[strategy] : "BOGUS",
 	       address, len);
 	unlock_kernel();
-}
-
-/* Places into character array, the status of all the pages in the passed
- * range from 'addr' to 'addr + len'.  -1 on failure, 0 on success...
- * The encoding in each character is:
- * low-bit is zero == Page is not in physical ram right now
- * low-bit is one  == Page is currently residing in core
- * All other bits are undefined within the character so there...
- * Also, if you try to get stats on an area outside of the user vm area
- * *or* the passed base address is not aligned on a page boundary you
- * get an error.
- */
-asmlinkage int sunos_mincore(u32 __addr, u32 len, u32 u_array)
-{
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	pte_t *ptep;
-	unsigned long limit, addr = (unsigned long)__addr;
-	int num_pages, pnum, retval = -EINVAL;
-	char *array = (char *)A(u_array);
-
-	lock_kernel();
-	if(addr & ~(4096))
-		goto out;
-	num_pages = (len / 4096);
-	retval = -EFAULT;
-	if(verify_area(VERIFY_WRITE, array, num_pages))
-		goto out;
-	retval = -ENOMEM;
-	if((addr >= 0xf0000000) || ((addr + len) > 0xf0000000))
-		goto out; /* I'm sure you're curious about kernel mappings.. */
-	/* Wheee, go through pte's */
-	pnum = 0;
-	for(limit = addr + len; addr < limit; addr += 4096, pnum++) {
-		pgdp = pgd_offset(current->mm, addr);
-		if(pgd_none(*pgdp))
-			goto out; /* As per SunOS manpage */
-		pmdp = pmd_offset(pgdp, addr);
-		if(pmd_none(*pmdp))
-			goto out; /* As per SunOS manpage */
-		ptep = pte_offset(pmdp, addr);
-		if(pte_none(*ptep))
-			goto out; /* As per SunOS manpage */
-		/* Page in core or Swapped page? */
-		__put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
-	}
-	retval = 0; /* Success... I think... */
-out:
-	unlock_kernel();
-	return retval;
 }
 
 /* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S
--- v2.3.51/linux/arch/sparc64/kernel/systbls.S	Sun Feb 20 21:12:38 2000
+++ linux/arch/sparc64/kernel/systbls.S	Mon Mar 13 22:18:10 2000
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.68 2000/02/16 07:31:38 davem Exp $
+/* $Id: systbls.S,v 1.69 2000/03/13 21:57:28 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -34,7 +34,7 @@
 /*60*/	.word sys_umask, sys_chroot, sys32_newfstat, sys_fstat64, sys_getpagesize
 	.word sys_msync, sys_vfork, sys32_pread, sys32_pwrite, sys_geteuid
 /*70*/	.word sys_getegid, sys32_mmap, sys_setreuid, sys_munmap, sys_mprotect
-	.word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys_nis_syscall, sys32_getgroups16
+	.word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys32_mincore, sys32_getgroups16
 /*80*/	.word sys32_setgroups16, sys_getpgrp, sys_setgroups, sys32_setitimer, sys32_ftruncate64
 	.word sys_swapon, sys32_getitimer, sys_setuid, sys_sethostname, sys_setgid
 /*90*/	.word sys_dup2, sys_setfsuid, sys32_fcntl, sys32_select, sys_setfsgid
@@ -93,7 +93,7 @@
 /*60*/	.word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
 	.word sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_nis_syscall
 /*70*/	.word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys64_munmap, sys_mprotect
-	.word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups
+	.word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_mincore, sys_getgroups
 /*80*/	.word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
 	.word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
 /*90*/	.word sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
@@ -164,7 +164,7 @@
 	.word sunos_nosys, sunos_sbrk, sunos_sstk
 	.word sunos_mmap, sunos_vadvise, sys_munmap
 	.word sys_mprotect, sunos_madvise, sys_vhangup
-	.word sunos_nosys, sunos_mincore, sys32_getgroups16
+	.word sunos_nosys, sys32_mincore, sys32_getgroups16
 	.word sys32_setgroups16, sys_getpgrp, sunos_setpgrp
 	.word sys32_setitimer, sunos_nosys, sys_swapon
 	.word sys32_getitimer, sys_gethostname, sys_sethostname
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/solaris/misc.c linux/arch/sparc64/solaris/misc.c
--- v2.3.51/linux/arch/sparc64/solaris/misc.c	Sun Feb 20 21:12:38 2000
+++ linux/arch/sparc64/solaris/misc.c	Mon Mar 13 22:18:10 2000
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.22 2000/02/16 07:31:41 davem Exp $
+/* $Id: misc.c,v 1.23 2000/03/13 21:57:34 davem Exp $
  * misc.c: Miscelaneous syscall emulation for Solaris
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -721,11 +721,8 @@
 {
 	lock_kernel();
 	current->personality = PER_SVR4;
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_DEC_USE_COUNT(current->exec_domain->module);
+	put_exec_domain(current->exec_domain);
 	current->exec_domain = lookup_exec_domain(current->personality);
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_INC_USE_COUNT(current->exec_domain->module);
 	unlock_kernel();
 }
 
diff -u --recursive --new-file v2.3.51/linux/arch/sparc64/solaris/systbl.S linux/arch/sparc64/solaris/systbl.S
--- v2.3.51/linux/arch/sparc64/solaris/systbl.S	Fri Jan 21 18:19:16 2000
+++ linux/arch/sparc64/solaris/systbl.S	Mon Mar 13 22:18:10 2000
@@ -1,4 +1,4 @@
-/* $Id: systbl.S,v 1.10 2000/01/12 02:59:26 davem Exp $
+/* $Id: systbl.S,v 1.11 2000/03/13 21:57:35 davem Exp $
  * systbl.S: System call entry point table for Solaris compatibility.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -142,7 +142,7 @@
 	.word solaris_unimplemented	/* async		111	*/
 	.word solaris_unimplemented	/* priocntlsys		112	*/
 	.word solaris_pathconf		/* pathconf	sd	113	*/
-	.word solaris_unimplemented	/* mincore	xdx	114	*/
+	.word CHAIN(mincore)		/* mincore	d	114	*/
 	.word solaris_mmap		/* mmap		xxxxdx	115	*/
 	.word CHAIN(mprotect)		/* mprotect	xdx	116	*/
 	.word CHAIN(munmap)		/* munmap	xd	117	*/
diff -u --recursive --new-file v2.3.51/linux/drivers/Makefile linux/drivers/Makefile
--- v2.3.51/linux/drivers/Makefile	Sat Feb 26 22:31:42 2000
+++ linux/drivers/Makefile	Sun Mar 12 19:49:24 2000
@@ -9,9 +9,9 @@
 
 SUB_DIRS     := block char net parport sound misc
 MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp i2o ieee1394 \
-				macintosh video dio zorro fc4 usb \
-				nubus tc atm pcmcia i2c telephony
+ALL_SUB_DIRS := $(SUB_DIRS) pci sgi ide scsi sbus cdrom isdn pnp i2o \
+				ieee1394 macintosh video dio zorro fc4 \
+				usb nubus tc atm pcmcia i2c telephony
 
 ifdef CONFIG_DIO
 SUB_DIRS += dio
@@ -93,6 +93,17 @@
   MOD_SUB_DIRS += i2o
   endif
 endif 
+
+# If CONFIG_IDE is set, the core of ATA support will be added to the kernel,
+# but some of the low-level things may also be modules.
+ifeq ($(CONFIG_IDE),y)
+SUB_DIRS += ide
+MOD_SUB_DIRS += ide
+else
+  ifeq ($(CONFIG_IDE),m)
+  MOD_SUB_DIRS += ide
+  endif
+endif
 
 # If CONFIG_SCSI is set, the core of SCSI support will be added to the kernel,
 # but some of the low-level things may also be modules.
diff -u --recursive --new-file v2.3.51/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.3.51/linux/drivers/block/Config.in	Tue Mar  7 14:32:25 2000
+++ linux/drivers/block/Config.in	Sun Mar 12 19:52:02 2000
@@ -16,172 +16,6 @@
       bool 'Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)' CONFIG_BLK_DEV_SWIM_IOP
    fi
 fi
-
-tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
-comment 'Please see Documentation/ide.txt for help/info on IDE drives'
-if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
-   bool 'Old hard disk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
-else
-   bool '  Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
-   dep_tristate '  Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
-   if [ "$CONFIG_BLK_DEV_IDEDISK" != "n" ]; then
-      bool '    Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE
-   fi
-   dep_tristate '  PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA
-   dep_tristate '  Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
-   dep_tristate '  Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
-   dep_tristate '  Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
-   dep_tristate '  SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
-   comment 'IDE chipset support/bugfixes'
-   if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
-      bool '  CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
-      if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
-	 bool '    CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED
-      fi
-      if [ "$CONFIG_ISAPNP" = "y" ]; then
-	 bool '  ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP
-      fi
-      if [ "$CONFIG_PCI" = "y" ]; then
-	 bool '  RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
-	 bool '  Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
-	 if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
-	    bool '    Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
-	    bool '    Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
-	    if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
-	       bool '      Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO
-	       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-		  bool '      Good-Bad DMA Model-Firmware (EXPERIMENTAL)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
-		  define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL y
-	       else
-		  define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL n
-	       fi
-	    fi
-	    if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
-	       bool '    ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP
-	    fi
-	    bool '    Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
-	    bool '    AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210
-	    if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_AEC6210" = "y" ]; then
-	       bool '      AEC6210 Tuning support (WIP)' CONFIG_AEC6210_TUNING
-	    fi
-	    if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
-	       bool '    ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3
-	       bool '    AMD Viper support' CONFIG_BLK_DEV_AMD7409
-	       if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_AMD7409" = "y" ]; then
-		  bool '      AMD Viper ATA-66 Override (WIP)' CONFIG_AMD7409_OVERRIDE
-	       fi
-	    fi
-	    bool '    CMD64X chipset support' CONFIG_BLK_DEV_CMD64X
-	    if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_CMD64X" = "y" ]; then
-	       bool '      CMD64X chipset RAID (WIP)' CONFIG_CMD64X_RAID
-	    fi
-	    if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
-	       bool '    CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693
-	    fi
-	    bool '    Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530
-	    if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
-  	       bool '    HPT34X chipset support' CONFIG_BLK_DEV_HPT34X
-	       if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_HPT34X" = "y" ]; then
-		  bool '      HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA
-	       fi
-	       bool '    HPT366 chipset support' CONFIG_BLK_DEV_HPT366
-	       if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_HPT366" = "y" ]; then
-		  bool '      HPT366 Fast Interrupts (WIP)' CONFIG_HPT366_FIP
-		  bool '      HPT366 mode Three (WIP)' CONFIG_HPT366_MODE3
-	       fi
-	       if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then
-		  bool '    Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX
-		  if [ "$CONFIG_BLK_DEV_PIIX" = "y" -a "$CONFIG_IDEDMA_PCI_AUTO" = "y" ]; then
-		     bool '      PIIXn Tuning support' CONFIG_PIIX_TUNING
-		  fi
-	       fi
-	    fi
-	    if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
-	       bool '    NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
-	    fi
-	    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-	       bool '    OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
-	    fi
-	    if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
-	       bool '    PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX
-	       if [ "$CONFIG_BLK_DEV_PDC202XX" = "y" ]; then
-		  bool '      Special UDMA Feature' CONFIG_PDC202XX_BURST
-		  if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" ]; then
-		     bool '      Special Mode Feature (WIP)' CONFIG_PDC202XX_MASTER
-		  fi
-	       fi
-  	       if [ "$CONFIG_X86" = "y" ]; then
-	          bool '    SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513
-	       fi
-	    fi
-	    if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
-	       bool '    Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
-	       bool '    VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX
-	    fi
-         fi
-	 if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
-	    bool '    Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
-	 fi
-      fi
-      if [ "$CONFIG_PMAC" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
-	 bool '    Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
-	 if [ "$CONFIG_BLK_DEV_IDE_PMAC" != "n" ]; then
-	    bool '      PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC
-	    if [ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" ]; then
-	       bool '        Use DMA by default' CONFIG_IDEDMA_PMAC_AUTO
-	    fi
-	 fi
-      fi
-      if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
-	 bool '    ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE
-	 if [ "$CONFIG_BLK_DEV_IDE_ICSIDE" = "y" ]; then
-	    bool '      ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS
-	    if [ "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
-	       bool '        Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO
-	    fi
-	 fi
-	 bool '    RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE
-      fi
-      if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
-	   "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
-	   "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
-	 define_bool CONFIG_BLK_DEV_IDEDMA y
-	 if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
-	      "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
-	      "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
-	    define_bool CONFIG_IDEDMA_AUTO y
-	 fi
-      fi
-      bool '  Other IDE chipset support' CONFIG_IDE_CHIPSETS
-      if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
-	 comment 'Note: most of these also require special kernel boot parameters'
-	 bool '    Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
-	 bool '    ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
-	 bool '    DTC-2278 support' CONFIG_BLK_DEV_DTC2278
-	 bool '    Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
-	 if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
-	    bool '    PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
-	 fi
-	 bool '    QDI QD6580 support' CONFIG_BLK_DEV_QD6580
-	 bool '    UMC-8672 support' CONFIG_BLK_DEV_UMC8672
-      fi
-      if [ "$CONFIG_AMIGA" = "y" ]; then
-	 bool '    Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE
-	 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-	    dep_tristate '      Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE
-	 fi
-      fi
-      if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
-	 bool '    Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA
-      fi
-      if [ "$CONFIG_ATARI" = "y" ]; then
-	 bool '    Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE
-      fi
-      if [ "$CONFIG_MAC" = "y" ]; then
-	 bool '    Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE
-      fi
-   fi
-fi
 if [ "$CONFIG_MCA" = "y" ]; then
    tristate 'PS/2 ESDI hard disk support' CONFIG_BLK_DEV_PS2
 fi
@@ -196,8 +30,15 @@
       tristate '  Atari SLM laser printer support' CONFIG_ATARI_SLM
    fi
 fi
+tristate 'XT hard disk support' CONFIG_BLK_DEV_XD
+dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
+if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
+   source drivers/block/paride/Config.in
+fi
+
 if [ "$CONFIG_PCI" = "y" ]; then
    tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA
+   tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960
 fi
 
 comment 'Additional Block Devices'
@@ -219,42 +60,6 @@
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
 if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
    bool '  Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
-fi
-tristate 'XT hard disk support' CONFIG_BLK_DEV_XD
-if [ "$CONFIG_PCI" = "y" ]; then
-  tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960
-fi
-
-dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
-if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
-   source drivers/block/paride/Config.in
-fi
-
-if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
-     "$CONFIG_BLK_DEV_AEC6210" = "y" -o \
-     "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \
-     "$CONFIG_BLK_DEV_AMD7409" = "y" -o \
-     "$CONFIG_BLK_DEV_CMD640" = "y" -o \
-     "$CONFIG_BLK_DEV_CMD64X" = "y" -o \
-     "$CONFIG_BLK_DEV_CS5530" = "y" -o \
-     "$CONFIG_BLK_DEV_CY82C693" = "y" -o \
-     "$CONFIG_BLK_DEV_HPT34X" = "y" -o \
-     "$CONFIG_BLK_DEV_HPT366" = "y" -o \
-     "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
-     "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
-     "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \
-     "$CONFIG_BLK_DEV_PIIX" = "y" -o \
-     "$CONFIG_BLK_DEV_SIS5513" = "y" -o \
-     "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
-   define_bool CONFIG_BLK_DEV_IDE_MODES y
-else
-   define_bool CONFIG_BLK_DEV_IDE_MODES n
-fi
-
-if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
-   define_bool CONFIG_BLK_DEV_HD y
-else
-   define_bool CONFIG_BLK_DEV_HD n
 fi
 
 endmenu
diff -u --recursive --new-file v2.3.51/linux/drivers/block/DAC960.c linux/drivers/block/DAC960.c
--- v2.3.51/linux/drivers/block/DAC960.c	Fri Mar 10 16:40:41 2000
+++ linux/drivers/block/DAC960.c	Sun Mar 12 19:32:57 2000
@@ -1021,7 +1021,7 @@
 
 	if (req->nr_segments < max_segments) {
 		req->nr_segments++;
-		q->nr_segments++;
+		q->elevator.nr_segments++;
 		return 1;
 	}
 	return 0;
@@ -1051,23 +1051,23 @@
 	int max_segments;
 	DAC960_Controller_T * Controller = q->queuedata;
 	int total_segments = req->nr_segments + next->nr_segments;
-       int same_segment;
+	int same_segment;
 
 	max_segments = Controller->MaxSegmentsPerRequest[MINOR(req->rq_dev)];
 	if (__max_segments < max_segments)
 		max_segments = __max_segments;
 
-       same_segment = 0;
+	same_segment = 0;
 	if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data)
 	{
 		total_segments--;
-               same_segment = 1;
+		same_segment = 1;
 	}
     
 	if (total_segments > max_segments)
 		return 0;
 
-       q->nr_segments -= same_segment;
+	q->elevator.nr_segments -= same_segment;
 	req->nr_segments = total_segments;
 	return 1;
 }
diff -u --recursive --new-file v2.3.51/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.3.51/linux/drivers/block/Makefile	Fri Mar 10 16:40:41 2000
+++ linux/drivers/block/Makefile	Sun Mar 12 19:52:27 2000
@@ -20,7 +20,7 @@
 
 
 L_TARGET := block.a
-L_OBJS   := genhd.o ide-geometry.o
+L_OBJS   := genhd.o elevator.o
 M_OBJS   :=
 MOD_LIST_NAME := BLOCK_MODULES
 LX_OBJS := ll_rw_blk.o blkpg.o
@@ -98,214 +98,6 @@
   endif
 endif
 
-#
-# IDE-STUFF
-#
-
-ifeq ($(CONFIG_BLK_DEV_AEC6210),y)
-IDE_OBJS += aec6210.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
-IDE_OBJS += ali14xx.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ALI15X3),y)
-IDE_OBJS += alim15x3.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_AMD7409),y)
-IDE_OBJS += amd7409.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_BUDDHA),y)
-IDE_OBJS += buddha.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CMD640),y)
-IDE_OBJS += cmd640.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CMD64X),y)
-IDE_OBJS += cmd64x.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CS5530),y)
-IDE_OBJS += cs5530.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CY82C693),y)
-IDE_OBJS += cy82c693.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
-IDE_OBJS += dtc2278.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_FALCON_IDE),y)
-IDE_OBJS += falconide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_GAYLE),y)
-IDE_OBJS += gayle.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_Q40IDE),y)
-IDE_OBJS += q40ide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HD),y)
-L_OBJS += hd.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HPT34X),y)
-IDE_OBJS += hpt34x.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HPT366),y)
-IDE_OBJS += hpt366.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
-IDE_OBJS += ht6560b.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
-IDE_OBJS += icside.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEDMA),y)
-IDE_OBJS += ide-dma.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEPCI),y)
-IDE_OBJS += ide-pci.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ISAPNP),y)
-IDE_OBJS += ide-pnp.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_PMAC),y)
-IDE_OBJS += ide-pmac.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_MAC_IDE),y)
-IDE_OBJS += macide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_NS87415),y)
-IDE_OBJS += ns87415.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
-IDE_OBJS += opti621.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PDC202XX),y)
-IDE_OBJS += pdc202xx.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PDC4030),y)
-IDE_OBJS += pdc4030.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PIIX),y)
-IDE_OBJS += piix.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_QD6580),y)
-IDE_OBJS += qd6580.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
-IDE_OBJS += rapide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
-IDE_OBJS += rz1000.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_SIS5513),y)
-IDE_OBJS += sis5513.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_SL82C105),y)
-IDE_OBJS += sl82c105.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_TRM290),y)
-IDE_OBJS += trm290.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
-IDE_OBJS += umc8672.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_VIA82CXXX),y)
-IDE_OBJS += via82cxxx.o
-endif
-
-### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored
-
-ifeq ($(CONFIG_PROC_FS),y)
-IDE_OBJS += ide-proc.o
-endif
-  
-###Collect
-
-ifeq ($(CONFIG_BLK_DEV_IDE),y)
-  LX_OBJS += ide.o ide-features.o
-  L_OBJS += ide-probe.o $(IDE_OBJS)
-else
-  ifeq ($(CONFIG_BLK_DEV_IDE),m)
-  MIX_OBJS += ide.o ide-features.o $(IDE_OBJS)
-  M_OBJS += ide-mod.o ide-probe-mod.o
-  endif
-endif
-
-############
-
-ifeq ($(CONFIG_BLK_DEV_IDECS),y)
-L_OBJS += ide-cs.o
-else
-  ifeq ($(CONFIG_BLK_DEV_IDECS),m)
-  M_OBJS += ide-cs.o
-  endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
-L_OBJS += ide-disk.o
-else
-  ifeq ($(CONFIG_BLK_DEV_IDEDISK),m)
-  M_OBJS += ide-disk.o
-  endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDECD),y)
-L_OBJS += ide-cd.o
-else
-  ifeq ($(CONFIG_BLK_DEV_IDECD),m)
-  M_OBJS += ide-cd.o
-  endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
-L_OBJS += ide-tape.o
-else
-  ifeq ($(CONFIG_BLK_DEV_IDETAPE),m)
-  M_OBJS += ide-tape.o
-  endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
-L_OBJS += ide-floppy.o
-else
-  ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),m)
-  M_OBJS += ide-floppy.o
-  endif
-endif
-
 ifeq ($(CONFIG_BLK_DEV_PS2),y)
 L_OBJS += ps2esdi.o
 else
@@ -417,12 +209,6 @@
 endif
 
 include $(TOPDIR)/Rules.make
-
-ide-mod.o: ide.o ide-features.o $(IDE_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@ ide.o ide-features.o $(IDE_OBJS)
-
-ide-probe-mod.o: ide-probe.o ide-geometry.o
-	$(LD) $(LD_RFLAG) -r -o $@ ide-probe.o ide-geometry.o
 
 lvm-mod.o:	lvm.o lvm-snap.o
 	$(LD) -r -o $@ lvm.o lvm-snap.o
diff -u --recursive --new-file v2.3.51/linux/drivers/block/aec6210.c linux/drivers/block/aec6210.c
--- v2.3.51/linux/drivers/block/aec6210.c	Fri Mar 10 16:40:41 2000
+++ linux/drivers/block/aec6210.c	Wed Dec 31 16:00:00 1969
@@ -1,372 +0,0 @@
-/*
- * linux/drivers/block/aec6210.c		Version 0.05	Feb. 10, 2000
- *
- * Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
- * May be copied or modified under the terms of the GNU General Public License
- *
- *  pio 0 ::       40: 00 07 00 00 00 00 00 00 02 07 a6 04 00 02 00 02
- *  pio 1 ::       40: 0a 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02
- *  pio 2 ::       40: 08 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02
- *  pio 3 ::       40: 03 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *  pio 4 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *  dma 0 ::       40: 0a 07 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *  dma 1 ::       40: 02 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *  dma 2 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *                 50: ff ff ff ff 00 06 04 00 00 00 00 00 00 00 00 00
- *
- * udma 0 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *                 50: ff ff ff ff 01 06 04 00 00 00 00 00 00 00 00 00
- *
- * udma 1 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *                 50: ff ff ff ff 01 06 04 00 00 00 00 00 00 00 00 00
- *
- * udma 2 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *                 50: ff ff ff ff 02 06 04 00 00 00 00 00 00 00 00 00
- *
- * auto   ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
- *                 50: ff ff ff ff 02 06 04 00 00 00 00 00 00 00 00 00
- *
- * auto   ::       40: 01 04 01 04 01 04 01 04 02 05 a6 cf 00 02 00 02
- *                 50: ff ff ff ff aa 06 04 00 00 00 00 00 00 00 00 00
- *
- *                 NO-Devices
- *                 40: 00 00 00 00 00 00 00 00 02 05 a6 00 00 02 00 02
- *                 50: ff ff ff ff 00 06 00 00 00 00 00 00 00 00 00 00
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "ide_modes.h"
-
-#define ACARD_DEBUG_DRIVE_INFO		0
-
-#undef DISPLAY_AEC6210_TIMINGS
-
-#if defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int aec6210_get_info(char *, char **, off_t, int);
-extern int (*aec6210_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-extern char *ide_media_verbose(ide_drive_t *);
-static struct pci_dev *bmide_dev;
-
-static int aec6210_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-
-	u32 bibma = bmide_dev->resource[4].start;
-	u8 c0 = 0, c1 = 0;
-	
-	p += sprintf(p, "\n                                AEC6210 Chipset.\n");
-
-        /*
-         * at that point bibma+0x2 et bibma+0xa are byte registers
-         * to investigate:
-         */
-	c0 = inb_p((unsigned short)bibma + 0x02);
-	c1 = inb_p((unsigned short)bibma + 0x0a);
-
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %sabled                         %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-	return p-buffer;/* => must be less than 4k! */
-}
-#endif	/* defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-byte aec6210_proc = 0;
-
-#ifdef CONFIG_AEC6210_TUNING
-
-struct chipset_bus_clock_list_entry {
-	byte		xfer_speed;
-	unsigned short	chipset_settings;
-	byte		ultra_settings;
-};
-
-struct chipset_bus_clock_list_entry aec6210_base [] = {
-	{	XFER_UDMA_2,	0x0401,	0x02	},
-	{	XFER_UDMA_1,	0x0401,	0x01	},
-	{	XFER_UDMA_0,	0x0401,	0x01	},
-
-	{	XFER_MW_DMA_2,	0x0401,	0x00	},
-	{	XFER_MW_DMA_1,	0x0402,	0x00	},
-	{	XFER_MW_DMA_0,	0x070a,	0x00	},
-
-	{	XFER_PIO_4,	0x0401,	0x00	},
-	{	XFER_PIO_3,	0x0403,	0x00	},
-	{	XFER_PIO_2,	0x0708,	0x00	},
-	{	XFER_PIO_1,	0x070a,	0x00	},
-	{	XFER_PIO_0,	0x0700,	0x00	},
-	{	0,		0x0000,	0x00	}
-};
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-/*
- * TO DO: active tuning and correction of cards without a bios.
- */
-
-static unsigned short pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
-{
-	for ( ; chipset_table->xfer_speed ; chipset_table++)
-		if (chipset_table->xfer_speed == speed) {
-			return chipset_table->chipset_settings;
-		}
-	return 0x0000;
-}
-
-static byte pci_bus_clock_list_ultra (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
-{
-	for ( ; chipset_table->xfer_speed ; chipset_table++)
-		if (chipset_table->xfer_speed == speed) {
-			return chipset_table->ultra_settings;
-		}
-	return 0x00;
-}
-
-static int aec6210_tune_chipset (ide_drive_t *drive, byte speed)
-{
-	ide_hwif_t *hwif	= HWIF(drive);
-
-	int			err;
-	byte			drive_pci;
-	unsigned short		drive_conf = 0x0000;
-	byte			ultra = 0x00, ultra_conf = 0x00;
-	byte			tmp1 = 0x00, tmp2 = 0x00;
-
-	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-
-	switch(drive_number) {
-		case 0:		drive_pci = 0x40; break;
-		case 1:		drive_pci = 0x42; break;
-		case 2:		drive_pci = 0x44; break;
-		case 3:		drive_pci = 0x46; break;
-		default:	return -1;
-	}
-
-	pci_read_config_word(HWIF(drive)->pci_dev, drive_pci, &drive_conf);
-	drive_conf = pci_bus_clock_list(speed, aec6210_base);
-	pci_write_config_word(HWIF(drive)->pci_dev, drive_pci, drive_conf);
-
-	pci_read_config_byte(HWIF(drive)->pci_dev, 0x54, &ultra);
-	tmp1 = ((0x00 << (2*drive_number)) | (ultra & ~(3 << (2*drive_number))));
-	ultra_conf = pci_bus_clock_list_ultra(speed, aec6210_base);
-	tmp2 = ((ultra_conf << (2*drive_number)) | (tmp1 & ~(3 << (2*drive_number))));
-	pci_write_config_byte(HWIF(drive)->pci_dev, 0x54, tmp2);
-
-	err = ide_config_drive_speed(drive, speed);
-
-#if ACARD_DEBUG_DRIVE_INFO
-	printk("%s: %s drive%d 0x04%x 0x02%x 0x02%x 0x02%x 0x02%x\n",
-		drive->name, ide_xfer_verbose(speed), drive_number,
-		drive_conf, ultra, tmp1, ultra_conf, tmp2);
-#endif /* ACARD_DEBUG_DRIVE_INFO */
-
-	return(err);
-}
-
-static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
-{
-	struct hd_driveid *id = drive->id;
-	byte speed = -1;
-
-	if (drive->media != ide_disk)
-		return ((int) ide_dma_off_quietly);
-
-	if (((id->dma_ultra & 0x0010) ||
-	     (id->dma_ultra & 0x0008) ||
-	     (id->dma_ultra & 0x0004)) && (ultra)) {
-		speed = XFER_UDMA_2;
-	} else if ((id->dma_ultra & 0x0002) && (ultra)) {
-		speed = XFER_UDMA_1;
-	} else if ((id->dma_ultra & 0x0001) && (ultra)) {
-		speed = XFER_UDMA_0;
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-	} else if (id->dma_1word & 0x0004) {
-		speed = XFER_SW_DMA_2;
-	} else if (id->dma_1word & 0x0002) {
-		speed = XFER_SW_DMA_1;
-	} else if (id->dma_1word & 0x0001) {
-		speed = XFER_SW_DMA_0;
-	} else {
-		return ((int) ide_dma_off_quietly);
-	}
-	(void) aec6210_tune_chipset(drive, speed);
-
-	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_off :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-}
-
-static void aec6210_tune_drive (ide_drive_t *drive, byte pio)
-{
-	byte speed;
-
-	switch(pio) {
-		case 5:
-			speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
-		case 4:
-			speed = XFER_PIO_4; break;
-		case 3:
-			speed = XFER_PIO_3; break;
-		case 2:
-			speed = XFER_PIO_2; break;
-		case 1:
-			speed = XFER_PIO_1; break;
-		default:
-			speed = XFER_PIO_0; break;
-	}
-	(void) aec6210_tune_chipset(drive, speed);
-}
-
-static int config_drive_xfer_rate (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
-
-	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
-			goto fast_ata_pio;
-		}
-		dma_func = ide_dma_off_quietly;
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & 0x001F) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, 1);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & 0x0007) ||
-			    (id->dma_1word & 0x0007)) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, 0);
-				if (dma_func != ide_dma_on)
-					goto no_dma_set;
-			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			if (id->eide_dma_time > 150) {
-				goto no_dma_set;
-			}
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, 0);
-			if (dma_func != ide_dma_on)
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
-no_dma_set:
-		aec6210_tune_drive(drive, 5);
-	}
-	return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-/*
- * aec6210_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- */
-int aec6210_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-}
-#endif /* CONFIG_AEC6210_TUNING */
-
-unsigned int __init pci_init_aec6210 (struct pci_dev *dev, const char *name)
-{
-	if (dev->resource[PCI_ROM_RESOURCE].start) {
-		pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
-		printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
-	}
-
-#if defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS)
-	aec6210_proc = 1;
-	bmide_dev = dev;
-	aec6210_display_info = &aec6210_get_info;
-#endif /* DISPLAY_AEC6210_TIMINGS && CONFIG_PROC_FS */
-
-	return dev->irq;
-}
-
-void __init ide_init_aec6210 (ide_hwif_t *hwif)
-{
-#ifdef CONFIG_AEC6210_TUNING
-	hwif->tuneproc = &aec6210_tune_drive;
-
-	if (hwif->dma_base) {
-		hwif->dmaproc = &aec6210_dmaproc;
-	} else {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-#endif /* CONFIG_AEC6210_TUNING */
-}
-
-void __init ide_dmacapable_aec6210 (ide_hwif_t *hwif, unsigned long dmabase)
-{
-	byte dma_new	= 0;
-	byte dma_old	= inb(dmabase+2);
-	byte reg54h	= 0;
-	byte masterdma	= hwif->channel ? 0x30 : 0x03;
-	byte slavedma	= hwif->channel ? 0xc0 : 0x0c;
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-
-	dma_new = dma_old;
-
-	pci_read_config_byte(hwif->pci_dev, 0x54, &reg54h);
-
-	if (reg54h & masterdma)	dma_new |= 0x20;
-	if (reg54h & slavedma)	dma_new |= 0x40;
-	if (dma_new != dma_old)	outb(dma_new, dmabase+2);
-
-	__restore_flags(flags);	/* local CPU only */
-
-	ide_setup_dma(hwif, dmabase, 8);
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ali14xx.c linux/drivers/block/ali14xx.c
--- v2.3.51/linux/drivers/block/ali14xx.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/block/ali14xx.c	Wed Dec 31 16:00:00 1969
@@ -1,225 +0,0 @@
-/*
- *  linux/drivers/block/ali14xx.c		Version 0.03	Feb 09, 1996
- *
- *  Copyright (C) 1996  Linus Torvalds & author (see below)
- */
-
-/*
- * ALI M14xx chipset EIDE controller
- *
- * Works for ALI M1439/1443/1445/1487/1489 chipsets.
- *
- * Adapted from code developed by derekn@vw.ece.cmu.edu.  -ml
- * Derek's notes follow:
- *
- * I think the code should be pretty understandable,
- * but I'll be happy to (try to) answer questions.
- *
- * The critical part is in the setupDrive function.  The initRegisters
- * function doesn't seem to be necessary, but the DOS driver does it, so
- * I threw it in.
- *
- * I've only tested this on my system, which only has one disk.  I posted
- * it to comp.sys.linux.hardware, so maybe some other people will try it
- * out.
- *
- * Derek Noonburg  (derekn@ece.cmu.edu)
- * 95-sep-26
- *
- * Update 96-jul-13:
- *
- * I've since upgraded to two disks and a CD-ROM, with no trouble, and
- * I've also heard from several others who have used it successfully.
- * This driver appears to work with both the 1443/1445 and the 1487/1489
- * chipsets.  I've added support for PIO mode 4 for the 1487.  This
- * seems to work just fine on the 1443 also, although I'm not sure it's
- * advertised as supporting mode 4.  (I've been running a WDC AC21200 in
- * mode 4 for a while now with no trouble.)  -Derek
- */
-
-#undef REALLY_SLOW_IO           /* most systems can safely undef this */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-/* port addresses for auto-detection */
-#define ALI_NUM_PORTS 4
-static int ports[ALI_NUM_PORTS] __initdata = {0x074, 0x0f4, 0x034, 0x0e4};
-
-/* register initialization data */
-typedef struct { byte reg, data; } RegInitializer;
-
-static RegInitializer initData[] __initdata = {
-	{0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00},
-	{0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f},
-	{0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
-	{0x29, 0x00}, {0x2a, 0x00}, {0x2f, 0x00}, {0x2b, 0x00},
-	{0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x30, 0x00},
-	{0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0xff},
-	{0x35, 0x03}, {0x00, 0x00}
-};
-
-#define ALI_MAX_PIO 4
-
-/* timing parameter registers for each drive */
-static struct { byte reg1, reg2, reg3, reg4; } regTab[4] = {
-	{0x03, 0x26, 0x04, 0x27},     /* drive 0 */
-	{0x05, 0x28, 0x06, 0x29},     /* drive 1 */
-	{0x2b, 0x30, 0x2c, 0x31},     /* drive 2 */
-	{0x2d, 0x32, 0x2e, 0x33},     /* drive 3 */
-};
-
-static int basePort = 0;	/* base port address */
-static int regPort = 0;		/* port for register number */
-static int dataPort = 0;	/* port for register data */
-static byte regOn;	/* output to base port to access registers */
-static byte regOff;	/* output to base port to close registers */
-
-/*------------------------------------------------------------------------*/
-
-/*
- * Read a controller register.
- */
-static inline byte inReg (byte reg)
-{
-	outb_p(reg, regPort);
-	return inb(dataPort);
-}
-
-/*
- * Write a controller register.
- */
-static void outReg (byte data, byte reg)
-{
-	outb_p(reg, regPort);
-	outb_p(data, dataPort);
-}
-
-/*
- * Set PIO mode for the specified drive.
- * This function computes timing parameters
- * and sets controller registers accordingly.
- */
-static void ali14xx_tune_drive (ide_drive_t *drive, byte pio)
-{
-	int driveNum;
-	int time1, time2;
-	byte param1, param2, param3, param4;
-	unsigned long flags;
-	ide_pio_data_t d;
-	int bus_speed = ide_system_bus_speed();
-
-	pio = ide_get_best_pio_mode(drive, pio, ALI_MAX_PIO, &d);
-
-	/* calculate timing, according to PIO mode */
-	time1 = d.cycle_time;
-	time2 = ide_pio_timings[pio].active_time;
-	param3 = param1 = (time2 * bus_speed + 999) / 1000;
-	param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1;
-	if (pio < 3) {
-		param3 += 8;
-		param4 += 8;
-	}
-	printk("%s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d\n",
-		drive->name, pio, time1, time2, param1, param2, param3, param4);
-
-	/* stuff timing parameters into controller registers */
-	driveNum = (HWIF(drive)->index << 1) + drive->select.b.unit;
-	save_flags(flags);	/* all CPUs */
-	cli();			/* all CPUs */
-	outb_p(regOn, basePort);
-	outReg(param1, regTab[driveNum].reg1);
-	outReg(param2, regTab[driveNum].reg2);
-	outReg(param3, regTab[driveNum].reg3);
-	outReg(param4, regTab[driveNum].reg4);
-	outb_p(regOff, basePort);
-	restore_flags(flags);	/* all CPUs */
-}
-
-/*
- * Auto-detect the IDE controller port.
- */
-static int __init findPort (void)
-{
-	int i;
-	byte t;
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	for (i = 0; i < ALI_NUM_PORTS; ++i) {
-		basePort = ports[i];
-		regOff = inb(basePort);
-		for (regOn = 0x30; regOn <= 0x33; ++regOn) {
-			outb_p(regOn, basePort);
-			if (inb(basePort) == regOn) {
-				regPort = basePort + 4;
-				dataPort = basePort + 8;
-				t = inReg(0) & 0xf0;
-				outb_p(regOff, basePort);
-				__restore_flags(flags);	/* local CPU only */
-				if (t != 0x50)
-					return 0;
-				return 1;  /* success */
-			}
-		}
-		outb_p(regOff, basePort);
-	}
-	__restore_flags(flags);	/* local CPU only */
-	return 0;
-}
-
-/*
- * Initialize controller registers with default values.
- */
-static int __init initRegisters (void) {
-	RegInitializer *p;
-	byte t;
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	outb_p(regOn, basePort);
-	for (p = initData; p->reg != 0; ++p)
-		outReg(p->data, p->reg);
-	outb_p(0x01, regPort);
-	t = inb(regPort) & 0x01;
-	outb_p(regOff, basePort);
-	__restore_flags(flags);	/* local CPU only */
-	return t;
-}
-
-void __init init_ali14xx (void)
-{
-	/* auto-detect IDE controller port */
-	if (!findPort()) {
-		printk("\nali14xx: not found");
-		return;
-	}
-
-	printk("\nali14xx: base= 0x%03x, regOn = 0x%02x", basePort, regOn);
-	ide_hwifs[0].chipset = ide_ali14xx;
-	ide_hwifs[1].chipset = ide_ali14xx;
-	ide_hwifs[0].tuneproc = &ali14xx_tune_drive;
-	ide_hwifs[1].tuneproc = &ali14xx_tune_drive;
-	ide_hwifs[0].mate = &ide_hwifs[1];
-	ide_hwifs[1].mate = &ide_hwifs[0];
-	ide_hwifs[1].channel = 1;
-
-	/* initialize controller registers */
-	if (!initRegisters()) {
-		printk("\nali14xx: Chip initialization failed");
-		return;
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/alim15x3.c linux/drivers/block/alim15x3.c
--- v2.3.51/linux/drivers/block/alim15x3.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/alim15x3.c	Wed Dec 31 16:00:00 1969
@@ -1,689 +0,0 @@
-/*
- * linux/drivers/block/alim15x3.c		Version 0.08	Jan. 14, 2000
- *
- *  Copyright (C) 1998-2000 Michel Aubry, Maintainer
- *  Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer
- *
- *  Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
- *  May be copied or modified under the terms of the GNU General Public License
- *
- *  (U)DMA capable version of ali 1533/1543(C), 1535(D)
- *
- *  version: 1.0 beta2 (Sep. 2, 1999)
- *	e-mail your problems to cjtsai@ali.com.tw
- *
- **********************************************************************
- *  9/7/99 --Parts from the above author are included and need to be
- *  converted into standard interface, once I finish the thought.
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-#define DISPLAY_ALI_TIMINGS
-
-#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int ali_get_info(char *buffer, char **addr, off_t offset, int count);
-extern int (*ali_display_info)(char *, char **, off_t, int);  /* ide-proc.c */
-struct pci_dev *bmide_dev;
-
-char *fifo[4] = {
-	"FIFO Off",
-	"FIFO On ",
-	"DMA mode",
-	"PIO mode" };
-
-char *udmaT[8] = {
-	"1.5T",
-	"  2T",
-	"2.5T",
-	"  3T",
-	"3.5T",
-	"  4T",
-	"  6T",
-	"  8T"
-};
-
-char *channel_status[8] = {
-	"OK            ",
-	"busy          ",
-	"DRQ           ",
-	"DRQ busy      ",
-	"error         ",
-	"error busy    ",
-	"error DRQ     ",
-	"error DRQ busy"
-};
-
-static int ali_get_info(char *buffer, char **addr, off_t offset, int count)
-{
-	byte reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1;
-	unsigned int bibma;
-	byte c0, c1;
-	byte rev, tmp;
-	char *p = buffer;
-	char *q;
-
-	/* fetch rev. */
-	pci_read_config_byte(bmide_dev, 0x08, &rev);
-	if (rev >= 0xc1)	/* M1543C or newer */
-		udmaT[7] = " ???";
-	else
-		fifo[3]  = "   ???  ";
-
-	/* first fetch bibma: */
-	pci_read_config_dword(bmide_dev, 0x20, &bibma);
-	bibma = (bibma & 0xfff0) ;
-	/*
-	 * at that point bibma+0x2 et bibma+0xa are byte
-	 * registers to investigate:
-	 */
-	c0 = inb((unsigned short)bibma + 0x02);
-	c1 = inb((unsigned short)bibma + 0x0a);
-
-	p += sprintf(p,
-		"\n                                Ali M15x3 Chipset.\n");
-	p += sprintf(p,
-		"                                ------------------\n");
-	pci_read_config_byte(bmide_dev, 0x78, &reg53h);
-	p += sprintf(p, "PCI Clock: %d.\n", reg53h);
-
-	pci_read_config_byte(bmide_dev, 0x53, &reg53h);
-	p += sprintf(p,
-		"CD_ROM FIFO:%s, CD_ROM DMA:%s\n",
-		(reg53h & 0x02) ? "Yes" : "No ",
-		(reg53h & 0x01) ? "Yes" : "No " );
-	pci_read_config_byte(bmide_dev, 0x74, &reg53h);
-	p += sprintf(p,
-		"FIFO Status: contains %d Words, runs%s%s\n\n",
-		(reg53h & 0x3f),
-		(reg53h & 0x40) ? " OVERWR" : "",
-		(reg53h & 0x80) ? " OVERRD." : "." );
-
-	p += sprintf(p,
-		"-------------------primary channel-------------------secondary channel---------\n\n");
-
-	pci_read_config_byte(bmide_dev, 0x09, &reg53h);
-	p += sprintf(p,
-		"channel status:       %s                               %s\n",
-		(reg53h & 0x20) ? "On " : "Off",
-		(reg53h & 0x10) ? "On " : "Off" );
-
-	p += sprintf(p,
-		"both channels togth:  %s                               %s\n",
-		(c0&0x80) ? "No " : "Yes",
-		(c1&0x80) ? "No " : "Yes" );
-
-	pci_read_config_byte(bmide_dev, 0x76, &reg53h);
-	p += sprintf(p,
-		"Channel state:        %s                    %s\n",
-		channel_status[reg53h & 0x07],
-		channel_status[(reg53h & 0x70) >> 4] );
-
-	pci_read_config_byte(bmide_dev, 0x58, &reg5xh);
-	pci_read_config_byte(bmide_dev, 0x5c, &reg5yh);
-	p += sprintf(p,
-		"Add. Setup Timing:    %dT                                %dT\n",
-		(reg5xh & 0x07) ? (reg5xh & 0x07) : 8,
-		(reg5yh & 0x07) ? (reg5yh & 0x07) : 8 );
-
-	pci_read_config_byte(bmide_dev, 0x59, &reg5xh);
-	pci_read_config_byte(bmide_dev, 0x5d, &reg5yh);
-	p += sprintf(p,
-		"Command Act. Count:   %dT                                %dT\n"
-		"Command Rec. Count:   %dT                               %dT\n\n",
-		(reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
-		(reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8, 
-		(reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
-		(reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16 );
-
-	p += sprintf(p,
-		"----------------drive0-----------drive1------------drive0-----------drive1------\n\n");
-	p += sprintf(p,
-		"DMA enabled:      %s              %s               %s              %s\n",
-		(c0&0x20) ? "Yes" : "No ",
-		(c0&0x40) ? "Yes" : "No ",
-		(c1&0x20) ? "Yes" : "No ",
-		(c1&0x40) ? "Yes" : "No " );
-
-	pci_read_config_byte(bmide_dev, 0x54, &reg5xh);
-	pci_read_config_byte(bmide_dev, 0x55, &reg5yh);
-	q = "FIFO threshold:   %2d Words         %2d Words          %2d Words         %2d Words\n";
-	if (rev < 0xc1) {
-		if ((rev == 0x20) && (pci_read_config_byte(bmide_dev, 0x4f, &tmp), (tmp &= 0x20))) {
-			p += sprintf(p, q, 8, 8, 8, 8);
-		} else {
-			p += sprintf(p, q,
-				(reg5xh & 0x03) + 12,
-				((reg5xh & 0x30)>>4) + 12,
-				(reg5yh & 0x03) + 12,
-				((reg5yh & 0x30)>>4) + 12 );
-		}
-	} else {
-		p += sprintf(p, q,
-			(tmp = (reg5xh & 0x03)) ? (tmp << 3) : 4,
-			(tmp = ((reg5xh & 0x30)>>4)) ? (tmp << 3) : 4,
-			(tmp = (reg5yh & 0x03)) ? (tmp << 3) : 4,
-			(tmp = ((reg5yh & 0x30)>>4)) ? (tmp << 3) : 4 );
-	}
-
-#if 0
-	p += sprintf(p, 
-		"FIFO threshold:   %2d Words         %2d Words          %2d Words         %2d Words\n",
-		(reg5xh & 0x03) + 12,
-		((reg5xh & 0x30)>>4) + 12,
-		(reg5yh & 0x03) + 12,
-		((reg5yh & 0x30)>>4) + 12 );
-#endif
-
-	p += sprintf(p,
-		"FIFO mode:        %s         %s          %s         %s\n",
-		fifo[((reg5xh & 0x0c) >> 2)],
-		fifo[((reg5xh & 0xc0) >> 6)],
-		fifo[((reg5yh & 0x0c) >> 2)],
-		fifo[((reg5yh & 0xc0) >> 6)] );
-
-	pci_read_config_byte(bmide_dev, 0x5a, &reg5xh);
-	pci_read_config_byte(bmide_dev, 0x5b, &reg5xh1);
-	pci_read_config_byte(bmide_dev, 0x5e, &reg5yh);
-	pci_read_config_byte(bmide_dev, 0x5f, &reg5yh1);
-
-	p += sprintf(p,/*
-		"------------------drive0-----------drive1------------drive0-----------drive1------\n")*/
-		"Dt RW act. Cnt    %2dT              %2dT               %2dT              %2dT\n"
-		"Dt RW rec. Cnt    %2dT              %2dT               %2dT              %2dT\n\n",
-		(reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
-		(reg5xh1 & 0x70) ? ((reg5xh1 & 0x70) >> 4) : 8,
-		(reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
-		(reg5yh1 & 0x70) ? ((reg5yh1 & 0x70) >> 4) : 8,
-		(reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
-		(reg5xh1 & 0x0f) ? (reg5xh1 & 0x0f) : 16,
-		(reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16,
-		(reg5yh1 & 0x0f) ? (reg5yh1 & 0x0f) : 16 );
-
-	p += sprintf(p,
-		"-----------------------------------UDMA Timings--------------------------------\n\n");
-
-	pci_read_config_byte(bmide_dev, 0x56, &reg5xh);
-	pci_read_config_byte(bmide_dev, 0x57, &reg5yh);
-	p += sprintf(p,
-		"UDMA:             %s               %s                %s               %s\n"
-		"UDMA timings:     %s             %s              %s             %s\n\n",
-		(reg5xh & 0x08) ? "OK" : "No",
-		(reg5xh & 0x80) ? "OK" : "No",
-		(reg5yh & 0x08) ? "OK" : "No",
-		(reg5yh & 0x80) ? "OK" : "No",
-		udmaT[(reg5xh & 0x07)],
-		udmaT[(reg5xh & 0x70) >> 4],
-		udmaT[reg5yh & 0x07],
-		udmaT[(reg5yh & 0x70) >> 4] );
-
-	return p-buffer; /* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-static byte m5229_revision	= 0;
-static byte chip_is_1543c_e	= 0;
-static byte cable_80_pin[2]	= { 0, 0 };
-
-byte ali_proc = 0;
-static struct pci_dev *isa_dev;
-
-static void ali15x3_tune_drive (ide_drive_t *drive, byte pio)
-{
-	ide_pio_data_t d;
-	ide_hwif_t *hwif = HWIF(drive);
-	struct pci_dev *dev = hwif->pci_dev;
-	int s_time, a_time, c_time;
-	byte s_clc, a_clc, r_clc;
-	unsigned long flags;
-	int bus_speed = ide_system_bus_speed();
-	int port = hwif->index ? 0x5c : 0x58;
-	int portFIFO = hwif->channel ? 0x55 : 0x54;
-	byte cd_dma_fifo = 0;
-
-	pio = ide_get_best_pio_mode(drive, pio, 5, &d);
-	s_time = ide_pio_timings[pio].setup_time;
-	a_time = ide_pio_timings[pio].active_time;
-	if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
-		s_clc = 0;
-	if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8)
-		a_clc = 0;
-	c_time = ide_pio_timings[pio].cycle_time;
-
-#if 0
-	if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16)
-		r_clc = 0;
-#endif
-
-	if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) {
-		r_clc = 1;
-	} else {
-		if (r_clc >= 16)
-			r_clc = 0;
-	}
-	__save_flags(flags);
-	__cli();
-	
-	/* 
-	 * PIO mode => ATA FIFO on, ATAPI FIFO off
-	 */
-	pci_read_config_byte(dev, portFIFO, &cd_dma_fifo);
-	if (drive->media==ide_disk) {
-		if (hwif->index) {
-			pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0x0F) | 0x50);
-		} else {
-			pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0xF0) | 0x05);
-		}
-	} else {
-		if (hwif->index) {
-			pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0x0F);
-		} else {
-			pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0xF0);
-		}
-	}
-	
-	pci_write_config_byte(dev, port, s_clc);
-	pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc);
-	__restore_flags(flags);
-
-	/*
-	 * setup   active  rec
-	 * { 70,   165,    365 },   PIO Mode 0
-	 * { 50,   125,    208 },   PIO Mode 1
-	 * { 30,   100,    110 },   PIO Mode 2
-	 * { 30,   80,     70  },   PIO Mode 3 with IORDY
-	 * { 25,   70,     25  },   PIO Mode 4 with IORDY  ns
-	 * { 20,   50,     30  }    PIO Mode 5 with IORDY (nonstandard)
-	 */
-
-}
-
-static int ali15x3_tune_chipset (ide_drive_t *drive, byte speed)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	byte unit		= (drive->select.b.unit & 0x01);
-	byte tmpbyte		= 0x00;
-	int m5229_udma		= hwif->channel? 0x57 : 0x56;
-	int err			= 0;
-
-	if (speed < XFER_UDMA_0) {
-		byte ultra_enable	= (unit) ? 0x7f : 0xf7;
-		/*
-		 * clear "ultra enable" bit
-		 */
-		pci_read_config_byte(dev, m5229_udma, &tmpbyte);
-		tmpbyte &= ultra_enable;
-		pci_write_config_byte(dev, m5229_udma, tmpbyte);
-	}
-
-	err = ide_config_drive_speed(drive, speed);
-
-	if (speed >= XFER_SW_DMA_0) {
-		unsigned long dma_base = hwif->dma_base;
-
-		outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-	}
-
-	if (speed >= XFER_UDMA_0) {
-		pci_read_config_byte(dev, m5229_udma, &tmpbyte);
-		tmpbyte &= (0x0f << ((1-unit) << 2));
-		/*
-		 * enable ultra dma and set timing
-		 */
-		tmpbyte |= ((0x08 | (4-speed)) << (unit << 2));
-		pci_write_config_byte(dev, m5229_udma, tmpbyte);
-		if (speed >= XFER_UDMA_3) {
-			pci_read_config_byte(dev, 0x4b, &tmpbyte);
-			tmpbyte |= 1;
-			pci_write_config_byte(dev, 0x4b, tmpbyte);
-		}
-	}
-
-	return (err);
-}
-
-static int config_chipset_for_dma (ide_drive_t *drive, byte ultra33)
-{
-	struct hd_driveid *id	= drive->id;
-	ide_hwif_t *hwif	= HWIF(drive);
-	byte speed		= 0x00;
-	byte ultra66		= ((hwif->udma_four) && (id->hw_config & 0x2000)) ? 1 : 0;
-	int  rval;
-
-	if ((id->dma_ultra & 0x0010) && (ultra66) && (ultra33)) {
-		speed = XFER_UDMA_4;
-	} else if ((id->dma_ultra & 0x0008) && (ultra66) && (ultra33)) {
-		speed = XFER_UDMA_3;
-	} else if ((id->dma_ultra & 0x0004) && (ultra33)) {
-		speed = XFER_UDMA_2;
-	} else if ((id->dma_ultra & 0x0002) && (ultra33)) {
-		speed = XFER_UDMA_1;
-	} else if ((id->dma_ultra & 0x0001) && (ultra33)) {
-		speed = XFER_UDMA_0;
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-	} else if (id->dma_1word & 0x0004) {
-		speed = XFER_SW_DMA_2;
-	} else if (id->dma_1word & 0x0002) {
-		speed = XFER_SW_DMA_1;
-	} else if (id->dma_1word & 0x0001) {
-		speed = XFER_SW_DMA_0;
-	} else {
-		return ((int) ide_dma_off_quietly);
-	}
-
-	(void) ali15x3_tune_chipset(drive, speed);
-
-	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-
-	return rval;
-}
-
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	ali15x3_tune_drive(drive, 5);
-}
-
-
-static byte ali15x3_can_ultra (ide_drive_t *drive)
-{
-	struct hd_driveid *id	= drive->id;
-
-#if 0
-	if (m5229_revision < 0x20) {
-#else
-	if (m5229_revision <= 0x20) {
-#endif
-		return 0;
-	} else if ((m5229_revision < 0xC2) &&
-		   ((drive->media!=ide_disk) ||
-		    (chip_is_1543c_e &&
-		     strstr(id->model, "WDC ")))) {
-		return 0;
-	} else {
-		return 1;
-	}
-}
-
-static int ali15x3_config_drive_for_dma(ide_drive_t *drive)
-{
-	struct hd_driveid *id		= drive->id;
-	ide_hwif_t *hwif		= HWIF(drive);
-	ide_dma_action_t dma_func	= ide_dma_on;
-	byte can_ultra_dma		= ali15x3_can_ultra(drive);
-
-	if ((m5229_revision<=0x20) && (drive->media!=ide_disk))
-		return hwif->dmaproc(ide_dma_off_quietly, drive);
-
-	if ((id != NULL) && ((id->capability & 1) != 0) && hwif->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
-			goto fast_ata_pio;
-		}
-		dma_func = ide_dma_off_quietly;
-		if ((id->field_valid & 4) && (m5229_revision >= 0xC2)) {
-			if (id->dma_ultra & 0x001F) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, can_ultra_dma);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & 0x0007) ||
-			    (id->dma_1word & 0x0007)) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, can_ultra_dma);
-				if (dma_func != ide_dma_on)
-					goto no_dma_set;
-			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			if (id->eide_dma_time > 150) {
-				goto no_dma_set;
-			}
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, can_ultra_dma);
-			if (dma_func != ide_dma_on)
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
-no_dma_set:
-		config_chipset_for_pio(drive);
-	}
-	return hwif->dmaproc(dma_func, drive);
-}
-
-static int ali15x3_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch(func) {
-		case ide_dma_check:
-			return ali15x3_config_drive_for_dma(drive);
-		case ide_dma_write:
-			if ((m5229_revision < 0xC2) && (drive->media != ide_disk))
-				return 1;	/* try PIO instead of DMA */
-			break;
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-}
-
-unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name)
-{
-	unsigned long fixdma_base = dev->resource[4].start;
-
-	pci_read_config_byte(dev, PCI_REVISION_ID, &m5229_revision);
-
-	isa_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
-
-	if (!fixdma_base || fixdma_base == PCI_BASE_ADDRESS_IO_MASK) {
-		/*
-		 *
-		 */
-	} else {
-		/*
-		 * enable DMA capable bit, and "not" simplex only
-		 */
-		outb(inb(fixdma_base+2) & 0x60, fixdma_base+2);
-
-		if (inb(fixdma_base+2) & 0x80)
-			printk("%s: simplex device: DMA will fail!!\n", name);
-	}
-
-#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
-	ali_proc = 1;
-	bmide_dev = dev;
-	ali_display_info = &ali_get_info;
-#endif  /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-	return 0;
-}
-
-unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif)
-{
-	struct pci_dev *dev	= hwif->pci_dev;
-	byte ata66mask		= hwif->channel ? 0x02 : 0x01;
-	unsigned int ata66	= 0;
-
-	unsigned long flags;
-	byte tmpbyte;
-
-	__save_flags(flags);
-	__cli();
-
-	if (m5229_revision >= 0xC2) {
-		/*
-		 * 1543C-B?, 1535, 1535D, 1553
-		 * Note 1: not all "motherboard" support this detection
-		 * Note 2: if no udma 66 device, the detection may "error".
-		 *         but in this case, we will not set the device to
-		 *         ultra 66, the detection result is not important
-		 */
-
-		/*
-		 * enable "Cable Detection", m5229, 0x4b, bit3
-		 */
-		pci_read_config_byte(dev, 0x4b, &tmpbyte);
-		pci_write_config_byte(dev, 0x4b, tmpbyte | 0x08);
-
-		/*
-		 * set south-bridge's enable bit, m1533, 0x79
-		 */
-		pci_read_config_byte(isa_dev, 0x79, &tmpbyte);
-		if (m5229_revision == 0xC2) {
-			/*
-			 * 1543C-B0 (m1533, 0x79, bit 2)
-			 */
-			pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x04);
-		} else if (m5229_revision == 0xC3) {
-			/*
-			 * 1553/1535 (m1533, 0x79, bit 1)
-			 */
-			pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x02);
-		}
-		/*
-		 * Ultra66 cable detection (from Host View)
-		 * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin
-		 */
-		pci_read_config_byte(dev, 0x4a, &tmpbyte);
-		/*
-		 * 0x4a, bit0 is 0 => primary channel
-		 * has 80-pin (from host view)
-		 */
-		if (!(tmpbyte & 0x01)) cable_80_pin[0] = 1;
-		/*
-		 * 0x4a, bit1 is 0 => secondary channel
-		 * has 80-pin (from host view)
-		 */
-		if (!(tmpbyte & 0x02)) cable_80_pin[1] = 1;
-	} else {
-		/*
-		 * revision 0x20 (1543-E, 1543-F)
-		 * revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E)
-		 * clear CD-ROM DMA write bit, m5229, 0x4b, bit 7
-		 */
-		pci_read_config_byte(dev, 0x4b, &tmpbyte);
-		/*
-		 * clear bit 7
-		 */
-		pci_write_config_byte(dev, 0x4b, tmpbyte & 0x7F);
-		/*
-		 * check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010
-		 */
-		pci_read_config_byte(isa_dev, 0x5e, &tmpbyte);
-		chip_is_1543c_e = ((tmpbyte & 0x1e) == 0x12) ? 1: 0;
-	}
-
-	/*
-	 * CD_ROM DMA on (m5229, 0x53, bit0)
-	 *      Enable this bit even if we want to use PIO
-	 * PIO FIFO off (m5229, 0x53, bit1)
-	 *      The hardware will use 0x54h and 0x55h to control PIO FIFO
-	 */
-	pci_read_config_byte(dev, 0x53, &tmpbyte);
-	tmpbyte = (tmpbyte & (~0x02)) | 0x01;
-
-	pci_write_config_byte(dev, 0x53, tmpbyte);
-
-	/*
-	 * Ultra66 cable detection (from Host View)
-	 * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin
-	 *
-	 * 0x4a, bit0 is 0 => primary channel
-	 * has 80-pin (from host view)
-	 *
-	 * 0x4a, bit1 is 0 => secondary channel
-	 * has 80-pin (from host view)
-	 */
-	pci_read_config_byte(dev, 0x4a, &tmpbyte);
-	ata66 = (!(tmpbyte & ata66mask)) ? 0 : 1;
-	__restore_flags(flags);
-
-	return(ata66);
-}
-
-void __init ide_init_ali15x3 (ide_hwif_t *hwif)
-{
-	byte ideic, inmir;
-	byte irq_routing_table[] = { -1,  9, 3, 10, 4,  5, 7,  6,
-				      1, 11, 0, 12, 0, 14, 0, 15 };
-
-	hwif->irq = hwif->channel ? 15 : 14;
-
-	if (isa_dev) {
-		/*
-		 * read IDE interface control
-		 */
-		pci_read_config_byte(isa_dev, 0x58, &ideic);
-
-		/* bit0, bit1 */
-		ideic = ideic & 0x03;
-
-		/* get IRQ for IDE Controller */
-		if ((hwif->channel && ideic == 0x03) || (!hwif->channel && !ideic)) {
-			/*
-			 * get SIRQ1 routing table
-			 */
-			pci_read_config_byte(isa_dev, 0x44, &inmir);
-			inmir = inmir & 0x0f;
-			hwif->irq = irq_routing_table[inmir];
-		} else if (hwif->channel && !(ideic & 0x01)) {
-			/*
-			 * get SIRQ2 routing table
-			 */
-			pci_read_config_byte(isa_dev, 0x75, &inmir);
-			inmir = inmir & 0x0f;
-			hwif->irq = irq_routing_table[inmir];
-		}
-	}
-
-	hwif->tuneproc = &ali15x3_tune_drive;
-	if ((hwif->dma_base) && (m5229_revision >= 0x20)) {
-		/*
-		 * M1543C or newer for DMAing
-		 */
-		hwif->dmaproc = &ali15x3_dmaproc;
-		hwif->autodma = 1;
-	} else {
-		hwif->autodma = 0;
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-	return;
-}
-
-void ide_dmacapable_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase)
-{
-	if ((dmabase) && (m5229_revision < 0x20)) {
-		return;
-	}
-	ide_setup_dma(hwif, dmabase, 8);
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/amd7409.c linux/drivers/block/amd7409.c
--- v2.3.51/linux/drivers/block/amd7409.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/block/amd7409.c	Wed Dec 31 16:00:00 1969
@@ -1,411 +0,0 @@
-/*
- * linux/drivers/block/amd7409.c		Version 0.03	Feb. 10, 2000
- *
- * Copyright (C) 2000			Andre Hedrick <andre@suse.com>
- * May be copied or modified under the terms of the GNU General Public License
- *
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "ide_modes.h"
-
-#define DISPLAY_VIPER_TIMINGS
-
-#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int amd7409_get_info(char *, char **, off_t, int);
-extern int (*amd7409_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-extern char *ide_media_verbose(ide_drive_t *);
-static struct pci_dev *bmide_dev;
-
-static int amd7409_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-	u32 bibma = bmide_dev->resource[4].start;
-	u8 c0 = 0, c1 = 0;
-
-	/*
-	 * at that point bibma+0x2 et bibma+0xa are byte registers
-	 * to investigate:
-	 */
-	c0 = inb_p((unsigned short)bibma + 0x02);
-	c1 = inb_p((unsigned short)bibma + 0x0a);
-
-	p += sprintf(p, "\n                                AMD 7409 VIPER Chipset.\n");
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %sabled                         %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
-	return p-buffer;	/* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-byte amd7409_proc = 0;
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-/*
- * Here is where all the hard work goes to program the chipset.
- *
- */
-static int amd7409_tune_chipset (ide_drive_t *drive, byte speed)
-{
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	int err			= 0;
-	byte unit		= (drive->select.b.unit & 0x01);
-	int drive_number	= ((HWIF(drive)->channel ? 2 : 0) + unit);
-	unsigned long dma_base	= hwif->dma_base;
-	byte drive_pci		= 0x00;
-	byte drive_pci2		= 0x00;
-	byte ultra_timing	= 0x00;
-	byte dma_pio_timing	= 0x00;
-	byte pio_timing		= 0x00;
-
-        switch (drive_number) {
-		case 0: drive_pci = 0x53; drive_pci2 = 0x4b; break;
-		case 1: drive_pci = 0x52; drive_pci2 = 0x4a; break;
-		case 2: drive_pci = 0x51; drive_pci2 = 0x49; break;
-		case 3: drive_pci = 0x50; drive_pci2 = 0x48; break;
-		default:
-                        return ((int) ide_dma_off_quietly);
-        }
-
-	pci_read_config_byte(dev, drive_pci, &ultra_timing);
-	pci_read_config_byte(dev, drive_pci2, &dma_pio_timing);
-	pci_read_config_byte(dev, 0x4c, &pio_timing);
-
-#ifdef DEBUG
-	printk("%s: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ",
-		drive->name, ultra_timing, dma_pio_timing, pio_timing);
-#endif
-
-	ultra_timing &= ~0xC7;
-	dma_pio_timing &= ~0xFF;
-	pio_timing &= ~(0x03 << drive_number);
-
-#ifdef DEBUG
-	printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ",
-		ultra_timing, dma_pio_timing, pio_timing);
-#endif
-
-	switch(speed) {
-		case XFER_UDMA_4:
-			ultra_timing |= 0x45;
-			dma_pio_timing |= 0x20;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_UDMA_3:
-			ultra_timing |= 0x44;
-			dma_pio_timing |= 0x20;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_UDMA_2:
-			ultra_timing |= 0x40;
-			dma_pio_timing |= 0x20;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_UDMA_1:
-			ultra_timing |= 0x41;
-			dma_pio_timing |= 0x20;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_UDMA_0:
-			ultra_timing |= 0x42;
-			dma_pio_timing |= 0x20;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_MW_DMA_2:
-			dma_pio_timing |= 0x20;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_MW_DMA_1:
-			dma_pio_timing |= 0x21;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_MW_DMA_0:
-			dma_pio_timing |= 0x77;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_PIO_4:
-			dma_pio_timing |= 0x20;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_PIO_3:
-			dma_pio_timing |= 0x22;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_PIO_2:
-			dma_pio_timing |= 0x42;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_PIO_1:
-			dma_pio_timing |= 0x65;
-			pio_timing |= (0x03 << drive_number);
-			break;
-		case XFER_PIO_0:
-		default:
-			dma_pio_timing |= 0xA8;
-			pio_timing |= (0x03 << drive_number);
-			break;
-        }
-
-	pci_write_config_byte(dev, drive_pci, ultra_timing);
-	pci_write_config_byte(dev, drive_pci2, dma_pio_timing);
-	pci_write_config_byte(dev, 0x4c, pio_timing);
-
-#ifdef DEBUG
-	printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x\n",
-		ultra_timing, dma_pio_timing, pio_timing);
-#endif
-
-	if (speed > XFER_PIO_4) {
-		outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-	} else {
-		outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
-	}
-	err = ide_config_drive_speed(drive, speed);
-	return (err);
-}
-
-/*
- * This allows the configuration of ide_pci chipset registers
- * for cards that learn about the drive's UDMA, DMA, PIO capabilities
- * after the drive is reported by the OS.
- */
-static int config_chipset_for_dma (ide_drive_t *drive)
-{
-	struct hd_driveid *id	= drive->id;
-	byte udma_66		= ((id->hw_config & 0x2000) &&
-				   (HWIF(drive)->udma_four)) ? 1 : 0;
-	byte speed		= 0x00;
-	int  rval;
-
-	if ((id->dma_ultra & 0x0010) && (udma_66)) {
-		speed = XFER_UDMA_4;
-	} else if ((id->dma_ultra & 0x0008) && (udma_66)) {
-		speed = XFER_UDMA_3;
-	} else if (id->dma_ultra & 0x0004) {
-		speed = XFER_UDMA_2;
-	} else if (id->dma_ultra & 0x0002) {
-		speed = XFER_UDMA_1;
-	} else if (id->dma_ultra & 0x0001) {
-		speed = XFER_UDMA_0;
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-        } else {
-		return ((int) ide_dma_off_quietly);
-	}
-
-	(void) amd7409_tune_chipset(drive, speed);
-
-	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-
-	return rval;
-}
-
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
-	unsigned short xfer_pio	= drive->id->eide_pio_modes;
-	byte			timing, speed, pio;
-
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 :
-			   (drive->id->tPIO & 2) ? 0x02 :
-			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-	switch(timing) {
-		case 4: speed = XFER_PIO_4;break;
-		case 3: speed = XFER_PIO_3;break;
-		case 2: speed = XFER_PIO_2;break;
-		case 1: speed = XFER_PIO_1;break;
-		default:
-			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
-			break;
-	}
-	(void) amd7409_tune_chipset(drive, speed);
-}
-
-static void amd7409_tune_drive (ide_drive_t *drive, byte pio)
-{
-	byte speed;
-	switch(pio) {
-		case 4:		speed = XFER_PIO_4;break;
-		case 3:		speed = XFER_PIO_3;break;
-		case 2:		speed = XFER_PIO_2;break;
-		case 1:		speed = XFER_PIO_1;break;
-		default:	speed = XFER_PIO_0;break;
-	}
-	(void) amd7409_tune_chipset(drive, speed);
-}
-
-static int config_drive_xfer_rate (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
-
-	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
-			goto fast_ata_pio;
-		}
-		dma_func = ide_dma_off_quietly;
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & 0x001F) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if (id->dma_mword & 0x0007) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive);
-				if (dma_func != ide_dma_on)
-					goto no_dma_set;
-			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			if (id->eide_dma_time > 150) {
-				goto no_dma_set;
-			}
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive);
-			if (dma_func != ide_dma_on)
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
-no_dma_set:
-
-		config_chipset_for_pio(drive);
-	}
-	return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-/*
- * amd7409_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- */
-
-int amd7409_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-}
-
-unsigned int __init pci_init_amd7409 (struct pci_dev *dev, const char *name)
-{
-	unsigned long fixdma_base = dev->resource[4].start;
-
-	if (!fixdma_base || fixdma_base == PCI_BASE_ADDRESS_IO_MASK) {
-		/*
-		 *
-		 */
-	} else {
-		/*
-		 * enable DMA capable bit, and "not" simplex only
-		 */
-		outb(inb(fixdma_base+2) & 0x60, fixdma_base+2);
-
-		if (inb(fixdma_base+2) & 0x80)
-			printk("%s: simplex device: DMA will fail!!\n", name);
-	}
-#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
-	amd7409_proc = 1;
-	bmide_dev = dev;
-	amd7409_display_info = &amd7409_get_info;
-#endif /* DISPLAY_VIPER_TIMINGS && CONFIG_PROC_FS */
-
-	return 0;
-}
-
-unsigned int __init ata66_amd7409 (ide_hwif_t *hwif)
-{
-#ifdef CONFIG_AMD7409_OVERRIDE
-	byte ata66 = 1;
-#else
-	byte ata66 = 0;
-#endif /* CONFIG_AMD7409_OVERRIDE */
-
-#if 0
-	pci_read_config_byte(hwif->pci_dev, 0x48, &ata66);
-	return ((ata66 & 0x02) ? 0 : 1);
-#endif
-	return ata66;
-}
-
-void __init ide_init_amd7409 (ide_hwif_t *hwif)
-{
-	hwif->tuneproc = &amd7409_tune_drive;
-	if (hwif->dma_base) {
-		hwif->dmaproc = &amd7409_dmaproc;
-	} else {
-		hwif->autodma = 0;
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-}
-
-void ide_dmacapable_amd7409 (ide_hwif_t *hwif, unsigned long dmabase)
-{
-	ide_setup_dma(hwif, dmabase, 8);
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/blkpg.c linux/drivers/block/blkpg.c
--- v2.3.51/linux/drivers/block/blkpg.c	Thu Feb 10 17:11:06 2000
+++ linux/drivers/block/blkpg.c	Sun Mar 12 19:32:57 2000
@@ -268,6 +268,13 @@
 		case BLKPG:
 			return blkpg_ioctl(dev, (struct blkpg_ioctl_arg *) arg);
 			
+		case BLKELVGET:
+			return blkelvget_ioctl(&blk_get_queue(dev)->elevator,
+					       (blkelv_ioctl_arg_t *) arg);
+		case BLKELVSET:
+			return blkelvset_ioctl(&blk_get_queue(dev)->elevator,
+					       (blkelv_ioctl_arg_t *) arg);
+
 		default:
 			return -EINVAL;
 	}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/buddha.c linux/drivers/block/buddha.c
--- v2.3.51/linux/drivers/block/buddha.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/buddha.c	Wed Dec 31 16:00:00 1969
@@ -1,165 +0,0 @@
-/*
- *  linux/drivers/block/buddha.c -- Amiga Buddha and Catweasel IDE Driver
- *
- *	Copyright (C) 1997 by Geert Uytterhoeven
- *
- *  This driver was written by based on the specifications in README.buddha.
- *
- *  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.
- *
- *  TODO:
- *    - test it :-)
- *    - tune the timings using the speed-register
- */
-
-#include <linux/types.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/zorro.h>
-#include <linux/ide.h>
-
-#include <asm/amigahw.h>
-#include <asm/amigaints.h>
-
-
-    /*
-     *  The Buddha has 2 IDE interfaces, the Catweasel has 3
-     */
-
-#define BUDDHA_NUM_HWIFS	2
-#define CATWEASEL_NUM_HWIFS	3
-
-
-    /*
-     *  Bases of the IDE interfaces (relative to the board address)
-     */
-
-#define BUDDHA_BASE1	0x800
-#define BUDDHA_BASE2	0xa00
-#define BUDDHA_BASE3	0xc00
-
-static const u_int __init buddha_bases[CATWEASEL_NUM_HWIFS] = {
-    BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3
-};
-
-
-    /*
-     *  Offsets from one of the above bases
-     */
-
-#define BUDDHA_DATA	0x00
-#define BUDDHA_ERROR	0x06		/* see err-bits */
-#define BUDDHA_NSECTOR	0x0a		/* nr of sectors to read/write */
-#define BUDDHA_SECTOR	0x0e		/* starting sector */
-#define BUDDHA_LCYL	0x12		/* starting cylinder */
-#define BUDDHA_HCYL	0x16		/* high byte of starting cyl */
-#define BUDDHA_SELECT	0x1a		/* 101dhhhh , d=drive, hhhh=head */
-#define BUDDHA_STATUS	0x1e		/* see status-bits */
-#define BUDDHA_CONTROL	0x11a
-
-static int __init buddha_offsets[IDE_NR_PORTS] = {
-    BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL,
-    BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL
-};
-
-
-    /*
-     *  Other registers
-     */
-
-#define BUDDHA_IRQ1	0xf00		/* MSB = 1, Harddisk is source of */
-#define BUDDHA_IRQ2	0xf40		/* interrupt */
-#define BUDDHA_IRQ3	0xf80
-
-static const int __init buddha_irqports[CATWEASEL_NUM_HWIFS] = {
-    BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3
-};
-
-#define BUDDHA_IRQ_MR	0xfc0		/* master interrupt enable */
-
-
-    /*
-     *  Board information
-     */
-
-static u_long buddha_board = 0;
-static int buddha_num_hwifs = -1;
-
-
-    /*
-     *  Check and acknowledge the interrupt status
-     */
-
-static int buddha_ack_intr(ide_hwif_t *hwif)
-{
-    unsigned char ch;
-
-    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
-    if (!(ch & 0x80))
-	return 0;
-    return 1;
-}
-
-
-    /*
-     *  Any Buddha or Catweasel boards present?
-     */
-
-static int __init find_buddha(void)
-{
-    struct zorro_dev *z = NULL;
-
-    buddha_num_hwifs = 0;
-    while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
-	unsigned long board;
-	const char *name;
-	if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) {
-	    buddha_num_hwifs = BUDDHA_NUM_HWIFS;
-	    name = "Buddha IDE Interface";
-	} else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL) {
-	    buddha_num_hwifs = CATWEASEL_NUM_HWIFS;
-	    name = "Catweasel IDE Interface and Floppy Controller";
-	} else
-	    continue;
-	board = z->resource.start;
-	if (!request_mem_region(board+BUDDHA_BASE1, 0x800, "IDE"))
-	    continue;
-	strcpy(z->name, name);
-	buddha_board = ZTWO_VADDR(board);
-	/* write to BUDDHA_IRQ_MR to enable the board IRQ */
-	*(char *)(buddha_board+BUDDHA_IRQ_MR) = 0;
-	break;
-    }
-    return buddha_num_hwifs;
-}
-
-
-    /*
-     *  Probe for a Buddha or Catweasel IDE interface
-     *  We support only _one_ of them, no multiple boards!
-     */
-
-void __init buddha_init(void)
-{
-    hw_regs_t hw;
-    int i, index;
-
-    if (buddha_num_hwifs < 0 && !find_buddha())
-	return;
-
-    for (i = 0; i < buddha_num_hwifs; i++) {
-	ide_setup_ports(&hw, (ide_ioreg_t)(buddha_board+buddha_bases[i]),
-			buddha_offsets, 0,
-			(ide_ioreg_t)(buddha_board+buddha_irqports[i]),
-			buddha_ack_intr, IRQ_AMIGA_PORTS);
-	index = ide_register_hw(&hw, NULL);
-	if (index != -1)
-	    printk("ide%d: %s IDE interface\n", index,
-		   buddha_num_hwifs == BUDDHA_NUM_HWIFS ? "Buddha" :
-		   					  "Catweasel");
-    }
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- v2.3.51/linux/drivers/block/cmd640.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/cmd640.c	Wed Dec 31 16:00:00 1969
@@ -1,855 +0,0 @@
-/*
- *  linux/drivers/block/cmd640.c		Version 1.02  Sep 01, 1996
- *
- *  Copyright (C) 1995-1996  Linus Torvalds & authors (see below)
- */
-
-/*
- *  Original authors:	abramov@cecmow.enet.dec.com (Igor Abramov)
- *  			mlord@pobox.com (Mark Lord)
- *
- *  See linux/MAINTAINERS for address of current maintainer.
- *
- *  This file provides support for the advanced features and bugs
- *  of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
- *
- *  These chips are basically fucked by design, and getting this driver
- *  to work on every motherboard design that uses this screwed chip seems
- *  bloody well impossible.  However, we're still trying.
- *
- *  Version 0.97 worked for everybody.
- *
- *  User feedback is essential.  Many thanks to the beta test team:
- *
- *  A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com,
- *  bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz,
- *  chrisc@dbass.demon.co.uk, dalecki@namu26.Num.Math.Uni-Goettingen.de,
- *  derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de,
- *  flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net,
- *  j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net,
- *  kerouac@ssnet.com, meskes@informatik.rwth-aachen.de, hzoli@cs.elte.hu,
- *  peter@udgaard.isgtec.com, phil@tazenda.demon.co.uk, roadcapw@cfw.com,
- *  s0033las@sun10.vsz.bme.hu, schaffer@tam.cornell.edu, sjd@slip.net,
- *  steve@ei.org, ulrpeg@bigcomm.gun.de, ism@tardis.ed.ac.uk, mack@cray.com
- *  liug@mama.indstate.edu, and others.
- *
- *  Version 0.01	Initial version, hacked out of ide.c,
- *			and #include'd rather than compiled separately.
- *			This will get cleaned up in a subsequent release.
- *
- *  Version 0.02	Fixes for vlb initialization code, enable prefetch
- *			for versions 'B' and 'C' of chip by default,
- *			some code cleanup.
- *
- *  Version 0.03	Added reset of secondary interface,
- *			and black list for devices which are not compatible
- *			with prefetch mode. Separate function for setting
- *			prefetch is added, possibly it will be called some
- *			day from ioctl processing code.
- *
- *  Version 0.04	Now configs/compiles separate from ide.c
- *
- *  Version 0.05	Major rewrite of interface timing code.
- *			Added new function cmd640_set_mode to set PIO mode
- *			from ioctl call. New drives added to black list.
- *
- *  Version 0.06	More code cleanup. Prefetch is enabled only for
- *			detected hard drives, not included in prefetch
- *			black list.
- *
- *  Version 0.07	Changed to more conservative drive tuning policy.
- *			Unknown drives, which report PIO < 4 are set to
- *			(reported_PIO - 1) if it is supported, or to PIO0.
- *			List of known drives extended by info provided by
- *			CMD at their ftp site.
- *
- *  Version 0.08	Added autotune/noautotune support.
- *
- *  Version 0.09	Try to be smarter about 2nd port enabling.
- *  Version 0.10	Be nice and don't reset 2nd port.
- *  Version 0.11	Try to handle more wierd situations.
- *
- *  Version 0.12	Lots of bug fixes from Laszlo Peter
- *			irq unmasking disabled for reliability.
- *			try to be even smarter about the second port.
- *			tidy up source code formatting.
- *  Version 0.13	permit irq unmasking again.
- *  Version 0.90	massive code cleanup, some bugs fixed.
- *			defaults all drives to PIO mode0, prefetch off.
- *			autotune is OFF by default, with compile time flag.
- *			prefetch can be turned OFF/ON using "hdparm -p8/-p9"
- *			 (requires hdparm-3.1 or newer)
- *  Version 0.91	first release to linux-kernel list.
- *  Version 0.92	move initial reg dump to separate callable function
- *			change "readahead" to "prefetch" to avoid confusion
- *  Version 0.95	respect original BIOS timings unless autotuning.
- *			tons of code cleanup and rearrangement.
- *			added CONFIG_BLK_DEV_CMD640_ENHANCED option
- *			prevent use of unmask when prefetch is on
- *  Version 0.96	prevent use of io_32bit when prefetch is off
- *  Version 0.97	fix VLB secondary interface for sjd@slip.net
- *			other minor tune-ups:  0.96 was very good.
- *  Version 0.98	ignore PCI version when disabled by BIOS
- *  Version 0.99	display setup/active/recovery clocks with PIO mode
- *  Version 1.00	Mmm.. cannot depend on PCMD_ENA in all systems
- *  Version 1.01	slow/fast devsel can be selected with "hdparm -p6/-p7"
- *			 ("fast" is necessary for 32bit I/O in some systems)
- *  Version 1.02	fix bug that resulted in slow "setup times"
- *			 (patch courtesy of Zoltan Hidvegi)
- */
-
-#undef REALLY_SLOW_IO		/* most systems can safely undef this */
-#define CMD640_PREFETCH_MASKS 1
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-/*
- * This flag is set in ide.c by the parameter:  ide0=cmd640_vlb
- */
-int cmd640_vlb = 0;
-
-/*
- * CMD640 specific registers definition.
- */
-
-#define VID		0x00
-#define DID		0x02
-#define PCMD		0x04
-#define   PCMD_ENA	0x01
-#define PSTTS		0x06
-#define REVID		0x08
-#define PROGIF		0x09
-#define SUBCL		0x0a
-#define BASCL		0x0b
-#define BaseA0		0x10
-#define BaseA1		0x14
-#define BaseA2		0x18
-#define BaseA3		0x1c
-#define INTLINE		0x3c
-#define INPINE		0x3d
-
-#define	CFR		0x50
-#define   CFR_DEVREV		0x03
-#define   CFR_IDE01INTR		0x04
-#define	  CFR_DEVID		0x18
-#define	  CFR_AT_VESA_078h	0x20
-#define	  CFR_DSA1		0x40
-#define	  CFR_DSA0		0x80
-
-#define CNTRL		0x51
-#define	  CNTRL_DIS_RA0		0x40
-#define   CNTRL_DIS_RA1		0x80
-#define	  CNTRL_ENA_2ND		0x08
-
-#define	CMDTIM		0x52
-#define	ARTTIM0		0x53
-#define	DRWTIM0		0x54
-#define ARTTIM1 	0x55
-#define DRWTIM1		0x56
-#define ARTTIM23	0x57
-#define   ARTTIM23_DIS_RA2	0x04
-#define   ARTTIM23_DIS_RA3	0x08
-#define DRWTIM23	0x58
-#define BRST		0x59
-
-/*
- * Registers and masks for easy access by drive index:
- */
-static byte prefetch_regs[4]  = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
-static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
-
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
-
-static byte arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
-static byte drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
-
-/*
- * Current cmd640 timing values for each drive.
- * The defaults for each are the slowest possible timings.
- */
-static byte setup_counts[4]    = {4, 4, 4, 4};     /* Address setup count (in clocks) */
-static byte active_counts[4]   = {16, 16, 16, 16}; /* Active count   (encoded) */
-static byte recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
-
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
-
-/*
- * These are initialized to point at the devices we control
- */
-static ide_hwif_t  *cmd_hwif0, *cmd_hwif1;
-static ide_drive_t *cmd_drives[4];
-
-/*
- * Interface to access cmd640x registers
- */
-static unsigned int cmd640_key;
-static void (*put_cmd640_reg)(unsigned short reg, byte val);
-static byte (*get_cmd640_reg)(unsigned short reg);
-
-/*
- * This is read from the CFR reg, and is used in several places.
- */
-static unsigned int cmd640_chip_version;
-
-/*
- * The CMD640x chip does not support DWORD config write cycles, but some
- * of the BIOSes use them to implement the config services.
- * Therefore, we must use direct IO instead.
- */
-
-/* PCI method 1 access */
-
-static void put_cmd640_reg_pci1 (unsigned short reg, byte val)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
-	outb_p(val, (reg & 3) | 0xcfc);
-	restore_flags(flags);
-}
-
-static byte get_cmd640_reg_pci1 (unsigned short reg)
-{
-	byte b;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
-	b = inb_p((reg & 3) | 0xcfc);
-	restore_flags(flags);
-	return b;
-}
-
-/* PCI method 2 access (from CMD datasheet) */
-
-static void put_cmd640_reg_pci2 (unsigned short reg, byte val)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	outb_p(0x10, 0xcf8);
-	outb_p(val, cmd640_key + reg);
-	outb_p(0, 0xcf8);
-	restore_flags(flags);
-}
-
-static byte get_cmd640_reg_pci2 (unsigned short reg)
-{
-	byte b;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	outb_p(0x10, 0xcf8);
-	b = inb_p(cmd640_key + reg);
-	outb_p(0, 0xcf8);
-	restore_flags(flags);
-	return b;
-}
-
-/* VLB access */
-
-static void put_cmd640_reg_vlb (unsigned short reg, byte val)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	outb_p(reg, cmd640_key);
-	outb_p(val, cmd640_key + 4);
-	restore_flags(flags);
-}
-
-static byte get_cmd640_reg_vlb (unsigned short reg)
-{
-	byte b;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	outb_p(reg, cmd640_key);
-	b = inb_p(cmd640_key + 4);
-	restore_flags(flags);
-	return b;
-}
-
-static int __init match_pci_cmd640_device (void)
-{
-	const byte ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
-	unsigned int i;
-	for (i = 0; i < 4; i++) {
-		if (get_cmd640_reg(i) != ven_dev[i])
-			return 0;
-	}
-#ifdef STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT
-	if ((get_cmd640_reg(PCMD) & PCMD_ENA) == 0) {
-		printk("ide: cmd640 on PCI disabled by BIOS\n");
-		return 0;
-	}
-#endif /* STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT */
-	return 1; /* success */
-}
-
-/*
- * Probe for CMD640x -- pci method 1
- */
-static int __init probe_for_cmd640_pci1 (void)
-{
-	get_cmd640_reg = get_cmd640_reg_pci1;
-	put_cmd640_reg = put_cmd640_reg_pci1;
-	for (cmd640_key = 0x80000000; cmd640_key <= 0x8000f800; cmd640_key += 0x800) {
-		if (match_pci_cmd640_device())
-			return 1; /* success */
-	}
-	return 0;
-}
-
-/*
- * Probe for CMD640x -- pci method 2
- */
-static int __init probe_for_cmd640_pci2 (void)
-{
-	get_cmd640_reg = get_cmd640_reg_pci2;
-	put_cmd640_reg = put_cmd640_reg_pci2;
-	for (cmd640_key = 0xc000; cmd640_key <= 0xcf00; cmd640_key += 0x100) {
-		if (match_pci_cmd640_device())
-			return 1; /* success */
-	}
-	return 0;
-}
-
-/*
- * Probe for CMD640x -- vlb
- */
-static int __init probe_for_cmd640_vlb (void)
-{
-	byte b;
-
-	get_cmd640_reg = get_cmd640_reg_vlb;
-	put_cmd640_reg = put_cmd640_reg_vlb;
-	cmd640_key = 0x178;
-	b = get_cmd640_reg(CFR);
-	if (b == 0xff || b == 0x00 || (b & CFR_AT_VESA_078h)) {
-		cmd640_key = 0x78;
-		b = get_cmd640_reg(CFR);
-		if (b == 0xff || b == 0x00 || !(b & CFR_AT_VESA_078h))
-			return 0;
-	}
-	return 1; /* success */
-}
-
-/*
- *  Returns 1 if an IDE interface/drive exists at 0x170,
- *  Returns 0 otherwise.
- */
-static int __init secondary_port_responding (void)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	outb_p(0x0a, 0x170 + IDE_SELECT_OFFSET);	/* select drive0 */
-	udelay(100);
-	if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x0a) {
-		outb_p(0x1a, 0x170 + IDE_SELECT_OFFSET); /* select drive1 */
-		udelay(100);
-		if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x1a) {
-			restore_flags(flags);
-			return 0; /* nothing responded */
-		}
-	}
-	restore_flags(flags);
-	return 1; /* success */
-}
-
-#ifdef CMD640_DUMP_REGS
-/*
- * Dump out all cmd640 registers.  May be called from ide.c
- */
-void cmd640_dump_regs (void)
-{
-	unsigned int reg = cmd640_vlb ? 0x50 : 0x00;
-
-	/* Dump current state of chip registers */
-	printk("ide: cmd640 internal register dump:");
-	for (; reg <= 0x59; reg++) {
-		if (!(reg & 0x0f))
-			printk("\n%04x:", reg);
-		printk(" %02x", get_cmd640_reg(reg));
-	}
-	printk("\n");
-}
-#endif
-
-/*
- * Check whether prefetch is on for a drive,
- * and initialize the unmask flags for safe operation.
- */
-static void __init check_prefetch (unsigned int index)
-{
-	ide_drive_t *drive = cmd_drives[index];
-	byte b = get_cmd640_reg(prefetch_regs[index]);
-
-	if (b & prefetch_masks[index]) {	/* is prefetch off? */
-		drive->no_unmask = 0;
-		drive->no_io_32bit = 1;
-		drive->io_32bit = 0;
-	} else {
-#if CMD640_PREFETCH_MASKS
-		drive->no_unmask = 1;
-		drive->unmask = 0;
-#endif
-		drive->no_io_32bit = 0;
-	}
-}
-
-/*
- * Figure out which devices we control
- */
-static void __init setup_device_ptrs (void)
-{
-	unsigned int i;
-
-	cmd_hwif0 = &ide_hwifs[0]; /* default, if not found below */
-	cmd_hwif1 = &ide_hwifs[1]; /* default, if not found below */
-	for (i = 0; i < MAX_HWIFS; i++) {
-		ide_hwif_t *hwif = &ide_hwifs[i];
-		if (hwif->chipset == ide_unknown || hwif->chipset == ide_generic) {
-			if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0)
-				cmd_hwif0 = hwif;
-			else if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
-				cmd_hwif1 = hwif;
-		}
-	}
-	cmd_drives[0] = &cmd_hwif0->drives[0];
-	cmd_drives[1] = &cmd_hwif0->drives[1];
-	cmd_drives[2] = &cmd_hwif1->drives[0];
-	cmd_drives[3] = &cmd_hwif1->drives[1];
-}
-
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
-
-/*
- * Sets prefetch mode for a drive.
- */
-static void set_prefetch_mode (unsigned int index, int mode)
-{
-	ide_drive_t *drive = cmd_drives[index];
-	int reg = prefetch_regs[index];
-	byte b;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	b = get_cmd640_reg(reg);
-	if (mode) {	/* want prefetch on? */
-#if CMD640_PREFETCH_MASKS
-		drive->no_unmask = 1;
-		drive->unmask = 0;
-#endif
-		drive->no_io_32bit = 0;
-		b &= ~prefetch_masks[index];	/* enable prefetch */
-	} else {
-		drive->no_unmask = 0;
-		drive->no_io_32bit = 1;
-		drive->io_32bit = 0;
-		b |= prefetch_masks[index];	/* disable prefetch */
-	}
-	put_cmd640_reg(reg, b);
-	restore_flags(flags);
-}
-
-/*
- * Dump out current drive clocks settings
- */
-static void display_clocks (unsigned int index)
-{
-	byte active_count, recovery_count;
-
-	active_count = active_counts[index];
-	if (active_count == 1)
-		++active_count;
-	recovery_count = recovery_counts[index];
-	if (active_count > 3 && recovery_count == 1)
-		++recovery_count;
-	if (cmd640_chip_version > 1)
-		recovery_count += 1;  /* cmd640b uses (count + 1)*/
-	printk(", clocks=%d/%d/%d\n", setup_counts[index], active_count, recovery_count);
-}
-
-/*
- * Pack active and recovery counts into single byte representation
- * used by controller
- */
-inline static byte pack_nibbles (byte upper, byte lower)
-{
-	return ((upper & 0x0f) << 4) | (lower & 0x0f);
-}
-
-/*
- * This routine retrieves the initial drive timings from the chipset.
- */
-static void __init retrieve_drive_counts (unsigned int index)
-{
-	byte b;
-
-	/*
-	 * Get the internal setup timing, and convert to clock count
-	 */
-	b = get_cmd640_reg(arttim_regs[index]) & ~0x3f;
-	switch (b) {
-		case 0x00: b = 4; break;
-		case 0x80: b = 3; break;
-		case 0x40: b = 2; break;
-		default:   b = 5; break;
-	}
-	setup_counts[index] = b;
-
-	/*
-	 * Get the active/recovery counts
-	 */
-	b = get_cmd640_reg(drwtim_regs[index]);
-	active_counts[index]   = (b >> 4)   ? (b >> 4)   : 0x10;
-	recovery_counts[index] = (b & 0x0f) ? (b & 0x0f) : 0x10;
-}
-
-
-/*
- * This routine writes the prepared setup/active/recovery counts
- * for a drive into the cmd640 chipset registers to active them.
- */
-static void program_drive_counts (unsigned int index)
-{
-	unsigned long flags;
-	byte setup_count    = setup_counts[index];
-	byte active_count   = active_counts[index];
-	byte recovery_count = recovery_counts[index];
-
-	/*
-	 * Set up address setup count and drive read/write timing registers.
-	 * Primary interface has individual count/timing registers for
-	 * each drive.  Secondary interface has one common set of registers,
-	 * so we merge the timings, using the slowest value for each timing.
-	 */
-	if (index > 1) {
-		unsigned int mate;
-		if (cmd_drives[mate = index ^ 1]->present) {
-			if (setup_count < setup_counts[mate])
-				setup_count = setup_counts[mate];
-			if (active_count < active_counts[mate])
-				active_count = active_counts[mate];
-			if (recovery_count < recovery_counts[mate])
-				recovery_count = recovery_counts[mate];
-		}
-	}
-
-	/*
-	 * Convert setup_count to internal chipset representation
-	 */
-	switch (setup_count) {
-		case 4:	 setup_count = 0x00; break;
-		case 3:	 setup_count = 0x80; break;
-		case 1:
-		case 2:	 setup_count = 0x40; break;
-		default: setup_count = 0xc0; /* case 5 */
-	}
-
-	/*
-	 * Now that everything is ready, program the new timings
-	 */
-	save_flags (flags);
-	cli();
-	/*
-	 * Program the address_setup clocks into ARTTIM reg,
-	 * and then the active/recovery counts into the DRWTIM reg
-	 * (this converts counts of 16 into counts of zero -- okay).
-	 */
-	setup_count |= get_cmd640_reg(arttim_regs[index]) & 0x3f;
-	put_cmd640_reg(arttim_regs[index], setup_count);
-	put_cmd640_reg(drwtim_regs[index], pack_nibbles(active_count, recovery_count));
-	restore_flags(flags);
-}
-
-/*
- * Set a specific pio_mode for a drive
- */
-static void cmd640_set_mode (unsigned int index, byte pio_mode, unsigned int cycle_time)
-{
-	int setup_time, active_time, recovery_time, clock_time;
-	byte setup_count, active_count, recovery_count, recovery_count2, cycle_count;
-	int bus_speed = ide_system_bus_speed();
-
-	if (pio_mode > 5)
-		pio_mode = 5;
-	setup_time  = ide_pio_timings[pio_mode].setup_time;
-	active_time = ide_pio_timings[pio_mode].active_time;
-	recovery_time = cycle_time - (setup_time + active_time);
-	clock_time = 1000 / bus_speed;
-	cycle_count = (cycle_time + clock_time - 1) / clock_time;
-
-	setup_count = (setup_time + clock_time - 1) / clock_time;
-
-	active_count = (active_time + clock_time - 1) / clock_time;
-	if (active_count < 2)
-		active_count = 2; /* minimum allowed by cmd640 */
-
-	recovery_count = (recovery_time + clock_time - 1) / clock_time;
-	recovery_count2 = cycle_count - (setup_count + active_count);
-	if (recovery_count2 > recovery_count)
-		recovery_count = recovery_count2;
-	if (recovery_count < 2)
-		recovery_count = 2; /* minimum allowed by cmd640 */
-	if (recovery_count > 17) {
-		active_count += recovery_count - 17;
-		recovery_count = 17;
-	}
-	if (active_count > 16)
-		active_count = 16; /* maximum allowed by cmd640 */
-	if (cmd640_chip_version > 1)
-		recovery_count -= 1;  /* cmd640b uses (count + 1)*/
-	if (recovery_count > 16)
-		recovery_count = 16; /* maximum allowed by cmd640 */
-
-	setup_counts[index]    = setup_count;
-	active_counts[index]   = active_count;
-	recovery_counts[index] = recovery_count;
-
-	/*
-	 * In a perfect world, we might set the drive pio mode here
-	 * (using WIN_SETFEATURE) before continuing.
-	 *
-	 * But we do not, because:
-	 *	1) this is the wrong place to do it (proper is do_special() in ide.c)
-	 * 	2) in practice this is rarely, if ever, necessary
-	 */
-	program_drive_counts (index);
-}
-
-/*
- * Drive PIO mode selection:
- */
-static void cmd640_tune_drive (ide_drive_t *drive, byte mode_wanted)
-{
-	byte b;
-	ide_pio_data_t  d;
-	unsigned int index = 0;
-
-	while (drive != cmd_drives[index]) {
-		if (++index > 3) {
-			printk("%s: bad news in cmd640_tune_drive\n", drive->name);
-			return;
-		}
-	}
-	switch (mode_wanted) {
-		case 6: /* set fast-devsel off */
-		case 7: /* set fast-devsel on */
-			mode_wanted &= 1;
-			b = get_cmd640_reg(CNTRL) & ~0x27;
-			if (mode_wanted)
-				b |= 0x27;
-			put_cmd640_reg(CNTRL, b);
-			printk("%s: %sabled cmd640 fast host timing (devsel)\n", drive->name, mode_wanted ? "en" : "dis");
-			return;
-
-		case 8: /* set prefetch off */
-		case 9: /* set prefetch on */
-			mode_wanted &= 1;
-			set_prefetch_mode(index, mode_wanted);
-			printk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
-			return;
-	}
-
-	(void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
-	cmd640_set_mode (index, d.pio_mode, d.cycle_time);
-
-	printk ("%s: selected cmd640 PIO mode%d (%dns)%s",
-		drive->name,
-		d.pio_mode,
-		d.cycle_time,
-		d.overridden ? " (overriding vendor mode)" : "");
-	display_clocks(index);
-	return;
-}
-
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
-
-/*
- * Probe for a cmd640 chipset, and initialize it if found.  Called from ide.c
- */
-int __init ide_probe_for_cmd640x (void)
-{
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
-	int second_port_toggled = 0;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
-	int second_port_cmd640 = 0;
-	const char *bus_type, *port2;
-	unsigned int index;
-	byte b, cfr;
-
-	if (cmd640_vlb && probe_for_cmd640_vlb()) {
-		bus_type = "VLB";
-	} else {
-		cmd640_vlb = 0;
-		if (probe_for_cmd640_pci1())
-			bus_type = "PCI (type1)";
-		else if (probe_for_cmd640_pci2())
-			bus_type = "PCI (type2)";
-		else
-			return 0;
-	}
-	/*
-	 * Undocumented magic (there is no 0x5b reg in specs)
-	 */
-	put_cmd640_reg(0x5b, 0xbd);
-	if (get_cmd640_reg(0x5b) != 0xbd) {
-		printk("ide: cmd640 init failed: wrong value in reg 0x5b\n");
-		return 0;
-	}
-	put_cmd640_reg(0x5b, 0);
-
-#ifdef CMD640_DUMP_REGS
-	CMD640_DUMP_REGS;
-#endif
-
-	/*
-	 * Documented magic begins here
-	 */
-	cfr = get_cmd640_reg(CFR);
-	cmd640_chip_version = cfr & CFR_DEVREV;
-	if (cmd640_chip_version == 0) {
-		printk ("ide: bad cmd640 revision: %d\n", cmd640_chip_version);
-		return 0;
-	}
-
-	/*
-	 * Initialize data for primary port
-	 */
-	setup_device_ptrs ();
-	printk("%s: buggy cmd640%c interface on %s, config=0x%02x\n",
-	       cmd_hwif0->name, 'a' + cmd640_chip_version - 1, bus_type, cfr);
-	cmd_hwif0->chipset = ide_cmd640;
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
-	cmd_hwif0->tuneproc = &cmd640_tune_drive;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
-
-	/*
-	 * Ensure compatibility by always using the slowest timings
-	 * for access to the drive's command register block,
-	 * and reset the prefetch burstsize to default (512 bytes).
-	 *
-	 * Maybe we need a way to NOT do these on *some* systems?
-	 */
-	put_cmd640_reg(CMDTIM, 0);
-	put_cmd640_reg(BRST, 0x40);
-
-	/*
-	 * Try to enable the secondary interface, if not already enabled
-	 */
-	if (cmd_hwif1->noprobe) {
-		port2 = "not probed";
-	} else {
-		b = get_cmd640_reg(CNTRL);
-		if (secondary_port_responding()) {
-			if ((b & CNTRL_ENA_2ND)) {
-				second_port_cmd640 = 1;
-				port2 = "okay";
-			} else if (cmd640_vlb) {
-				second_port_cmd640 = 1;
-				port2 = "alive";
-			} else
-				port2 = "not cmd640";
-		} else {
-			put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
-			if (secondary_port_responding()) {
-				second_port_cmd640 = 1;
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
-				second_port_toggled = 1;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
-				port2 = "enabled";
-			} else {
-				put_cmd640_reg(CNTRL, b); /* restore original setting */
-				port2 = "not responding";
-			}
-		}
-	}
-
-	/*
-	 * Initialize data for secondary cmd640 port, if enabled
-	 */
-	if (second_port_cmd640) {
-		cmd_hwif0->serialized = 1;
-		cmd_hwif1->serialized = 1;
-		cmd_hwif1->chipset = ide_cmd640;
-		cmd_hwif0->mate = cmd_hwif1;
-		cmd_hwif1->mate = cmd_hwif0;
-		cmd_hwif1->channel = 1;
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
-		cmd_hwif1->tuneproc = &cmd640_tune_drive;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
-	}
-	printk("%s: %sserialized, secondary interface %s\n", cmd_hwif1->name,
-		cmd_hwif0->serialized ? "" : "not ", port2);
-
-	/*
-	 * Establish initial timings/prefetch for all drives.
-	 * Do not unnecessarily disturb any prior BIOS setup of these.
-	 */
-	for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) {
-		ide_drive_t *drive = cmd_drives[index];
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
-		if (drive->autotune || ((index > 1) && second_port_toggled)) {
-	 		/*
-	 		 * Reset timing to the slowest speed and turn off prefetch.
-			 * This way, the drive identify code has a better chance.
-			 */
-			setup_counts    [index] = 4;	/* max possible */
-			active_counts   [index] = 16;	/* max possible */
-			recovery_counts [index] = 16;	/* max possible */
-			program_drive_counts (index);
-			set_prefetch_mode (index, 0);
-			printk("cmd640: drive%d timings/prefetch cleared\n", index);
-		} else {
-			/*
-			 * Record timings/prefetch without changing them.
-			 * This preserves any prior BIOS setup.
-			 */
-			retrieve_drive_counts (index);
-			check_prefetch (index);
-			printk("cmd640: drive%d timings/prefetch(%s) preserved",
-				index, drive->no_io_32bit ? "off" : "on");
-			display_clocks(index);
-		}
-#else
-		/*
-		 * Set the drive unmask flags to match the prefetch setting
-		 */
-		check_prefetch (index);
-		printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
-			index, drive->no_io_32bit ? "off" : "on");
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
-	}
-
-#ifdef CMD640_DUMP_REGS
-	CMD640_DUMP_REGS;
-#endif
-	return 1;
-}
-
diff -u --recursive --new-file v2.3.51/linux/drivers/block/cmd64x.c linux/drivers/block/cmd64x.c
--- v2.3.51/linux/drivers/block/cmd64x.c	Fri Mar 10 16:40:41 2000
+++ linux/drivers/block/cmd64x.c	Wed Dec 31 16:00:00 1969
@@ -1,721 +0,0 @@
-/* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16
- *
- * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
- *           Note, this driver is not used at all on other systems because
- *           there the "BIOS" has done all of the following already.
- *           Due to massive hardware bugs, UltraDMA is only supported
- *           on the 646U2 and not on the 646U.
- *
- * Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be)
- * Copyright (C) 1998       David S. Miller (davem@redhat.com)
- * Copyright (C) 1999-2000  Andre Hedrick (andre@suse.com)
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include "ide_modes.h"
-
-#ifndef SPLIT_BYTE
-#define SPLIT_BYTE(B,H,L)	((H)=(B>>4), (L)=(B-((B>>4)<<4)))
-#endif
-
-#define CMD_DEBUG 0
-
-#if CMD_DEBUG
-#define cmdprintk(x...)	printk(##x)
-#else
-#define cmdprintk(x...)
-#endif
-
-/*
- * CMD64x specific registers definition.
- */
-
-#define CNTRL		0x51
-#define	  CNTRL_DIS_RA0		0x40
-#define   CNTRL_DIS_RA1		0x80
-#define	  CNTRL_ENA_2ND		0x08
-
-#define	CMDTIM		0x52
-#define	ARTTIM0		0x53
-#define	DRWTIM0		0x54
-#define ARTTIM1 	0x55
-#define DRWTIM1		0x56
-#define ARTTIM23	0x57
-#define   ARTTIM23_DIS_RA2	0x04
-#define   ARTTIM23_DIS_RA3	0x08
-#define ARTTIM2		0x57
-#define ARTTIM3		0x57
-#define DRWTIM23	0x58
-#define DRWTIM2		0x58
-#define BRST		0x59
-#define DRWTIM3		0x5b
-
-#define BMIDECR0	0x70
-#define MRDMODE		0x71
-#define BMIDESR0	0x72
-#define UDIDETCR0	0x73
-#define DTPR0		0x74
-#define BMIDECR1	0x78
-#define BMIDECSR	0x79
-#define BMIDESR1	0x7A
-#define UDIDETCR1	0x7B
-#define DTPR1		0x7C
-
-#undef DISPLAY_CMD64X_TIMINGS
-
-#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int cmd64x_get_info(char *, char **, off_t, int);
-extern int (*cmd64x_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-extern char *ide_media_verbose(ide_drive_t *);
-static struct pci_dev *bmide_dev;
-
-static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-	u8 reg53 = 0, reg54 = 0, reg55 = 0, reg56 = 0;	/* primary */
-	u8 reg57 = 0, reg58 = 0, reg5b;			/* secondary */
-	u8 reg72 = 0, reg73 = 0;			/* primary */
-	u8 reg7a = 0, reg7b = 0;			/* secondary */
-	u8 hi_byte = 0, lo_byte = 0;
-
-	switch(bmide_dev->device) {
-		case PCI_DEVICE_ID_CMD_648:
-			p += sprintf(p, "\n                                CMD648 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_CMD_646:
-			p += sprintf(p, "\n                                CMD646 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_CMD_643:
-			p += sprintf(p, "\n                                CMD643 Chipset.\n");
-			break;
-		default:
-			p += sprintf(p, "\n                                CMD64? Chipse.\n");
-			break;
-	}
-	(void) pci_read_config_byte(bmide_dev, ARTTIM0,   &reg53);
-	(void) pci_read_config_byte(bmide_dev, DRWTIM0,   &reg54);
-	(void) pci_read_config_byte(bmide_dev, ARTTIM1,   &reg55);
-	(void) pci_read_config_byte(bmide_dev, DRWTIM1,   &reg56);
-	(void) pci_read_config_byte(bmide_dev, ARTTIM2,   &reg57);
-	(void) pci_read_config_byte(bmide_dev, DRWTIM2,   &reg58);
-	(void) pci_read_config_byte(bmide_dev, DRWTIM3,   &reg5b);
-	(void) pci_read_config_byte(bmide_dev, BMIDESR0,  &reg72);
-	(void) pci_read_config_byte(bmide_dev, UDIDETCR0, &reg73);
-	(void) pci_read_config_byte(bmide_dev, BMIDESR1,  &reg7a);
-	(void) pci_read_config_byte(bmide_dev, UDIDETCR1, &reg7b);
-
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %sabled                         %sabled\n",
-			(reg72&0x80) ? "dis" : " en", (reg7a&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(reg72&0x20) ? "yes" : "no ", (reg72&0x40) ? "yes" : "no ", (reg7a&0x20) ? "yes" : "no ", (reg7a&0x40) ? "yes" : "no " );
-	p += sprintf(p, "UDMA enabled:   %s              %s             %s               %s\n",
-			(reg73&0x01) ? "yes" : "no ", (reg73&0x02) ? "yes" : "no ", (reg7b&0x01) ? "yes" : "no ", (reg7b&0x02) ? "yes" : "no " );
-	p += sprintf(p, "UDMA enabled:   %s                %s               %s                 %s\n",
-			(reg73&0x15) ? "4" : (reg73&0x25) ? "3" : (reg73&0x11) ? "2" : (reg73&0x21) ? "1" : (reg73&0x31) ? "0" : "X",
-			(reg73&0x4A) ? "4" : (reg73&0x8A) ? "3" : (reg73&0x42) ? "2" : (reg73&0x82) ? "1" : (reg73&0xC2) ? "0" : "X",
-			(reg7b&0x15) ? "4" : (reg7b&0x25) ? "3" : (reg7b&0x11) ? "2" : (reg7b&0x21) ? "1" : (reg7b&0x31) ? "0" : "X",
-			(reg7b&0x4A) ? "4" : (reg7b&0x8A) ? "3" : (reg7b&0x42) ? "2" : (reg7b&0x82) ? "1" : (reg7b&0xC2) ? "0" : "X" );
-	p += sprintf(p, "DMA enabled:    %s                %s               %s                 %s\n",
-			(reg73&0x10) ? "2" : (reg73&0x20) ? "1" : (reg73&0x30) ? "0" : "X",
-			(reg73&0x40) ? "2" : (reg73&0x80) ? "1" : (reg73&0xC0) ? "0" : "X",
-			(reg7b&0x10) ? "2" : (reg7b&0x20) ? "1" : (reg7b&0x30) ? "0" : "X",
-			(reg7b&0x40) ? "2" : (reg7b&0x80) ? "1" : (reg7b&0xC0) ? "0" : "X" );
-	p += sprintf(p, "PIO\n");
-
-	SPLIT_BYTE(reg53, hi_byte, lo_byte);
-	p += sprintf(p, "ARTTIM0   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg53, hi_byte, lo_byte);
-	SPLIT_BYTE(reg54, hi_byte, lo_byte);
-	p += sprintf(p, "DRWTIM0   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg54, hi_byte, lo_byte);
-	SPLIT_BYTE(reg55, hi_byte, lo_byte);
-	p += sprintf(p, "ARTTIM1   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg55, hi_byte, lo_byte);
-	SPLIT_BYTE(reg56, hi_byte, lo_byte);
-	p += sprintf(p, "DRWTIM1   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg56, hi_byte, lo_byte);
-	SPLIT_BYTE(reg57, hi_byte, lo_byte);
-	p += sprintf(p, "ARTTIM23  = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg57, hi_byte, lo_byte);
-	SPLIT_BYTE(reg58, hi_byte, lo_byte);
-	p += sprintf(p, "DRWTIM2   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg58, hi_byte, lo_byte);
-	SPLIT_BYTE(reg5b, hi_byte, lo_byte);
-	p += sprintf(p, "DRWTIM3   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg5b, hi_byte, lo_byte);
-	SPLIT_BYTE(reg73, hi_byte, lo_byte);
-	p += sprintf(p, "UDIDETCR0 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg73, hi_byte, lo_byte);
-	SPLIT_BYTE(reg7b, hi_byte, lo_byte);
-	p += sprintf(p, "UDIDETCR1 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg7b, hi_byte, lo_byte);
-
-	return p-buffer;	/* => must be less than 4k! */
-}
-#endif	/* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-byte cmd64x_proc = 0;
-
-/*
- * Registers and masks for easy access by drive index:
- */
-#if 0
-static byte prefetch_regs[4]  = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
-static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
-#endif
-
-/*
- * This routine writes the prepared setup/active/recovery counts
- * for a drive into the cmd646 chipset registers to active them.
- */
-static void program_drive_counts (ide_drive_t *drive, int setup_count, int active_count, int recovery_count)
-{
-	unsigned long flags;
-	ide_drive_t *drives = HWIF(drive)->drives;
-	byte temp_b;
-	static const byte setup_counts[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
-	static const byte recovery_counts[] =
-		{15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
-	static const byte arttim_regs[2][2] = {
-			{ ARTTIM0, ARTTIM1 },
-			{ ARTTIM23, ARTTIM23 }
-		};
-	static const byte drwtim_regs[2][2] = {
-			{ DRWTIM0, DRWTIM1 },
-			{ DRWTIM2, DRWTIM3 }
-		};
-	int channel = (int) HWIF(drive)->channel;
-	int slave = (drives != drive);  /* Is this really the best way to determine this?? */
-
-	cmdprintk("program_drive_count parameters = s(%d),a(%d),r(%d),p(%d)\n", setup_count,
-		active_count, recovery_count, drive->present);
-	/*
-	 * Set up address setup count registers.
-	 * Primary interface has individual count/timing registers for
-	 * each drive.  Secondary interface has one common set of registers,
-	 * for address setup so we merge these timings, using the slowest
-	 * value.
-	 */
-	if (channel) {
-		drive->drive_data = setup_count;
-		setup_count = IDE_MAX(drives[0].drive_data, drives[1].drive_data);
-		cmdprintk("Secondary interface, setup_count = %d\n", setup_count);
-	}
-
-	/*
-	 * Convert values to internal chipset representation
-	 */
-	setup_count = (setup_count > 5) ? 0xc0 : (int) setup_counts[setup_count];
-	active_count &= 0xf; /* Remember, max value is 16 */
-	recovery_count = (int) recovery_counts[recovery_count];
-
-	cmdprintk("Final values = %d,%d,%d\n", setup_count, active_count, recovery_count);
-
-	/*
-	 * Now that everything is ready, program the new timings
-	 */
-	__save_flags (flags);
-	__cli();
-	/*
-	 * Program the address_setup clocks into ARTTIM reg,
-	 * and then the active/recovery counts into the DRWTIM reg
-	 */
-	(void) pci_read_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave], &temp_b);
-	(void) pci_write_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave],
-		((byte) setup_count) | (temp_b & 0x3f));
-	(void) pci_write_config_byte(HWIF(drive)->pci_dev, drwtim_regs[channel][slave],
-		(byte) ((active_count << 4) | recovery_count));
-	cmdprintk ("Write %x to %x\n", ((byte) setup_count) | (temp_b & 0x3f), arttim_regs[channel][slave]);
-	cmdprintk ("Write %x to %x\n", (byte) ((active_count << 4) | recovery_count), drwtim_regs[channel][slave]);
-	__restore_flags(flags);
-}
-
-/*
- * Attempts to set the interface PIO mode.
- * The preferred method of selecting PIO modes (e.g. mode 4) is 
- * "echo 'piomode:4' > /proc/ide/hdx/settings".  Special cases are
- * 8: prefetch off, 9: prefetch on, 255: auto-select best mode.
- * Called with 255 at boot time.
- */
-static void cmd64x_tuneproc (ide_drive_t *drive, byte mode_wanted)
-{
-	int setup_time, active_time, recovery_time, clock_time, pio_mode, cycle_time;
-	byte recovery_count2, cycle_count;
-	int setup_count, active_count, recovery_count;
-	int bus_speed = ide_system_bus_speed();
-	/*byte b;*/
-	ide_pio_data_t  d;
-
-	switch (mode_wanted) {
-		case 8: /* set prefetch off */
-		case 9: /* set prefetch on */
-			mode_wanted &= 1;
-			/*set_prefetch_mode(index, mode_wanted);*/
-			cmdprintk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
-			return;
-	}
-
-	mode_wanted = ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
-	pio_mode = d.pio_mode;
-	cycle_time = d.cycle_time;
-
-	/*
-	 * I copied all this complicated stuff from cmd640.c and made a few minor changes.
-	 * For now I am just going to pray that it is correct.
-	 */
-	if (pio_mode > 5)
-		pio_mode = 5;
-	setup_time  = ide_pio_timings[pio_mode].setup_time;
-	active_time = ide_pio_timings[pio_mode].active_time;
-	recovery_time = cycle_time - (setup_time + active_time);
-	clock_time = 1000 / bus_speed;
-	cycle_count = (cycle_time + clock_time - 1) / clock_time;
-
-	setup_count = (setup_time + clock_time - 1) / clock_time;
-
-	active_count = (active_time + clock_time - 1) / clock_time;
-
-	recovery_count = (recovery_time + clock_time - 1) / clock_time;
-	recovery_count2 = cycle_count - (setup_count + active_count);
-	if (recovery_count2 > recovery_count)
-		recovery_count = recovery_count2;
-	if (recovery_count > 16) {
-		active_count += recovery_count - 16;
-		recovery_count = 16;
-	}
-	if (active_count > 16)
-		active_count = 16; /* maximum allowed by cmd646 */
-
-	/*
-	 * In a perfect world, we might set the drive pio mode here
-	 * (using WIN_SETFEATURE) before continuing.
-	 *
-	 * But we do not, because:
-	 *	1) this is the wrong place to do it (proper is do_special() in ide.c)
-	 * 	2) in practice this is rarely, if ever, necessary
-	 */
-	program_drive_counts (drive, setup_count, active_count, recovery_count);
-
-	cmdprintk("%s: selected cmd646 PIO mode%d : %d (%dns)%s, clocks=%d/%d/%d\n",
-		drive->name, pio_mode, mode_wanted, cycle_time,
-		d.overridden ? " (overriding vendor mode)" : "",
-		setup_count, active_count, recovery_count);
-}
-
-static int tune_chipset_for_dma (ide_drive_t *drive, byte speed)
-{
-#if 0
-	struct hd_driveid *id	= drive->id;
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned long dma_base	= hwif->dma_base;
-	byte unit		= (drive->select.b.unit & 0x01);
-
-	u8 reg72 = 0, reg73 = 0;	/* primary */
-	u8 reg7a = 0, reg7b = 0;	/* secondary */
-	u8 pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0;
-	u8 pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0;
-	u8 regU = (hwif->channel) ? 2 : 0;
-	u8 regD = (hwif->channel) ? 2 : 0;
-
-	(void) pci_read_config_byte(dev, BMIDESR0,  &reg72);
-	(void) pci_read_config_byte(dev, UDIDETCR0, &reg73);
-	(void) pci_read_config_byte(dev, BMIDESR1,  &reg7a);
-	(void) pci_read_config_byte(dev, UDIDETCR1, &reg7b);
-
-	switch(speed) {
-		case XFER_UDMA_4:
-			pciU = unit ? 0x4A : 0x15;
-		case XFER_UDMA_3:
-			pciU = unit ? 0x8A : 0x25;
-		case XFER_UDMA_2:
-			pciU = unit ? 0x42 : 0x11;
-		case XFER_UDMA_1:
-			pciU = unit ? 0x82 : 0x21;
-		case XFER_UDMA_0:
-			pciU = unit ? 0xC2 : 0x31
-(reg73&0x15)?"4":(reg73&0x25)?"3":(reg73&0x11)?"2":(reg73&0x21)?"1":(reg73&0x31)?"0":"X",
-(reg73&0x4A)?"4":(reg73&0x8A)?"3":(reg73&0x42)?"2":(reg73&0x82)?"1":(reg73&0xC2)?"0":"X",
-(reg7b&0x15)?"4":(reg7b&0x25)?"3":(reg7b&0x11)?"2":(reg7b&0x21)?"1":(reg7b&0x31)?"0":"X",
-(reg7b&0x4A)?"4":(reg7b&0x8A)?"3":(reg7b&0x42)?"2":(reg7b&0x82)?"1":(reg7b&0xC2)?"0":"X",
-
-		case XFER_MW_DMA_2:
-			pciD = unit ? 0x40 : 0x10;
-		case XFER_MW_DMA_1:
-			pciD = unit ? 0x80 : 0x20;
-		case XFER_MW_DMA_0:
-			pciD = unit ? 0xC0 : 0x30;
-		case XFER_SW_DMA_2:
-		case XFER_SW_DMA_1:
-		case XFER_SW_DMA_0:
-(reg73&0x10)?"2":(reg73&0x20)?"1":(reg73&0x30)?"0":"X",
-(reg73&0x40)?"2":(reg73&0x80)?"1":(reg73&0xC0)?"0":"X",
-(reg7b&0x10)?"2":(reg7b&0x20)?"1":(reg7b&0x30)?"0":"X",
-(reg7b&0x40)?"2":(reg7b&0x80)?"1":(reg7b&0xC0)?"0":"X" );
-
-		default:
-			return 1;
-	}
-
-	(void) ide_config_drive_speed(drive, speed);
-	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-#endif
-	return 0;
-}
-
-static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed)
-{
-	byte speed	= 0x00;
-	byte set_pio	= ide_get_best_pio_mode(drive, 4, 5, NULL);	
-
-	cmd64x_tuneproc(drive, set_pio);
-	speed = XFER_PIO_0 + set_pio;
-	if (set_speed)
-		(void) ide_config_drive_speed(drive, speed);
-}
-
-static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
-{
-	struct hd_driveid *id	= drive->id;
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned long dma_base	= hwif->dma_base;
-
- 	byte unit		= (drive->select.b.unit & 0x01);
-	byte speed		= 0x00;
-	byte set_pio		= 0x00;
-	byte udma_timing_bits	= 0x00;
-	byte udma_33		= ((rev >= 0x05) || (ultra_66)) ? 1 : 0;
-	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
-	/* int drive_number	= ((hwif->channel ? 2 : 0) + unit); */
-	int rval;
-
-	switch(dev->device) {
-		case PCI_DEVICE_ID_CMD_643:
-		case PCI_DEVICE_ID_CMD_646:
-		case PCI_DEVICE_ID_CMD_648:
-		default:
-			break;
-	}
-
-	if (drive->media != ide_disk) {
-		cmdprintk("CMD64X: drive->media != ide_disk at double check, inital check failed!!\n");
-		return ((int) ide_dma_off);
-	}
-
-	/* UltraDMA only supported on PCI646U and PCI646U2,
-	 * which correspond to revisions 0x03, 0x05 and 0x07 respectively.
-	 * Actually, although the CMD tech support people won't
-	 * tell me the details, the 0x03 revision cannot support
-	 * UDMA correctly without hardware modifications, and even
-	 * then it only works with Quantum disks due to some
-	 * hold time assumptions in the 646U part which are fixed
-	 * in the 646U2.
-	 * So we only do UltraDMA on revision 0x05 and 0x07 chipsets.
-	 */
-
-	if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
-		speed = XFER_UDMA_4;
-		udma_timing_bits = 0x10;	/* 2 clock */
-	} else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
-		speed = XFER_UDMA_3;
-		udma_timing_bits = 0x20;	/* 3 clock */
-	} else if ((id->dma_ultra & 0x0004) && (udma_33)) {
-		speed = XFER_UDMA_2;
-		udma_timing_bits = 0x10;	/* 2 clock */
-	} else if ((id->dma_ultra & 0x0002) && (udma_33)) {
-		speed = XFER_UDMA_1;
-		udma_timing_bits = 0x20;	/* 3 clock */
-	} else if ((id->dma_ultra & 0x0001) && (udma_33)) {
-		speed = XFER_UDMA_0;
-		udma_timing_bits = 0x30;	/* 4 clock */
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-	} else if (id->dma_1word & 0x0004) {
-		speed = XFER_SW_DMA_2;
-	} else if (id->dma_1word & 0x0002) {
-		speed = XFER_SW_DMA_1;
-	} else if (id->dma_1word & 0x0001) {
-		speed = XFER_SW_DMA_0;
-	} else {
-		set_pio = 1;
-	}
-
-	config_chipset_for_pio(drive, set_pio);
-
-	if (set_pio)
-		return ((int) ide_dma_off_quietly);
-
-#if 1
-	/*
-	 * This the alternate access method. :-(
-	 * The correct method is to directly setup the pci-config space.
-	 */ 
-	(void) ide_config_drive_speed(drive, speed);
-	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-
-	if (speed >= XFER_UDMA_0) {
-		byte udma_ctrl = inb(dma_base + 3);
-		/* Put this channel into UDMA mode. */
-		udma_ctrl |= (1 << unit);
-		udma_ctrl &= ~(0x04 << unit);
-		if (udma_66)
-			udma_ctrl |= (0x04 << unit);
-		udma_ctrl &= ~(0x30 << (unit * 2));
-		udma_ctrl |=  (udma_timing_bits << (unit * 2));
-		outb(udma_ctrl, dma_base+3);
-	}
-#endif
-
-	if (tune_chipset_for_dma(drive, speed))
-		return ((int) ide_dma_off);
-
-	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-
-	return rval;
-}
-
-static int cmd64x_config_drive_for_dma (ide_drive_t *drive)
-{
-	struct hd_driveid *id	= drive->id;
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned int class_rev	= 0;
-	byte can_ultra_33	= 0;
-	byte can_ultra_66	= 0;
-	ide_dma_action_t dma_func = ide_dma_on;
-
-	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
-	class_rev &= 0xff;	
-
-	switch(dev->device) {
-		case PCI_DEVICE_ID_CMD_643:
-			can_ultra_33 = 1;
-			can_ultra_66 = 0;
-			break;
-		case PCI_DEVICE_ID_CMD_646:
-			can_ultra_33 = (class_rev >= 0x05) ? 1 : 0;
-			can_ultra_66 = 0;
-			break;
-		case PCI_DEVICE_ID_CMD_648:
-			can_ultra_33 = 1;
-			can_ultra_66 = 1;
-			break;
-		default:
-			return hwif->dmaproc(ide_dma_off, drive);
-	}
-
-	if ((id != NULL) && ((id->capability & 1) != 0) &&
-	    hwif->autodma && (drive->media == ide_disk)) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
-			goto fast_ata_pio;
-		}
-		dma_func = ide_dma_off_quietly;
-		if ((id->field_valid & 4) && (can_ultra_33)) {
-			if (id->dma_ultra & 0x001F) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, class_rev, can_ultra_66);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & 0x0007) ||
-			    (id->dma_1word & 0x0007)) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, class_rev, 0);
-				if (dma_func != ide_dma_on)
-					goto no_dma_set;
-			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			if (id->eide_dma_time > 150) {
-				goto no_dma_set;
-			}
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, class_rev, 0);
-			if (dma_func != ide_dma_on)
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
-no_dma_set:
-		config_chipset_for_pio(drive, 1);
-	}
-	return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-static int cmd64x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch (func) {
-		case ide_dma_check:
-			return cmd64x_config_drive_for_dma(drive);
-		default:
-			break;
-	}
-	/* Other cases are done by generic IDE-DMA code. */
-	return ide_dmaproc(func, drive);
-}
-
-/*
- * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old
- * event order for DMA transfers.
- */
-static int cmd646_1_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	unsigned long dma_base = hwif->dma_base;
-	byte dma_stat;
-
-	if (func == ide_dma_end) {
-		drive->waiting_for_dma = 0;
-		dma_stat = inb(dma_base+2);		/* get DMA status */
-		outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-		outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
-		ide_destroy_dmatable(drive);		/* and free any DMA resources */
-		return (dma_stat & 7) != 4;		/* verify good DMA status */
-	}
-
-	/* Other cases are done by generic IDE-DMA code. */
-	return cmd64x_dmaproc(func, drive);
-}
-
-unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name)
-{
-	unsigned char mrdmode;
-	unsigned int class_rev;
-
-	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
-	class_rev &= 0xff;
-
-#if 0
-	if (dev->resource[PCI_ROM_RESOURCE].start)
-		pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
-#endif
-
-	switch(dev->device) {
-		case PCI_DEVICE_ID_CMD_643:
-			break;
-		case PCI_DEVICE_ID_CMD_646:
-			printk("%s: chipset revision 0x%02X, ", name, class_rev);
-			switch(class_rev) {
-				case 0x07:
-				case 0x05:
-					printk("UltraDMA Capable");
-					break;
-				case 0x03:
-					printk("MultiWord DMA Force Limited");
-					break;
-				case 0x01:
-				default:
-					printk("MultiWord DMA Limited, IRQ workaround enabled");
-					break;
-				}
-			printk("\n");
-                        break;
-		case PCI_DEVICE_ID_CMD_648:
-			break;
-		default:
-			break;
-	}
-
-	/* Set a good latency timer and cache line size value. */
-	(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
-#ifdef __sparc_v9__
-	(void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
-#endif
-
-	/* Setup interrupts. */
-	(void) pci_read_config_byte(dev, MRDMODE, &mrdmode);
-	mrdmode &= ~(0x30);
-	(void) pci_write_config_byte(dev, MRDMODE, mrdmode);
-
-	/* Use MEMORY READ LINE for reads.
-	 * NOTE: Although not mentioned in the PCI0646U specs,
-	 *       these bits are write only and won't be read
-	 *       back as set or not.  The PCI0646U2 specs clarify
-	 *       this point.
-	 */
-	(void) pci_write_config_byte(dev, MRDMODE, mrdmode | 0x02);
-
-	/* Set reasonable active/recovery/address-setup values. */
-	(void) pci_write_config_byte(dev, ARTTIM0,  0x40);
-	(void) pci_write_config_byte(dev, DRWTIM0,  0x3f);
-	(void) pci_write_config_byte(dev, ARTTIM1,  0x40);
-	(void) pci_write_config_byte(dev, DRWTIM1,  0x3f);
-#ifdef __i386__
-	(void) pci_write_config_byte(dev, ARTTIM23, 0x1c);
-#else
-	(void) pci_write_config_byte(dev, ARTTIM23, 0x5c);
-#endif
-	(void) pci_write_config_byte(dev, DRWTIM23, 0x3f);
-	(void) pci_write_config_byte(dev, DRWTIM3,  0x3f);
-
-#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
-	cmd64x_proc = 1;
-	bmide_dev = dev;
-	cmd64x_display_info = &cmd64x_get_info;
-#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */
-
-	return 0;
-}
-
-unsigned int __init ata66_cmd64x (ide_hwif_t *hwif)
-{
-	byte ata66 = 0;
-	byte mask = (hwif->channel) ? 0x02 : 0x01;
-
-	pci_read_config_byte(hwif->pci_dev, BMIDECSR, &ata66);
-	return (ata66 & mask) ? 1 : 0;
-}
-
-void __init ide_init_cmd64x (ide_hwif_t *hwif)
-{
-	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned int class_rev;
-
-	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
-	class_rev &= 0xff;
-
-	hwif->tuneproc = &cmd64x_tuneproc;
-	hwif->drives[0].autotune = 1;
-	hwif->drives[0].autotune = 1;
-
-	if (!hwif->dma_base)
-		return;
-
-	switch(dev->device) {
-		case PCI_DEVICE_ID_CMD_643:
-			hwif->dmaproc = &cmd64x_dmaproc;
-			break;
-		case PCI_DEVICE_ID_CMD_646:
-			hwif->chipset = ide_cmd646;
-			if (class_rev == 0x01) {
-				hwif->dmaproc = &cmd646_1_dmaproc;
-			} else {
-				hwif->dmaproc = &cmd64x_dmaproc;
-			}
-			break;
-		case PCI_DEVICE_ID_CMD_648:
-			hwif->dmaproc = &cmd64x_dmaproc;
-			break;
-		default:
-			break;
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/cs5530.c linux/drivers/block/cs5530.c
--- v2.3.51/linux/drivers/block/cs5530.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/block/cs5530.c	Wed Dec 31 16:00:00 1969
@@ -1,361 +0,0 @@
-/*
- * linux/drivers/block/cs5530.c			Version 0.5	Feb 13, 2000
- *
- * Copyright (C) 2000			Mark Lord <mlord@pobox.com>
- * May be copied or modified under the terms of the GNU General Public License
- *
- * Development of this chipset driver was funded
- * by the nice folks at National Semiconductor.
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include "ide_modes.h"
-
-#define DISPLAY_CS5530_TIMINGS
-
-#if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int cs5530_get_info(char *, char **, off_t, int);
-extern int (*cs5530_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-extern char *ide_media_verbose(ide_drive_t *);
-static struct pci_dev *bmide_dev;
-
-static int cs5530_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-	u32 bibma = bmide_dev->resource[4].start;
-	u8  c0 = 0, c1 = 0;
-
-	/*
-	 * at that point bibma+0x2 et bibma+0xa are byte registers
-	 * to investigate:
-	 */
-
-	c0 = inb_p((unsigned short)bibma + 0x02);
-	c1 = inb_p((unsigned short)bibma + 0x0a);
-
-	p += sprintf(p, "\n                                Cyrix 5530 Chipset.\n");
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %sabled                         %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
-	return p-buffer;
-}
-#endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */
-
-byte cs5530_proc = 0;
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-/*
- * Set a new transfer mode at the drive
- */
-int cs5530_set_xfer_mode (ide_drive_t *drive, byte mode)
-{
-	int error = 0;
-
-	printk("%s: cs5530_set_xfer_mode(%s)\n", drive->name, ide_xfer_verbose(mode));
-	error = ide_config_drive_speed(drive, mode);
-
-	return error;
-}
-
-/*
- * Here are the standard PIO mode 0-4 timings for each "format".
- * Format-0 uses fast data reg timings, with slower command reg timings.
- * Format-1 uses fast timings for all registers, but won't work with all drives.
- */
-static unsigned int cs5530_pio_timings[2][5] =
-	{{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010},
-	 {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}};
-
-/*
- * After chip reset, the PIO timings are set to 0x0000e132, which is not valid.
- */
-#define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132)
-#define CS5530_BASEREG(hwif)	(((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20))
-
-/*
- * cs5530_tuneproc() handles selection/setting of PIO modes
- * for both the chipset and drive.
- *
- * The ide_init_cs5530() routine guarantees that all drives
- * will have valid default PIO timings set up before we get here.
- */
-static void cs5530_tuneproc (ide_drive_t *drive, byte pio)	/* pio=255 means "autotune" */
-{
-	ide_hwif_t	*hwif = HWIF(drive);
-	unsigned int	format, basereg = CS5530_BASEREG(hwif);
-	static byte	modes[5] = {XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4};
-
-	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
-	if (!cs5530_set_xfer_mode(drive, modes[pio])) {
-		format = (inl(basereg+4) >> 31) & 1;
-		outl(cs5530_pio_timings[format][pio], basereg+(drive->select.b.unit<<3));
-	}
-}
-
-/*
- * cs5530_config_dma() handles selection/setting of DMA/UDMA modes
- * for both the chipset and drive.
- */
-static int cs5530_config_dma (ide_drive_t *drive)
-{
-	int			udma_ok = 1, mode = 0;
-	ide_hwif_t		*hwif = HWIF(drive);
-	int			unit = drive->select.b.unit;
-	ide_drive_t		*mate = &hwif->drives[unit^1];
-	struct hd_driveid	*id = drive->id;
-	unsigned int		basereg, reg, timings;
-
-
-	/*
-	 * Default to DMA-off in case we run into trouble here.
-	 */
-	(void)hwif->dmaproc(ide_dma_off_quietly, drive);	/* turn off DMA while we fiddle */
-	outb(inb(hwif->dma_base+2)&~(unit?0x40:0x20), hwif->dma_base+2); /* clear DMA_capable bit */
-
-	/*
-	 * The CS5530 specifies that two drives sharing a cable cannot
-	 * mix UDMA/MDMA.  It has to be one or the other, for the pair,
-	 * though different timings can still be chosen for each drive.
-	 * We could set the appropriate timing bits on the fly,
-	 * but that might be a bit confusing.  So, for now we statically
-	 * handle this requirement by looking at our mate drive to see
-	 * what it is capable of, before choosing a mode for our own drive.
-	 */
-	if (mate->present) {
-		struct hd_driveid *mateid = mate->id;
-		if (mateid && (mateid->capability & 1) && !hwif->dmaproc(ide_dma_bad_drive, mate)) {
-			if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7))
-				udma_ok = 1;
-			else if ((mateid->field_valid & 2) && (mateid->dma_mword & 7))
-				udma_ok = 0;
-			else
-				udma_ok = 1;
-		}
-	}
-
-	/*
-	 * Now see what the current drive is capable of,
-	 * selecting UDMA only if the mate said it was ok.
-	 */
-	if (id && (id->capability & 1) && hwif->autodma && !hwif->dmaproc(ide_dma_bad_drive, drive)) {
-		if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) {
-			if      (id->dma_ultra & 4)
-				mode = XFER_UDMA_2;
-			else if (id->dma_ultra & 2)
-				mode = XFER_UDMA_1;
-			else if (id->dma_ultra & 1)
-				mode = XFER_UDMA_0;
-		}
-		if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) {
-			if      (id->dma_mword & 4)
-				mode = XFER_MW_DMA_2;
-			else if (id->dma_mword & 2)
-				mode = XFER_MW_DMA_1;
-			else if (id->dma_mword & 1)
-				mode = XFER_MW_DMA_0;
-		}
-	}
-
-	/*
-	 * Tell the drive to switch to the new mode; abort on failure.
-	 */
-	if (!mode || cs5530_set_xfer_mode(drive, mode))
-		return 1;	/* failure */
-
-	/*
-	 * Now tune the chipset to match the drive:
-	 */
-	switch (mode) {
-		case XFER_UDMA_0:	timings = 0x00921250; break;
-		case XFER_UDMA_1:	timings = 0x00911140; break;
-		case XFER_UDMA_2:	timings = 0x00911030; break;
-		case XFER_MW_DMA_0:	timings = 0x00077771; break;
-		case XFER_MW_DMA_1:	timings = 0x00012121; break;
-		case XFER_MW_DMA_2:	timings = 0x00002020; break;
-		default:
-			printk("%s: cs5530_config_dma: huh? mode=%02x\n", drive->name, mode);
-			return 1;	/* failure */
-	}
-	basereg = CS5530_BASEREG(hwif);
-	reg = inl(basereg+4);			/* get drive0 config register */
-	timings |= reg & 0x80000000;		/* preserve PIO format bit */
-	if (unit == 0) {			/* are we configuring drive0? */
-		outl(timings, basereg+4);	/* write drive0 config register */
-	} else {
-		if (timings & 0x00100000)
-			reg |=  0x00100000;	/* enable UDMA timings for both drives */
-		else
-			reg &= ~0x00100000;	/* disable UDMA timings for both drives */
-		outl(reg,     basereg+4);	/* write drive0 config register */
-		outl(timings, basereg+12);	/* write drive1 config register */
-	}
-	outb(inb(hwif->dma_base+2)|(unit?0x40:0x20), hwif->dma_base+2);	/* set DMA_capable bit */
-
-	/*
-	 * Finally, turn DMA on in software, and exit.
-	 */
-	return hwif->dmaproc(ide_dma_on, drive);	/* success */
-}
-
-/*
- * This is a CS5530-specific wrapper for the standard ide_dmaproc().
- * We need it for our custom "ide_dma_check" function.
- * All other requests are forwarded to the standard ide_dmaproc().
- */
-int cs5530_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch (func) {
-		case ide_dma_check:
-			return cs5530_config_dma(drive);
-		default:
-			break;
-	}
-	/* Other cases are done by generic IDE-DMA code. */
-	return ide_dmaproc(func, drive);
-}
-
-/*
- * Initialize the cs5530 bridge for reliable IDE DMA operation.
- */
-unsigned int __init pci_init_cs5530 (struct pci_dev *dev, const char *name)
-{
-	struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
-	unsigned short pcicmd = 0;
-	unsigned long flags;
-
-	pci_for_each_dev (dev) {
-		if (dev->vendor == PCI_VENDOR_ID_CYRIX) {
-			switch (dev->device) {
-				case PCI_DEVICE_ID_CYRIX_PCI_MASTER:
-					master_0 = dev;
-					break;
-				case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
-					cs5530_0 = dev;
-					break;
-			}
-		}
-	}
-	if (!master_0) {
-		printk("%s: unable to locate PCI MASTER function\n", name);
-		return 0;
-	}
-	if (!cs5530_0) {
-		printk("%s: unable to locate CS5530 LEGACY function\n", name);
-		return 0;
-	}
-
-	save_flags(flags);
-	cli();	/* all CPUs (there should only be one CPU with this chipset) */
-
-	/*
-	 * Enable BusMaster and MemoryWriteAndInvalidate for the cs5530:
-	 * -->  OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530
-	 */
-	pci_read_config_word (cs5530_0, PCI_COMMAND, &pcicmd);
-	pci_write_config_word(cs5530_0, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE);
-
-	/*
-	 * Set PCI CacheLineSize to 16-bytes:
-	 * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530
-	 */
-	pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04);
-
-	/*
-	 * Disable trapping of UDMA register accesses (Win98 hack):
-	 * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530
-	 */
-	pci_write_config_word(cs5530_0, 0xd0, 0x5006);
-
-	/*
-	 * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus:
-	 * The other settings are what is necessary to get the register
-	 * into a sane state for IDE DMA operation.
-	 */
-	pci_write_config_byte(master_0, 0x40, 0x1e);
-
-	/* 
-	 * Set max PCI burst size (16-bytes seems to work best):
-	 *	   16bytes: set bit-1 at 0x41 (reg value of 0x16)
-	 *	all others: clear bit-1 at 0x41, and do:
-	 *	  128bytes: OR 0x00 at 0x41
-	 *	  256bytes: OR 0x04 at 0x41
-	 *	  512bytes: OR 0x08 at 0x41
-	 *	 1024bytes: OR 0x0c at 0x41
-	 */
-	pci_write_config_byte(master_0, 0x41, 0x14);
-
-	/*
-	 * These settings are necessary to get the chip
-	 * into a sane state for IDE DMA operation.
-	 */
-	pci_write_config_byte(master_0, 0x42, 0x00);
-	pci_write_config_byte(master_0, 0x43, 0xc1);
-
-	restore_flags(flags);
-
-#if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS)
-	cs5530_proc = 1;
-	bmide_dev = dev;
-	cs5530_display_info = &cs5530_get_info;
-#endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */
-
-	return 0;
-}
-
-/*
- * This gets invoked by the IDE driver once for each channel,
- * and performs channel-specific pre-initialization before drive probing.
- */
-void __init ide_init_cs5530 (ide_hwif_t *hwif)
-{
-	if (hwif->mate)
-		hwif->serialized = hwif->mate->serialized = 1;
-	if (!hwif->dma_base) {
-		hwif->autodma = 0;
-	} else {
-		unsigned int basereg, d0_timings;
-
-		hwif->dmaproc  = &cs5530_dmaproc;
-		hwif->tuneproc = &cs5530_tuneproc;
-		basereg = CS5530_BASEREG(hwif);
-		d0_timings = inl(basereg+0);
-		if (CS5530_BAD_PIO(d0_timings)) {	/* PIO timings not initialized? */
-			outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+0);
-			if (!hwif->drives[0].autotune)
-				hwif->drives[0].autotune = 1;	/* needs autotuning later */
-		}
-		if (CS5530_BAD_PIO(inl(basereg+8))) {	/* PIO timings not initialized? */
-			outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+8);
-			if (!hwif->drives[1].autotune)
-				hwif->drives[1].autotune = 1;	/* needs autotuning later */
-		}
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/cy82c693.c linux/drivers/block/cy82c693.c
--- v2.3.51/linux/drivers/block/cy82c693.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/cy82c693.c	Wed Dec 31 16:00:00 1969
@@ -1,441 +0,0 @@
-/*
- * linux/drivers/block/cy82c693.c		Version 0.34	Dec. 13, 1999
- *
- *  Copyright (C) 1998-99 Andreas S. Krebs (akrebs@altavista.net), Maintainer
- *  Copyright (C) 1998-99 Andre Hedrick, Integrater
- *
- * CYPRESS CY82C693 chipset IDE controller
- *
- * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards.
- * Writting the driver was quite simple, since most of the job is
- * done by the generic pci-ide support. 
- * The hard part was finding the CY82C693's datasheet on Cypress's
- * web page :-(. But Altavista solved this problem :-).
- *
- *
- * Notes:
- * - I recently got a 16.8G IBM DTTA, so I was able to test it with
- *   a large and fast disk - the results look great, so I'd say the
- *   driver is working fine :-)
- *   hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA 
- * - this is my first linux driver, so there's probably a lot  of room 
- *   for optimizations and bug fixing, so feel free to do it.
- * - use idebus=xx parameter to set PCI bus speed - needed to calc
- *   timings for PIO modes (default will be 40)
- * - if using PIO mode it's a good idea to set the PIO mode and 
- *   32-bit I/O support (if possible), e.g. hdparm -p2 -c1 /dev/hda
- * - I had some problems with my IBM DHEA with PIO modes < 2
- *   (lost interrupts) ?????
- * - first tests with DMA look okay, they seem to work, but there is a
- *   problem with sound - the BusMaster IDE TimeOut should fixed this
- *
- *
- * History:
- * AMH@1999-08-24: v0.34 init_cy82c693_chip moved to pci_init_cy82c693
- * ASK@1999-01-23: v0.33 made a few minor code clean ups
- *                       removed DMA clock speed setting by default
- *                       added boot message
- * ASK@1998-11-01: v0.32 added support to set BusMaster IDE TimeOut
- *                       added support to set DMA Controller Clock Speed
- * ASK@1998-10-31: v0.31 fixed problem with setting to high DMA modes on some drive
- * ASK@1998-10-29: v0.3 added support to set DMA modes
- * ASK@1998-10-28: v0.2 added support to set PIO modes
- * ASK@1998-10-27: v0.1 first version - chipset detection
- *
- */
-
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-/* the current version */
-#define CY82_VERSION	"CY82C693U driver v0.34 99-13-12 Andreas S. Krebs (akrebs@altavista.net)"
-
-/*
- *	The following are used to debug the driver.
- */
-#define	CY82C693_DEBUG_LOGS	0
-#define	CY82C693_DEBUG_INFO	0
-
-/* define CY82C693_SETDMA_CLOCK to set DMA Controller Clock Speed to ATCLK */
-#undef CY82C693_SETDMA_CLOCK
-
-/*
- * note: the value for busmaster timeout is tricky and i got it by trial and error !
- *       using a to low value will cause DMA timeouts and drop IDE performance
- *       using a to high value will cause audio playback to scatter
- *       if you know a better value or how to calc it, please let me know 
- */
-#define BUSMASTER_TIMEOUT	0x50	/* twice the value written in cy82c693ub datasheet */
-/*
- * the value above was tested on my machine and it seems to work okay
- */
-
-/* here are the offset definitions for the registers */
-#define CY82_IDE_CMDREG		0x04
-#define CY82_IDE_ADDRSETUP	0x48
-#define CY82_IDE_MASTER_IOR	0x4C	
-#define CY82_IDE_MASTER_IOW	0x4D	
-#define CY82_IDE_SLAVE_IOR	0x4E	
-#define CY82_IDE_SLAVE_IOW	0x4F
-#define CY82_IDE_MASTER_8BIT	0x50	
-#define CY82_IDE_SLAVE_8BIT	0x51	
-
-#define CY82_INDEX_PORT		0x22
-#define CY82_DATA_PORT		0x23
-
-#define CY82_INDEX_CTRLREG1	0x01
-#define CY82_INDEX_CHANNEL0	0x30
-#define CY82_INDEX_CHANNEL1	0x31
-#define CY82_INDEX_TIMEOUT	0x32
-
-/* the max PIO mode - from datasheet */
-#define CY82C693_MAX_PIO	4
-
-/* the min and max PCI bus speed in MHz - from datasheet */
-#define CY82C963_MIN_BUS_SPEED	25
-#define CY82C963_MAX_BUS_SPEED	33
-
-/* the struct for the PIO mode timings */
-typedef struct pio_clocks_s {
-        byte	address_time;		/* Address setup (clocks) */
-	byte	time_16r;		/* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */
-	byte	time_16w;		/* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */
-	byte	time_8;			/* clocks for 8bit (0xF0=Active/data, 0x0F=Recovery) */
-} pio_clocks_t;
-
-/*
- * calc clocks using bus_speed
- * returns (rounded up) time in bus clocks for time in ns
- */
-static int calc_clk (int time, int bus_speed)
-{
-	int clocks;
-
-	clocks = (time*bus_speed+999)/1000 -1;
-
-	if (clocks < 0)
-		clocks = 0;
-
-	if (clocks > 0x0F)
-		clocks = 0x0F;
-
-	return clocks;
-}
-
-/*
- * compute the values for the clock registers for PIO
- * mode and pci_clk [MHz] speed
- *
- * NOTE: for mode 0,1 and 2 drives 8-bit IDE command control registers are used
- *       for mode 3 and 4 drives 8 and 16-bit timings are the same
- *
- */ 
-static void compute_clocks (byte pio, pio_clocks_t *p_pclk)
-{
-	int clk1, clk2;
-	int bus_speed;
-
-	bus_speed = ide_system_bus_speed();	/* get speed of PCI bus */
-	/* we don't check against CY82C693's min and max speed,
-	 * so you can play with the idebus=xx parameter
-	 */
-
-	if (pio > CY82C693_MAX_PIO)
-		pio = CY82C693_MAX_PIO;
-
-	/* let's calc the address setup time clocks */
-	p_pclk->address_time = (byte)calc_clk(ide_pio_timings[pio].setup_time, bus_speed);
-
-	/* let's calc the active and recovery time clocks */
-	clk1 = calc_clk(ide_pio_timings[pio].active_time, bus_speed);
-
-	/* calc recovery timing */
-	clk2 =	ide_pio_timings[pio].cycle_time -
-		ide_pio_timings[pio].active_time -
-		ide_pio_timings[pio].setup_time;
-
-	clk2 = calc_clk(clk2, bus_speed);
-
-	clk1 = (clk1<<4)|clk2;	/* combine active and recovery clocks */
-
-	/* note: we use the same values for 16bit IOR and IOW
-         *	those are all the same, since I don't have other
-	 *	timings than those from ide_modes.h
-	 */
-
-	p_pclk->time_16r = (byte)clk1;
-	p_pclk->time_16w = (byte)clk1;
-
-	/* what are good values for 8bit ?? */
-	p_pclk->time_8 = (byte)clk1;
-}
-
-/*
- * set DMA mode a specific channel for CY82C693
- */
-static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single)
-{
-        byte index;
-	byte data;
-
-        if (mode>2)	/* make sure we set a valid mode */
-		mode = 2;
-			   
-	if (mode > drive->id->tDMA)  /* to be absolutly sure we have a valid mode */
-		mode = drive->id->tDMA;
-	
-        index = (HWIF(drive)->channel==0) ? CY82_INDEX_CHANNEL0 : CY82_INDEX_CHANNEL1;
-
-#if CY82C693_DEBUG_LOGS
-       	/* for debug let's show the previous values */
-
-	OUT_BYTE(index, CY82_INDEX_PORT);
-	data = IN_BYTE(CY82_DATA_PORT);
-
-	printk (KERN_INFO "%s (ch=%d, dev=%d): DMA mode is %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, (data&0x3), ((data>>2)&1));
-#endif /* CY82C693_DEBUG_LOGS */
-
-	data = (byte)mode|(byte)(single<<2);
-
-	OUT_BYTE(index, CY82_INDEX_PORT);
-	OUT_BYTE(data, CY82_DATA_PORT);
-
-#if CY82C693_DEBUG_INFO
-	printk (KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, mode, single);
-#endif /* CY82C693_DEBUG_INFO */
-
-	/* 
-	 * note: below we set the value for Bus Master IDE TimeOut Register
-	 * I'm not absolutly sure what this does, but it solved my problem
-	 * with IDE DMA and sound, so I now can play sound and work with
-	 * my IDE driver at the same time :-)
-	 *
-	 * If you know the correct (best) value for this register please
-	 * let me know - ASK
-	 */
-
-	data = BUSMASTER_TIMEOUT;
-	OUT_BYTE(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT);
-	OUT_BYTE(data, CY82_DATA_PORT);
-
-#if CY82C693_DEBUG_INFO	
-	printk (KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n", drive->name, data);
-#endif /* CY82C693_DEBUG_INFO */
-}
-
-/* 
- * used to set DMA mode for CY82C693 (single and multi modes)
- */
-static int cy82c693_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
-{
-	/*
-	 * if the function is dma on, set dma mode for drive everything
-	 * else is done by the defaul func
-	 */
-	if (func == ide_dma_on) {
-		struct hd_driveid *id = drive->id;
-
-#if CY82C693_DEBUG_INFO
-		printk (KERN_INFO "dma_on: %s\n", drive->name);
-#endif /* CY82C693_DEBUG_INFO */
-
-		if (id != NULL) {		
-                       /* Enable DMA on any drive that has DMA (multi or single) enabled */
-                       if (id->field_valid & 2) {       /* regular DMA */
-			       int mmode, smode;
-
-			       mmode = id->dma_mword & (id->dma_mword >> 8);
-			       smode = id->dma_1word & (id->dma_1word >> 8);
-			       		      
-		               if (mmode != 0)
-				     cy82c693_dma_enable(drive, (mmode >> 1), 0); /* enable multi */
-			       else if (smode != 0)
-				     cy82c693_dma_enable(drive, (smode >> 1), 1); /* enable single */
-			}
-		}
-	}
-        return ide_dmaproc(func, drive);
-}
-
-/*
- * tune ide drive - set PIO mode
- */
-static void cy82c693_tune_drive (ide_drive_t *drive, byte pio)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	struct pci_dev *dev = hwif->pci_dev;
-	pio_clocks_t pclk;
-	unsigned int addrCtrl;
-
-	/* select primary or secondary channel */
-	if (hwif->index > 0) {  /* drive is on the secondary channel */
-		dev = pci_find_slot(dev->bus->number, dev->devfn+1);
-		if (!dev) {
-			printk(KERN_ERR "%s: tune_drive: Cannot find secondary interface!\n", drive->name);
-			return;
-		}
-	}
-
-#if CY82C693_DEBUG_LOGS
-	/* for debug let's show the register values */
-	
-       	if (drive->select.b.unit == 0) {
-		/*
-		 * get master drive registers               	
-		 * address setup control register
-		 * is 32 bit !!!
-		 */ 
-	  	pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);                
-		addrCtrl &= 0x0F;
-
-		/* now let's get the remaining registers */
-		pci_read_config_byte(dev, CY82_IDE_MASTER_IOR, &pclk.time_16r);
-		pci_read_config_byte(dev, CY82_IDE_MASTER_IOW, &pclk.time_16w);
-		pci_read_config_byte(dev, CY82_IDE_MASTER_8BIT, &pclk.time_8);
-	} else {
-		/*
-		 * set slave drive registers
-		 * address setup control register
-		 * is 32 bit !!!
-		 */ 
-		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
-
-		addrCtrl &= 0xF0;
-		addrCtrl >>= 4;
-
-		/* now let's get the remaining registers */
-		pci_read_config_byte(dev, CY82_IDE_SLAVE_IOR, &pclk.time_16r);
-		pci_read_config_byte(dev, CY82_IDE_SLAVE_IOW, &pclk.time_16w);
-		pci_read_config_byte(dev, CY82_IDE_SLAVE_8BIT, &pclk.time_8);
-	}
-
-	printk (KERN_INFO "%s (ch=%d, dev=%d): PIO timing is (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->channel, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
-#endif /* CY82C693_DEBUG_LOGS */
-
-        /* first let's calc the pio modes */
-	pio = ide_get_best_pio_mode(drive, pio, CY82C693_MAX_PIO, NULL);
-
-#if CY82C693_DEBUG_INFO
-	printk (KERN_INFO "%s: Selected PIO mode %d\n", drive->name, pio);
-#endif /* CY82C693_DEBUG_INFO */
-
-        compute_clocks(pio, &pclk);  /* let's calc the values for this PIO mode */
-
-	/* now let's write  the clocks registers */
-	if (drive->select.b.unit == 0) {
-		/*
-		 * set master drive
-		 * address setup control register
-		 * is 32 bit !!!
-		 */ 
-		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
-		
-		addrCtrl &= (~0xF);
-		addrCtrl |= (unsigned int)pclk.address_time;
-		pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
-
-		/* now let's set the remaining registers */
-		pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
-		pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
-		pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
-		
-		addrCtrl &= 0xF;
-	} else {
-		/*
-		 * set slave drive
-		 * address setup control register
-		 * is 32 bit !!!
-		 */ 
-		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
-
-		addrCtrl &= (~0xF0);
-		addrCtrl |= ((unsigned int)pclk.address_time<<4);
-		pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
-
-		/* now let's set the remaining registers */
-		pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r);
-		pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w);
-		pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8);
-
-		addrCtrl >>= 4;
-		addrCtrl &= 0xF;
-	}	
-
-#if CY82C693_DEBUG_INFO
-	printk (KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->channel, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
-#endif /* CY82C693_DEBUG_INFO */
-}
-
-/*
- * this function is called during init and is used to setup the cy82c693 chip
- */
-/*
- * FIXME! "pci_init_cy82c693" really should replace
- * the "init_cy82c693_chip", it is the correct location to tinker/setup
- * the device prior to INIT.
- */
-
-unsigned int __init pci_init_cy82c693(struct pci_dev *dev, const char *name)
-{
-#ifdef CY82C693_SETDMA_CLOCK
-        byte data;
-#endif /* CY82C693_SETDMA_CLOCK */ 
-
-	/* write info about this verion of the driver */
-	printk (KERN_INFO CY82_VERSION "\n");
-
-#ifdef CY82C693_SETDMA_CLOCK
-       /* okay let's set the DMA clock speed */
-        
-        OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
-        data = IN_BYTE(CY82_DATA_PORT);
-
-#if CY82C693_DEBUG_INFO
-	printk (KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", name, data);
-#endif /* CY82C693_DEBUG_INFO */
-
-        /*
-	 * for some reason sometimes the DMA controller
-	 * speed is set to ATCLK/2 ???? - we fix this here
-	 * 
-	 * note: i don't know what causes this strange behaviour,
-	 *       but even changing the dma speed doesn't solve it :-(
-	 *       the ide performance is still only half the normal speed 
-	 * 
-	 *       if anybody knows what goes wrong with my machine, please
-	 *       let me know - ASK
-         */
-
-	data |= 0x03;
-
-        OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
-        OUT_BYTE(data, CY82_DATA_PORT);
-
-#if CY82C693_DEBUG_INFO
-	printk (KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", name, data);
-#endif /* CY82C693_DEBUG_INFO */
-
-#endif /* CY82C693_SETDMA_CLOCK */
-	return 0;
-}
-
-/*
- * the init function - called for each ide channel once
- */
-void __init ide_init_cy82c693(ide_hwif_t *hwif)
-{
-	hwif->chipset = ide_cy82c693;
-	hwif->tuneproc = &cy82c693_tune_drive;
-	if (hwif->dma_base) {
-		hwif->dmaproc = &cy82c693_dmaproc;
-	} else {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/dtc2278.c linux/drivers/block/dtc2278.c
--- v2.3.51/linux/drivers/block/dtc2278.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/dtc2278.c	Wed Dec 31 16:00:00 1969
@@ -1,133 +0,0 @@
-/*
- *  linux/drivers/block/dtc2278.c		Version 0.02	Feb 10, 1996
- *
- *  Copyright (C) 1996  Linus Torvalds & author (see below)
- */
-
-#undef REALLY_SLOW_IO           /* most systems can safely undef this */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-/*
- * Changing this #undef to #define may solve start up problems in some systems.
- */
-#undef ALWAYS_SET_DTC2278_PIO_MODE
-
-/*
- * From: andy@cercle.cts.com (Dyan Wile)
- *
- * Below is a patch for DTC-2278 - alike software-programmable controllers
- * The code enables the secondary IDE controller and the PIO4 (3?) timings on
- * the primary (EIDE). You may probably have to enable the 32-bit support to
- * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
- * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
- * filesystem  corrupted with -u1, but under heavy disk load only :-)
- *
- * This card is now forced to use the "serialize" feature,
- * and irq-unmasking is disallowed.  If io_32bit is enabled,
- * it must be done for BOTH drives on each interface.
- *
- * This code was written for the DTC2278E, but might work with any of these:
- *
- * DTC2278S has only a single IDE interface.
- * DTC2278D has two IDE interfaces and is otherwise identical to the S version.
- * DTC2278E also has serial ports and a printer port
- * DTC2278EB: has onboard BIOS, and "works like a charm" -- Kent Bradford <kent@theory.caltech.edu>
- *
- * There may be a fourth controller type. The S and D versions use the
- * Winbond chip, and I think the E version does also.
- *
- */
-
-static void sub22 (char b, char c)
-{
-	int i;
-
-	for(i = 0; i < 3; ++i) {
-		inb(0x3f6);
-		outb_p(b,0xb0);
-		inb(0x3f6);
-		outb_p(c,0xb4);
-		inb(0x3f6);
-		if(inb(0xb4) == c) {
-			outb_p(7,0xb0);
-			inb(0x3f6);
-			return;	/* success */
-		}
-	}
-}
-
-static void tune_dtc2278 (ide_drive_t *drive, byte pio)
-{
-	unsigned long flags;
-
-	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
-
-	if (pio >= 3) {
-		save_flags(flags);	/* all CPUs */
-		cli();			/* all CPUs */
-		/*
-		 * This enables PIO mode4 (3?) on the first interface
-		 */
-		sub22(1,0xc3);
-		sub22(0,0xa0);
-		restore_flags(flags);	/* all CPUs */
-	} else {
-		/* we don't know how to set it back again.. */
-	}
-
-	/*
-	 * 32bit I/O has to be enabled for *both* drives at the same time.
-	 */
-	drive->io_32bit = 1;
-	HWIF(drive)->drives[!drive->select.b.unit].io_32bit = 1;
-}
-
-void __init init_dtc2278 (void)
-{
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	/*
-	 * This enables the second interface
-	 */
-	outb_p(4,0xb0);
-	inb(0x3f6);
-	outb_p(0x20,0xb4);
-	inb(0x3f6);
-#ifdef ALWAYS_SET_DTC2278_PIO_MODE
-	/*
-	 * This enables PIO mode4 (3?) on the first interface
-	 * and may solve start-up problems for some people.
-	 */
-	sub22(1,0xc3);
-	sub22(0,0xa0);
-#endif
-	__restore_flags(flags);	/* local CPU only */
-
-	ide_hwifs[0].serialized = 1;
-	ide_hwifs[1].serialized = 1;
-	ide_hwifs[0].chipset = ide_dtc2278;
-	ide_hwifs[1].chipset = ide_dtc2278;
-	ide_hwifs[0].tuneproc = &tune_dtc2278;
-	ide_hwifs[0].drives[0].no_unmask = 1;
-	ide_hwifs[0].drives[1].no_unmask = 1;
-	ide_hwifs[1].drives[0].no_unmask = 1;
-	ide_hwifs[1].drives[1].no_unmask = 1;
-	ide_hwifs[0].mate = &ide_hwifs[1];
-	ide_hwifs[1].mate = &ide_hwifs[0];
-	ide_hwifs[1].channel = 1;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/elevator.c linux/drivers/block/elevator.c
--- v2.3.51/linux/drivers/block/elevator.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/elevator.c	Sun Mar 12 19:32:57 2000
@@ -0,0 +1,150 @@
+/*
+ *  linux/drivers/block/elevator.c
+ *
+ *  Block device elevator/IO-scheduler.
+ *
+ *  Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
+ */
+
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/elevator.h>
+#include <linux/blk.h>
+#include <asm/uaccess.h>
+
+static void elevator_default(struct request * req, elevator_t * elevator,
+			     struct list_head * real_head,
+			     struct list_head * head, int orig_latency)
+{
+	struct list_head * entry = real_head, * point = NULL;
+	struct request * tmp;
+	int sequence = elevator->sequence;
+	int latency = orig_latency -= elevator->nr_segments, pass = 0;
+	int point_latency = 0xbeefbeef;
+
+	while ((entry = entry->prev) != head) {
+		if (!point && latency >= 0) {
+			point = entry;
+			point_latency = latency;
+		}
+		tmp = blkdev_entry_to_request(entry);
+		if (elevator_sequence_before(tmp->elevator_sequence, sequence) || !tmp->q)
+			break;
+		if (latency >= 0) {
+			if (IN_ORDER(tmp, req) ||
+			    (pass && !IN_ORDER(tmp, blkdev_next_request(tmp))))
+				goto link;
+		}
+		latency += tmp->nr_segments;
+		pass = 1;
+	}
+
+	if (point) {
+		entry = point;
+		latency = point_latency;
+	}
+
+ link:
+	list_add(&req->queue, entry);
+	req->elevator_sequence = elevator_sequence(elevator, latency);
+}
+
+#ifdef ELEVATOR_DEBUG
+void elevator_debug(request_queue_t * q, kdev_t dev)
+{
+	int read_pendings = 0, nr_segments = 0;
+	elevator_t * elevator = &q->elevator;
+	struct list_head * entry = &q->queue_head;
+	static int counter;
+
+	if (counter++ % 100)
+		return;
+
+	while ((entry = entry->prev) != &q->queue_head)
+	{
+		struct request * req;
+
+		req = blkdev_entry_to_request(entry);
+		if (req->cmd != READ && req->cmd != WRITE && (req->q || req->nr_segments))
+			printk(KERN_WARNING
+			       "%s: elevator req->cmd %d req->nr_segments %u req->q %p\n",
+			       kdevname(dev), req->cmd, req->nr_segments, req->q);
+		if (!req->q) {
+			if (req->nr_segments)
+				printk(KERN_WARNING
+				       "%s: elevator req->q NULL req->nr_segments %u\n",
+				       kdevname(dev), req->nr_segments);
+			continue;
+		}
+		if (req->cmd == READ)
+			read_pendings++;
+		nr_segments += req->nr_segments;
+	}
+
+	if (read_pendings != elevator->read_pendings)
+	{
+		printk(KERN_WARNING
+		       "%s: elevator read_pendings %d should be %d\n",
+		       kdevname(dev), elevator->read_pendings,
+		       read_pendings);
+		elevator->read_pendings = read_pendings;
+	}
+	if (nr_segments != elevator->nr_segments)
+	{
+		printk(KERN_WARNING
+		       "%s: elevator nr_segments %d should be %d\n",
+		       kdevname(dev), elevator->nr_segments,
+		       nr_segments);
+		elevator->nr_segments = nr_segments;
+	}
+}
+#endif
+
+int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg)
+{
+	int ret;
+	blkelv_ioctl_arg_t output;
+
+	output.queue_ID			= elevator;
+	output.read_latency		= elevator->read_latency;
+	output.write_latency		= elevator->write_latency;
+	output.max_bomb_segments	= elevator->max_bomb_segments;
+
+	ret = -EFAULT;
+	if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t)))
+		goto out;
+	ret = 0;
+ out:
+	return ret;
+}
+
+int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg)
+{
+	blkelv_ioctl_arg_t input;
+	int ret;
+
+	ret = -EFAULT;
+	if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t)))
+		goto out;
+
+	ret = -EINVAL;
+	if (input.read_latency < 0)
+		goto out;
+	if (input.write_latency < 0)
+		goto out;
+	if (input.max_bomb_segments <= 0)
+		goto out;
+
+	elevator->read_latency		= input.read_latency;
+	elevator->write_latency		= input.write_latency;
+	elevator->max_bomb_segments	= input.max_bomb_segments;
+
+	ret = 0;
+ out:
+	return ret;
+}
+
+void elevator_init(elevator_t * elevator)
+{
+	*elevator = ELEVATOR_DEFAULTS;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/falconide.c linux/drivers/block/falconide.c
--- v2.3.51/linux/drivers/block/falconide.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/falconide.c	Wed Dec 31 16:00:00 1969
@@ -1,66 +0,0 @@
-/*
- *  linux/drivers/block/falconide.c -- Atari Falcon IDE Driver
- *
- *     Created 12 Jul 1997 by Geert Uytterhoeven
- *
- *  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 <linux/types.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/atarihw.h>
-#include <asm/atariints.h>
-#include <asm/atari_stdma.h>
-
-
-    /*
-     *  Base of the IDE interface
-     */
-
-#define ATA_HD_BASE	0xfff00000
-
-    /*
-     *  Offsets from the above base
-     */
-
-#define ATA_HD_DATA	0x00
-#define ATA_HD_ERROR	0x05		/* see err-bits */
-#define ATA_HD_NSECTOR	0x09		/* nr of sectors to read/write */
-#define ATA_HD_SECTOR	0x0d		/* starting sector */
-#define ATA_HD_LCYL	0x11		/* starting cylinder */
-#define ATA_HD_HCYL	0x15		/* high byte of starting cyl */
-#define ATA_HD_SELECT	0x19		/* 101dhhhh , d=drive, hhhh=head */
-#define ATA_HD_STATUS	0x1d		/* see status-bits */
-#define ATA_HD_CONTROL	0x39
-
-static int __init falconide_offsets[IDE_NR_PORTS] = {
-    ATA_HD_DATA, ATA_HD_ERROR, ATA_HD_NSECTOR, ATA_HD_SECTOR, ATA_HD_LCYL,
-    ATA_HD_HCYL, ATA_HD_SELECT, ATA_HD_STATUS, ATA_HD_CONTROL, -1
-};
-
-
-    /*
-     *  Probe for a Falcon IDE interface
-     */
-
-void __init falconide_init(void)
-{
-    if (MACH_IS_ATARI && ATARIHW_PRESENT(IDE)) {
-	hw_regs_t hw;
-	int index;
-
-	ide_setup_ports(&hw, (ide_ioreg_t)ATA_HD_BASE, falconide_offsets,
-			0, 0, NULL, IRQ_MFP_IDE);
-	index = ide_register_hw(&hw, NULL);
-
-	if (index != -1)
-	    printk("ide%d: Falcon IDE interface\n", index);
-    }
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/gayle.c linux/drivers/block/gayle.c
--- v2.3.51/linux/drivers/block/gayle.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/gayle.c	Wed Dec 31 16:00:00 1969
@@ -1,169 +0,0 @@
-/*
- *  linux/drivers/block/gayle.c -- Amiga Gayle IDE Driver
- *
- *     Created 9 Jul 1997 by Geert Uytterhoeven
- *
- *  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 <linux/config.h>
-#include <linux/types.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/amigahw.h>
-#include <asm/amigaints.h>
-
-
-    /*
-     *  Bases of the IDE interfaces
-     */
-
-#define GAYLE_BASE_4000	0xdd2020	/* A4000/A4000T */
-#define GAYLE_BASE_1200	0xda0000	/* A1200/A600 */
-
-    /*
-     *  Offsets from one of the above bases
-     */
-
-#define GAYLE_DATA	0x00
-#define GAYLE_ERROR	0x06		/* see err-bits */
-#define GAYLE_NSECTOR	0x0a		/* nr of sectors to read/write */
-#define GAYLE_SECTOR	0x0e		/* starting sector */
-#define GAYLE_LCYL	0x12		/* starting cylinder */
-#define GAYLE_HCYL	0x16		/* high byte of starting cyl */
-#define GAYLE_SELECT	0x1a		/* 101dhhhh , d=drive, hhhh=head */
-#define GAYLE_STATUS	0x1e		/* see status-bits */
-#define GAYLE_CONTROL	0x101a
-
-static int __init gayle_offsets[IDE_NR_PORTS] = {
-    GAYLE_DATA, GAYLE_ERROR, GAYLE_NSECTOR, GAYLE_SECTOR, GAYLE_LCYL,
-    GAYLE_HCYL, GAYLE_SELECT, GAYLE_STATUS, -1, -1
-};
-
-
-    /*
-     *  These are at different offsets from the base
-     */
-
-#define GAYLE_IRQ_4000	0xdd3020	/* MSB = 1, Harddisk is source of */
-#define GAYLE_IRQ_1200	0xda9000	/* interrupt */
-
-
-    /*
-     *  Offset of the secondary port for IDE doublers
-     *  Note that GAYLE_CONTROL is NOT available then!
-     */
-
-#define GAYLE_NEXT_PORT	0x1000
-
-#ifndef CONFIG_BLK_DEV_IDEDOUBLER
-#define GAYLE_NUM_HWIFS		1
-#define GAYLE_NUM_PROBE_HWIFS	GAYLE_NUM_HWIFS
-#define GAYLE_HAS_CONTROL_REG	1
-#else /* CONFIG_BLK_DEV_IDEDOUBLER */
-#define GAYLE_NUM_HWIFS		2
-#define GAYLE_NUM_PROBE_HWIFS	(ide_doubler ? GAYLE_NUM_HWIFS : \
-					       GAYLE_NUM_HWIFS-1)
-#define GAYLE_HAS_CONTROL_REG	(!ide_doubler)
-int ide_doubler = 0;	/* support IDE doublers? */
-#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
-
-
-    /*
-     *  Check and acknowledge the interrupt status
-     */
-
-static int gayle_ack_intr_a4000(ide_hwif_t *hwif)
-{
-    unsigned char ch;
-
-    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
-    if (!(ch & 0x80))
-	return 0;
-    return 1;
-}
-
-static int gayle_ack_intr_a1200(ide_hwif_t *hwif)
-{
-    unsigned char ch;
-
-    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
-    if (!(ch & 0x80))
-	return 0;
-    (void)inb(hwif->io_ports[IDE_STATUS_OFFSET]);
-    outb(0x7c | (ch & 0x03), hwif->io_ports[IDE_IRQ_OFFSET]);
-    return 1;
-}
-
-    /*
-     *  Probe for a Gayle IDE interface (and optionally for an IDE doubler)
-     */
-
-void __init gayle_init(void)
-{
-    int a4000, i;
-
-    if (!MACH_IS_AMIGA)
-	return;
-
-    if (!(a4000 = AMIGAHW_PRESENT(A4000_IDE)) && !AMIGAHW_PRESENT(A1200_IDE))
-	return;
-
-    for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) {
-	ide_ioreg_t base, ctrlport, irqport;
-	ide_ack_intr_t *ack_intr;
-	hw_regs_t hw;
-	int index;
-
-	if (a4000) {
-	    base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_4000);
-	    irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_4000);
-	    ack_intr = gayle_ack_intr_a4000;
-	} else {
-	    base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_1200);
-	    irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_1200);
-	    ack_intr = gayle_ack_intr_a1200;
-	}
-
-	if (GAYLE_HAS_CONTROL_REG)
-	    ctrlport = base + GAYLE_CONTROL;
-	else
-	    ctrlport = 0;
-
-	base += i*GAYLE_NEXT_PORT;
-
-	ide_setup_ports(&hw, base, gayle_offsets,
-			ctrlport, irqport, ack_intr, IRQ_AMIGA_PORTS);
-
-	index = ide_register_hw(&hw, NULL);
-	if (index != -1) {
-	    switch (i) {
-		case 0:
-		    printk("ide%d: Gayle IDE interface (A%d style)\n", index,
-			   a4000 ? 4000 : 1200);
-		    break;
-#ifdef CONFIG_BLK_DEV_IDEDOUBLER
-		case 1:
-		    printk("ide%d: IDE doubler\n", index);
-		    break;
-#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
-	    }
-	}
-#if 1 /* TESTING */
-	if (i == 1) {
-	    volatile u_short *addr = (u_short *)base;
-	    u_short data;
-	    printk("+++ Probing for IDE doubler... ");
-	    *addr = 0xffff;
-	    data = *addr;
-	    printk("probe returned 0x%02x (PLEASE REPORT THIS!!)\n", data);
-	}
-#endif /* TESTING */
-    }
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v2.3.51/linux/drivers/block/hd.c	Wed Feb 16 17:03:51 2000
+++ linux/drivers/block/hd.c	Wed Dec 31 16:00:00 1969
@@ -1,883 +0,0 @@
-/*
- *  linux/drivers/block/hd.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-/*
- * This is the low-level hd interrupt support. It traverses the
- * request-list, using interrupts to jump between functions. As
- * all the functions are called within interrupts, we may not
- * sleep. Special care is recommended.
- * 
- *  modified by Drew Eckhardt to check nr of hd's from the CMOS.
- *
- *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- *  in the early extended-partition checks and added DM partitions
- *
- *  IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- *  and general streamlining by Mark Lord.
- *
- *  Removed 99% of above. Use Mark's ide driver for those options.
- *  This is now a lightweight ST-506 driver. (Paul Gortmaker)
- *
- *  Modified 1995 Russell King for ARM processor.
- */
-  
-/* Uncomment the following if you want verbose error reports. */
-/* #define VERBOSE_ERRORS */
-  
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/devfs_fs_kernel.h>
-#include <linux/kernel.h>
-#include <linux/hdreg.h>
-#include <linux/genhd.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/mc146818rtc.h> /* CMOS defines */
-#include <linux/init.h>
-#include <linux/blkpg.h>
-
-#define REALLY_SLOW_IO
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#define MAJOR_NR HD_MAJOR
-#include <linux/blk.h>
-
-#ifdef __arm__
-#undef  HD_IRQ
-#endif
-#include <asm/irq.h>
-#ifdef __arm__
-#define HD_IRQ IRQ_HARDDISK
-#endif
-
-static int revalidate_hddisk(kdev_t, int);
-
-#define	HD_DELAY	0
-
-#define MAX_ERRORS     16	/* Max read/write errors/sector */
-#define RESET_FREQ      8	/* Reset controller every 8th retry */
-#define RECAL_FREQ      4	/* Recalibrate every 4th retry */
-#define MAX_HD		2
-
-#define STAT_OK		(READY_STAT|SEEK_STAT)
-#define OK_STATUS(s)	(((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
-
-static void recal_intr(void);
-static void bad_rw_intr(void);
-
-static char recalibrate[MAX_HD];
-static char special_op[MAX_HD];
-static int access_count[MAX_HD];
-static char busy[MAX_HD];
-static DECLARE_WAIT_QUEUE_HEAD(busy_wait);
-
-static int reset;
-static int hd_error;
-
-#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
-
-/*
- *  This struct defines the HD's and their types.
- */
-struct hd_i_struct {
-	unsigned int head,sect,cyl,wpcom,lzone,ctl;
-};
-	
-#ifdef HD_TYPE
-static struct hd_i_struct hd_info[] = { HD_TYPE };
-static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
-#else
-static struct hd_i_struct hd_info[MAX_HD];
-static int NR_HD;
-#endif
-
-static struct hd_struct hd[MAX_HD<<6];
-static int hd_sizes[MAX_HD<<6];
-static int hd_blocksizes[MAX_HD<<6];
-static int hd_hardsectsizes[MAX_HD<<6];
-
-#if (HD_DELAY > 0)
-unsigned long last_req;
-
-unsigned long read_timer(void)
-{
-	unsigned long t, flags;
-	int i;
-
-	save_flags(flags);
-	cli();
-	t = jiffies * 11932;
-    	outb_p(0, 0x43);
-	i = inb_p(0x40);
-	i |= inb(0x40) << 8;
-	restore_flags(flags);
-	return(t - i);
-}
-#endif
-
-void __init hd_setup(char *str, int *ints)
-{
-	int hdind = 0;
-
-	if (ints[0] != 3)
-		return;
-	if (hd_info[0].head != 0)
-		hdind=1;
-	hd_info[hdind].head = ints[2];
-	hd_info[hdind].sect = ints[3];
-	hd_info[hdind].cyl = ints[1];
-	hd_info[hdind].wpcom = 0;
-	hd_info[hdind].lzone = ints[1];
-	hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
-	NR_HD = hdind+1;
-}
-
-static void dump_status (const char *msg, unsigned int stat)
-{
-	unsigned long flags;
-	char devc;
-
-	devc = !QUEUE_EMPTY ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
-	save_flags (flags);
-	sti();
-#ifdef VERBOSE_ERRORS
-	printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
-	if (stat & BUSY_STAT)	printk("Busy ");
-	if (stat & READY_STAT)	printk("DriveReady ");
-	if (stat & WRERR_STAT)	printk("WriteFault ");
-	if (stat & SEEK_STAT)	printk("SeekComplete ");
-	if (stat & DRQ_STAT)	printk("DataRequest ");
-	if (stat & ECC_STAT)	printk("CorrectedError ");
-	if (stat & INDEX_STAT)	printk("Index ");
-	if (stat & ERR_STAT)	printk("Error ");
-	printk("}\n");
-	if ((stat & ERR_STAT) == 0) {
-		hd_error = 0;
-	} else {
-		hd_error = inb(HD_ERROR);
-		printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);
-		if (hd_error & BBD_ERR)		printk("BadSector ");
-		if (hd_error & ECC_ERR)		printk("UncorrectableError ");
-		if (hd_error & ID_ERR)		printk("SectorIdNotFound ");
-		if (hd_error & ABRT_ERR)	printk("DriveStatusError ");
-		if (hd_error & TRK0_ERR)	printk("TrackZeroNotFound ");
-		if (hd_error & MARK_ERR)	printk("AddrMarkNotFound ");
-		printk("}");
-		if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
-			printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
-				inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
-			if (!QUEUE_EMPTY)
-				printk(", sector=%ld", CURRENT->sector);
-		}
-		printk("\n");
-	}
-#else
-	printk("hd%c: %s: status=0x%02x.\n", devc, msg, stat & 0xff);
-	if ((stat & ERR_STAT) == 0) {
-		hd_error = 0;
-	} else {
-		hd_error = inb(HD_ERROR);
-		printk("hd%c: %s: error=0x%02x.\n", devc, msg, hd_error & 0xff);
-	}
-#endif	/* verbose errors */
-	restore_flags (flags);
-}
-
-void check_status(void)
-{
-	int i = inb_p(HD_STATUS);
-
-	if (!OK_STATUS(i)) {
-		dump_status("check_status", i);
-		bad_rw_intr();
-	}
-}
-
-static int controller_busy(void)
-{
-	int retries = 100000;
-	unsigned char status;
-
-	do {
-		status = inb_p(HD_STATUS);
-	} while ((status & BUSY_STAT) && --retries);
-	return status;
-}
-
-static int status_ok(void)
-{
-	unsigned char status = inb_p(HD_STATUS);
-
-	if (status & BUSY_STAT)
-		return 1;	/* Ancient, but does it make sense??? */
-	if (status & WRERR_STAT)
-		return 0;
-	if (!(status & READY_STAT))
-		return 0;
-	if (!(status & SEEK_STAT))
-		return 0;
-	return 1;
-}
-
-static int controller_ready(unsigned int drive, unsigned int head)
-{
-	int retry = 100;
-
-	do {
-		if (controller_busy() & BUSY_STAT)
-			return 0;
-		outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
-		if (status_ok())
-			return 1;
-	} while (--retry);
-	return 0;
-}
-
-static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
-		unsigned int head,unsigned int cyl,unsigned int cmd,
-		void (*intr_addr)(void))
-{
-	unsigned short port;
-
-#if (HD_DELAY > 0)
-	while (read_timer() - last_req < HD_DELAY)
-		/* nothing */;
-#endif
-	if (reset)
-		return;
-	if (!controller_ready(drive, head)) {
-		reset = 1;
-		return;
-	}
-	SET_INTR(intr_addr);
-	outb_p(hd_info[drive].ctl,HD_CMD);
-	port=HD_DATA;
-	outb_p(hd_info[drive].wpcom>>2,++port);
-	outb_p(nsect,++port);
-	outb_p(sect,++port);
-	outb_p(cyl,++port);
-	outb_p(cyl>>8,++port);
-	outb_p(0xA0|(drive<<4)|head,++port);
-	outb_p(cmd,++port);
-}
-
-static void hd_request (void);
-
-static int drive_busy(void)
-{
-	unsigned int i;
-	unsigned char c;
-
-	for (i = 0; i < 500000 ; i++) {
-		c = inb_p(HD_STATUS);
-		if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
-			return 0;
-	}
-	dump_status("reset timed out", c);
-	return 1;
-}
-
-static void reset_controller(void)
-{
-	int	i;
-
-	outb_p(4,HD_CMD);
-	for(i = 0; i < 1000; i++) barrier();
-	outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
-	for(i = 0; i < 1000; i++) barrier();
-	if (drive_busy())
-		printk("hd: controller still busy\n");
-	else if ((hd_error = inb(HD_ERROR)) != 1)
-		printk("hd: controller reset failed: %02x\n",hd_error);
-}
-
-static void reset_hd(void)
-{
-	static int i;
-
-repeat:
-	if (reset) {
-		reset = 0;
-		i = -1;
-		reset_controller();
-	} else {
-		check_status();
-		if (reset)
-			goto repeat;
-	}
-	if (++i < NR_HD) {
-		special_op[i] = recalibrate[i] = 1;
-		hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
-			hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
-		if (reset)
-			goto repeat;
-	} else
-		hd_request();
-}
-
-/*
- * Ok, don't know what to do with the unexpected interrupts: on some machines
- * doing a reset and a retry seems to result in an eternal loop. Right now I
- * ignore it, and just set the timeout.
- *
- * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
- * drive enters "idle", "standby", or "sleep" mode, so if the status looks
- * "good", we just ignore the interrupt completely.
- */
-void unexpected_hd_interrupt(void)
-{
-	unsigned int stat = inb_p(HD_STATUS);
-
-	if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
-		dump_status ("unexpected interrupt", stat);
-		SET_TIMER;
-	}
-}
-
-/*
- * bad_rw_intr() now tries to be a bit smarter and does things
- * according to the error returned by the controller.
- * -Mika Liljeberg (liljeber@cs.Helsinki.FI)
- */
-static void bad_rw_intr(void)
-{
-	int dev;
-
-	if (QUEUE_EMPTY)
-		return;
-	dev = DEVICE_NR(CURRENT->rq_dev);
-	if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
-		end_request(0);
-		special_op[dev] = recalibrate[dev] = 1;
-	} else if (CURRENT->errors % RESET_FREQ == 0)
-		reset = 1;
-	else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
-		special_op[dev] = recalibrate[dev] = 1;
-	/* Otherwise just retry */
-}
-
-static inline int wait_DRQ(void)
-{
-	int retries = 100000, stat;
-
-	while (--retries > 0)
-		if ((stat = inb_p(HD_STATUS)) & DRQ_STAT)
-			return 0;
-	dump_status("wait_DRQ", stat);
-	return -1;
-}
-
-static void read_intr(void)
-{
-	int i, retries = 100000;
-
-	do {
-		i = (unsigned) inb_p(HD_STATUS);
-		if (i & BUSY_STAT)
-			continue;
-		if (!OK_STATUS(i))
-			break;
-		if (i & DRQ_STAT)
-			goto ok_to_read;
-	} while (--retries > 0);
-	dump_status("read_intr", i);
-	bad_rw_intr();
-	hd_request();
-	return;
-ok_to_read:
-	insw(HD_DATA,CURRENT->buffer,256);
-	CURRENT->sector++;
-	CURRENT->buffer += 512;
-	CURRENT->errors = 0;
-	i = --CURRENT->nr_sectors;
-	--CURRENT->current_nr_sectors;
-#ifdef DEBUG
-	printk("hd%c: read: sector %ld, remaining = %ld, buffer=0x%08lx\n",
-		dev+'a', CURRENT->sector, CURRENT->nr_sectors,
-		(unsigned long) CURRENT->buffer+512));
-#endif
-	if (CURRENT->current_nr_sectors <= 0)
-		end_request(1);
-	if (i > 0) {
-		SET_INTR(&read_intr);
-		return;
-	}
-	(void) inb_p(HD_STATUS);
-#if (HD_DELAY > 0)
-	last_req = read_timer();
-#endif
-	if (!QUEUE_EMPTY)
-		hd_request();
-	return;
-}
-
-static void write_intr(void)
-{
-	int i;
-	int retries = 100000;
-
-	do {
-		i = (unsigned) inb_p(HD_STATUS);
-		if (i & BUSY_STAT)
-			continue;
-		if (!OK_STATUS(i))
-			break;
-		if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
-			goto ok_to_write;
-	} while (--retries > 0);
-	dump_status("write_intr", i);
-	bad_rw_intr();
-	hd_request();
-	return;
-ok_to_write:
-	CURRENT->sector++;
-	i = --CURRENT->nr_sectors;
-	--CURRENT->current_nr_sectors;
-	CURRENT->buffer += 512;
-	if (!i || (CURRENT->bh && !SUBSECTOR(i)))
-		end_request(1);
-	if (i > 0) {
-		SET_INTR(&write_intr);
-		outsw(HD_DATA,CURRENT->buffer,256);
-		sti();
-	} else {
-#if (HD_DELAY > 0)
-		last_req = read_timer();
-#endif
-		hd_request();
-	}
-	return;
-}
-
-static void recal_intr(void)
-{
-	check_status();
-#if (HD_DELAY > 0)
-	last_req = read_timer();
-#endif
-	hd_request();
-}
-
-/*
- * This is another of the error-routines I don't know what to do with. The
- * best idea seems to just set reset, and start all over again.
- */
-static void hd_times_out(void)
-{
-	unsigned int dev;
-
-	DEVICE_INTR = NULL;
-	if (QUEUE_EMPTY)
-		return;
-	disable_irq(HD_IRQ);
-	sti();
-	reset = 1;
-	dev = DEVICE_NR(CURRENT->rq_dev);
-	printk("hd%c: timeout\n", dev+'a');
-	if (++CURRENT->errors >= MAX_ERRORS) {
-#ifdef DEBUG
-		printk("hd%c: too many errors\n", dev+'a');
-#endif
-		end_request(0);
-	}
-	cli();
-	hd_request();
-	enable_irq(HD_IRQ);
-}
-
-int do_special_op (unsigned int dev)
-{
-	if (recalibrate[dev]) {
-		recalibrate[dev] = 0;
-		hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
-		return reset;
-	}
-	if (hd_info[dev].head > 16) {
-		printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
-		end_request(0);
-	}
-	special_op[dev] = 0;
-	return 1;
-}
-
-/*
- * The driver enables interrupts as much as possible.  In order to do this,
- * (a) the device-interrupt is disabled before entering hd_request(),
- * and (b) the timeout-interrupt is disabled before the sti().
- *
- * Interrupts are still masked (by default) whenever we are exchanging
- * data/cmds with a drive, because some drives seem to have very poor
- * tolerance for latency during I/O. The IDE driver has support to unmask
- * interrupts for non-broken hardware, so use that driver if required.
- */
-static void hd_request(void)
-{
-	unsigned int dev, block, nsect, sec, track, head, cyl;
-
-	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) return;
-	if (DEVICE_INTR)
-		return;
-repeat:
-	timer_active &= ~(1<<HD_TIMER);
-	sti();
-	INIT_REQUEST;
-	if (reset) {
-		cli();
-		reset_hd();
-		return;
-	}
-	dev = MINOR(CURRENT->rq_dev);
-	block = CURRENT->sector;
-	nsect = CURRENT->nr_sectors;
-	if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
-#ifdef DEBUG
-		if (dev >= (NR_HD<<6))
-			printk("hd: bad minor number: device=%s\n",
-			       kdevname(CURRENT->rq_dev));
-		else
-			printk("hd%c: bad access: block=%d, count=%d\n",
-				(MINOR(CURRENT->rq_dev)>>6)+'a', block, nsect);
-#endif
-		end_request(0);
-		goto repeat;
-	}
-	block += hd[dev].start_sect;
-	dev >>= 6;
-	if (special_op[dev]) {
-		if (do_special_op(dev))
-			goto repeat;
-		return;
-	}
-	sec   = block % hd_info[dev].sect + 1;
-	track = block / hd_info[dev].sect;
-	head  = track % hd_info[dev].head;
-	cyl   = track / hd_info[dev].head;
-#ifdef DEBUG
-	printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx\n",
-		dev+'a', (CURRENT->cmd == READ)?"read":"writ",
-		cyl, head, sec, nsect, (unsigned long) CURRENT->buffer);
-#endif
-	if (CURRENT->cmd == READ) {
-		hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
-		if (reset)
-			goto repeat;
-		return;
-	}
-	if (CURRENT->cmd == WRITE) {
-		hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
-		if (reset)
-			goto repeat;
-		if (wait_DRQ()) {
-			bad_rw_intr();
-			goto repeat;
-		}
-		outsw(HD_DATA,CURRENT->buffer,256);
-		return;
-	}
-	panic("unknown hd-command");
-}
-
-static void do_hd_request (request_queue_t * q)
-{
-	disable_irq(HD_IRQ);
-	hd_request();
-	enable_irq(HD_IRQ);
-}
-
-static int hd_ioctl(struct inode * inode, struct file * file,
-	unsigned int cmd, unsigned long arg)
-{
-	struct hd_geometry *loc = (struct hd_geometry *) arg;
-	int dev;
-
-	if ((!inode) || !(inode->i_rdev))
-		return -EINVAL;
-	dev = DEVICE_NR(inode->i_rdev);
-	if (dev >= NR_HD)
-		return -EINVAL;
-	switch (cmd) {
-		case HDIO_GETGEO:
-		{
-			struct hd_geometry g; 
-			if (!loc)  return -EINVAL;
-			g.heads = hd_info[dev].head;
-			g.sectors = hd_info[dev].sect;
-			g.cylinders = hd_info[dev].cyl;
-			g.start = hd[MINOR(inode->i_rdev)].start_sect;
-			return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; 
-		}
-
-         	case BLKGETSIZE:   /* Return device size */
-			if (!arg)  return -EINVAL;
-			return put_user(hd[MINOR(inode->i_rdev)].nr_sects, 
-					(long *) arg);
-
-		case BLKRRPART: /* Re-read partition tables */
-			if (!capable(CAP_SYS_ADMIN))
-				return -EACCES;
-			return revalidate_hddisk(inode->i_rdev, 1);
-
-		case BLKROSET:
-		case BLKROGET:
-		case BLKRASET:
-		case BLKRAGET:
-		case BLKFLSBUF:
-		case BLKPG:
-			return blk_ioctl(inode->i_rdev, cmd, arg);
-
-		default:
-			return -EINVAL;
-	}
-}
-
-static int hd_open(struct inode * inode, struct file * filp)
-{
-	int target;
-	target =  DEVICE_NR(inode->i_rdev);
-
-	if (target >= NR_HD)
-		return -ENODEV;
-	while (busy[target])
-		sleep_on(&busy_wait);
-	access_count[target]++;
-	return 0;
-}
-
-/*
- * Releasing a block device means we sync() it, so that it can safely
- * be forgotten about...
- */
-static int hd_release(struct inode * inode, struct file * file)
-{
-        int target =  DEVICE_NR(inode->i_rdev);
-	access_count[target]--;
-	return 0;
-}
-
-extern struct block_device_operations hd_fops;
-
-static struct gendisk hd_gendisk = {
-	MAJOR_NR,	/* Major number */	
-	"hd",		/* Major name */
-	6,		/* Bits to shift to get real from partition */
-	1 << 6,		/* Number of partitions per real */
-	hd,		/* hd struct */
-	hd_sizes,	/* block sizes */
-	0,		/* number */
-	NULL,		/* internal use, not presently used */
-	NULL,		/* next */
-	&hd_fops,       /* file operations */
-};
-	
-static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	void (*handler)(void) = DEVICE_INTR;
-
-	DEVICE_INTR = NULL;
-	timer_active &= ~(1<<HD_TIMER);
-	if (!handler)
-		handler = unexpected_hd_interrupt;
-	handler();
-	sti();
-}
-
-static struct block_device_operations hd_fops = {
-	open:		hd_open,
-	release:	hd_release,
-	ioctl:		hd_ioctl,
-};
-
-/*
- * This is the hard disk IRQ description. The SA_INTERRUPT in sa_flags
- * means we run the IRQ-handler with interrupts disabled:  this is bad for
- * interrupt latency, but anything else has led to problems on some
- * machines.
- *
- * We enable interrupts in some of the routines after making sure it's
- * safe.
- */
-static void hd_geninit(void)
-{
-	int drive;
-
-	for(drive=0; drive < (MAX_HD << 6); drive++) {
-		hd_blocksizes[drive] = 1024;
-		hd_hardsectsizes[drive] = 512;
-	}
-	blksize_size[MAJOR_NR] = hd_blocksizes;
-	hardsect_size[MAJOR_NR] = hd_hardsectsizes;
-
-#ifdef __i386__
-	if (!NR_HD) {
-		extern struct drive_info drive_info;
-		unsigned char *BIOS = (unsigned char *) &drive_info;
-		int cmos_disks;
-
-		for (drive=0 ; drive<2 ; drive++) {
-			hd_info[drive].cyl = *(unsigned short *) BIOS;
-			hd_info[drive].head = *(2+BIOS);
-			hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
-			hd_info[drive].ctl = *(8+BIOS);
-			hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
-			hd_info[drive].sect = *(14+BIOS);
-#ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp
-			if (hd_info[drive].cyl && NR_HD == drive)
-				NR_HD++;
-#endif
-			BIOS += 16;
-		}
-
-	/*
-		We query CMOS about hard disks : it could be that 
-		we have a SCSI/ESDI/etc controller that is BIOS
-		compatible with ST-506, and thus showing up in our
-		BIOS table, but not register compatible, and therefore
-		not present in CMOS.
-
-		Furthermore, we will assume that our ST-506 drives
-		<if any> are the primary drives in the system, and 
-		the ones reflected as drive 1 or 2.
-
-		The first drive is stored in the high nibble of CMOS
-		byte 0x12, the second in the low nibble.  This will be
-		either a 4 bit drive type or 0xf indicating use byte 0x19 
-		for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
-
-		Needless to say, a non-zero value means we have 
-		an AT controller hard disk for that drive.
-
-		
-	*/
-
-		if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) {
-			if (cmos_disks & 0x0f)
-				NR_HD = 2;
-			else
-				NR_HD = 1;
-		}
-	}
-#endif /* __i386__ */
-#ifdef __arm__
-	if (!NR_HD) {
-		/* We don't know anything about the drive.  This means
-		 * that you *MUST* specify the drive parameters to the
-		 * kernel yourself.
-		 */
-		printk("hd: no drives specified - use hd=cyl,head,sectors"
-			" on kernel command line\n");
-	}
-#endif
-
-	for (drive=0 ; drive < NR_HD ; drive++) {
-		printk ("hd%c: %ldMB, CHS=%d/%d/%d\n", drive+'a',
-			hd[drive<<6].nr_sects / 2048, hd_info[drive].cyl,
-			hd_info[drive].head, hd_info[drive].sect);
-	}
-	if (!NR_HD)
-		return;
-
-	if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
-		printk("hd: unable to get IRQ%d for the hard disk driver\n",
-			HD_IRQ);
-		NR_HD = 0;
-		return;
-	}
-	request_region(HD_DATA, 8, "hd");
-	request_region(HD_CMD, 1, "hd(cmd)");
-
-	hd_gendisk.nr_real = NR_HD;
-
-	for(drive=0; drive < NR_HD; drive++)
-		register_disk(&hd_gendisk, MKDEV(MAJOR_NR,drive<<6), 1<<6,
-			&hd_fops, hd_info[drive].head * hd_info[drive].sect *
-			hd_info[drive].cyl);
-}
-
-int __init hd_init(void)
-{
-	if (devfs_register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
-		printk("hd: unable to get major %d for hard disk\n",MAJOR_NR);
-		return -1;
-	}
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
-	read_ahead[MAJOR_NR] = 8;		/* 8 sector (4kB) read-ahead */
-	hd_gendisk.next = gendisk_head;
-	gendisk_head = &hd_gendisk;
-	timer_table[HD_TIMER].fn = hd_times_out;
-	hd_geninit();
-	return 0;
-}
-
-#define DEVICE_BUSY busy[target]
-#define USAGE access_count[target]
-#define CAPACITY (hd_info[target].head*hd_info[target].sect*hd_info[target].cyl)
-/* We assume that the BIOS parameters do not change, so the disk capacity
-   will not change */
-#undef MAYBE_REINIT
-#define GENDISK_STRUCT hd_gendisk
-
-/*
- * This routine is called to flush all partitions and partition tables
- * for a changed disk, and then re-read the new partition table.
- * If we are revalidating a disk because of a media change, then we
- * enter with usage == 0.  If we are using an ioctl, we automatically have
- * usage == 1 (we need an open channel to use an ioctl :-), so this
- * is our limit.
- */
-static int revalidate_hddisk(kdev_t dev, int maxusage)
-{
-	int target;
-	struct gendisk * gdev;
-	int max_p;
-	int start;
-	int i;
-	long flags;
-
-	target = DEVICE_NR(dev);
-	gdev = &GENDISK_STRUCT;
-
-	save_flags(flags);
-	cli();
-	if (DEVICE_BUSY || USAGE > maxusage) {
-		restore_flags(flags);
-		return -EBUSY;
-	}
-	DEVICE_BUSY = 1;
-	restore_flags(flags);
-
-	max_p = gdev->max_p;
-	start = target << gdev->minor_shift;
-
-	for (i=max_p - 1; i >=0 ; i--) {
-		int minor = start + i;
-		kdev_t devi = MKDEV(MAJOR_NR, minor);
-		struct super_block *sb = get_super(devi); 
-
-		sync_dev(devi);
-		if (sb)
-			invalidate_inodes(sb);
-		invalidate_buffers(devi);
-		gdev->part[minor].start_sect = 0;
-		gdev->part[minor].nr_sects = 0;
-	}
-
-#ifdef MAYBE_REINIT
-	MAYBE_REINIT;
-#endif
-
-	grok_partitions(gdev, target, 1<<6, CAPACITY);
-
-	DEVICE_BUSY = 0;
-	wake_up(&busy_wait);
-	return 0;
-}
-
diff -u --recursive --new-file v2.3.51/linux/drivers/block/hpt34x.c linux/drivers/block/hpt34x.c
--- v2.3.51/linux/drivers/block/hpt34x.c	Fri Mar 10 16:40:41 2000
+++ linux/drivers/block/hpt34x.c	Wed Dec 31 16:00:00 1969
@@ -1,419 +0,0 @@
-/*
- * linux/drivers/block/hpt34x.c		Version 0.29	Feb. 10, 2000
- *
- * Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
- * May be copied or modified under the terms of the GNU General Public License
- *
- *
- * 00:12.0 Unknown mass storage controller:
- * Triones Technologies, Inc.
- * Unknown device 0003 (rev 01)
- *
- * hde: UDMA 2 (0x0000 0x0002) (0x0000 0x0010)
- * hdf: UDMA 2 (0x0002 0x0012) (0x0010 0x0030)
- * hde: DMA 2  (0x0000 0x0002) (0x0000 0x0010)
- * hdf: DMA 2  (0x0002 0x0012) (0x0010 0x0030)
- * hdg: DMA 1  (0x0012 0x0052) (0x0030 0x0070)
- * hdh: DMA 1  (0x0052 0x0252) (0x0070 0x00f0)
- *
- * ide-pci.c reference
- *
- * Since there are two cards that report almost identically,
- * the only discernable difference is the values reported in pcicmd.
- * Booting-BIOS card or HPT363 :: pcicmd == 0x07
- * Non-bootable card or HPT343 :: pcicmd == 0x05
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include "ide_modes.h"
-
-#ifndef SPLIT_BYTE
-#define SPLIT_BYTE(B,H,L)	((H)=(B>>4), (L)=(B-((B>>4)<<4)))
-#endif
-
-#define HPT343_DEBUG_DRIVE_INFO		0
-
-#undef DISPLAY_HPT34X_TIMINGS
-
-#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int hpt34x_get_info(char *, char **, off_t, int);
-extern int (*hpt34x_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-extern char *ide_media_verbose(ide_drive_t *);
-static struct pci_dev *bmide_dev;
-
-static int hpt34x_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-	u32 bibma = bmide_dev->resource[4].start;
-	u8  c0 = 0, c1 = 0;
-
-        /*
-         * at that point bibma+0x2 et bibma+0xa are byte registers
-         * to investigate:
-         */
-	c0 = inb_p((unsigned short)bibma + 0x02);
-	c1 = inb_p((unsigned short)bibma + 0x0a);
-
-	p += sprintf(p, "\n                                HPT34X Chipset.\n");
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %sabled                         %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
-	return p-buffer;	/* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-byte hpt34x_proc = 0;
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-static void hpt34x_clear_chipset (ide_drive_t *drive)
-{
-	int drive_number	= ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-	unsigned int reg1	= 0, tmp1 = 0;
-	unsigned int reg2	= 0, tmp2 = 0;
-
-	pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, &reg1);
-	pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, &reg2);
-	tmp1 = ((0x00 << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
-	tmp2 = (reg2 & ~(0x11 << drive_number));
-	pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
-	pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
-}
-
-static int hpt34x_tune_chipset (ide_drive_t *drive, byte speed)
-{
-	int			err;
-	byte			hi_speed, lo_speed;
-	int drive_number	= ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-	unsigned int reg1	= 0, tmp1 = 0;
-	unsigned int reg2	= 0, tmp2 = 0;
-
-	SPLIT_BYTE(speed, hi_speed, lo_speed);
-
-	if (hi_speed & 7) {
-		hi_speed = (hi_speed & 4) ? 0x01 : 0x10;
-	} else {
-		lo_speed <<= 5;
-		lo_speed >>= 5;
-	}
-
-	pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, &reg1);
-	pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, &reg2);
-	tmp1 = ((lo_speed << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
-	tmp2 = ((hi_speed << drive_number) | reg2);
-	err = ide_config_drive_speed(drive, speed);
-	pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
-	pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
-
-#if HPT343_DEBUG_DRIVE_INFO
-	printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \
-		" (0x%02x 0x%02x) 0x%04x\n",
-		drive->name, ide_xfer_verbose(speed),
-		drive_number, reg1, tmp1, reg2, tmp2,
-		hi_speed, lo_speed, err);
-#endif /* HPT343_DEBUG_DRIVE_INFO */
-
-	return(err);
-}
-
-/*
- * This allows the configuration of ide_pci chipset registers
- * for cards that learn about the drive's UDMA, DMA, PIO capabilities
- * after the drive is reported by the OS.  Initally for designed for
- * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc.
- */
-static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
-{
-	struct hd_driveid *id	= drive->id;
-	byte speed		= 0x00;
-
-	if (drive->media != ide_disk)
-		return ((int) ide_dma_off_quietly);
-
-	hpt34x_clear_chipset(drive);
-
-	if ((id->dma_ultra & 0x0010) && ultra) {
-		speed = XFER_UDMA_2;
-	} else if ((id->dma_ultra & 0x0008) && ultra) {
-		speed = XFER_UDMA_2;
-	} else if ((id->dma_ultra & 0x0004) && ultra) {
-		speed = XFER_UDMA_2;
-	} else if ((id->dma_ultra & 0x0002) && ultra) {
-		speed = XFER_UDMA_1;
-	} else if ((id->dma_ultra & 0x0001) && ultra) {
-		speed = XFER_UDMA_0;
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-	} else if (id->dma_1word & 0x0004) {
-		speed = XFER_SW_DMA_2;
-	} else if (id->dma_1word & 0x0002) {
-		speed = XFER_SW_DMA_1;
-	} else if (id->dma_1word & 0x0001) {
-		speed = XFER_SW_DMA_0;
-        } else {
-		return ((int) ide_dma_off_quietly);
-	}
-
-	(void) hpt34x_tune_chipset(drive, speed);
-
-	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_off :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-}
-
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
-	unsigned short xfer_pio	= drive->id->eide_pio_modes;
-
-	byte			timing, speed, pio;
-
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-	switch(timing) {
-		case 4: speed = XFER_PIO_4;break;
-		case 3: speed = XFER_PIO_3;break;
-		case 2: speed = XFER_PIO_2;break;
-		case 1: speed = XFER_PIO_1;break;
-		default:
-			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
-			break;
-	}
-	(void) hpt34x_tune_chipset(drive, speed);
-}
-
-static void hpt34x_tune_drive (ide_drive_t *drive, byte pio)
-{
-	byte speed;
-
-	switch(pio) {
-		case 4:		speed = XFER_PIO_4;break;
-		case 3:		speed = XFER_PIO_3;break;
-		case 2:		speed = XFER_PIO_2;break;
-		case 1:		speed = XFER_PIO_1;break;
-		default:	speed = XFER_PIO_0;break;
-	}
-	hpt34x_clear_chipset(drive);
-	(void) hpt34x_tune_chipset(drive, speed);
-}
-
-static int config_drive_xfer_rate (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
-
-	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
-			goto fast_ata_pio;
-		}
-		dma_func = ide_dma_off_quietly;
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & 0x0007) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, 1);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & 0x0007) ||
-			    (id->dma_1word & 0x0007)) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, 0);
-				if (dma_func != ide_dma_on)
-					goto no_dma_set;
-			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			if (id->eide_dma_time > 150) {
-				goto no_dma_set;
-			}
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, 0);
-			if (dma_func != ide_dma_on)
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
-no_dma_set:
-		config_chipset_for_pio(drive);
-	}
-
-#ifndef CONFIG_HPT34X_AUTODMA
-	if (dma_func == ide_dma_on)
-		dma_func = ide_dma_off;
-#endif /* CONFIG_HPT34X_AUTODMA */
-
-	return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-/*
- * hpt34x_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- *
- * This is specific to the HPT343 UDMA bios-less chipset
- * and HPT345 UDMA bios chipset (stamped HPT363)
- * by HighPoint|Triones Technologies, Inc.
- */
-
-int hpt34x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	unsigned long dma_base = hwif->dma_base;
-	unsigned int count, reading = 0;
-	byte dma_stat;
-
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		case ide_dma_read:
-			reading = 1 << 3;
-		case ide_dma_write:
-			if (!(count = ide_build_dmatable(drive, func)))
-				return 1;	/* try PIO instead of DMA */
-			outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
-			reading |= 0x01;
-			outb(reading, dma_base);		/* specify r/w */
-			outb(inb(dma_base+2)|6, dma_base+2);	/* clear INTR & ERROR flags */
-			drive->waiting_for_dma = 1;
-			if (drive->media != ide_disk)
-				return 0;
-			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);	/* issue cmd to drive */
-			OUT_BYTE((reading == 9) ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
-			return 0;
-		case ide_dma_end:	/* returns 1 on error, 0 otherwise */
-			drive->waiting_for_dma = 0;
-			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-			dma_stat = inb(dma_base+2);		/* get DMA status */
-			outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
-			ide_destroy_dmatable(drive);		/* purge DMA mappings */
-			return (dma_stat & 7) != 4;		/* verify good DMA status */
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-}
-
-/*
- * If the BIOS does not set the IO base addaress to XX00, 343 will fail.
- */
-#define	HPT34X_PCI_INIT_REG		0x80
-
-unsigned int __init pci_init_hpt34x (struct pci_dev *dev, const char *name)
-{
-	int i = 0;
-	unsigned long hpt34xIoBase = dev->resource[4].start;
-	unsigned short cmd;
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-
-	pci_write_config_byte(dev, HPT34X_PCI_INIT_REG, 0x00);
-	pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
-	if (cmd & PCI_COMMAND_MEMORY) {
-		if (dev->resource[PCI_ROM_RESOURCE].start) {
-			pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
-			printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n", dev->resource[PCI_ROM_RESOURCE].start);
-		}
-		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0);
-	} else {
-		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
-	}
-
-	pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
-	dev->resource[0].start = (hpt34xIoBase + 0x20);
-	dev->resource[1].start = (hpt34xIoBase + 0x34);
-	dev->resource[2].start = (hpt34xIoBase + 0x28);
-	dev->resource[3].start = (hpt34xIoBase + 0x3c);
-	for(i=0; i<4; i++)
-		dev->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
-	/*
-	 * Since 20-23 can be assigned and are R/W, we correct them.
-	 */
-	pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dev->resource[0].start);
-	pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, dev->resource[1].start);
-	pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, dev->resource[2].start);
-	pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, dev->resource[3].start);
-	pci_write_config_word(dev, PCI_COMMAND, cmd);
-
-	__restore_flags(flags);	/* local CPU only */
-
-#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS)
-	hpt34x_proc = 1;
-	bmide_dev = dev;
-	hpt34x_display_info = &hpt34x_get_info;
-#endif /* DISPLAY_HPT34X_TIMINGS && CONFIG_PROC_FS */
-
-	return dev->irq;
-}
-
-void __init ide_init_hpt34x (ide_hwif_t *hwif)
-{
-	hwif->tuneproc = &hpt34x_tune_drive;
-	if (hwif->dma_base) {
-		unsigned short pcicmd = 0;
-
-		pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd);
-		hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0;
-		hwif->dmaproc = &hpt34x_dmaproc;
-	} else {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/hpt366.c linux/drivers/block/hpt366.c
--- v2.3.51/linux/drivers/block/hpt366.c	Fri Mar 10 16:40:41 2000
+++ linux/drivers/block/hpt366.c	Wed Dec 31 16:00:00 1969
@@ -1,564 +0,0 @@
-/*
- * linux/drivers/block/hpt366.c		Version 0.16	Feb. 10, 2000
- *
- * Copyright (C) 1999-2000		Andre Hedrick <andre@suse.com>
- * May be copied or modified under the terms of the GNU General Public License
- *
- * Thanks to HighPoint Technologies for their assistance, and hardware.
- * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his
- * donation of an ABit BP6 mainboard, processor, and memory acellerated
- * development and support.
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "ide_modes.h"
-
-#undef DISPLAY_HPT366_TIMINGS
-
-#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-#endif  /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-const char *bad_ata66_4[] = {
-	"WDC AC310200R",
-	NULL
-};
-
-const char *bad_ata66_3[] = {
-	"WDC AC310200R",
-	NULL
-};
-
-const char *bad_ata33[] = {
-	"Maxtor 92720U8", "Maxtor 92040U6", "Maxtor 91360U4", "Maxtor 91020U3", "Maxtor 90845U3", "Maxtor 90650U2",
-	"Maxtor 91360D8", "Maxtor 91190D7", "Maxtor 91020D6", "Maxtor 90845D5", "Maxtor 90680D4", "Maxtor 90510D3", "Maxtor 90340D2",
-	"Maxtor 91152D8", "Maxtor 91008D7", "Maxtor 90845D6", "Maxtor 90840D6", "Maxtor 90720D5", "Maxtor 90648D5", "Maxtor 90576D4",
-	"Maxtor 90510D4",
-	"Maxtor 90432D3", "Maxtor 90288D2", "Maxtor 90256D2",
-	"Maxtor 91000D8", "Maxtor 90910D8", "Maxtor 90875D7", "Maxtor 90840D7", "Maxtor 90750D6", "Maxtor 90625D5", "Maxtor 90500D4",
-	"Maxtor 91728D8", "Maxtor 91512D7", "Maxtor 91303D6", "Maxtor 91080D5", "Maxtor 90845D4", "Maxtor 90680D4", "Maxtor 90648D3", "Maxtor 90432D2",
-	NULL
-};
-
-struct chipset_bus_clock_list_entry {
-	byte		xfer_speed;
-	unsigned int	chipset_settings;
-};
-
-struct chipset_bus_clock_list_entry forty_base [] = {
-
-	{	XFER_UDMA_4	,	0x900fd943	},
-	{	XFER_UDMA_3	,	0x900ad943	},
-	{	XFER_UDMA_2	,	0x900bd943	},
-	{	XFER_UDMA_1	,	0x9008d943	},
-	{	XFER_UDMA_0	,	0x9008d943	},
-
-	{	XFER_MW_DMA_2	,	0xa008d943	},
-	{	XFER_MW_DMA_1	,	0xa010d955	},
-	{	XFER_MW_DMA_0	,	0xa010d9fc	},
-
-	{	XFER_PIO_4	,	0xc008d963	},
-	{	XFER_PIO_3	,	0xc010d974	},
-	{	XFER_PIO_2	,	0xc010d997	},
-	{	XFER_PIO_1	,	0xc010d9c7	},
-	{	XFER_PIO_0	,	0xc018d9d9	},
-	{	0		,	0x0120d9d9	}
-};
-
-struct chipset_bus_clock_list_entry thirty_three_base [] = {
-
-	{	XFER_UDMA_4	,	0x90c9a731	},
-	{	XFER_UDMA_3	,	0x90cfa731	},
-	{	XFER_UDMA_2	,	0x90caa731	},
-	{	XFER_UDMA_1	,	0x90cba731	},
-	{	XFER_UDMA_0	,	0x90c8a731	},
-
-	{	XFER_MW_DMA_2	,	0xa0c8a731	},
-	{	XFER_MW_DMA_1	,	0xa0c8a732	},	/* 0xa0c8a733 */
-	{	XFER_MW_DMA_0	,	0xa0c8a797	},
-
-	{	XFER_PIO_4	,	0xc0c8a731	},
-	{	XFER_PIO_3	,	0xc0c8a742	},
-	{	XFER_PIO_2	,	0xc0d0a753	},
-	{	XFER_PIO_1	,	0xc0d0a7a3	},	/* 0xc0d0a793 */
-	{	XFER_PIO_0	,	0xc0d0a7aa	},	/* 0xc0d0a7a7 */
-	{	0		,	0x0120a7a7	}
-};
-
-struct chipset_bus_clock_list_entry twenty_five_base [] = {
-
-	{	XFER_UDMA_4	,	0x90c98521	},
-	{	XFER_UDMA_3	,	0x90cf8521	},
-	{	XFER_UDMA_2	,	0x90cf8521	},
-	{	XFER_UDMA_1	,	0x90cb8521	},
-	{	XFER_UDMA_0	,	0x90cb8521	},
-
-	{	XFER_MW_DMA_2	,	0xa0ca8521	},
-	{	XFER_MW_DMA_1	,	0xa0ca8532	},
-	{	XFER_MW_DMA_0	,	0xa0ca8575	},
-
-	{	XFER_PIO_4	,	0xc0ca8521	},
-	{	XFER_PIO_3	,	0xc0ca8532	},
-	{	XFER_PIO_2	,	0xc0ca8542	},
-	{	XFER_PIO_1	,	0xc0d08572	},
-	{	XFER_PIO_0	,	0xc0d08585	},
-	{	0		,	0x01208585	}
-};
-
-#define HPT366_DEBUG_DRIVE_INFO		0
-#define HPT366_ALLOW_ATA66_4		1
-#define HPT366_ALLOW_ATA66_3		1
-
-#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
-static int hpt366_get_info(char *, char **, off_t, int);
-extern int (*hpt366_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-extern char *ide_media_verbose(ide_drive_t *);
-static struct pci_dev *bmide_dev;
-static struct pci_dev *bmide2_dev;
-
-static int hpt366_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p		= buffer;
-	u32 bibma	= bmide_dev->resource[4].start;
-	u32 bibma2 	= bmide2_dev->resource[4].start;
-	u8  c0 = 0, c1 = 0;
-
-        /*
-         * at that point bibma+0x2 et bibma+0xa are byte registers
-         * to investigate:
-         */
-	c0 = inb_p((unsigned short)bibma + 0x02);
-	if (bmide2_dev)
-		c1 = inb_p((unsigned short)bibma2 + 0x02);
-
-	p += sprintf(p, "\n                                HPT366 Chipset.\n");
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %sabled                         %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
-	return p-buffer;/* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-byte hpt366_proc = 0;
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-byte hpt363_shared_irq = 0;
-byte hpt363_shared_pin = 0;
-
-static int check_in_drive_lists (ide_drive_t *drive, const char **list)
-{
-	struct hd_driveid *id = drive->id;
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("check_in_drive_lists(%s, %p)\n", drive->name, list);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-
-	while (*list) {
-		if (!strcmp(*list++,id->model)) {
-#ifdef DEBUG
-			printk("%s: Broken ASIC, BackSpeeding (U)DMA for %s\n", drive->name, id->model);
-#endif /* DEBUG */
-			return 1;
-		}
-	}
-	return 0;
-}
-
-static unsigned int pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
-{
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("pci_bus_clock_list(speed=0x%02x, table=%p)\n", speed, chipset_table);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	for ( ; chipset_table->xfer_speed ; chipset_table++)
-		if (chipset_table->xfer_speed == speed) {
-#if HPT366_DEBUG_DRIVE_INFO
-			printk("pci_bus_clock_list: found match: 0x%08x\n", chipset_table->chipset_settings);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-			return chipset_table->chipset_settings;
-		}
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("pci_bus_clock_list: using default: 0x%08x\n", 0x01208585);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	return 0x01208585;
-}
-
-static int hpt366_tune_chipset (ide_drive_t *drive, byte speed)
-{
-	int			err;
-#if HPT366_DEBUG_DRIVE_INFO
-	int drive_number	= ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	byte regtime		= (drive->select.b.unit & 0x01) ? 0x44 : 0x40;
-	unsigned int reg1	= 0;
-	unsigned int reg2	= 0;
-
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("hpt366_tune_chipset(%s, speed=0x%02x)\n", drive->name, speed);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-
-	pci_read_config_dword(HWIF(drive)->pci_dev, regtime, &reg1);
-	/* detect bus speed by looking at control reg timing: */
-	switch((reg1 >> 8) & 7) {
-		case 5:
-			reg2 = pci_bus_clock_list(speed, forty_base);
-			break;
-		case 9:
-			reg2 = pci_bus_clock_list(speed, twenty_five_base);
-			break;
-		default:
-			printk("hpt366: assuming 33Mhz PCI bus\n");
-		case 7:
-			reg2 = pci_bus_clock_list(speed, thirty_three_base);
-			break;
-	}
-	/*
-	 * Disable on-chip PIO FIFO/buffer (to avoid problems handling I/O errors later)
-	 */
-	if (speed >= XFER_MW_DMA_0) {
-		reg2 = (reg2 & ~0xc0000000) | (reg1 & 0xc0000000);
-	} else {
-		reg2 = (reg2 & ~0x30070000) | (reg1 & 0x30070000);
-	}	
-	reg2 &= ~0x80000000;
-
-	pci_write_config_dword(HWIF(drive)->pci_dev, regtime, reg2);
-	err = ide_config_drive_speed(drive, speed);
-
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("%s: speed=0x%02x(%s), drive%d, old=0x%08x, new=0x%08x, err=0x%04x\n",
-		drive->name, speed, ide_xfer_verbose(speed),
-		drive_number, reg1, reg2, err);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	return(err);
-}
-
-/*
- * This allows the configuration of ide_pci chipset registers
- * for cards that learn about the drive's UDMA, DMA, PIO capabilities
- * after the drive is reported by the OS.  Initally for designed for
- * HPT366 UDMA chipset by HighPoint|Triones Technologies, Inc.
- *
- * check_in_drive_lists(drive, bad_ata66_4)
- * check_in_drive_lists(drive, bad_ata66_3)
- * check_in_drive_lists(drive, bad_ata33)
- *
- */
-static int config_chipset_for_dma (ide_drive_t *drive)
-{
-	struct hd_driveid *id	= drive->id;
-	byte speed		= 0x00;
-	byte reg51h		= 0;
-	int  rval;
-
-	if ((id->dma_ultra & 0x0010) &&
-	    (!check_in_drive_lists(drive, bad_ata66_4)) &&
-	    (HPT366_ALLOW_ATA66_4) &&
-	    (HWIF(drive)->udma_four)) {
-		speed = XFER_UDMA_4;
-	} else if ((id->dma_ultra & 0x0008) &&
-		   (!check_in_drive_lists(drive, bad_ata66_3)) &&
-		   (HPT366_ALLOW_ATA66_3) &&
-		   (HWIF(drive)->udma_four)) {
-		speed = XFER_UDMA_3;
-	} else if (id->dma_ultra && (!check_in_drive_lists(drive, bad_ata33))) {
-		if (id->dma_ultra & 0x0004) {
-			speed = XFER_UDMA_2;
-		} else if (id->dma_ultra & 0x0002) {
-			speed = XFER_UDMA_1;
-		} else if (id->dma_ultra & 0x0001) {
-			speed = XFER_UDMA_0;
-		}
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-	} else if (id->dma_1word & 0x0004) {
-		speed = XFER_SW_DMA_2;
-	} else if (id->dma_1word & 0x0002) {
-		speed = XFER_SW_DMA_1;
-	} else if (id->dma_1word & 0x0001) {
-		speed = XFER_SW_DMA_0;
-        } else {
-#if HPT366_DEBUG_DRIVE_INFO
-		printk("%s: config_chipset_for_dma: returning 'ide_dma_off_quietly'\n", drive->name);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-		return ((int) ide_dma_off_quietly);
-	}
-
-	pci_read_config_byte(HWIF(drive)->pci_dev, 0x51, &reg51h);
-
-#ifdef CONFIG_HPT366_FIP
-	/*
-	 * Some drives prefer/allow for the method of handling interrupts.
-	 */
-	if (!(reg51h & 0x80))
-		pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, reg51h|0x80);
-#else /* ! CONFIG_HPT366_FIP */
-	/*
-	 * Disable the "fast interrupt" prediction.
-	 * Instead, always wait for the real interrupt from the drive!
-	 */
-	if (reg51h & 0x80)
-		pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, reg51h & ~0x80);
-#endif /* CONFIG_HPT366_FIP */
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("%s: config_chipset_for_dma:  speed=0x%04x\n", drive->name, speed);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	(void) hpt366_tune_chipset(drive, speed);
-
-	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("%s: config_chipset_for_dma: returning %d (%s)\n", drive->name, rval, rval == ide_dma_on ? "dma_on" : "dma_off");
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	return rval;
-}
-
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
-	unsigned short xfer_pio	= drive->id->eide_pio_modes;
-	byte			timing, speed, pio;
-
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("%s: config_chipset_for_pio\n", drive->name);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 :
-			   (drive->id->tPIO & 2) ? 0x02 :
-			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-	switch(timing) {
-		case 4: speed = XFER_PIO_4;break;
-		case 3: speed = XFER_PIO_3;break;
-		case 2: speed = XFER_PIO_2;break;
-		case 1: speed = XFER_PIO_1;break;
-		default:
-			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
-			break;
-	}
-#if HPT366_DEBUG_DRIVE_INFO
-	printk("%s: config_chipset_for_pio:  speed=0x%04x\n", drive->name, speed);
-#endif /* HPT366_DEBUG_DRIVE_INFO */
-	(void) hpt366_tune_chipset(drive, speed);
-}
-
-static void hpt366_tune_drive (ide_drive_t *drive, byte pio)
-{
-	byte speed;
-	switch(pio) {
-		case 4:		speed = XFER_PIO_4;break;
-		case 3:		speed = XFER_PIO_3;break;
-		case 2:		speed = XFER_PIO_2;break;
-		case 1:		speed = XFER_PIO_1;break;
-		default:	speed = XFER_PIO_0;break;
-	}
-	(void) hpt366_tune_chipset(drive, speed);
-}
-
-static int config_drive_xfer_rate (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
-
-	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
-			goto fast_ata_pio;
-		}
-		dma_func = ide_dma_off_quietly;
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & 0x001F) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & 0x0007) ||
-			    (id->dma_1word & 0x0007)) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive);
-				if (dma_func != ide_dma_on)
-					goto no_dma_set;
-			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			if (id->eide_dma_time > 150) {
-				goto no_dma_set;
-			}
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive);
-			if (dma_func != ide_dma_on)
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
-no_dma_set:
-
-		config_chipset_for_pio(drive);
-	}
-	return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-/*
- * hpt366_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- *
- * This is specific to the HPT366 UDMA bios chipset
- * by HighPoint|Triones Technologies, Inc.
- */
-int hpt366_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	byte reg50h = 0;
-
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		case ide_dma_lostirq:
-			pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, &reg50h);
-			pci_write_config_byte(HWIF(drive)->pci_dev, 0x50, reg50h|0x03);
-			pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, &reg50h);
-			/* ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); */
-		case ide_dma_timeout:
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-}
-
-unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name)
-{
-	byte test = 0;
-
-	if (dev->resource[PCI_ROM_RESOURCE].start)
-		pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
-
-	pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &test);
-	if (test != 0x08)
-		pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x08);
-
-	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &test);
-	if (test != 0x78)
-		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78);
-
-	pci_read_config_byte(dev, PCI_MIN_GNT, &test);
-	if (test != 0x08)
-		pci_write_config_byte(dev, PCI_MIN_GNT, 0x08);
-
-	pci_read_config_byte(dev, PCI_MAX_LAT, &test);
-	if (test != 0x08)
-		pci_write_config_byte(dev, PCI_MAX_LAT, 0x08);
-
-#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
-	if (!hpt366_proc) {
-		hpt366_proc = 1;
-		bmide_dev = dev;
-		hpt366_display_info = &hpt366_get_info;
-	}
-	if ((hpt366_proc) && ((dev->devfn - bmide_dev->devfn) == 1)) {
-		bmide2_dev = dev;
-	}
-#endif /* DISPLAY_HPT366_TIMINGS && CONFIG_PROC_FS */
-
-	return dev->irq;
-}
-
-unsigned int __init ata66_hpt366 (ide_hwif_t *hwif)
-{
-	byte ata66 = 0;
-
-	pci_read_config_byte(hwif->pci_dev, 0x5a, &ata66);
-#ifdef DEBUG
-	printk("HPT366: reg5ah=0x%02x ATA-%s Cable Port%d\n",
-		ata66, (ata66 & 0x02) ? "33" : "66",
-		PCI_FUNC(hwif->pci_dev->devfn));
-#endif /* DEBUG */
-	return ((ata66 & 0x02) ? 0 : 1);
-}
-
-void __init ide_init_hpt366 (ide_hwif_t *hwif)
-{
-	hwif->tuneproc = &hpt366_tune_drive;
-	if (hwif->dma_base) {
-		hwif->dmaproc = &hpt366_dmaproc;
-	} else {
-		hwif->autodma = 0;
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-}
-
-void ide_dmacapable_hpt366 (ide_hwif_t *hwif, unsigned long dmabase)
-{
-	byte masterdma = 0, slavedma = 0;
-	byte dma_new = 0, dma_old = inb(dmabase+2);
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-
-	dma_new = dma_old;
-	pci_read_config_byte(hwif->pci_dev, 0x43, &masterdma);
-	pci_read_config_byte(hwif->pci_dev, 0x47, &slavedma);
-
-	if (masterdma & 0x30)	dma_new |= 0x20;
-	if (slavedma & 0x30)	dma_new |= 0x40;
-	if (dma_new != dma_old) outb(dma_new, dmabase+2);
-
-	__restore_flags(flags);	/* local CPU only */
-
-	ide_setup_dma(hwif, dmabase, 8);
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ht6560b.c linux/drivers/block/ht6560b.c
--- v2.3.51/linux/drivers/block/ht6560b.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/ht6560b.c	Wed Dec 31 16:00:00 1969
@@ -1,342 +0,0 @@
-/*
- *  linux/drivers/block/ht6560b.c		Version 0.07	Feb  1, 2000
- *
- *  Copyright (C) 1995-2000  Linus Torvalds & author (see below)
- */
-
-/*
- *
- *  Version 0.01        Initial version hacked out of ide.c
- *
- *  Version 0.02        Added support for PIO modes, auto-tune
- *
- *  Version 0.03        Some cleanups
- *
- *  Version 0.05        PIO mode cycle timings auto-tune using bus-speed
- *
- *  Version 0.06        Prefetch mode now defaults no OFF. To set
- *                      prefetch mode OFF/ON use "hdparm -p8/-p9".
- *                      Unmask irq is disabled when prefetch mode
- *                      is enabled.
- *
- *  Version 0.07        Trying to fix CD-ROM detection problem.
- *                      "Prefetch" mode bit OFF for ide disks and
- *                      ON for anything else.
- *
- *
- *  HT-6560B EIDE-controller support
- *  To activate controller support use kernel parameter "ide0=ht6560b".
- *  Use hdparm utility to enable PIO mode support.
- *
- *  Author:    Mikko Ala-Fossi            <maf@iki.fi>
- *             Jan Evert van Grootheest   <janevert@iae.nl>
- *
- *  Try:  http://www.maf.iki.fi/~maf/ht6560b/
- */
-
-#define HT6560B_VERSION "v0.07"
-
-#undef REALLY_SLOW_IO		/* most systems can safely undef this */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-/* #define DEBUG */  /* remove comments for DEBUG messages */
-
-/*
- * The special i/o-port that HT-6560B uses to configuration:
- *    bit0 (0x01): "1" selects secondary interface
- *    bit2 (0x04): "1" enables FIFO function
- *    bit5 (0x20): "1" enables prefetched data read function  (???)
- *
- * The special i/o-port that HT-6560A uses to configuration:
- *    bit0 (0x01): "1" selects secondary interface
- *    bit1 (0x02): "1" enables prefetched data read function
- *    bit2 (0x04): "0" enables multi-master system	      (?)
- *    bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time	      (?)
- */
-#define HT_CONFIG_PORT	  0x3e6
-#define HT_CONFIG(drivea) (byte)(((drivea)->drive_data & 0xff00) >> 8)
-/*
- * FIFO + PREFETCH (both a/b-model)
- */
-#define HT_CONFIG_DEFAULT 0x1c /* no prefetch */
-/* #define HT_CONFIG_DEFAULT 0x3c */ /* with prefetch */
-#define HT_SECONDARY_IF	  0x01
-#define HT_PREFETCH_MODE  0x20
-
-/*
- * ht6560b Timing values:
- *
- * I reviewed some assembler source listings of htide drivers and found
- * out how they setup those cycle time interfacing values, as they at Holtek
- * call them. IDESETUP.COM that is supplied with the drivers figures out
- * optimal values and fetches those values to drivers. I found out that
- * they use IDE_SELECT_REG to fetch timings to the ide board right after
- * interface switching. After that it was quite easy to add code to
- * ht6560b.c.
- *
- * IDESETUP.COM gave me values 0x24, 0x45, 0xaa, 0xff that worked fine
- * for hda and hdc. But hdb needed higher values to work, so I guess
- * that sometimes it is necessary to give higher value than IDESETUP
- * gives.   [see cmd640.c for an extreme example of this. -ml]
- *
- * Perhaps I should explain something about these timing values:
- * The higher nibble of value is the Recovery Time  (rt) and the lower nibble
- * of the value is the Active Time  (at). Minimum value 2 is the fastest and
- * the maximum value 15 is the slowest. Default values should be 15 for both.
- * So 0x24 means 2 for rt and 4 for at. Each of the drives should have
- * both values, and IDESETUP gives automatically rt=15 st=15 for CDROMs or
- * similar. If value is too small there will be all sorts of failures.
- *
- * Timing byte consists of
- *	High nibble:  Recovery Cycle Time  (rt)
- *	     The valid values range from 2 to 15. The default is 15.
- *
- *	Low nibble:   Active Cycle Time	   (at)
- *	     The valid values range from 2 to 15. The default is 15.
- *
- * You can obtain optimized timing values by running Holtek IDESETUP.COM
- * for DOS. DOS drivers get their timing values from command line, where
- * the first value is the Recovery Time and the second value is the
- * Active Time for each drive. Smaller value gives higher speed.
- * In case of failures you should probably fall back to a higher value.
- */
-#define HT_TIMING(drivea) (byte)((drivea)->drive_data & 0x00ff)
-#define HT_TIMING_DEFAULT 0xff
-
-/*
- * This routine handles interface switching for the peculiar hardware design
- * on the F.G.I./Holtek HT-6560B VLB IDE interface.
- * The HT-6560B can only enable one IDE port at a time, and requires a
- * silly sequence (below) whenever we switch between primary and secondary.
- */
-
-/*
- * This routine is invoked from ide.c to prepare for access to a given drive.
- */
-static void ht6560b_selectproc (ide_drive_t *drive)
-{
-	unsigned long flags;
-	static byte current_select = 0;
-	static byte current_timing = 0;
-	byte select, timing;
-	
-	__save_flags (flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	
-	select = HT_CONFIG(drive);
-	timing = HT_TIMING(drive);
-	
-	if (select != current_select || timing != current_timing) {
-		current_select = select;
-		current_timing = timing;
-		if (drive->media != ide_disk || !drive->present)
-			select |= HT_PREFETCH_MODE;
-		(void) inb(HT_CONFIG_PORT);
-		(void) inb(HT_CONFIG_PORT);
-		(void) inb(HT_CONFIG_PORT);
-		(void) inb(HT_CONFIG_PORT);
-		outb(select, HT_CONFIG_PORT);
-		/*
-		 * Set timing for this drive:
-		 */
-		outb(timing, IDE_SELECT_REG);
-		(void) inb(IDE_STATUS_REG);
-#ifdef DEBUG
-		printk("ht6560b: %s: select=%#x timing=%#x\n", drive->name, select, timing);
-#endif
-	}
-	__restore_flags (flags);	/* local CPU only */
-}
-
-/*
- * Autodetection and initialization of ht6560b
- */
-static int __init try_to_init_ht6560b(void)
-{
-	byte orig_value;
-	int i;
-	
-	/* Autodetect ht6560b */
-	if ((orig_value=inb(HT_CONFIG_PORT)) == 0xff)
-		return 0;
-	
-	for (i=3;i>0;i--) {
-		outb(0x00, HT_CONFIG_PORT);
-		if (!( (~inb(HT_CONFIG_PORT)) & 0x3f )) {
-			outb(orig_value, HT_CONFIG_PORT);
-			return 0;
-		}
-	}
-	outb(0x00, HT_CONFIG_PORT);
-	if ((~inb(HT_CONFIG_PORT))& 0x3f) {
-		outb(orig_value, HT_CONFIG_PORT);
-		return 0;
-	}
-	/*
-	 * Ht6560b autodetected
-	 */
-	outb(HT_CONFIG_DEFAULT, HT_CONFIG_PORT);
-	outb(HT_TIMING_DEFAULT, 0x1f6);  /* IDE_SELECT_REG */
-	(void) inb(0x1f7);               /* IDE_STATUS_REG */
-	
-	printk("\nht6560b " HT6560B_VERSION
-	       ": chipset detected and initialized"
-#ifdef DEBUG
-	       " with debug enabled"
-#endif
-		);
-	return 1;
-}
-
-static byte ht_pio2timings(ide_drive_t *drive, byte pio)
-{
-	int bus_speed, active_time, recovery_time;
-	int active_cycles, recovery_cycles;
-	ide_pio_data_t d;
-	
-        if (pio) {
-		pio = ide_get_best_pio_mode(drive, pio, 5, &d);
-		
-		/*
-		 *  Just like opti621.c we try to calculate the
-		 *  actual cycle time for recovery and activity
-		 *  according system bus speed.
-		 */
-		bus_speed = ide_system_bus_speed();
-		active_time = ide_pio_timings[pio].active_time;
-		recovery_time = d.cycle_time 
-			- active_time
-			- ide_pio_timings[pio].setup_time;
-		/*
-		 *  Cycle times should be Vesa bus cycles
-		 */
-		active_cycles   = (active_time   * bus_speed + 999) / 1000;
-		recovery_cycles = (recovery_time * bus_speed + 999) / 1000;
-		/*
-		 *  Upper and lower limits
-		 */
-		if (active_cycles   < 2)  active_cycles   = 2;
-		if (recovery_cycles < 2)  recovery_cycles = 2;
-		if (active_cycles   > 15) active_cycles   = 15;
-		if (recovery_cycles > 15) recovery_cycles = 0;  /* 0==16 */
-		
-#ifdef DEBUG
-		printk("ht6560b: drive %s setting pio=%d recovery=%d (%dns) active=%d (%dns)\n", drive->name, pio, recovery_cycles, recovery_time, active_cycles, active_time);
-#endif
-		
-		return (byte)((recovery_cycles << 4) | active_cycles);
-	} else {
-		
-#ifdef DEBUG
-		printk("ht6560b: drive %s setting pio=0\n", drive->name);
-#endif
-		
-		return HT_TIMING_DEFAULT;    /* default setting */
-	}
-}
-
-/*
- *  Enable/Disable so called prefetch mode
- */
-static void ht_set_prefetch(ide_drive_t *drive, byte state)
-{
-	unsigned long flags;
-	int t = HT_PREFETCH_MODE << 8;
-	
-	save_flags (flags);	/* all CPUs */
-	cli();		        /* all CPUs */
-	
-	/*
-	 *  Prefetch mode and unmask irq seems to conflict
-	 */
-	if (state) {
-		drive->drive_data |= t;   /* enable prefetch mode */
-		drive->no_unmask = 1;
-		drive->unmask = 0;
-	} else {
-		drive->drive_data &= ~t;  /* disable prefetch mode */
-		drive->no_unmask = 0;
-	}
-	
-	restore_flags (flags);	/* all CPUs */
-	
-#ifdef DEBUG
-	printk("ht6560b: drive %s prefetch mode %sabled\n", drive->name, (state ? "en" : "dis"));
-#endif
-}
-
-static void tune_ht6560b (ide_drive_t *drive, byte pio)
-{
-	unsigned long flags;
-	byte timing;
-	
-	switch (pio) {
-	case 8:         /* set prefetch off */
-	case 9:         /* set prefetch on */
-		ht_set_prefetch(drive, pio & 1);
-		return;
-	}
-	
-	timing = ht_pio2timings(drive, pio);
-	
-	save_flags (flags);	/* all CPUs */
-	cli();		        /* all CPUs */
-	
-	drive->drive_data &= 0xff00;
-	drive->drive_data |= timing;
-	
-	restore_flags (flags);	/* all CPUs */
-	
-#ifdef DEBUG
-	printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing);
-#endif
-}
-
-void __init init_ht6560b (void)
-{
-	int t;
-	
-	if (check_region(HT_CONFIG_PORT,1)) {
-		printk(KERN_ERR "ht6560b: PORT %#x ALREADY IN USE\n", HT_CONFIG_PORT);
-	} else {
-		if (try_to_init_ht6560b()) {
-			request_region(HT_CONFIG_PORT, 1, ide_hwifs[0].name);
-			ide_hwifs[0].chipset = ide_ht6560b;
-			ide_hwifs[1].chipset = ide_ht6560b;
-			ide_hwifs[0].selectproc = &ht6560b_selectproc;
-			ide_hwifs[1].selectproc = &ht6560b_selectproc;
-			ide_hwifs[0].tuneproc = &tune_ht6560b;
-			ide_hwifs[1].tuneproc = &tune_ht6560b;
-			ide_hwifs[0].serialized = 1;  /* is this needed? */
-			ide_hwifs[1].serialized = 1;  /* is this needed? */
-			ide_hwifs[0].mate = &ide_hwifs[1];
-			ide_hwifs[1].mate = &ide_hwifs[0];
-			ide_hwifs[1].channel = 1;
-			
-			/*
-			 * Setting default configurations for drives
-			 */
-			t = (HT_CONFIG_DEFAULT << 8);
-			t |= HT_TIMING_DEFAULT;
-			ide_hwifs[0].drives[0].drive_data = t;
-			ide_hwifs[0].drives[1].drive_data = t;
-			t |= (HT_SECONDARY_IF << 8);
-			ide_hwifs[1].drives[0].drive_data = t;
-			ide_hwifs[1].drives[1].drive_data = t;
-		} else
-			printk(KERN_ERR "ht6560b: not found\n");
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/icside.c linux/drivers/block/icside.c
--- v2.3.51/linux/drivers/block/icside.c	Sun Feb 20 21:12:38 2000
+++ linux/drivers/block/icside.c	Wed Dec 31 16:00:00 1969
@@ -1,643 +0,0 @@
-/*
- * linux/drivers/block/icside.c
- *
- * Copyright (c) 1996,1997 Russell King.
- *
- * Changelog:
- *  08-Jun-1996	RMK	Created
- *  12-Sep-1997	RMK	Added interrupt enable/disable
- *  17-Apr-1999	RMK	Added support for V6 EASI
- *  22-May-1999	RMK	Added support for V6 DMA
- */
-
-#include <linux/config.h>
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/blkdev.h>
-#include <linux/errno.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/dma.h>
-#include <asm/ecard.h>
-#include <asm/io.h>
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-/*
- * Maximum number of interfaces per card
- */
-#define MAX_IFS	2
-
-#define ICS_IDENT_OFFSET		0x8a0
-
-#define ICS_ARCIN_V5_INTRSTAT		0x000
-#define ICS_ARCIN_V5_INTROFFSET		0x001
-#define ICS_ARCIN_V5_IDEOFFSET		0xa00
-#define ICS_ARCIN_V5_IDEALTOFFSET	0xae0
-#define ICS_ARCIN_V5_IDESTEPPING	4
-
-#define ICS_ARCIN_V6_IDEOFFSET_1	0x800
-#define ICS_ARCIN_V6_INTROFFSET_1	0x880
-#define ICS_ARCIN_V6_INTRSTAT_1		0x8a4
-#define ICS_ARCIN_V6_IDEALTOFFSET_1	0x8e0
-#define ICS_ARCIN_V6_IDEOFFSET_2	0xc00
-#define ICS_ARCIN_V6_INTROFFSET_2	0xc80
-#define ICS_ARCIN_V6_INTRSTAT_2		0xca4
-#define ICS_ARCIN_V6_IDEALTOFFSET_2	0xce0
-#define ICS_ARCIN_V6_IDESTEPPING	4
-
-struct cardinfo {
-	unsigned int dataoffset;
-	unsigned int ctrloffset;
-	unsigned int stepping;
-};
-
-static struct cardinfo icside_cardinfo_v5 = {
-	ICS_ARCIN_V5_IDEOFFSET,
-	ICS_ARCIN_V5_IDEALTOFFSET,
-	ICS_ARCIN_V5_IDESTEPPING
-};
-
-static struct cardinfo icside_cardinfo_v6_1 = {
-	ICS_ARCIN_V6_IDEOFFSET_1,
-	ICS_ARCIN_V6_IDEALTOFFSET_1,
-	ICS_ARCIN_V6_IDESTEPPING
-};
-
-static struct cardinfo icside_cardinfo_v6_2 = {
-	ICS_ARCIN_V6_IDEOFFSET_2,
-	ICS_ARCIN_V6_IDEALTOFFSET_2,
-	ICS_ARCIN_V6_IDESTEPPING
-};
-
-static const card_ids icside_cids[] = {
-	{ MANU_ICS,  PROD_ICS_IDE  },
-	{ MANU_ICS2, PROD_ICS2_IDE },
-	{ 0xffff, 0xffff }
-};
-
-typedef enum {
-	ics_if_unknown,
-	ics_if_arcin_v5,
-	ics_if_arcin_v6
-} iftype_t;
-
-/* ---------------- Version 5 PCB Support Functions --------------------- */
-/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
- * Purpose  : enable interrupts from card
- */
-static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
-{
-	unsigned int memc_port = (unsigned int)ec->irq_data;
-	outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
-}
-
-/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
- * Purpose  : disable interrupts from card
- */
-static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
-{
-	unsigned int memc_port = (unsigned int)ec->irq_data;
-	inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
-}
-
-static const expansioncard_ops_t icside_ops_arcin_v5 = {
-	icside_irqenable_arcin_v5,
-	icside_irqdisable_arcin_v5,
-	NULL,
-	NULL,
-	NULL,
-	NULL
-};
-
-
-/* ---------------- Version 6 PCB Support Functions --------------------- */
-/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
- * Purpose  : enable interrupts from card
- */
-static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
-{
-	unsigned int ide_base_port = (unsigned int)ec->irq_data;
-
-	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
-	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
-}
-
-/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
- * Purpose  : disable interrupts from card
- */
-static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
-{
-	unsigned int ide_base_port = (unsigned int)ec->irq_data;
-
-	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
-	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
-}
-
-/* Prototype: icside_irqprobe(struct expansion_card *ec)
- * Purpose  : detect an active interrupt from card
- */
-static int icside_irqpending_arcin_v6(struct expansion_card *ec)
-{
-	unsigned int ide_base_port = (unsigned int)ec->irq_data;
-
-	return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
-	       inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
-}
-
-static const expansioncard_ops_t icside_ops_arcin_v6 = {
-	icside_irqenable_arcin_v6,
-	icside_irqdisable_arcin_v6,
-	icside_irqpending_arcin_v6,
-	NULL,
-	NULL,
-	NULL
-};
-
-/* Prototype: icside_identifyif (struct expansion_card *ec)
- * Purpose  : identify IDE interface type
- * Notes    : checks the description string
- */
-static iftype_t icside_identifyif (struct expansion_card *ec)
-{
-	unsigned int addr;
-	iftype_t iftype;
-	int id = 0;
-
-	iftype = ics_if_unknown;
-
-	addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
-
-	id = inb (addr) & 1;
-	id |= (inb (addr + 1) & 1) << 1;
-	id |= (inb (addr + 2) & 1) << 2;
-	id |= (inb (addr + 3) & 1) << 3;
-
-	switch (id) {
-	case 0: /* A3IN */
-		printk("icside: A3IN unsupported\n");
-		break;
-
-	case 1: /* A3USER */
-		printk("icside: A3USER unsupported\n");
-		break;
-
-	case 3:	/* ARCIN V6 */
-		printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
-		iftype = ics_if_arcin_v6;
-		break;
-
-	case 15:/* ARCIN V5 (no id) */
-		printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
-		iftype = ics_if_arcin_v5;
-		break;
-
-	default:/* we don't know - complain very loudly */
-		printk("icside: ***********************************\n");
-		printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
-		printk("icside: ***********************************\n");
-		printk("icside: please report this to linux@arm.linux.org.uk\n");
-		printk("icside: defaulting to ARCIN V5\n");
-		iftype = ics_if_arcin_v5;
-		break;
-	}
-
-	return iftype;
-}
-
-#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
-/*
- * SG-DMA support.
- *
- * Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers.
- * There is only one DMA controller per card, which means that only
- * one drive can be accessed at one time.  NOTE! We do not enforce that
- * here, but we rely on the main IDE driver spotting that both
- * interfaces use the same IRQ, which should guarantee this.
- */
-#define TABLE_SIZE 2048
-
-static int
-icside_build_dmatable(ide_drive_t *drive, int reading)
-{
-	struct request *rq = HWGROUP(drive)->rq;
-	struct buffer_head *bh = rq->bh;
-	unsigned long addr, size;
-	unsigned char *virt_addr;
-	unsigned int count = 0;
-	dmasg_t *sg = (dmasg_t *)HWIF(drive)->dmatable_cpu;
-
-	do {
-		if (bh == NULL) {
-			/* paging requests have (rq->bh == NULL) */
-			virt_addr = rq->buffer;
-			addr = virt_to_bus (virt_addr);
-			size = rq->nr_sectors << 9;
-		} else {
-			/* group sequential buffers into one large buffer */
-			virt_addr = bh->b_data;
-			addr = virt_to_bus (virt_addr);
-			size = bh->b_size;
-			while ((bh = bh->b_reqnext) != NULL) {
-				if ((addr + size) != virt_to_bus (bh->b_data))
-					break;
-				size += bh->b_size;
-			}
-		}
-
-		if (addr & 3) {
-			printk("%s: misaligned DMA buffer\n", drive->name);
-			return 0;
-		}
-
-		if (size) {
-			if (reading)
-				dma_cache_inv((unsigned int)virt_addr, size);
-			else
-				dma_cache_wback((unsigned int)virt_addr, size);
-		}
-
-		sg[count].address = addr;
-		sg[count].length = size;
-		if (++count >= (TABLE_SIZE / sizeof(dmasg_t))) {
-			printk("%s: DMA table too small\n", drive->name);
-			return 0;
-		}
-	} while (bh != NULL);
-
-	if (!count)
-		printk("%s: empty DMA table?\n", drive->name);
-
-	return count;
-}
-
-static int
-icside_config_if(ide_drive_t *drive, int xfer_mode)
-{
-	int func = ide_dma_off;
-
-	switch (xfer_mode) {
-	case XFER_MW_DMA_2:
-		/*
-		 * The cycle time is limited to 250ns by the r/w
-		 * pulse width (90ns), however we should still
-		 * have a maximum burst transfer rate of 8MB/s.
-		 */
-		drive->drive_data = 250;
-		break;
-
-	case XFER_MW_DMA_1:
-		drive->drive_data = 250;
-		break;
-
-	case XFER_MW_DMA_0:
-		drive->drive_data = 480;
-		break;
-
-	default:
-		drive->drive_data = 0;
-		break;
-	}
-
-	if (drive->drive_data &&
-	    ide_config_drive_speed(drive, (byte) xfer_mode) == 0)
-		func = ide_dma_on;
-	else
-		drive->drive_data = 480;
-
-	printk("%s: %s selected (peak %dMB/s)\n", drive->name,
-		ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
-
-	return func;
-}
-
-static int
-icside_dma_check(ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	ide_hwif_t *hwif = HWIF(drive);
-	int autodma = hwif->autodma;
-	int xfer_mode = XFER_PIO_2;
-	int func = ide_dma_off_quietly;
-
-	if (!id || !(id->capability & 1) || !autodma)
-		goto out;
-
-	/*
-	 * Consult the list of known "bad" drives
-	 */
-	if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-		func = ide_dma_off;
-		goto out;
-	}
-
-	/*
-	 * Enable DMA on any drive that has multiword DMA
-	 */
-	if (id->field_valid & 2) {
-		if (id->dma_mword & 4) {
-			xfer_mode = XFER_MW_DMA_2;
-			func = ide_dma_on;
-		} else if (id->dma_mword & 2) {
-			xfer_mode = XFER_MW_DMA_1;
-			func = ide_dma_on;
-		} else if (id->dma_mword & 1) {
-			xfer_mode = XFER_MW_DMA_0;
-			func = ide_dma_on;
-		}
-		goto out;
-	}
-
-	/*
-	 * Consult the list of known "good" drives
-	 */
-	if (ide_dmaproc(ide_dma_good_drive, drive)) {
-		if (id->eide_dma_time > 150)
-			goto out;
-		xfer_mode = XFER_MW_DMA_1;
-		func = ide_dma_on;
-	}
-
-out:
-	func = icside_config_if(drive, xfer_mode);
-
-	return hwif->dmaproc(func, drive);
-}
-
-static int
-icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	int count, reading = 0;
-
-	switch (func) {
-	case ide_dma_check:
-		return icside_dma_check(drive);
-
-	case ide_dma_read:
-		reading = 1;
-	case ide_dma_write:
-		count = icside_build_dmatable(drive, reading);
-		if (!count)
-			return 1;
-		disable_dma(hwif->hw.dma);
-
-		/* Route the DMA signals to
-		 * to the correct interface.
-		 */
-		outb(hwif->select_data, hwif->config_data);
-
-		/* Select the correct timing
-		 * for this drive
-		 */
-		set_dma_speed(hwif->hw.dma, drive->drive_data);
-
-		set_dma_sg(hwif->hw.dma, (dmasg_t *)hwif->dmatable_cpu, count);
-		set_dma_mode(hwif->hw.dma, reading ? DMA_MODE_READ
-			     : DMA_MODE_WRITE);
-
-		drive->waiting_for_dma = 1;
-		if (drive->media != ide_disk)
-			return 0;
-
-		ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
-		OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA,
-			 IDE_COMMAND_REG);
-
-	case ide_dma_begin:
-		enable_dma(hwif->hw.dma);
-		return 0;
-
-	case ide_dma_end:
-		drive->waiting_for_dma = 0;
-		disable_dma(hwif->hw.dma);
-		return get_dma_residue(hwif->hw.dma) != 0;
-
-	case ide_dma_test_irq:
-		return inb((unsigned long)hwif->hw.priv) & 1;
-
-	default:
-		return ide_dmaproc(func, drive);
-	}
-}
-
-static unsigned long
-icside_alloc_dmatable(void)
-{
-	static unsigned long dmatable;
-	static unsigned int leftover;
-	unsigned long table;
-
-	if (leftover < TABLE_SIZE) {
-#if PAGE_SIZE == TABLE_SIZE * 2
-		dmatable = __get_free_pages(GFP_KERNEL, 1);
-		leftover = PAGE_SIZE;
-#else
-		dmatable = kmalloc(TABLE_SIZE, GFP_KERNEL);
-		leftover = TABLE_SIZE;
-#endif
-	}
-
-	table = dmatable;
-	if (table) {
-		dmatable += TABLE_SIZE;
-		leftover -= TABLE_SIZE;
-	}
-
-	return table;
-}
-
-static int
-icside_setup_dma(ide_hwif_t *hwif, int autodma)
-{
-	unsigned long table = icside_alloc_dmatable();
-
-	printk("    %s: SG-DMA", hwif->name);
-
-	if (!table)
-		printk(" -- ERROR, unable to allocate DMA table\n");
-	else {
-		hwif->dmatable_cpu = (void *)table;
-		hwif->dmaproc = icside_dmaproc;
-		hwif->autodma = autodma;
-
-		printk(" capable%s\n", autodma ?
-			", auto-enable" : "");
-	}
-
-	return hwif->dmatable_cpu != NULL;
-}
-#endif
-
-static ide_hwif_t *
-icside_find_hwif(unsigned long dataport)
-{
-	ide_hwif_t *hwif;
-	int index;
-
-	for (index = 0; index < MAX_HWIFS; ++index) {
-		hwif = &ide_hwifs[index];
-		if (hwif->hw.io_ports[IDE_DATA_OFFSET] == (ide_ioreg_t)dataport)
-			goto found;
-	}
-
-	for (index = 0; index < MAX_HWIFS; ++index) {
-		hwif = &ide_hwifs[index];
-		if (!hwif->hw.io_ports[IDE_DATA_OFFSET])
-			goto found;
-	}
-
-	return NULL;
-found:
-	return hwif;
-}
-
-static ide_hwif_t *
-icside_setup(unsigned long base, struct cardinfo *info, int irq)
-{
-	unsigned long port = base + info->dataoffset;
-	ide_hwif_t *hwif;
-
-	hwif = icside_find_hwif(base);
-	if (hwif) {
-		int i;
-
-		memset(&hwif->hw, 0, sizeof(hw_regs_t));
-
-		for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
-			hwif->hw.io_ports[i] = (ide_ioreg_t)port;
-			port += 1 << info->stepping;
-		}
-		hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset;
-		hwif->hw.irq  = irq;
-		hwif->hw.dma  = NO_DMA;
-		hwif->noprobe = 0;
-		hwif->chipset = ide_acorn;
-	}
-
-	return hwif;
-}
-
-static int icside_register_v5(struct expansion_card *ec, int autodma)
-{
-	unsigned long slot_port;
-	ide_hwif_t *hwif;
-
-	slot_port = ecard_address(ec, ECARD_MEMC, 0);
-
-	ec->irqaddr  = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT);
-	ec->irqmask  = 1;
-	ec->irq_data = (void *)slot_port;
-	ec->ops      = (expansioncard_ops_t *)&icside_ops_arcin_v5;
-
-	/*
-	 * Be on the safe side - disable interrupts
-	 */
-	inb(slot_port + ICS_ARCIN_V5_INTROFFSET);
-
-	hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq);
-
-	return hwif ? 0 : -1;
-}
-
-static int icside_register_v6(struct expansion_card *ec, int autodma)
-{
-	unsigned long slot_port, port;
-	ide_hwif_t *hwif, *mate;
-	int sel = 0;
-
-	slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST);
-	port      = ecard_address(ec, ECARD_EASI, ECARD_FAST);
-
-	if (port == 0)
-		port = slot_port;
-	else
-		sel = 1 << 5;
-
-	outb(sel, slot_port);
-
-	ec->irq_data = (void *)port;
-	ec->ops      = (expansioncard_ops_t *)&icside_ops_arcin_v6;
-
-	/*
-	 * Be on the safe side - disable interrupts
-	 */
-	inb(port + ICS_ARCIN_V6_INTROFFSET_1);
-	inb(port + ICS_ARCIN_V6_INTROFFSET_2);
-
-	hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq);
-	mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq);
-
-#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
-	if (ec->dma != NO_DMA) {
-		if (request_dma(ec->dma, hwif->name))
-			goto no_dma;
-
-		if (hwif) {
-			hwif->config_data = slot_port;
-			hwif->select_data = sel;
-			hwif->hw.dma  = ec->dma;
-			hwif->hw.priv = (void *)
-					(port + ICS_ARCIN_V6_INTRSTAT_1);
-			hwif->channel = 0;
-			icside_setup_dma(hwif, autodma);
-		}
-		if (mate) {
-			mate->config_data = slot_port;
-			mate->select_data = sel | 1;
-			mate->hw.dma  = ec->dma;
-			mate->hw.priv = (void *)
-					(port + ICS_ARCIN_V6_INTRSTAT_2);
-			mate->channel = 1;
-			icside_setup_dma(mate, autodma);
-		}
-	}
-#endif
-
-no_dma:
-	return hwif || mate ? 0 : -1;
-}
-
-int icside_init(void)
-{
-	int autodma = 0;
-
-#ifdef CONFIG_IDEDMA_ICS_AUTO
-	autodma = 1;
-#endif
-
-	ecard_startfind ();
-
-	do {
-		struct expansion_card *ec;
-		int result;
-
-		ec = ecard_find(0, icside_cids);
-		if (ec == NULL)
-			break;
-
-		ecard_claim(ec);
-
-		switch (icside_identifyif(ec)) {
-		case ics_if_arcin_v5:
-			result = icside_register_v5(ec, autodma);
-			break;
-
-		case ics_if_arcin_v6:
-			result = icside_register_v6(ec, autodma);
-			break;
-
-		default:
-			result = -1;
-			break;
-		}
-
-		if (result)
-			ecard_release(ec);
-	} while (1);
-
-	return 0;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.3.51/linux/drivers/block/ide-cd.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/block/ide-cd.c	Wed Dec 31 16:00:00 1969
@@ -1,2729 +0,0 @@
-/*
- * linux/drivers/block/ide-cd.c
- * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
- * Copyright (C) 1996-1998  Erik Andersen <andersee@debian.org>
- * Copyright (C) 1998, 1999 Jens Axboe <axboe@image.dk>
- *
- * May be copied or modified under the terms of the GNU General Public
- * License.  See linux/COPYING for more information.
- *
- * ATAPI CD-ROM driver.  To be used with ide.c.
- * See Documentation/cdrom/ide-cd for usage information.
- *
- * Suggestions are welcome. Patches that work are more welcome though. ;-)
- * For those wishing to work on this driver, please be sure you download
- * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI 
- * (SFF-8020i rev 2.6) standards. These documents can be obtained by 
- * anonymous ftp from:
- * ftp://fission.dt.wdc.com/pub/standards/SFF/specs/INF-8020.PDF
- * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r01.pdf
- *
- * Drives that deviate from these standards will be accomodated as much
- * as possible via compile time or command-line options.  Since I only have
- * a few drives, you generally need to send me patches...
- *
- * ----------------------------------
- * TO DO LIST:
- * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on
- *   boot
- *
- * ----------------------------------
- * 1.00  Oct 31, 1994 -- Initial version.
- * 1.01  Nov  2, 1994 -- Fixed problem with starting request in
- *                       cdrom_check_status.
- * 1.03  Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
- * (from mlord)       -- minor changes to cdrom_setup()
- *                    -- renamed ide_dev_s to ide_drive_t, enable irq on command
- * 2.00  Nov 27, 1994 -- Generalize packet command interface;
- *                       add audio ioctls.
- * 2.01  Dec  3, 1994 -- Rework packet command interface to handle devices
- *                       which send an interrupt when ready for a command.
- * 2.02  Dec 11, 1994 -- Cache the TOC in the driver.
- *                       Don't use SCMD_PLAYAUDIO_TI; it's not included
- *                       in the current version of ATAPI.
- *                       Try to use LBA instead of track or MSF addressing
- *                       when possible.
- *                       Don't wait for READY_STAT.
- * 2.03  Jan 10, 1995 -- Rewrite block read routines to handle block sizes
- *                       other than 2k and to move multiple sectors in a
- *                       single transaction.
- * 2.04  Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives.
- *                       Thanks to Nick Saw <cwsaw@pts7.pts.mot.com> for
- *                       help in figuring this out.  Ditto for Acer and
- *                       Aztech drives, which seem to have the same problem.
- * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
- * 2.05  Jun  8, 1995 -- Don't attempt to retry after an illegal request
- *                        or data protect error.
- *                       Use HWIF and DEV_HWIF macros as in ide.c.
- *                       Always try to do a request_sense after
- *                        a failed command.
- *                       Include an option to give textual descriptions
- *                        of ATAPI errors.
- *                       Fix a bug in handling the sector cache which
- *                        showed up if the drive returned data in 512 byte
- *                        blocks (like Pioneer drives).  Thanks to
- *                        Richard Hirst <srh@gpt.co.uk> for diagnosing this.
- *                       Properly supply the page number field in the
- *                        MODE_SELECT command.
- *                       PLAYAUDIO12 is broken on the Aztech; work around it.
- * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
- *                       (my apologies to Scott, but now ide-cd.c is independent)
- * 3.00  Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl.
- *                       Implement CDROMREADAUDIO ioctl (UNTESTED).
- *                       Use input_ide_data() and output_ide_data().
- *                       Add door locking.
- *                       Fix usage count leak in cdrom_open, which happened
- *                        when a read-write mount was attempted.
- *                       Try to load the disk on open.
- *                       Implement CDROMEJECT_SW ioctl (off by default).
- *                       Read total cdrom capacity during open.
- *                       Rearrange logic in cdrom_decode_status.  Issue
- *                        request sense commands for failed packet commands
- *                        from here instead of from cdrom_queue_packet_command.
- *                        Fix a race condition in retrieving error information.
- *                       Suppress printing normal unit attention errors and
- *                        some drive not ready errors.
- *                       Implement CDROMVOLREAD ioctl.
- *                       Implement CDROMREADMODE1/2 ioctls.
- *                       Fix race condition in setting up interrupt handlers
- *                        when the `serialize' option is used.
- * 3.01  Sep  2, 1995 -- Fix ordering of reenabling interrupts in
- *                        cdrom_queue_request.
- *                       Another try at using ide_[input,output]_data.
- * 3.02  Sep 16, 1995 -- Stick total disk capacity in partition table as well.
- *                       Make VERBOSE_IDE_CD_ERRORS dump failed command again.
- *                       Dump out more information for ILLEGAL REQUEST errs.
- *                       Fix handling of errors occurring before the
- *                        packet command is transferred.
- *                       Fix transfers with odd bytelengths.
- * 3.03  Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
- *                       `DCI-2S10' drives are broken too.
- * 3.04  Nov 20, 1995 -- So are Vertos drives.
- * 3.05  Dec  1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
- * 3.06  Dec 16, 1995 -- Add support needed for partitions.
- *                       More workarounds for Vertos bugs (based on patches
- *                        from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
- *                       Try to eliminate byteorder assumptions.
- *                       Use atapi_cdrom_subchnl struct definition.
- *                       Add STANDARD_ATAPI compilation option.
- * 3.07  Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
- *                        Vertos 300.
- *                       Add NO_DOOR_LOCKING configuration option.
- *                       Handle drive_cmd requests w/NULL args (for hdparm -t).
- *                       Work around sporadic Sony55e audio play problem.
- * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
- *                        problem with "hde=cdrom" with no drive present.  -ml
- * 3.08  Mar  6, 1996 -- More Vertos workarounds.
- * 3.09  Apr  5, 1996 -- Add CDROMCLOSETRAY ioctl.
- *                       Switch to using MSF addressing for audio commands.
- *                       Reformat to match kernel tabbing style.
- *                       Add CDROM_GET_UPC ioctl.
- * 3.10  Apr 10, 1996 -- Fix compilation error with STANDARD_ATAPI.
- * 3.11  Apr 29, 1996 -- Patch from Heiko Eissfeldt <heiko@colossus.escape.de>
- *                       to remove redundant verify_area calls.
- * 3.12  May  7, 1996 -- Rudimentary changer support.  Based on patches
- *                        from Gerhard Zuber <zuber@berlin.snafu.de>.
- *                       Let open succeed even if there's no loaded disc.
- * 3.13  May 19, 1996 -- Fixes for changer code.
- * 3.14  May 29, 1996 -- Add work-around for Vertos 600.
- *                        (From Hennus Bergman <hennus@sky.ow.nl>.)
- * 3.15  July 2, 1996 -- Added support for Sanyo 3 CD changers
- *                        from Ben Galliart <bgallia@luc.edu> with 
- *                        special help from Jeff Lightfoot 
- *                        <jeffml@pobox.com>
- * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification
- * 3.16  Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
- * 3.17  Sep 17, 1996 -- Tweak audio reads for some drives.
- *                       Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
- * 3.18  Oct 31, 1996 -- Added module and DMA support.
- *                       
- *                       
- * 4.00  Nov 5, 1996   -- New ide-cd maintainer,
- *                                 Erik B. Andersen <andersee@debian.org>
- *                     -- Newer Creative drives don't always set the error
- *                          register correctly.  Make sure we see media changes
- *                          regardless.
- *                     -- Integrate with generic cdrom driver.
- *                     -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
- *                          a patch from Ciro Cattuto <>.
- *                     -- Call set_device_ro.
- *                     -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
- *                          ioctls, based on patch by Erik Andersen
- *                     -- Add some probes of drive capability during setup.
- *
- * 4.01  Nov 11, 1996  -- Split into ide-cd.c and ide-cd.h
- *                     -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE 
- *                          ioctls in favor of a generalized approach 
- *                          using the generic cdrom driver.
- *                     -- Fully integrated with the 2.1.X kernel.
- *                     -- Other stuff that I forgot (lots of changes)
- *
- * 4.02  Dec 01, 1996  -- Applied patch from Gadi Oxman <gadio@netvision.net.il>
- *                          to fix the drive door locking problems.
- *
- * 4.03  Dec 04, 1996  -- Added DSC overlap support.
- * 4.04  Dec 29, 1996  -- Added CDROMREADRAW ioclt based on patch 
- *                          by Ales Makarov (xmakarov@sun.felk.cvut.cz)
- *
- * 4.05  Nov 20, 1997  -- Modified to print more drive info on init
- *                        Minor other changes
- *                        Fix errors on CDROMSTOP (If you have a "Dolphin",
- *                          you must define IHAVEADOLPHIN)
- *                        Added identifier so new Sanyo CD-changer works
- *                        Better detection if door locking isn't supported
- *
- * 4.06  Dec 17, 1997  -- fixed endless "tray open" messages  -ml
- * 4.07  Dec 17, 1997  -- fallback to set pc->stat on "tray open"
- * 4.08  Dec 18, 1997  -- spew less noise when tray is empty
- *                     -- fix speed display for ACER 24X, 18X
- * 4.09  Jan 04, 1998  -- fix handling of the last block so we return
- *                         an end of file instead of an I/O error (Gadi)
- * 4.10  Jan 24, 1998  -- fixed a bug so now changers can change to a new
- *                         slot when there is no disc in the current slot.
- *                     -- Fixed a memory leak where info->changer_info was
- *                         malloc'ed but never free'd when closing the device.
- *                     -- Cleaned up the global namespace a bit by making more
- *                         functions static that should already have been.
- * 4.11  Mar 12, 1998  -- Added support for the CDROM_SELECT_SPEED ioctl
- *                         based on a patch for 2.0.33 by Jelle Foks 
- *                         <jelle@scintilla.utwente.nl>, a patch for 2.0.33
- *                         by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI
- *                         version, and my own efforts.  -erik
- *                     -- Fixed a stupid bug which egcs was kind enough to
- *                         inform me of where "Illegal mode for this track"
- *                         was never returned due to a comparison on data
- *                         types of limited range.
- * 4.12  Mar 29, 1998  -- Fixed bug in CDROM_SELECT_SPEED so write speed is 
- *                         now set ionly for CD-R and CD-RW drives.  I had 
- *                         removed this support because it produced errors.
- *                         It produced errors _only_ for non-writers. duh.
- * 4.13  May 05, 1998  -- Suppress useless "in progress of becoming ready"
- *                         messages, since this is not an error.
- *                     -- Change error messages to be const
- *                     -- Remove a "\t" which looks ugly in the syslogs
- * 4.14  July 17, 1998 -- Change to pointing to .ps version of ATAPI spec
- *                         since the .pdf version doesn't seem to work...
- *                     -- Updated the TODO list to something more current.
- *
- * 4.15  Aug 25, 1998  -- Updated ide-cd.h to respect mechine endianess, 
- *                         patch thanks to "Eddie C. Dost" <ecd@skynet.be>
- *
- * 4.50  Oct 19, 1998  -- New maintainers!
- *                         Jens Axboe <axboe@image.dk>
- *                         Chris Zwilling <chris@cloudnet.com>
- *
- * 4.51  Dec 23, 1998  -- Jens Axboe <axboe@image.dk>
- *                      - ide_cdrom_reset enabled since the ide subsystem
- *                         handles resets fine now. <axboe@image.dk>
- *                      - Transfer size fix for Samsung CD-ROMs, thanks to
- *                        "Ville Hallik" <ville.hallik@mail.ee>.
- *                      - other minor stuff.
- *
- * 4.52  Jan 19, 1999  -- Jens Axboe <axboe@image.dk>
- *                      - Detect DVD-ROM/RAM drives
- *
- * 4.53  Feb 22, 1999   - Include other model Samsung and one Goldstar
- *                         drive in transfer size limit.
- *                      - Fix the I/O error when doing eject without a medium
- *                         loaded on some drives.
- *                      - CDROMREADMODE2 is now implemented through
- *                         CDROMREADRAW, since many drives don't support
- *                         MODE2 (even though ATAPI 2.6 says they must).
- *                      - Added ignore parameter to ide-cd (as a module), eg
- *                         	insmod ide-cd ignore='hda hdb'
- *                         Useful when using ide-cd in conjunction with
- *                         ide-scsi. TODO: non-modular way of doing the
- *                         same.
- *
- * 4.54  Aug 5, 1999	- Support for MMC2 class commands through the generic
- *			  packet interface to cdrom.c.
- *			- Unified audio ioctl support, most of it.
- *			- cleaned up various deprecated verify_area().
- *			- Added ide_cdrom_packet() as the interface for
- *			  the Uniform generic_packet().
- *			- bunch of other stuff, will fill in logs later.
- *			- report 1 slot for non-changers, like the other
- *			  cd-rom drivers. don't report select disc for
- *			  non-changers as well.
- *			- mask out audio playing, if the device can't do it.
- *
- * 4.55  Sep 1, 1999	- Eliminated the rest of the audio ioctls, except
- *			  for CDROMREADTOC[ENTRY|HEADER]. Some of the drivers
- *			  use this independently of the actual audio handling.
- *			  They will disappear later when I get the time to
- *			  do it cleanly.
- *			- Minimize the TOC reading - only do it when we
- *			  know a media change has occured.
- *			- Moved all the CDROMREADx ioctls to the Uniform layer.
- *			- Heiko Eissfeldt <heiko@colossus.escape.de> supplied
- *			  some fixes for CDI.
- *			- CD-ROM leaving door locked fix from Andries
- *			  Brouwer <Andries.Brouwer@cwi.nl>
- *			- Erik Andersen <andersen@xmission.com> unified
- *			  commands across the various drivers and how
- *			  sense errors are handled.
- *
- * 4.56  Sep 12, 1999	- Removed changer support - it is now in the
- *			  Uniform layer.
- *			- Added partition based multisession handling.
- *			- Mode sense and mode select moved to the
- *			  Uniform layer.
- *			- Fixed a problem with WPI CDS-32X drive - it
- *			  failed the capabilities 
- *
- *
- *************************************************************************/
- 
-#define IDECD_VERSION "4.56"
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/errno.h>
-#include <linux/cdrom.h>
-#include <linux/ide.h>
-
-#include <asm/irq.h>
-#include <asm/io.h>
-#include <asm/byteorder.h>
-#include <asm/uaccess.h>
-#include <asm/unaligned.h>
-
-#include "ide-cd.h"
-
-/****************************************************************************
- * Generic packet command support and error handling routines.
- */
-
-/* Mark that we've seen a media change, and invalidate our internal
-   buffers. */
-static void cdrom_saw_media_change (ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	
-	CDROM_STATE_FLAGS (drive)->media_changed = 1;
-	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
-	info->nsectors_buffered = 0;
-}
-
-
-static
-void cdrom_analyze_sense_data (ide_drive_t *drive, struct request_sense *reqbuf,
-			       struct packet_command *failed_command)
-{
-	if (reqbuf->sense_key == NOT_READY ||
-	    reqbuf->sense_key == UNIT_ATTENTION) {
-		/* Make good and sure we've seen this potential media change.
-		   Some drives (i.e. Creative) fail to present the correct
-		   sense key in the error register. */
-		cdrom_saw_media_change (drive);
-
-
-		/* Don't print not ready or unit attention errors for
-		   READ_SUBCHANNEL.  Workman (and probably other programs)
-		   uses this command to poll the drive, and we don't want
-		   to fill the syslog with useless errors. */
-		if (failed_command &&
-		    failed_command->c[0] == GPCMD_READ_SUBCHANNEL)
-			return;
-	}
-
-	if (reqbuf->error_code == 0x70 && reqbuf->sense_key  == 0x02
-	 && ((reqbuf->asc      == 0x3a && reqbuf->ascq       == 0x00) ||
-	     (reqbuf->asc      == 0x04 && reqbuf->ascq       == 0x01)))
-	{
-		/*
-		 * Suppress the following errors:
-		 * "Medium not present", "in progress of becoming ready",
-		 * and "writing" to keep the noise level down to a dull roar.
-		 */
-		return;
-	}
-
-#if VERBOSE_IDE_CD_ERRORS
-	{
-		int i;
-		const char *s;
-		char buf[80];
-
-		printk ("ATAPI device %s:\n", drive->name);
-		if (reqbuf->error_code==0x70)
-			printk("  Error: ");
-		else if (reqbuf->error_code==0x71)
-			printk("  Deferred Error: ");
-		else
-			printk("  Unknown Error Type: ");
-
-		if ( reqbuf->sense_key < ARY_LEN (sense_key_texts))
-			s = sense_key_texts[reqbuf->sense_key];
-		else
-			s = "bad sense key!";
-
-		printk ("%s -- (Sense key=0x%02x)\n", s, reqbuf->sense_key);
-
-		if (reqbuf->asc == 0x40) {
-			sprintf (buf, "Diagnostic failure on component 0x%02x",
-				 reqbuf->ascq);
-			s = buf;
-		} else {
-			int lo=0, mid, hi=ARY_LEN (sense_data_texts);
-			unsigned long key = (reqbuf->sense_key << 16);
-			key |= (reqbuf->asc << 8);
-			if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
-				key |= reqbuf->ascq;
-			s = NULL;
-
-			while (hi > lo) {
-				mid = (lo + hi) / 2;
-				if (sense_data_texts[mid].asc_ascq == key ||
-				    sense_data_texts[mid].asc_ascq == (0xff0000|key)) {
-					s = sense_data_texts[mid].text;
-					break;
-				}
-				else if (sense_data_texts[mid].asc_ascq > key)
-					hi = mid;
-				else
-					lo = mid+1;
-			}
-		}
-
-		if (s == NULL) {
-			if (reqbuf->asc > 0x80)
-				s = "(vendor-specific error)";
-			else
-				s = "(reserved error code)";
-		}
-
-		printk ("  %s -- (asc=0x%02x, ascq=0x%02x)\n",
-			s, reqbuf->asc, reqbuf->ascq);
-
-		if (failed_command != NULL) {
-
-			int lo=0, mid, hi= ARY_LEN (packet_command_texts);
-			s = NULL;
-
-			while (hi > lo) {
-				mid = (lo + hi) / 2;
-				if (packet_command_texts[mid].packet_command == failed_command->c[0]) {
-					s = packet_command_texts[mid].text;
-					break;
-				}
-				else if (packet_command_texts[mid].packet_command > failed_command->c[0])
-					hi = mid;
-				else
-					lo = mid+1;
-			}
-
-			printk ("  The failed \"%s\" packet command was: \n  \"", s);
-			for (i=0; i<sizeof (failed_command->c); i++)
-				printk ("%02x ", failed_command->c[i]);
-			printk ("\"\n");
-		}
-
-		/* The SKSV bit specifies validity of the sense_key_specific
-		 * in the next two commands. It is bit 7 of the first byte.
-		 * In the case of NOT_READY, if SKSV is set the drive can
-		 * give us nice ETA readings.
-		 */
-		if (reqbuf->sense_key == NOT_READY && (reqbuf->sks[0] & 0x80)) {
-			int progress = (reqbuf->sks[1] << 8 | reqbuf->sks[2]) * 100;
-			printk("  Command is %02d%% complete\n", progress / 0xffff);
-
-		}
-
-		if (reqbuf->sense_key == ILLEGAL_REQUEST &&
-		    (reqbuf->sks[0] & 0x80) != 0) {
-			printk ("  Error in %s byte %d",
-				(reqbuf->sks[0] & 0x40) != 0 ?
-				"command packet" : "command data",
-				(reqbuf->sks[1] << 8) + reqbuf->sks[2]);
-
-			if ((reqbuf->sks[0] & 0x40) != 0)
-				printk (" bit %d", reqbuf->sks[0] & 0x07);
-
-			printk ("\n");
-		}
-	}
-
-#else /* not VERBOSE_IDE_CD_ERRORS */
-
-	/* Suppress printing unit attention and `in progress of becoming ready'
-	   errors when we're not being verbose. */
-
-	if (reqbuf->sense_key == UNIT_ATTENTION ||
-	    (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
-						reqbuf->asc == 0x3a)))
-		return;
-
-	printk ("%s: error code: 0x%02x  sense_key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
-		drive->name,
-		reqbuf->error_code, reqbuf->sense_key,
-		reqbuf->asc, reqbuf->ascq);
-#endif /* not VERBOSE_IDE_CD_ERRORS */
-}
-
-static void cdrom_queue_request_sense (ide_drive_t *drive, 
-				       struct semaphore *sem,
-				       struct packet_command *failed_command)
-{
-	struct cdrom_info *info = drive->driver_data;
-	struct request *rq;
-	struct packet_command *pc;
-
-	/* Make up a new request to retrieve sense information. */
-	pc = &info->request_sense_pc;
-	memset(pc, 0, sizeof (*pc));
-
-	pc->c[0] = GPCMD_REQUEST_SENSE;
-
-	/* just get the first 18 bytes of the sense info, there might not
-	 * be more available */
-	pc->c[4] = pc->buflen = 18;
-	pc->buffer = (char *)&info->sense_data;
-	pc->sense_data = (struct request_sense *)failed_command;
-
-	/* stuff the sense request in front of our current request */
-	rq = &info->request_sense_request;
-	ide_init_drive_cmd (rq);
-	rq->cmd = REQUEST_SENSE_COMMAND;
-	rq->buffer = (char *)pc;
-	rq->sem = sem;
-	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
-}
-
-
-static void cdrom_end_request (int uptodate, ide_drive_t *drive)
-{
-	struct request *rq = HWGROUP(drive)->rq;
-
-	if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) {
-		struct packet_command *pc = (struct packet_command *)
-			                      rq->buffer;
-		cdrom_analyze_sense_data (drive,
-			(struct request_sense *) (pc->buffer - pc->c[4]),
-			(struct packet_command *) pc->sense_data);
-	}
-	if (rq->cmd == READ && !rq->current_nr_sectors)
-		uptodate = 1;
-
-	ide_end_request (uptodate, HWGROUP(drive));
-}
-
-
-/* Returns 0 if the request should be continued.
-   Returns 1 if the request was ended. */
-static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, int good_stat,
-				int *stat_ret)
-{
-	struct request *rq = HWGROUP(drive)->rq;
-	int stat, cmd, err, sense_key;
-	struct packet_command *pc = (struct packet_command *) rq->buffer;
-	
-	/* Check for errors. */
-	stat = GET_STAT();
-	*stat_ret = stat;
-
-	if (OK_STAT (stat, good_stat, BAD_R_STAT))
-		return 0;
-
-	/* Get the IDE error register. */
-	err = GET_ERR();
-	sense_key = err >> 4;
-
-	if (rq == NULL)
-		printk ("%s: missing request in cdrom_decode_status\n",
-			drive->name);
-	else {
-		cmd = rq->cmd;
-
-		if (cmd == REQUEST_SENSE_COMMAND) {
-			/* We got an error trying to get sense info
-			   from the drive (probably while trying
-			   to recover from a former error).  Just give up. */
-
-			pc->stat = 1;
-			cdrom_end_request (1, drive);
-			*startstop = ide_error (drive, "request sense failure", stat);
-			return 1;
-
-		} else if (cmd == PACKET_COMMAND) {
-			/* All other functions, except for READ. */
-
-			struct semaphore *sem = NULL;
-
-			/* Check for tray open. */
-			if (sense_key == NOT_READY) {
-				cdrom_saw_media_change (drive);
-			} else if (sense_key == UNIT_ATTENTION) {
-				/* Check for media change. */
-				cdrom_saw_media_change (drive);
-				/*printk("%s: media changed\n",drive->name);*/
-				return 0;
-			} else {
-				/* Otherwise, print an error. */
-				ide_dump_status (drive, "packet command error",
-						 stat);
-			}
-			
-			/* Set the error flag and complete the request.
-			   Then, if we have a CHECK CONDITION status,
-			   queue a request sense command.  We must be careful,
-			   though: we don't want the thread in
-			   cdrom_queue_packet_command to wake up until
-			   the request sense has completed.  We do this
-			   by transferring the semaphore from the packet
-			   command request to the request sense request. */
-
-			if ((stat & ERR_STAT) != 0) {
-				sem = rq->sem;
-				rq->sem = NULL;
-			}
-
-			pc->stat = 1;
-			cdrom_end_request (1, drive);
-
-			if ((stat & ERR_STAT) != 0)
-				cdrom_queue_request_sense(drive, sem, pc);
-		} else {
-			/* Handle errors from READ requests. */
-
-			if (sense_key == NOT_READY) {
-				/* Tray open. */
-				cdrom_saw_media_change (drive);
-
-				/* Fail the request. */
-				printk ("%s: tray open\n", drive->name);
-				cdrom_end_request (0, drive);
-			} else if (sense_key == UNIT_ATTENTION) {
-				/* Media change. */
-				cdrom_saw_media_change (drive);
-
-				/* Arrange to retry the request.
-				   But be sure to give up if we've retried
-				   too many times. */
-				if (++rq->errors > ERROR_MAX)
-					cdrom_end_request (0, drive);
-			} else if (sense_key == ILLEGAL_REQUEST ||
-				   sense_key == DATA_PROTECT) {
-				/* No point in retrying after an illegal
-				   request or data protect error.*/
-				ide_dump_status (drive, "command error", stat);
-				cdrom_end_request (0, drive);
-			} else if ((err & ~ABRT_ERR) != 0) {
-				/* Go to the default handler
-				   for other errors. */
-				*startstop = ide_error (drive, "cdrom_decode_status", stat);
-				return 1;
-			} else if ((++rq->errors > ERROR_MAX)) {
-				/* We've racked up too many retries.  Abort. */
-				cdrom_end_request (0, drive);
-			}
-
-			/* If we got a CHECK_CONDITION status,
-			   queue a request sense command. */
-			if ((stat & ERR_STAT) != 0)
-				cdrom_queue_request_sense(drive, NULL, NULL);
-		}
-	}
-
-	/* Retry, or handle the next request. */
-	*startstop = ide_stopped;
-	return 1;
-}
-
-static int cdrom_timer_expiry(ide_drive_t *drive)
-{
-	struct request *rq = HWGROUP(drive)->rq;
-	struct packet_command *pc = (struct packet_command *) rq->buffer;
-	unsigned long wait = 0;
-
-	/* blank and format can take an extremly long time to
-	 * complete, if the IMMED bit was not set.
-	 */
-	if (pc->c[0] == GPCMD_BLANK || pc->c[0] == GPCMD_FORMAT_UNIT)
-		wait = 60*60*HZ;
-
-	return wait;
-}
-
-/* Set up the device registers for transferring a packet command on DEV,
-   expecting to later transfer XFERLEN bytes.  HANDLER is the routine
-   which actually transfers the command to the drive.  If this is a
-   drq_interrupt device, this routine will arrange for HANDLER to be
-   called when the interrupt from the drive arrives.  Otherwise, HANDLER
-   will be called immediately after the drive is prepared for the transfer. */
-
-static ide_startstop_t cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
-				       ide_handler_t *handler)
-{
-	ide_startstop_t startstop;
-	struct cdrom_info *info = drive->driver_data;
-
-	/* Wait for the controller to be idle. */
-	if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY))
-		return startstop;
-
-	if (info->dma)
-		info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive);
-
-	/* Set up the controller registers. */
-	OUT_BYTE (info->dma, IDE_FEATURE_REG);
-	OUT_BYTE (0, IDE_NSECTOR_REG);
-	OUT_BYTE (0, IDE_SECTOR_REG);
-
-	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
-	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
-	if (IDE_CONTROL_REG)
-		OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
- 
-	if (info->dma)
-		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
-
-	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
-		ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry);
-		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
-		return ide_started;
-	} else {
-		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
-		return (*handler) (drive);
-	}
-}
-
-/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
-   The device registers must have already been prepared
-   by cdrom_start_packet_command.
-   HANDLER is the interrupt handler to call when the command completes
-   or there's data ready. */
-static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive,
-                                          unsigned char *cmd_buf, int cmd_len,
-					  ide_handler_t *handler)
-{
-	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
-		/* Here we should have been called after receiving an interrupt
-		   from the device.  DRQ should how be set. */
-		int stat_dum;
-		ide_startstop_t startstop;
-
-		/* Check for errors. */
-		if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum))
-			return startstop;
-	} else {
-		ide_startstop_t startstop;
-		/* Otherwise, we must wait for DRQ to get set. */
-		if (ide_wait_stat (&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
-			return startstop;
-	}
-
-	/* Arm the interrupt handler. */
-	ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry);
-
-	/* Send the command to the device. */
-	atapi_output_bytes (drive, cmd_buf, cmd_len);
-
-	return ide_started;
-}
-
-
-
-/****************************************************************************
- * Block read functions.
- */
-
-/*
- * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector
- * buffer.  Once the first sector is added, any subsequent sectors are
- * assumed to be continuous (until the buffer is cleared).  For the first
- * sector added, SECTOR is its sector number.  (SECTOR is then ignored until
- * the buffer is cleared.)
- */
-static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
-                                  int sectors_to_transfer)
-{
-	struct cdrom_info *info = drive->driver_data;
-
-	/* Number of sectors to read into the buffer. */
-	int sectors_to_buffer = MIN (sectors_to_transfer,
-				     (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
-				       info->nsectors_buffered);
-
-	char *dest;
-
-	/* If we couldn't get a buffer, don't try to buffer anything... */
-	if (info->buffer == NULL)
-			sectors_to_buffer = 0;
-
-	/* If this is the first sector in the buffer, remember its number. */
-	if (info->nsectors_buffered == 0)
-		info->sector_buffered = sector;
-
-	/* Read the data into the buffer. */
-	dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE;
-	while (sectors_to_buffer > 0) {
-		atapi_input_bytes (drive, dest, SECTOR_SIZE);
-		--sectors_to_buffer;
-		--sectors_to_transfer;
-		++info->nsectors_buffered;
-		dest += SECTOR_SIZE;
-	}
-
-	/* Throw away any remaining data. */
-	while (sectors_to_transfer > 0) {
-		char dum[SECTOR_SIZE];
-		atapi_input_bytes (drive, dum, sizeof (dum));
-		--sectors_to_transfer;
-	}
-}
-
-/*
- * Check the contents of the interrupt reason register from the cdrom
- * and attempt to recover if there are problems.  Returns  0 if everything's
- * ok; nonzero if the request has been terminated.
- */
-static inline
-int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
-{
-	ireason &= 3;
-	if (ireason == 2) return 0;
-
-	if (ireason == 0) {
-		/* Whoops... The drive is expecting to receive data from us! */
-		printk ("%s: cdrom_read_intr: "
-			"Drive wants to transfer data the wrong way!\n",
-			drive->name);
-
-		/* Throw some data at the drive so it doesn't hang
-		   and quit this request. */
-		while (len > 0) {
-			int dum = 0;
-			atapi_output_bytes (drive, &dum, sizeof (dum));
-			len -= sizeof (dum);
-		}
-	} else  if (ireason == 1) {
-		/* Some drives (ASUS) seem to tell us that status
-		 * info is available. just get it and ignore.
-		 */
-		GET_STAT();
-		return 0;
-	} else {
-		/* Drive wants a command packet, or invalid ireason... */
-		printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
-			drive->name, ireason);
-	}
-
-	cdrom_end_request (0, drive);
-	return -1;
-}
-
-/*
- * Interrupt routine.  Called when a read request has completed.
- */
-static ide_startstop_t cdrom_read_intr (ide_drive_t *drive)
-{
-	int stat;
-	int ireason, len, sectors_to_transfer, nskip;
-	struct cdrom_info *info = drive->driver_data;
-	int i, dma = info->dma, dma_error = 0;
-	ide_startstop_t startstop;
-
-	struct request *rq = HWGROUP(drive)->rq;
-
-	/* Check for errors. */
-	if (dma) {
-		info->dma = 0;
-		if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive)))
-			HWIF(drive)->dmaproc(ide_dma_off, drive);
-	}
-
-	if (cdrom_decode_status (&startstop, drive, 0, &stat))
-		return startstop;
- 
-	if (dma) {
-		if (!dma_error) {
-			for (i = rq->nr_sectors; i > 0;) {
-				i -= rq->current_nr_sectors;
-				ide_end_request(1, HWGROUP(drive));
-			}
-			return ide_stopped;
-		} else
-			return ide_error (drive, "dma error", stat);
-	}
-
-	/* Read the interrupt reason and the transfer length. */
-	ireason = IN_BYTE (IDE_NSECTOR_REG);
-	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
-
-	/* If DRQ is clear, the command has completed. */
-	if ((stat & DRQ_STAT) == 0) {
-		/* If we're not done filling the current buffer, complain.
-		   Otherwise, complete the command normally. */
-		if (rq->current_nr_sectors > 0) {
-			printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
-				drive->name, rq->current_nr_sectors);
-			cdrom_end_request (0, drive);
-		} else
-			cdrom_end_request (1, drive);
-		return ide_stopped;
-	}
-
-	/* Check that the drive is expecting to do the same thing we are. */
-	if (cdrom_read_check_ireason (drive, len, ireason))
-		return ide_stopped;
-
-	/* Assume that the drive will always provide data in multiples
-	   of at least SECTOR_SIZE, as it gets hairy to keep track
-	   of the transfers otherwise. */
-	if ((len % SECTOR_SIZE) != 0) {
-		printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
-			drive->name, len);
-		if (CDROM_CONFIG_FLAGS (drive)->limit_nframes)
-			printk ("  This drive is not supported by this version of the driver\n");
-		else {
-			printk ("  Trying to limit transfer sizes\n");
-			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
-		}
-		cdrom_end_request (0, drive);
-		return ide_stopped;
-	}
-
-	/* The number of sectors we need to read from the drive. */
-	sectors_to_transfer = len / SECTOR_SIZE;
-
-	/* First, figure out if we need to bit-bucket
-	   any of the leading sectors. */
-	nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
-		     sectors_to_transfer);
-
-	while (nskip > 0) {
-		/* We need to throw away a sector. */
-		char dum[SECTOR_SIZE];
-		atapi_input_bytes (drive, dum, sizeof (dum));
-
-		--rq->current_nr_sectors;
-		--nskip;
-		--sectors_to_transfer;
-	}
-
-	/* Now loop while we still have data to read from the drive. */
-	while (sectors_to_transfer > 0) {
-		int this_transfer;
-
-		/* If we've filled the present buffer but there's another
-		   chained buffer after it, move on. */
-		if (rq->current_nr_sectors == 0 &&
-		    rq->nr_sectors > 0)
-			cdrom_end_request (1, drive);
-
-		/* If the buffers are full, cache the rest of the data in our
-		   internal buffer. */
-		if (rq->current_nr_sectors == 0) {
-			cdrom_buffer_sectors(drive, rq->sector, sectors_to_transfer);
-			sectors_to_transfer = 0;
-		} else {
-			/* Transfer data to the buffers.
-			   Figure out how many sectors we can transfer
-			   to the current buffer. */
-			this_transfer = MIN (sectors_to_transfer,
-					     rq->current_nr_sectors);
-
-			/* Read this_transfer sectors
-			   into the current buffer. */
-			while (this_transfer > 0) {
-				atapi_input_bytes(drive, rq->buffer, SECTOR_SIZE);
-				rq->buffer += SECTOR_SIZE;
-				--rq->nr_sectors;
-				--rq->current_nr_sectors;
-				++rq->sector;
-				--this_transfer;
-				--sectors_to_transfer;
-			}
-		}
-	}
-
-	/* Done moving data!
-	   Wait for another interrupt. */
-	ide_set_handler(drive, &cdrom_read_intr, WAIT_CMD, NULL);
-	return ide_started;
-}
-
-/*
- * Try to satisfy some of the current read request from our cached data.
- * Returns nonzero if the request has been completed, zero otherwise.
- */
-static int cdrom_read_from_buffer (ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	struct request *rq = HWGROUP(drive)->rq;
-
-	/* Can't do anything if there's no buffer. */
-	if (info->buffer == NULL) return 0;
-
-	/* Loop while this request needs data and the next block is present
-	   in our cache. */
-	while (rq->nr_sectors > 0 &&
-	       rq->sector >= info->sector_buffered &&
-	       rq->sector < info->sector_buffered + info->nsectors_buffered) {
-		if (rq->current_nr_sectors == 0)
-			cdrom_end_request (1, drive);
-
-		memcpy (rq->buffer,
-			info->buffer +
-			(rq->sector - info->sector_buffered) * SECTOR_SIZE,
-			SECTOR_SIZE);
-		rq->buffer += SECTOR_SIZE;
-		--rq->current_nr_sectors;
-		--rq->nr_sectors;
-		++rq->sector;
-	}
-
-	/* If we've satisfied the current request,
-	   terminate it successfully. */
-	if (rq->nr_sectors == 0) {
-		cdrom_end_request (1, drive);
-		return -1;
-	}
-
-	/* Move on to the next buffer if needed. */
-	if (rq->current_nr_sectors == 0)
-		cdrom_end_request (1, drive);
-
-	/* If this condition does not hold, then the kluge i use to
-	   represent the number of sectors to skip at the start of a transfer
-	   will fail.  I think that this will never happen, but let's be
-	   paranoid and check. */
-	if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
-	    (rq->sector % SECTORS_PER_FRAME) != 0) {
-		printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
-			drive->name, rq->sector);
-		cdrom_end_request (0, drive);
-		return -1;
-	}
-
-	return 0;
-}
-
-/*
- * Routine to send a read packet command to the drive.
- * This is usually called directly from cdrom_start_read.
- * However, for drq_interrupt devices, it is called from an interrupt
- * when the drive is ready to accept the command.
- */
-static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive)
-{
-	struct packet_command pc;
-	struct request *rq = HWGROUP(drive)->rq;
-	int nsect, sector, nframes, frame, nskip;
-
-	/* Number of sectors to transfer. */
-	nsect = rq->nr_sectors;
-
-	/* Starting sector. */
-	sector = rq->sector;
-
-	/* If the requested sector doesn't start on a cdrom block boundary,
-	   we must adjust the start of the transfer so that it does,
-	   and remember to skip the first few sectors.
-	   If the CURRENT_NR_SECTORS field is larger than the size
-	   of the buffer, it will mean that we're to skip a number
-	   of sectors equal to the amount by which CURRENT_NR_SECTORS
-	   is larger than the buffer size. */
-	nskip = (sector % SECTORS_PER_FRAME);
-	if (nskip > 0) {
-		/* Sanity check... */
-		if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS) &&
-			(rq->sector % CD_FRAMESIZE != 0)) {
-			printk ("%s: cdrom_start_read_continuation: buffer botch (%lu)\n",
-				drive->name, rq->current_nr_sectors);
-			cdrom_end_request (0, drive);
-			return ide_stopped;
-		}
-		sector -= nskip;
-		nsect += nskip;
-		rq->current_nr_sectors += nskip;
-	}
-
-	/* Convert from sectors to cdrom blocks, rounding up the transfer
-	   length if needed. */
-	nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
-	frame = sector / SECTORS_PER_FRAME;
-
-	/* Largest number of frames was can transfer at once is 64k-1. For
-	   some drives we need to limit this even more. */
-	nframes = MIN (nframes, (CDROM_CONFIG_FLAGS (drive)->limit_nframes) ?
-		(65534 / CD_FRAMESIZE) : 65535);
-
-	/* Set up the command */
-	memset (&pc.c, 0, sizeof (pc.c));
-	pc.c[0] = GPCMD_READ_10;
-	pc.c[7] = (nframes >> 8);
-	pc.c[8] = (nframes & 0xff);
-	put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
-
-	/* Send the command to the drive and return. */
-	return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
-					      &cdrom_read_intr);
-}
-
-
-#define IDECD_SEEK_THRESHOLD	(1000)			/* 1000 blocks */
-#define IDECD_SEEK_TIMER	(5 * WAIT_MIN_SLEEP)	/* 100 ms */
-#define IDECD_SEEK_TIMEOUT     WAIT_CMD			/* 10 sec */
-
-static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	int stat;
-	static int retry = 10;
-	ide_startstop_t startstop;
-
-	if (cdrom_decode_status (&startstop, drive, 0, &stat))
-		return startstop;
-	CDROM_CONFIG_FLAGS(drive)->seeking = 1;
-
-	if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) {
-		if (--retry == 0) {
-			printk("%s: disabled DSC seek overlap\n", drive->name);
-			drive->dsc_overlap = 0;
-		}
-	}
-	return ide_stopped;
-}
-
-static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive)
-{
-	struct packet_command pc;
-	struct request *rq = HWGROUP(drive)->rq;
-	int sector, frame, nskip;
-
-	sector = rq->sector;
-	nskip = (sector % SECTORS_PER_FRAME);
-	if (nskip > 0)
-		sector -= nskip;
-	frame = sector / SECTORS_PER_FRAME;
-
-	memset (&pc.c, 0, sizeof (pc.c));
-	pc.c[0] = GPCMD_SEEK;
-	put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]);
-	return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr);
-}
-
-static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block)
-{
-	struct cdrom_info *info = drive->driver_data;
-
-	info->dma = 0;
-	info->start_seek = jiffies;
-	return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation);
-}
-
-/* Fix up a possibly partially-processed request so that we can
-   start it over entirely, or even put it back on the request queue. */
-static void restore_request (struct request *rq)
-{
-	if (rq->buffer != rq->bh->b_data) {
-		int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
-		rq->buffer = rq->bh->b_data;
-		rq->nr_sectors += n;
-		rq->sector -= n;
-	}
-	rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
-}
-
-/*
- * Start a read request from the CD-ROM.
- */
-static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block)
-{
-	struct cdrom_info *info = drive->driver_data;
-	struct request *rq = HWGROUP(drive)->rq;
-	int minor = MINOR (rq->rq_dev);
-
-	/* If the request is relative to a partition, fix it up to refer to the
-	   absolute address.  */
-	if ((minor & PARTN_MASK) != 0) {
-		rq->sector = block;
-		minor &= ~PARTN_MASK;
-		rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
-	}
-
-	/* We may be retrying this request after an error.  Fix up
-	   any weirdness which might be present in the request packet. */
-	restore_request (rq);
-
-	/* Satisfy whatever we can of this request from our cached sector. */
-	if (cdrom_read_from_buffer(drive))
-		return ide_stopped;
-
-	/* Clear the local sector buffer. */
-	info->nsectors_buffered = 0;
-
-	/* use dma, if possible. */
-	if (drive->using_dma && (rq->sector % SECTORS_PER_FRAME == 0) &&
-				(rq->nr_sectors % SECTORS_PER_FRAME == 0))
-		info->dma = 1;
-	else
-		info->dma = 0;
-
-	/* Start sending the read request to the drive. */
-	return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation);
-}
-
-/****************************************************************************
- * Execute all other packet commands.
- */
-
-/* Forward declarations. */
-static int cdrom_lockdoor(ide_drive_t *drive, int lockflag);
-
-/* Interrupt routine for packet command completion. */
-static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
-{
-	int ireason, len, stat, thislen;
-	struct request *rq = HWGROUP(drive)->rq;
-	struct packet_command *pc = (struct packet_command *)rq->buffer;
-	struct cdrom_info *info = drive->driver_data;
-	ide_startstop_t startstop;
-
-	pc->sense_data = &info->sense_data;
-
-	/* Check for errors. */
-	if (cdrom_decode_status (&startstop, drive, 0, &stat))
-		return startstop;
-
-	/* Read the interrupt reason and the transfer length. */
-	ireason = IN_BYTE (IDE_NSECTOR_REG);
-	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
-
-	/* If DRQ is clear, the command has completed.
-	   Complain if we still have data left to transfer. */
-	if ((stat & DRQ_STAT) == 0) {
-		/* Some of the trailing request sense fields are optional, and
-		   some drives don't send them.  Sigh. */
-		if (pc->c[0] == GPCMD_REQUEST_SENSE &&
-		    pc->buflen > 0 &&
-		    pc->buflen <= 5) {
-			while (pc->buflen > 0) {
-				*pc->buffer++ = 0;
-				--pc->buflen;
-			}
-		}
-
-		if (pc->buflen == 0)
-			cdrom_end_request (1, drive);
-		else {
-			/* Comment this out, because this always happens 
-			   right after a reset occurs, and it is annoying to 
-			   always print expected stuff.  */
-			/*
-			printk ("%s: cdrom_pc_intr: data underrun %d\n",
-				drive->name, pc->buflen);
-			*/
-			pc->stat = 1;
-			cdrom_end_request (1, drive);
-		}
-		return ide_stopped;
-	}
-
-	/* Figure out how much data to transfer. */
-	thislen = pc->buflen;
-	if (thislen > len) thislen = len;
-
-	/* The drive wants to be written to. */
-	if ((ireason & 3) == 0) {
-		/* Transfer the data. */
-		atapi_output_bytes (drive, pc->buffer, thislen);
-
-		/* If we haven't moved enough data to satisfy the drive,
-		   add some padding. */
-		while (len > thislen) {
-			int dum = 0;
-			atapi_output_bytes (drive, &dum, sizeof (dum));
-			len -= sizeof (dum);
-		}
-
-		/* Keep count of how much data we've moved. */
-		pc->buffer += thislen;
-		pc->buflen -= thislen;
-	}
-
-	/* Same drill for reading. */
-	else if ((ireason & 3) == 2) {
-
-		/* Transfer the data. */
-		atapi_input_bytes (drive, pc->buffer, thislen);
-
-		/* If we haven't moved enough data to satisfy the drive,
-		   add some padding. */
-		while (len > thislen) {
-			int dum = 0;
-			atapi_input_bytes (drive, &dum, sizeof (dum));
-			len -= sizeof (dum);
-		}
-
-		/* Keep count of how much data we've moved. */
-		pc->buffer += thislen;
-		pc->buflen -= thislen;
-	} else {
-		printk ("%s: cdrom_pc_intr: The drive "
-			"appears confused (ireason = 0x%2x)\n",
-			drive->name, ireason);
-		pc->stat = 1;
-	}
-
-	/* Now we wait for another interrupt. */
-	ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD, cdrom_timer_expiry);
-	return ide_started;
-}
-
-
-static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive)
-{
-	struct request *rq = HWGROUP(drive)->rq;
-	struct packet_command *pc = (struct packet_command *)rq->buffer;
-
-	/* Send the command to the drive and return. */
-	return cdrom_transfer_packet_command (drive, pc->c,
-				       sizeof (pc->c), &cdrom_pc_intr);
-}
-
-
-static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
-{
-	int len;
-	struct request *rq = HWGROUP(drive)->rq;
-	struct packet_command *pc = (struct packet_command *)rq->buffer;
-	struct cdrom_info *info = drive->driver_data;
-
-	info->dma = 0;
-	pc->stat = 0;
-	len = pc->buflen;
-
-	/* Start sending the command to the drive. */
-	return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
-}
-
-
-/* Sleep for TIME jiffies.
-   Not to be called from an interrupt handler. */
-static
-void cdrom_sleep (int time)
-{
-	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(time);
-}
-
-static
-int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc)
-{
-	int retries = 10;
-	struct request req;
-
-	/* Start of retry loop. */
-	do {
-		ide_init_drive_cmd (&req);
-		req.cmd = PACKET_COMMAND;
-		req.buffer = (char *)pc;
-		if (ide_do_drive_cmd (drive, &req, ide_wait)) {
-			printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n",
-				drive->name, req.buffer[0], req.buffer[1]);
-			/* FIXME: we should probably abort/retry or something */
-		}
-		if (pc->stat != 0) {
-			/* The request failed.  Retry if it was due to a unit
-			   attention status
-			   (usually means media was changed). */
-			struct request_sense *reqbuf = pc->sense_data;
-
-			if (reqbuf->sense_key == UNIT_ATTENTION)
-				cdrom_saw_media_change (drive);
-			else if (reqbuf->sense_key == NOT_READY &&
-				 reqbuf->asc == 4 && reqbuf->ascq != 4) {
-				/* The drive is in the process of loading
-				   a disk.  Retry, but wait a little to give
-				   the drive time to complete the load. */
-				cdrom_sleep (HZ);
-			} else {
-				/* Otherwise, don't retry. */
-				retries = 0;
-			}
-			--retries;
-		}
-
-		/* End of retry loop. */
-	} while (pc->stat != 0 && retries >= 0);
-
-	/* Return an error if the command failed. */
-	if (pc->stat)
-		return -EIO;
-
-	/* The command succeeded.  If it was anything other than
-	   a request sense, eject, or door lock command,
-	   and we think that the door is presently unlocked, lock it
-	   again. (The door was probably unlocked via an explicit
-	   CDROMEJECT ioctl.) */
-	if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
-	    (pc->c[0] != GPCMD_TEST_UNIT_READY &&
-	     pc->c[0] != GPCMD_REQUEST_SENSE &&
-	     pc->c[0] != GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL &&
-	     pc->c[0] != GPCMD_START_STOP_UNIT &&
-	     pc->c[0] != GPCMD_MODE_SENSE_10 &&
-	     pc->c[0] != GPCMD_MODE_SELECT_10)) {
-		(void) cdrom_lockdoor (drive, 1);
-	}
-	return 0;
-}
-
-/****************************************************************************
- * cdrom driver request routine.
- */
-static ide_startstop_t
-ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
-{
-	ide_startstop_t action;
-	struct cdrom_info *info = drive->driver_data;
-
-	switch (rq->cmd) {
-		case READ: {
-			if (CDROM_CONFIG_FLAGS(drive)->seeking) {
-				unsigned long elpased = jiffies - info->start_seek;
-				int stat = GET_STAT();
-
-				if ((stat & SEEK_STAT) != SEEK_STAT) {
-					if (elpased < IDECD_SEEK_TIMEOUT) {
-						ide_stall_queue(drive, IDECD_SEEK_TIMER);
-						return ide_stopped;
-					}
-					printk ("%s: DSC timeout\n", drive->name);
-				}
-				CDROM_CONFIG_FLAGS(drive)->seeking = 0;
-			}
-			if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap)
-				action = cdrom_start_seek (drive, block);
-			else
-				action = cdrom_start_read (drive, block);
-			info->last_block = block;
-			return action;
-		}
-
-		case PACKET_COMMAND:
-		case REQUEST_SENSE_COMMAND: {
-			return cdrom_do_packet_command(drive);
-		}
-
-		case RESET_DRIVE_COMMAND: {
-			cdrom_end_request(1, drive);
-			return ide_do_reset(drive);
-		}
-
-		default: {
-			printk("ide-cd: bad cmd %d\n", rq -> cmd);
-			cdrom_end_request(0, drive);
-			return ide_stopped;
-		}
-	}
-}
-
-
-
-/****************************************************************************
- * Ioctl handling.
- *
- * Routines which queue packet commands take as a final argument a pointer
- * to a request_sense struct.  If execution of the command results
- * in an error with a CHECK CONDITION status, this structure will be filled
- * with the results of the subsequent request sense command.  The pointer
- * can also be NULL, in which case no sense information is returned.
- */
-
-#if ! STANDARD_ATAPI
-static inline
-int bin2bcd (int x)
-{
-	return (x%10) | ((x/10) << 4);
-}
-
-
-static inline
-int bcd2bin (int x)
-{
-	return (x >> 4) * 10 + (x & 0x0f);
-}
-
-static
-void msf_from_bcd (struct atapi_msf *msf)
-{
-	msf->minute = bcd2bin (msf->minute);
-	msf->second = bcd2bin (msf->second);
-	msf->frame  = bcd2bin (msf->frame);
-}
-
-#endif /* not STANDARD_ATAPI */
-
-
-static inline
-void lba_to_msf (int lba, byte *m, byte *s, byte *f)
-{
-	lba += CD_MSF_OFFSET;
-	lba &= 0xffffff;  /* negative lbas use only 24 bits */
-	*m = lba / (CD_SECS * CD_FRAMES);
-	lba %= (CD_SECS * CD_FRAMES);
-	*s = lba / CD_FRAMES;
-	*f = lba % CD_FRAMES;
-}
-
-
-static inline
-int msf_to_lba (byte m, byte s, byte f)
-{
-	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
-}
-
-static int cdrom_check_status (ide_drive_t  *drive)
-{
-	struct packet_command pc;
-	struct cdrom_info *info = drive->driver_data;
-	struct cdrom_device_info *cdi = &info->devinfo;
-
-	memset(&pc, 0, sizeof(pc));
-
-	pc.c[0] = GPCMD_TEST_UNIT_READY;
-
-#if ! STANDARD_ATAPI
-        /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
-           switch CDs instead of supporting the LOAD_UNLOAD opcode   */
-
-        pc.c[7] = cdi->sanyo_slot % 3;
-#endif /* not STANDARD_ATAPI */
-
-	return cdrom_queue_packet_command(drive, &pc);
-}
-
-
-/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
-static int
-cdrom_lockdoor(ide_drive_t *drive, int lockflag)
-{
-	struct request_sense *sense;
-	struct packet_command pc;
-	int stat;
-
-	/* If the drive cannot lock the door, just pretend. */
-	if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
-		stat = 0;
-	else {
-		memset(&pc, 0, sizeof(pc));
-		pc.c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
-		pc.c[4] = (lockflag != 0);
-		stat = cdrom_queue_packet_command (drive, &pc);
-	}
-
-	sense = pc.sense_data;
-
-	/* If we got an illegal field error, the drive
-	   probably cannot lock the door. */
-	if (stat != 0 &&
-	    sense->sense_key == ILLEGAL_REQUEST &&
-	    (sense->asc == 0x24 || sense->asc == 0x20)) {
-		printk ("%s: door locking not supported\n",
-			drive->name);
-		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
-		stat = 0;
-	}
-	
-	/* no medium, that's alright. */
-	if (stat != 0 && sense->sense_key == NOT_READY && sense->asc == 0x3a)
-		stat = 0;
-
-	if (stat == 0)
-		CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
-
-	return stat;
-}
-
-
-/* Eject the disk if EJECTFLAG is 0.
-   If EJECTFLAG is 1, try to reload the disk. */
-static int cdrom_eject(ide_drive_t *drive, int ejectflag)
-{
-	struct packet_command pc;
-
-	if (CDROM_CONFIG_FLAGS(drive)->no_eject && !ejectflag)
-		return -EDRIVE_CANT_DO_THIS;
-	
-	/* reload fails on some drives, if the tray is locked */
-	if (CDROM_STATE_FLAGS(drive)->door_locked && ejectflag)
-		return 0;
-
-	memset(&pc, 0, sizeof (pc));
-
-	pc.c[0] = GPCMD_START_STOP_UNIT;
-	pc.c[4] = 0x02 + (ejectflag != 0);
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-static int cdrom_read_capacity(ide_drive_t *drive, unsigned *capacity)
-{
-	struct {
-		__u32 lba;
-		__u32 blocklen;
-	} capbuf;
-
-	int stat;
-	struct packet_command pc;
-
-	memset(&pc, 0, sizeof (pc));
-
-	pc.c[0] = GPCMD_READ_CDVD_CAPACITY;
-	pc.buffer = (char *)&capbuf;
-	pc.buflen = sizeof(capbuf);
-
-	stat = cdrom_queue_packet_command(drive, &pc);
-	if (stat == 0)
-		*capacity = be32_to_cpu(capbuf.lba);
-
-	return stat;
-}
-
-static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
-				int format, char *buf, int buflen)
-{
-	struct packet_command pc;
-
-	memset(&pc, 0, sizeof(pc));
-
-	pc.buffer =  buf;
-	pc.buflen = buflen;
-	pc.c[0] = GPCMD_READ_TOC_PMA_ATIP;
-	pc.c[6] = trackno;
-	pc.c[7] = (buflen >> 8);
-	pc.c[8] = (buflen & 0xff);
-	pc.c[9] = (format << 6);
-
-	if (msf_flag)
-		pc.c[1] = 2;
-
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-
-/* Try to read the entire TOC for the disk into our internal buffer. */
-static int cdrom_read_toc (ide_drive_t *drive)
-{
-	int stat, ntracks, i;
-	struct cdrom_info *info = drive->driver_data;
-	struct atapi_toc *toc = info->toc;
-	int minor = drive->select.b.unit << PARTN_BITS;
-	struct {
-		struct atapi_toc_header hdr;
-		struct atapi_toc_entry  ent;
-	} ms_tmp;
-
-	if (toc == NULL) {
-		/* Try to allocate space. */
-		toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
-						    GFP_KERNEL);
-		info->toc = toc;
-		if (toc == NULL) {
-			printk ("%s: No cdrom TOC buffer!\n", drive->name);
-			return -ENOMEM;
-		}
-	}
-
-	/* Check to see if the existing data is still valid.
-	   If it is, just return. */
-	if (CDROM_STATE_FLAGS (drive)->toc_valid)
-		(void) cdrom_check_status(drive);
-
-	if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
-
-	/* First read just the header, so we know how long the TOC is. */
-	stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
-				    sizeof (struct atapi_toc_header));
-	if (stat) return stat;
-
-#if ! STANDARD_ATAPI
-	if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
-		toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
-		toc->hdr.last_track  = bcd2bin (toc->hdr.last_track);
-	}
-#endif  /* not STANDARD_ATAPI */
-
-	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
-	if (ntracks <= 0) return -EIO;
-	if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
-
-	/* Now read the whole schmeer. */
-	stat = cdrom_read_tocentry (drive, toc->hdr.first_track, 1, 0, (char *)&toc->hdr,
-				    sizeof (struct atapi_toc_header) +
-				    (ntracks + 1) *
-				    sizeof (struct atapi_toc_entry));
-
-	if (stat && toc->hdr.first_track > 1) {
-		/* Cds with CDI tracks only don't have any TOC entries,
-		   despite of this the returned values are
-		   first_track == last_track = number of CDI tracks + 1,
-		   so that this case is indistinguishable from the same
-		   layout plus an additional audio track.
-		   If we get an error for the regular case, we assume
-		   a CDI without additional audio tracks. In this case
-		   the readable TOC is empty (CDI tracks are not included)
-		   and only holds the Leadout entry. Heiko Eißfeldt */
-		ntracks = 0;
-		stat = cdrom_read_tocentry (drive, CDROM_LEADOUT, 1,
-					    0, (char *)&toc->hdr,
-					    sizeof (struct atapi_toc_header) +
-					   (ntracks+1) *
-					    sizeof (struct atapi_toc_entry));
-		if (stat) {
-			return stat;
-		}
-#if ! STANDARD_ATAPI
-		if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
-			toc->hdr.first_track = bin2bcd(CDROM_LEADOUT);
-			toc->hdr.last_track = bin2bcd(CDROM_LEADOUT);
-		} else
-#endif  /* not STANDARD_ATAPI */
-		{
-			toc->hdr.first_track = CDROM_LEADOUT;
-			toc->hdr.last_track = CDROM_LEADOUT;
-		}
-	} else if (stat) {
-		return stat;
-	}
-	if (stat) return stat;
-
-	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
-
-#if ! STANDARD_ATAPI
-	if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
-		toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
-		toc->hdr.last_track  = bcd2bin (toc->hdr.last_track);
-	}
-#endif  /* not STANDARD_ATAPI */
-
-	for (i=0; i<=ntracks; i++) {
-#if ! STANDARD_ATAPI
-		if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd) {
-			if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd)
-				toc->ent[i].track = bcd2bin (toc->ent[i].track);
-			msf_from_bcd (&toc->ent[i].addr.msf);
-		}
-#endif  /* not STANDARD_ATAPI */
-		toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.minute,
-						   toc->ent[i].addr.msf.second,
-						   toc->ent[i].addr.msf.frame);
-	}
-
-	/* Read the multisession information. */
-	if (toc->hdr.first_track != CDROM_LEADOUT) {
-		/* Read the multisession information. */
-		stat = cdrom_read_tocentry (drive, 0, 1, 1,
-					   (char *)&ms_tmp, sizeof (ms_tmp));
-		if (stat) return stat;
-	} else {
-		ms_tmp.ent.addr.msf.minute = 0;
-		ms_tmp.ent.addr.msf.second = 2;
-		ms_tmp.ent.addr.msf.frame  = 0;
-		ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT;
-	}
-
-#if ! STANDARD_ATAPI
-	if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
-		msf_from_bcd (&ms_tmp.ent.addr.msf);
-#endif  /* not STANDARD_ATAPI */
-
-	toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.minute,
-					    ms_tmp.ent.addr.msf.second,
-					    ms_tmp.ent.addr.msf.frame);
-
-	toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
-
-	/* Now try to get the total cdrom capacity. */
-#if 0
-	stat = cdrom_get_last_written(MKDEV(HWIF(drive)->major, minor),
-				     (long *)&toc->capacity);
-	if (stat)
-#endif
-	stat = cdrom_read_capacity (drive, &toc->capacity);
-	if (stat) toc->capacity = 0x1fffff;
-
-	/* for general /dev/cdrom like mounting, one big disc */
-	drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
-	HWIF(drive)->gd->sizes[minor] = (toc->capacity * SECTORS_PER_FRAME) >>
-					(BLOCK_SIZE_BITS - 9);
-
-	/* Remember that we've read this stuff. */
-	CDROM_STATE_FLAGS (drive)->toc_valid = 1;
-
-	/* should be "if multisession", but it does no harm. */
-	if (ntracks == 1)
-		return 0;
-
-	/* setup each minor to respond to a session */
-	minor++;
-	i = toc->hdr.first_track;
-	while ((i <= ntracks) && ((minor & CD_PART_MASK) < CD_PART_MAX)) {
-		drive->part[minor & PARTN_MASK].start_sect = 0;
- 		drive->part[minor & PARTN_MASK].nr_sects =
-			(toc->ent[i].addr.lba *
-			SECTORS_PER_FRAME) << (BLOCK_SIZE_BITS - 9);
-		HWIF(drive)->gd->sizes[minor] = (toc->ent[i].addr.lba *
-			SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
-		i++;
-		minor++;
-	}
-
-	return 0;
-}
-
-
-static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf,
-				 int buflen)
-{
-	struct packet_command pc;
-
-	memset(&pc, 0, sizeof(pc));
-
-	pc.buffer = buf;
-	pc.buflen = buflen;
-	pc.c[0] = GPCMD_READ_SUBCHANNEL;
-	pc.c[1] = 2;     /* MSF addressing */
-	pc.c[2] = 0x40;  /* request subQ data */
-	pc.c[3] = format;
-	pc.c[7] = (buflen >> 8);
-	pc.c[8] = (buflen & 0xff);
-	return cdrom_queue_packet_command(drive, &pc);
-}
-
-/* ATAPI cdrom drives are free to select the speed you request or any slower
-   rate :-( Requesting too fast a speed will _not_ produce an error. */
-static int cdrom_select_speed (ide_drive_t *drive, int speed)
-{
-	struct packet_command pc;
-	memset(&pc, 0, sizeof(pc));
-
-	if (speed == 0)
-		speed = 0xffff; /* set to max */
-	else
-		speed *= 177;   /* Nx to kbytes/s */
-
-	pc.c[0] = GPCMD_SET_SPEED;
-	/* Read Drive speed in kbytes/second MSB */
-	pc.c[2] = (speed >> 8) & 0xff;	
-	/* Read Drive speed in kbytes/second LSB */
-	pc.c[3] = speed & 0xff;
-	if ( CDROM_CONFIG_FLAGS(drive)->cd_r ||
-                   CDROM_CONFIG_FLAGS(drive)->cd_rw ) {
-		/* Write Drive speed in kbytes/second MSB */
-		pc.c[4] = (speed >> 8) & 0xff;
-		/* Write Drive speed in kbytes/second LSB */
-		pc.c[5] = speed & 0xff;
-       }
-
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-
-static int cdrom_get_toc_entry(ide_drive_t *drive, int track,
-				struct atapi_toc_entry **ent)
-{
-	struct cdrom_info *info = drive->driver_data;
-	struct atapi_toc *toc = info->toc;
-	int ntracks;
-
-	/* Check validity of requested track number. */
-	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
-	if (toc->hdr.first_track == CDROM_LEADOUT) ntracks = 0;
-	if (track == CDROM_LEADOUT)
-		*ent = &toc->ent[ntracks];
-	else if (track < toc->hdr.first_track ||
-		 track > toc->hdr.last_track)
-		return -EINVAL;
-	else
-		*ent = &toc->ent[track - toc->hdr.first_track];
-
-	return 0;
-}
-
-
-
-
-
-/* the generic packet interface to cdrom.c */
-static int ide_cdrom_packet(struct cdrom_device_info *cdi,
-			    struct cdrom_generic_command *cgc)
-{
-	struct packet_command pc;
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-
-	/* here we queue the commands from the uniform CD-ROM
-	   layer. the packet must be complete, as we do not
-	   touch it at all. */
-	memset(&pc, 0, sizeof(pc));
-	memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE);
-	pc.buffer = cgc->buffer;
-	pc.buflen = cgc->buflen;
-	cgc->stat = cdrom_queue_packet_command(drive, &pc);
-
-	/* There was an error, assign sense. */
-	if (cgc->stat)
-		cgc->sense = pc.sense_data;
-
-	return cgc->stat;
-}
-
-static
-int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
-			 unsigned int cmd, unsigned long arg)
-{
-	struct cdrom_generic_command cgc;
-	char buffer[16];
-	int stat;
-
-	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
-
-	/* These will be moved into the Uniform layer shortly... */
-	switch (cmd) {
- 	case CDROMSETSPINDOWN: {
- 		char spindown;
- 
- 		if (copy_from_user(&spindown, (void *) arg, sizeof(char)))
-			return -EFAULT;
- 
-                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
-			return stat;
-
- 		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
-
- 		return cdrom_mode_select(cdi, &cgc);
- 	} 
- 
- 	case CDROMGETSPINDOWN: {
- 		char spindown;
- 
-                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
-			return stat;
- 
- 		spindown = buffer[11] & 0x0f;
- 
-		if (copy_to_user((void *) arg, &spindown, sizeof (char)))
-			return -EFAULT;
- 
- 		return 0;
- 	}
-  
-	default:
-		return -EINVAL;
-	}
-
-}
-
-static
-int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
-			   unsigned int cmd, void *arg)
-			   
-{
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_info *info = drive->driver_data;
-
-	switch (cmd) {
-	case CDROMREADTOCHDR: {
-		int stat;
-		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
-		struct atapi_toc *toc;
-
-		/* Make sure our saved TOC is valid. */
-		stat = cdrom_read_toc(drive);
-		if (stat) return stat;
-
-		toc = info->toc;
-		tochdr->cdth_trk0 = toc->hdr.first_track;
-		tochdr->cdth_trk1 = toc->hdr.last_track;
-
-		return 0;
-	}
-
-	case CDROMREADTOCENTRY: {
-		int stat;
-		struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
-		struct atapi_toc_entry *toce;
-
-		stat = cdrom_get_toc_entry (drive, tocentry->cdte_track, &toce);
-		if (stat) return stat;
-
-		tocentry->cdte_ctrl = toce->control;
-		tocentry->cdte_adr  = toce->adr;
-		if (tocentry->cdte_format == CDROM_MSF) {
-			lba_to_msf (toce->addr.lba,
-				   &tocentry->cdte_addr.msf.minute,
-				   &tocentry->cdte_addr.msf.second,
-				   &tocentry->cdte_addr.msf.frame);
-		} else
-			tocentry->cdte_addr.lba = toce->addr.lba;
-
-		return 0;
-	}
-
-	default:
-		return -EINVAL;
-	}
-}
-
-static
-int ide_cdrom_reset (struct cdrom_device_info *cdi)
-{
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct request req;
-
-	ide_init_drive_cmd (&req);
-	req.cmd = RESET_DRIVE_COMMAND;
-	return ide_do_drive_cmd (drive, &req, ide_wait);
-}
-
-
-static
-int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
-{
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-
-	if (position) {
-		int stat = cdrom_lockdoor (drive, 0);
-		if (stat) return stat;
-	}
-
-	return cdrom_eject(drive, !position);
-}
-
-static
-int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
-{
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	return cdrom_lockdoor (drive, lock);
-}
-
-#undef __ACER50__
-
-#ifdef __ACER50__
-/*
- * the buffer struct used by ide_cdrom_get_capabilities()
- */
-struct get_capabilities_buf {
-	char pad[8];
-	struct atapi_capabilities_page cap;	/* this is 4 bytes short of ATAPI standard */
-	char extra_cap[4];			/* Acer 50X needs the regulation size buffer */
-};
-
-static
-int ide_cdrom_get_capabilities (struct cdrom_device_info *cdi, struct get_capabilities_buf *buf)
-{
-	int stat, attempts = 3, buflen = sizeof(*buf);
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_generic_command cgc;
-
-	/*
-	 * Most drives don't care about the buffer size;
-	 * they return as much info as there's room for.
-	 * But some older drives (?) had trouble with the
-	 * standard size, preferring 4 bytes less.
-	 * And the modern Acer 50X rejects anything smaller
-	 * than the standard size.
-	 */
-	if (!(drive->id && !strcmp(drive->id->model,"ATAPI CD ROM DRIVE 50X MAX")))
-		buflen -= sizeof(buf->extra_cap);	/* for all drives except Acer 50X */
-
-	do { /* we seem to get stat=0x01,err=0x00 the first time (??) */
-		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
-		if (stat == 0) {
-			/*
-			 * The ACER/AOpen 24X cdrom has the speed
-			 * fields byte-swapped from the standard.
-			 */
-			if (!(drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4))) {
-				buf->cap.curspeed = ntohs(buf->cap.curspeed);
-				buf->cap.maxspeed = ntohs(buf->cap.maxspeed);
-			}
-			CDROM_STATE_FLAGS (drive)->current_speed = (((unsigned int)buf->cap.curspeed) + (176/2)) / 176;
-			CDROM_CONFIG_FLAGS(drive)->max_speed     = (((unsigned int)buf->cap.maxspeed) + (176/2)) / 176;
-			return 0;
-		}
-	} while (--attempts);
-	return stat;
-}
-#endif /* __ACER50__ */
-
-static
-int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
-{
-#ifndef __ACER50__
-        int stat, attempts = 3;
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_generic_command cgc;
-	struct {
-		char pad[8];
-		struct atapi_capabilities_page cap;
-	} buf;
-#else
-	int stat;
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_generic_command cgc;
-	struct get_capabilities_buf buf;
-#endif /* __ACER50__ */
-
-	if ((stat = cdrom_select_speed (drive, speed)) < 0)
-		return stat;
-
-	init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
-
-#ifndef __ACER50__
-	/* Now with that done, update the speed fields */
-        do {    /* we seem to get stat=0x01,err=0x00 the first time (??) */
-                if (attempts-- <= 0)
-                        return 0;
-                stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
-        } while (stat);
-
-        /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
-        if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
-                CDROM_STATE_FLAGS (drive)->current_speed  = 
-			(((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
-                CDROM_CONFIG_FLAGS (drive)->max_speed = 
-			(((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
-        } else {
-                CDROM_STATE_FLAGS (drive)->current_speed  = 
-			(ntohs(buf.cap.curspeed) + (176/2)) / 176;
-                CDROM_CONFIG_FLAGS (drive)->max_speed = 
-			(ntohs(buf.cap.maxspeed) + (176/2)) / 176;
-        }
-#else
-	if (ide_cdrom_get_capabilities(cdi,&buf))
-		return 0;
-#endif /* __ACER50__ */
-
-        cdi->speed = CDROM_STATE_FLAGS (drive)->current_speed;
-        return 0;
-}
-
-static
-int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
-{
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_info *info = drive->driver_data;
-
-	if (slot_nr == CDSL_CURRENT) {
-		struct request_sense *sense = &info->sense_data;
-		int stat = cdrom_check_status(drive);
-		if (stat == 0 || sense->sense_key == UNIT_ATTENTION)
-			return CDS_DISC_OK;
-
-		if (sense->sense_key == NOT_READY && sense->asc == 0x04 &&
-		    sense->ascq == 0x04)
-			return CDS_DISC_OK;
-
-		if (sense->sense_key == NOT_READY) {
-			/* ATAPI doesn't have anything that can help
-			   us decide whether the drive is really
-			   emtpy or the tray is just open. irk. */
-			return CDS_TRAY_OPEN;
-		}
-
-		return CDS_DRIVE_NOT_READY;
-	} else {
-		return -EINVAL;
-	}
-}
-
-static
-int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
-				struct cdrom_multisession *ms_info)
-{
-	struct atapi_toc *toc;
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_info *info = drive->driver_data;
-
-	toc = info->toc;
-	ms_info->addr.lba = toc->last_session_lba;
-	ms_info->xa_flag = toc->xa_flag;
-
-	return 0;
-}
-
-static
-int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
-		       struct cdrom_mcn *mcn_info)
-{
-	int stat;
-	char mcnbuf[24];
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-
-/* get MCN */
-	if ((stat = cdrom_read_subchannel(drive, 2, mcnbuf, sizeof (mcnbuf))))
-		return stat;
-
-	memcpy (mcn_info->medium_catalog_number, mcnbuf+9,
-		sizeof (mcn_info->medium_catalog_number)-1);
-	mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1]
-		= '\0';
-
-	return 0;
-}
-
-
-
-/****************************************************************************
- * Other driver requests (open, close, check media change).
- */
-
-static
-int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi,
-				       int slot_nr)
-{
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	
-	if (slot_nr == CDSL_CURRENT) {
-		(void) cdrom_check_status(drive);
-		CDROM_STATE_FLAGS (drive)->media_changed = 0;
-		return CDROM_STATE_FLAGS (drive)->media_changed;
-	} else {
-		return -EINVAL;
-	}
-}
-
-
-static
-int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose)
-{
-	return 0;
-}
-
-
-/*
- * Close down the device.  Invalidate all cached blocks.
- */
-
-static
-void ide_cdrom_release_real (struct cdrom_device_info *cdi)
-{
-}
-
-
-
-/****************************************************************************
- * Device initialization.
- */
-
-static
-struct cdrom_device_ops ide_cdrom_dops = {
-	ide_cdrom_open_real,    /* open */
-	ide_cdrom_release_real, /* release */
-	ide_cdrom_drive_status, /* drive_status */
-	ide_cdrom_check_media_change_real, /* media_changed */
-	ide_cdrom_tray_move,    /* tray_move */
-	ide_cdrom_lock_door,    /* lock_door */
-	ide_cdrom_select_speed, /* select_speed */
-	NULL,			/* select_disc */
-	ide_cdrom_get_last_session, /* get_last_session */
-	ide_cdrom_get_mcn, /* get_mcn */
-	ide_cdrom_reset, /* reset */
-	ide_cdrom_audio_ioctl, /* audio_ioctl */
-	ide_cdrom_dev_ioctl,   /* dev_ioctl */
-	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED
-	| CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
-	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS
-	| CDC_DRIVE_STATUS | CDC_CD_R | CDC_CD_RW | CDC_DVD
-	| CDC_DVD_R| CDC_DVD_RAM | CDC_GENERIC_PACKET, /* capability */
-	0, /* n_minors */
-	ide_cdrom_packet
-};
-
-static int ide_cdrom_register (ide_drive_t *drive, int nslots)
-{
-	struct cdrom_info *info = drive->driver_data;
-	struct cdrom_device_info *devinfo = &info->devinfo;
-	int minor = (drive->select.b.unit)<<PARTN_BITS;
-
-	devinfo->dev = MKDEV (HWIF(drive)->major, minor | CD_PART_MASK);
-	devinfo->ops = &ide_cdrom_dops;
-	devinfo->mask = 0;
-	*(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed;
-	*(int *)&devinfo->capacity = nslots;
-	devinfo->handle = (void *) drive;
-	strcpy(devinfo->name, drive->name);
-	
-	/* set capability mask to match the probe. */
-	if (!CDROM_CONFIG_FLAGS (drive)->cd_r)
-		devinfo->mask |= CDC_CD_R;
-	if (!CDROM_CONFIG_FLAGS (drive)->cd_rw)
-		devinfo->mask |= CDC_CD_RW;
-	if (!CDROM_CONFIG_FLAGS (drive)->dvd)
-		devinfo->mask |= CDC_DVD;
-	if (!CDROM_CONFIG_FLAGS (drive)->dvd_r)
-		devinfo->mask |= CDC_DVD_R;
-	if (!CDROM_CONFIG_FLAGS (drive)->dvd_ram)
-		devinfo->mask |= CDC_DVD_RAM;
-	if (!CDROM_CONFIG_FLAGS (drive)->is_changer)
-		devinfo->mask |= CDC_SELECT_DISC;
-	if (!CDROM_CONFIG_FLAGS (drive)->audio_play)
-		devinfo->mask |= CDC_PLAY_AUDIO;
-	if (!CDROM_CONFIG_FLAGS (drive)->close_tray)
-		devinfo->mask |= CDC_CLOSE_TRAY;
-
-	devinfo->de = devfs_register (drive->de, "cd", 2, DEVFS_FL_DEFAULT,
-				      HWIF(drive)->major, minor,
-				      S_IFBLK | S_IRUGO | S_IWUGO, 0, 0,
-				      ide_fops, NULL);
-
-	return register_cdrom (devinfo);
-}
-
-
-static
-int ide_cdrom_probe_capabilities (ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	struct cdrom_device_info *cdi = &info->devinfo;
-#ifndef __ACER50__
-	int stat, nslots = 1, attempts = 3;
-	struct cdrom_generic_command cgc;
-	struct {
-		char pad[8];
-		struct atapi_capabilities_page cap;
-	} buf;
-#else
-	int nslots = 1;
-	struct cdrom_generic_command cgc;
-	struct get_capabilities_buf buf;
-#endif /* __ACER50__ */
-
-	if (CDROM_CONFIG_FLAGS (drive)->nec260) {
-		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;                       
-		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;       
-		return nslots;
-	}
-
-	init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
-	/* we have to cheat a little here. the packet will eventually
-	 * be queued with ide_cdrom_packet(), which extracts the
-	 * drive from cdi->handle. Since this device hasn't been
-	 * registered with the Uniform layer yet, it can't do this.
-	 * Same goes for cdi->ops.
-	 */
-	cdi->handle = (ide_drive_t *) drive;
-	cdi->ops = &ide_cdrom_dops;
-#ifndef __ACER50__
-	/* we seem to get stat=0x01,err=0x00 the first time (??) */
-	do {
-		if (attempts-- <= 0)
-			return 0;
-		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
-	} while (stat);
-#else
-	if (ide_cdrom_get_capabilities(cdi,&buf))
-		return 0;
-#endif /* __ACER50__ */
-
-	if (buf.cap.lock == 0)
-		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
-	if (buf.cap.eject)
-		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;
-	if (buf.cap.cd_r_write)
-		CDROM_CONFIG_FLAGS (drive)->cd_r = 1;
-	if (buf.cap.cd_rw_write)
-		CDROM_CONFIG_FLAGS (drive)->cd_rw = 1;
-	if (buf.cap.test_write)
-		CDROM_CONFIG_FLAGS (drive)->test_write = 1;
-	if (buf.cap.dvd_ram_read || buf.cap.dvd_r_read || buf.cap.dvd_rom)
-		CDROM_CONFIG_FLAGS (drive)->dvd = 1;
-	if (buf.cap.dvd_ram_write)
-		CDROM_CONFIG_FLAGS (drive)->dvd_r = 1;
-	if (buf.cap.dvd_r_write)
-		CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1;
-	if (buf.cap.audio_play)
-		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
-	if (buf.cap.mechtype == 0)
-		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
-
-#if ! STANDARD_ATAPI
-	if (cdi->sanyo_slot > 0) {
-		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
-		nslots = 3;
-	}
-
-	else
-#endif /* not STANDARD_ATAPI */
-	if (buf.cap.mechtype == mechtype_individual_changer ||
-	    buf.cap.mechtype == mechtype_cartridge_changer) {
-		if ((nslots = cdrom_number_of_slots(cdi)) > 1) {
-			CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
-			CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1;
-		}
-	}
-
-#ifndef __ACER50__
-	/* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
-	if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
-		CDROM_STATE_FLAGS (drive)->current_speed  = 
-			(((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
-		CDROM_CONFIG_FLAGS (drive)->max_speed = 
-			(((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
-	} else {
-		CDROM_STATE_FLAGS (drive)->current_speed  = 
-			(ntohs(buf.cap.curspeed) + (176/2)) / 176;
-		CDROM_CONFIG_FLAGS (drive)->max_speed = 
-			(ntohs(buf.cap.maxspeed) + (176/2)) / 176;
-	}
-#endif /* __ACER50__ */
-
-	/* don't print speed if the drive reported 0.
-	 */
-	printk("%s: ATAPI", drive->name);
-	if (CDROM_CONFIG_FLAGS(drive)->max_speed)
-		printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed);
-	printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM");
-
-	if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram)
-        	printk (" DVD%s%s", 
-        	(CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "", 
-        	(CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : "");
-
-        if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) 
-        	printk (" CD%s%s", 
-        	(CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", 
-        	(CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : "");
-
-        if (CDROM_CONFIG_FLAGS (drive)->is_changer) 
-        	printk (" changer w/%d slots", nslots);
-        else 	
-        	printk (" drive");
-
-	printk (", %dkB Cache", be16_to_cpu(buf.cap.buffer_size));
-
-	if (drive->using_dma) {
-		if ((drive->id->field_valid & 4) &&
-		    (drive->id->hw_config & 0x2000) &&
-		    (HWIF(drive)->udma_four) &&
-		    (drive->id->dma_ultra & (drive->id->dma_ultra >> 11) & 3)) {
-			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
-		} else if ((drive->id->field_valid & 4) &&
-			   (drive->id->dma_ultra & (drive->id->dma_ultra >> 8) & 7)) {
-			printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
-		} else if (drive->id->field_valid & 4) {
-			printk(", (U)DMA");	/* Can be BIOS-enabled! */
-		} else {
-			printk(", DMA");
-		}
-	}
-	printk("\n");
-
-	return nslots;
-}
-
-static void ide_cdrom_add_settings(ide_drive_t *drive)
-{
-	int major = HWIF(drive)->major;
-	int minor = drive->select.b.unit << PARTN_BITS;
-
-	ide_add_setting(drive,	"breada_readahead",	SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
-	ide_add_setting(drive,	"file_readahead",	SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor],	NULL);
-	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
-	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
-}
-
-static
-int ide_cdrom_setup (ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	struct cdrom_device_info *cdi = &info->devinfo;
-	int minor = drive->select.b.unit << PARTN_BITS;
-	int nslots;
-
-	set_device_ro(MKDEV(HWIF(drive)->major, minor), 1);
-	set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE);
-
-	drive->special.all	= 0;
-	drive->ready_stat	= 0;
-
-	CDROM_STATE_FLAGS (drive)->media_changed = 1;
-	CDROM_STATE_FLAGS (drive)->toc_valid     = 0;
-	CDROM_STATE_FLAGS (drive)->door_locked   = 0;
-
-#if NO_DOOR_LOCKING
-	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
-#else
-	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
-#endif
-
-	if (drive->id != NULL)
-		CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
-			((drive->id->config & 0x0060) == 0x20);
-	else
-		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
-
-	CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
-	CDROM_CONFIG_FLAGS (drive)->cd_r = 0;
-	CDROM_CONFIG_FLAGS (drive)->cd_rw = 0;
-	CDROM_CONFIG_FLAGS (drive)->test_write = 0;
-	CDROM_CONFIG_FLAGS (drive)->dvd = 0;
-	CDROM_CONFIG_FLAGS (drive)->dvd_r = 0;
-	CDROM_CONFIG_FLAGS (drive)->dvd_ram = 0;
-	CDROM_CONFIG_FLAGS (drive)->no_eject = 1;
-	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
-	CDROM_CONFIG_FLAGS (drive)->audio_play = 0;
-	CDROM_CONFIG_FLAGS (drive)->close_tray = 1;
-	
-	/* limit transfer size per interrupt. */
-	CDROM_CONFIG_FLAGS (drive)->limit_nframes = 0;
-	if (drive->id != NULL) {
-		/* a testament to the nice quality of Samsung drives... */
-		if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2430"))
-			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
-		else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2432"))
-			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
-		/* the 3231 model does not support the SET_CD_SPEED command */
-		else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-3231"))
-			cdi->mask |= CDC_SELECT_SPEED;
-	}
-
-#if ! STANDARD_ATAPI
-	/* by default Sanyo 3 CD changer support is turned off and
-           ATAPI Rev 2.2+ standard support for CD changers is used */
-	cdi->sanyo_slot = 0;
-
-	CDROM_CONFIG_FLAGS (drive)->nec260 = 0;
-	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
-	CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
-	CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 0;
-	CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 0;
-
-	if (drive->id != NULL) {
-		if (strcmp (drive->id->model, "V003S0DS") == 0 &&
-		    drive->id->fw_rev[4] == '1' &&
-		    drive->id->fw_rev[6] <= '2') {
-			/* Vertos 300.
-			   Some versions of this drive like to talk BCD. */
-			CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
-			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
-			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
-			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
-		}
-
-		else if (strcmp (drive->id->model, "V006E0DS") == 0 &&
-		    drive->id->fw_rev[4] == '1' &&
-		    drive->id->fw_rev[6] <= '2') {
-			/* Vertos 600 ESD. */
-			CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
-		}
-
-		else if (strcmp (drive->id->model,
-				 "NEC CD-ROM DRIVE:260") == 0 &&
-			 strncmp (drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */
-			/* Old NEC260 (not R).
-			   This drive was released before the 1.2 version
-			   of the spec. */
-			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
-			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
-			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
-			CDROM_CONFIG_FLAGS (drive)->nec260         = 1;
-		}
-
-		else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
-			 strncmp (drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */
-			/* Wearnes */
-			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
-			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
-		}
-
-                /* Sanyo 3 CD changer uses a non-standard command
-                    for CD changing */
-                 else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
-                         (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) ||
-                         (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) {
-                        /* uses CD in slot 0 when value is set to 3 */
-                        cdi->sanyo_slot = 3;
-                }
-
-
-	}
-#endif /* not STANDARD_ATAPI */
-
-	info->toc		= NULL;
-	info->buffer		= NULL;
-	info->sector_buffered	= 0;
-	info->nsectors_buffered	= 0;
-	info->changer_info      = NULL;
-	info->last_block	= 0;
-	info->start_seek	= 0;
-
-	nslots = ide_cdrom_probe_capabilities (drive);
-
-	if (ide_cdrom_register (drive, nslots)) {
-		printk ("%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name);
-		info->devinfo.handle = NULL;
-		return 1;
-	}
-	ide_cdrom_add_settings(drive);
-	return 0;
-}
-
-/* Forwarding functions to generic routines. */
-static
-int ide_cdrom_ioctl (ide_drive_t *drive,
-		     struct inode *inode, struct file *file,
-		     unsigned int cmd, unsigned long arg)
-{
-	return cdrom_fops.ioctl (inode, file, cmd, arg);
-}
-
-static
-int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	int rc;
-
-	MOD_INC_USE_COUNT;
-	if (info->buffer == NULL)
-		info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL);
-	rc = cdrom_fops.open (ip, fp);
-	if (rc) {
-		drive->usage--;
-		MOD_DEC_USE_COUNT;
-	}
-	return rc;
-}
-
-static
-void ide_cdrom_release (struct inode *inode, struct file *file,
-			ide_drive_t *drive)
-{
-	cdrom_fops.release (inode, file);
-	MOD_DEC_USE_COUNT;
-}
-
-static
-int ide_cdrom_check_media_change (ide_drive_t *drive)
-{
-	return cdrom_fops.check_media_change
-		(MKDEV (HWIF (drive)->major,
-			(drive->select.b.unit)<<PARTN_BITS));
-}
-
-static
-int ide_cdrom_cleanup(ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	struct cdrom_device_info *devinfo = &info->devinfo;
-
-	if (ide_unregister_subdriver (drive))
-		return 1;
-	if (info->buffer != NULL)
-		kfree(info->buffer);
-	if (info->toc != NULL)
-		kfree(info->toc);
-	if (info->changer_info != NULL)
-		kfree(info->changer_info);
-	if (devinfo->handle == drive && unregister_cdrom (devinfo))
-		printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name);
-	kfree(info);
-	drive->driver_data = NULL;
-	return 0;
-}
-
-static ide_driver_t ide_cdrom_driver = {
-	"ide-cdrom",			/* name */
-	IDECD_VERSION,			/* version */
-	ide_cdrom,			/* media */
-	0,				/* busy */
-	1,				/* supports_dma */
-	1,				/* supports_dsc_overlap */
-	ide_cdrom_cleanup,		/* cleanup */
-	ide_do_rw_cdrom,		/* do_request */
-	NULL,				/* ??? or perhaps cdrom_end_request? */
-	ide_cdrom_ioctl,		/* ioctl */
-	ide_cdrom_open,			/* open */
-	ide_cdrom_release,		/* release */
-	ide_cdrom_check_media_change,	/* media_change */
-	NULL,				/* pre_reset */
-	NULL,				/* capacity */
-	NULL,				/* special */
-	NULL				/* proc */
-};
-
-int ide_cdrom_init (void);
-static ide_module_t ide_cdrom_module = {
-	IDE_DRIVER_MODULE,
-	ide_cdrom_init,
-	&ide_cdrom_driver,
-	NULL
-};
-
-/* options */
-char *ignore = NULL;
-
-#ifdef MODULE
-MODULE_PARM(ignore, "s");
-MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
-
-void __exit ide_cdrom_exit(void)
-{
-	ide_drive_t *drive;
-	int failed = 0;
-
-	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL)
-		if (ide_cdrom_cleanup (drive)) {
-			printk ("%s: cleanup_module() called while still busy\n", drive->name);
-			failed++;
-		}
-	ide_unregister_module (&ide_cdrom_module);
-}
-#endif /* MODULE */
- 
-int ide_cdrom_init (void)
-{
-	ide_drive_t *drive;
-	struct cdrom_info *info;
-	int failed = 0;
-
-	MOD_INC_USE_COUNT;
-	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
-		/* skip drives that we were told to ignore */
-		if (ignore != NULL) {
-			if (strstr(ignore, drive->name)) {
-				printk("ide-cd: ignoring drive %s\n", drive->name);
-				continue;
-			}
-		}
-		if (drive->scsi) {
-			printk("ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name);
-			continue;
-		}
-		info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
-		if (info == NULL) {
-			printk ("%s: Can't allocate a cdrom structure\n", drive->name);
-			continue;
-		}
-		if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) {
-			printk ("%s: Failed to register the driver with ide.c\n", drive->name);
-			kfree (info);
-			continue;
-		}
-		memset (info, 0, sizeof (struct cdrom_info));
-		drive->driver_data = info;
-		DRIVER(drive)->busy++;
-		if (ide_cdrom_setup (drive)) {
-			DRIVER(drive)->busy--;
-			if (ide_cdrom_cleanup (drive))
-				printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name);
-			continue;
-		}
-		DRIVER(drive)->busy--;
-		failed--;
-	}
-	ide_register_module(&ide_cdrom_module);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-module_init(ide_cdrom_init);
-module_exit(ide_cdrom_exit);
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.3.51/linux/drivers/block/ide-cd.h	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/ide-cd.h	Wed Dec 31 16:00:00 1969
@@ -1,736 +0,0 @@
-/*
- *  linux/drivers/block/ide_cd.h
- *
- *  Copyright (C) 1996-98  Erik Andersen
- *  Copyright (C) 1998-2000 Jens Axboe
- */
-#ifndef _IDE_CD_H
-#define _IDE_CD_H
-
-#include <linux/cdrom.h>
-#include <asm/byteorder.h>
-
-/* Turn this on to have the driver print out the meanings of the
-   ATAPI error codes.  This will use up additional kernel-space
-   memory, though. */
-
-#ifndef VERBOSE_IDE_CD_ERRORS
-#define VERBOSE_IDE_CD_ERRORS 1
-#endif
-
-
-/* Turning this on will remove code to work around various nonstandard
-   ATAPI implementations.  If you know your drive follows the standard,
-   this will give you a slightly smaller kernel. */
-
-#ifndef STANDARD_ATAPI
-#define STANDARD_ATAPI 0
-#endif
-
-
-/* Turning this on will disable the door-locking functionality.
-   This is apparently needed for supermount. */
-
-#ifndef NO_DOOR_LOCKING
-#define NO_DOOR_LOCKING 0
-#endif
-
-/************************************************************************/
-
-#define SECTOR_SIZE		512
-#define SECTOR_BITS 		9
-#define SECTORS_PER_FRAME	(CD_FRAMESIZE / SECTOR_SIZE)
-#define SECTOR_BUFFER_SIZE	(CD_FRAMESIZE * 32)
-#define SECTORS_BUFFER		(SECTOR_BUFFER_SIZE / SECTOR_SIZE)
-
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
-/* special command codes for strategy routine. */
-#define PACKET_COMMAND        4315
-#define REQUEST_SENSE_COMMAND 4316
-#define RESET_DRIVE_COMMAND   4317
-
-
-/* Configuration flags.  These describe the capabilities of the drive.
-   They generally do not change after initialization, unless we learn
-   more about the drive from stuff failing. */
-struct ide_cd_config_flags {
-	__u8 drq_interrupt	: 1; /* Device sends an interrupt when ready
-					for a packet command. */
-	__u8 no_doorlock	: 1; /* Drive cannot lock the door. */
-	__u8 no_eject		: 1; /* Drive cannot eject the disc. */
-	__u8 nec260		: 1; /* Drive is a pre-1.2 NEC 260 drive. */
-	__u8 playmsf_as_bcd	: 1; /* PLAYMSF command takes BCD args. */
-	__u8 tocaddr_as_bcd	: 1; /* TOC addresses are in BCD. */
-	__u8 toctracks_as_bcd	: 1; /* TOC track numbers are in BCD. */
-	__u8 subchan_as_bcd	: 1; /* Subchannel info is in BCD. */
-	__u8 is_changer		: 1; /* Drive is a changer. */
-	__u8 cd_r		: 1; /* Drive can write to CD-R media . */
-	__u8 cd_rw		: 1; /* Drive can write to CD-R/W media . */
-	__u8 dvd		: 1; /* Drive is a DVD-ROM */
-	__u8 dvd_r		: 1; /* Drive can write DVD-R */
-	__u8 dvd_ram		: 1; /* Drive can write DVD-RAM */
-	__u8 test_write		: 1; /* Drive can fake writes */
-	__u8 supp_disc_present	: 1; /* Changer can report exact contents
-					of slots. */
-	__u8 limit_nframes	: 1; /* Drive does not provide data in
-					multiples of SECTOR_SIZE when more
-					than one interrupt is needed. */
-	__u8 seeking		: 1; /* Seeking in progress */
-	__u8 audio_play		: 1; /* can do audio related commands */
-	__u8 close_tray		: 1; /* can close the tray */
-	__u8 writing		: 1; /* pseudo write in progress */
-	__u8 reserved		: 3;
-	byte max_speed;		     /* Max speed of the drive */
-};
-#define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags))
-
- 
-/* State flags.  These give information about the current state of the
-   drive, and will change during normal operation. */
-struct ide_cd_state_flags {
-	__u8 media_changed : 1; /* Driver has noticed a media change. */
-	__u8 toc_valid     : 1; /* Saved TOC information is current. */
-	__u8 door_locked   : 1; /* We think that the drive door is locked. */
-	__u8 writing       : 1; /* the drive is currently writing */
-	__u8 reserved      : 4;
-	byte current_speed;	/* Current speed of the drive */
-};
-
-#define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags))
-
-struct packet_command {
-	char *buffer;
-	int buflen;
-	int stat;
-	struct request_sense *sense_data;
-	unsigned char c[12];
-};
-
-/* Structure of a MSF cdrom address. */
-struct atapi_msf {
-	byte reserved;
-	byte minute;
-	byte second;
-	byte frame;
-};
-
-/* Space to hold the disk TOC. */
-#define MAX_TRACKS 99
-struct atapi_toc_header {
-	unsigned short toc_length;
-	byte first_track;
-	byte last_track;
-};
-
-struct atapi_toc_entry {
-	byte reserved1;
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 adr     : 4;
-	__u8 control : 4;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8 control : 4;
-	__u8 adr     : 4;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-	byte track;
-	byte reserved2;
-	union {
-		unsigned lba;
-		struct atapi_msf msf;
-	} addr;
-};
-
-struct atapi_toc {
-	int    last_session_lba;
-	int    xa_flag;
-	unsigned capacity;
-	struct atapi_toc_header hdr;
-	struct atapi_toc_entry  ent[MAX_TRACKS+1];
-	  /* One extra for the leadout. */
-};
-
-
-/* This structure is annoyingly close to, but not identical with,
-   the cdrom_subchnl structure from cdrom.h. */
-struct atapi_cdrom_subchnl {
- 	u_char  acdsc_reserved;
- 	u_char  acdsc_audiostatus;
- 	u_short acdsc_length;
-	u_char  acdsc_format;
-
-#if defined(__BIG_ENDIAN_BITFIELD)
-	u_char  acdsc_ctrl:     4;
-	u_char  acdsc_adr:      4;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	u_char  acdsc_adr:	4;
-	u_char  acdsc_ctrl:	4;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-	u_char  acdsc_trk;
-	u_char  acdsc_ind;
-	union {
-		struct atapi_msf msf;
-		int	lba;
-	} acdsc_absaddr;
-	union {
-		struct atapi_msf msf;
-		int	lba;
-	} acdsc_reladdr;
-};
-
-
-
-/* This should probably go into cdrom.h along with the other
- * generic stuff now in the Mt. Fuji spec.
- */
-struct atapi_capabilities_page {
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 parameters_saveable : 1;
-	__u8 reserved1           : 1;
-	__u8 page_code           : 6;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8 page_code           : 6;
-	__u8 reserved1           : 1;
-	__u8 parameters_saveable : 1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-	byte     page_length;
-
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 reserved2           : 2;
-	/* Drive supports reading of DVD-RAM discs */
-	__u8 dvd_ram_read        : 1;
-	/* Drive supports reading of DVD-R discs */
-	__u8 dvd_r_read          : 1;
-	/* Drive supports reading of DVD-ROM discs */
-	__u8 dvd_rom             : 1;
-	/* Drive supports reading CD-R discs with addressing method 2 */
-	__u8 method2             : 1; /* reserved in 1.2 */
-	/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
-	__u8 cd_rw_read		 : 1; /* reserved in 1.2 */
-	/* Drive supports read from CD-R discs (orange book, part II) */
-	__u8 cd_r_read           : 1; /* reserved in 1.2 */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	/* Drive supports read from CD-R discs (orange book, part II) */
-	__u8 cd_r_read           : 1; /* reserved in 1.2 */
-	/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
-	__u8 cd_rw_read          : 1; /* reserved in 1.2 */
-	/* Drive supports reading CD-R discs with addressing method 2 */
-	__u8 method2             : 1;
-	/* Drive supports reading of DVD-ROM discs */
-	__u8 dvd_rom             : 1;
-	/* Drive supports reading of DVD-R discs */
-	__u8 dvd_r_read          : 1;
-	/* Drive supports reading of DVD-RAM discs */
-	__u8 dvd_ram_read        : 1;
-	__u8 reserved2		 : 2;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 reserved3           : 2;
-	/* Drive can write DVD-RAM discs */
-	__u8 dvd_ram_write       : 1;
-	/* Drive can write DVD-R discs */
-	__u8 dvd_r_write         : 1;
-	__u8 reserved3a          : 1;
-	/* Drive can fake writes */
-	__u8 test_write          : 1;
-	/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
-	__u8 cd_rw_write	 : 1; /* reserved in 1.2 */
-	/* Drive supports write to CD-R discs (orange book, part II) */
-	__u8 cd_r_write          : 1; /* reserved in 1.2 */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	/* Drive can write to CD-R discs (orange book, part II) */
-	__u8 cd_r_write          : 1; /* reserved in 1.2 */
-	/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
-	__u8 cd_rw_write	 : 1; /* reserved in 1.2 */
-	/* Drive can fake writes */
-	__u8 test_write          : 1;
-	__u8 reserved3a          : 1;
-	/* Drive can write DVD-R discs */
-	__u8 dvd_r_write         : 1;
-	/* Drive can write DVD-RAM discs */
-	__u8 dvd_ram_write       : 1;
-	__u8 reserved3           : 2;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 reserved4           : 1;
-	/* Drive can read multisession discs. */
-	__u8 multisession        : 1;
-	/* Drive can read mode 2, form 2 data. */
-	__u8 mode2_form2         : 1;
-	/* Drive can read mode 2, form 1 (XA) data. */
-	__u8 mode2_form1         : 1;
-	/* Drive supports digital output on port 2. */
-	__u8 digport2            : 1;
-	/* Drive supports digital output on port 1. */
-	__u8 digport1            : 1;
-	/* Drive can deliver a composite audio/video data stream. */
-	__u8 composite           : 1;
-	/* Drive supports audio play operations. */
-	__u8 audio_play          : 1;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	/* Drive supports audio play operations. */
-	__u8 audio_play          : 1;
-	/* Drive can deliver a composite audio/video data stream. */
-	__u8 composite           : 1;
-	/* Drive supports digital output on port 1. */
-	__u8 digport1            : 1;
-	/* Drive supports digital output on port 2. */
-	__u8 digport2            : 1;
-	/* Drive can read mode 2, form 1 (XA) data. */
-	__u8 mode2_form1         : 1;
-	/* Drive can read mode 2, form 2 data. */
-	__u8 mode2_form2         : 1;
-	/* Drive can read multisession discs. */
-	__u8 multisession        : 1;
-	__u8 reserved4           : 1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 reserved5           : 1;
-	/* Drive can return Media Catalog Number (UPC) info. */
-	__u8 upc                 : 1;
-	/* Drive can return International Standard Recording Code info. */
-	__u8 isrc                : 1;
-	/* Drive supports C2 error pointers. */
-	__u8 c2_pointers         : 1;
-	/* R-W data will be returned deinterleaved and error corrected. */
-	__u8 rw_corr             : 1;
-	/* Subchannel reads can return combined R-W information. */
-	__u8 rw_supported        : 1;
-	/* Drive can continue a read cdda operation from a loss of streaming.*/
-	__u8 cdda_accurate       : 1;
-	/* Drive can read Red Book audio data. */
-	__u8 cdda                : 1;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	/* Drive can read Red Book audio data. */
-	__u8 cdda                : 1;
-	/* Drive can continue a read cdda operation from a loss of streaming.*/
-	__u8 cdda_accurate       : 1;
-	/* Subchannel reads can return combined R-W information. */
-	__u8 rw_supported        : 1;
-	/* R-W data will be returned deinterleaved and error corrected. */
-	__u8 rw_corr             : 1;
-	/* Drive supports C2 error pointers. */
-	__u8 c2_pointers         : 1;
-	/* Drive can return International Standard Recording Code info. */
-	__u8 isrc                : 1;
-	/* Drive can return Media Catalog Number (UPC) info. */
-	__u8 upc                 : 1;
-	__u8 reserved5           : 1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-#if defined(__BIG_ENDIAN_BITFIELD)
-	/* Drive mechanism types. */
-	mechtype_t mechtype	 : 3;
-	__u8 reserved6           : 1;
-	/* Drive can eject a disc or changer cartridge. */
-	__u8 eject               : 1;
-	/* State of prevent/allow jumper. */
-	__u8 prevent_jumper      : 1;
-	/* Present state of door lock. */
-	__u8 lock_state          : 1;
-	/* Drive can lock the door. */
-	__u8 lock                : 1;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-
-	/* Drive can lock the door. */
-	__u8 lock                : 1;
-	/* Present state of door lock. */
-	__u8 lock_state          : 1;
-	/* State of prevent/allow jumper. */
-	__u8 prevent_jumper      : 1;
-	/* Drive can eject a disc or changer cartridge. */
-	__u8 eject               : 1;
-	__u8 reserved6           : 1;
-	/* Drive mechanism types. */
-	mechtype_t mechtype	 : 3;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 reserved7           : 4;
-	/* Drive supports software slot selection. */
-	__u8 sss                 : 1;  /* reserved in 1.2 */
-	/* Changer can report exact contents of slots. */
-	__u8 disc_present        : 1;  /* reserved in 1.2 */
-	/* Audio for each channel can be muted independently. */
-	__u8 separate_mute       : 1;
-	/* Audio level for each channel can be controlled independently. */
-	__u8 separate_volume     : 1;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-
-	/* Audio level for each channel can be controlled independently. */
-	__u8 separate_volume     : 1;
-	/* Audio for each channel can be muted independently. */
-	__u8 separate_mute       : 1;
-	/* Changer can report exact contents of slots. */
-	__u8 disc_present        : 1;  /* reserved in 1.2 */
-	/* Drive supports software slot selection. */
-	__u8 sss                 : 1;  /* reserved in 1.2 */
-	__u8 reserved7           : 4;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-	/* Note: the following four fields are returned in big-endian form. */
-	/* Maximum speed (in kB/s). */
-	unsigned short maxspeed;
-	/* Number of discrete volume levels. */
-	unsigned short n_vol_levels;
-	/* Size of cache in drive, in kB. */
-	unsigned short buffer_size;
-	/* Current speed (in kB/s). */
-	unsigned short curspeed;
-
-	/* Truncate the structure here, so we don't have headaches reading
-	   from older drives. */
-};
-
-
-struct atapi_mechstat_header {
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 fault         : 1;
-	__u8 changer_state : 2;
-	__u8 curslot       : 5;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8 curslot       : 5;
-	__u8 changer_state : 2;
-	__u8 fault         : 1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 mech_state    : 3;
-	__u8 door_open     : 1;
-	__u8 reserved1     : 4;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8 reserved1     : 4;
-	__u8 door_open     : 1;
-	__u8 mech_state    : 3;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-	byte     curlba[3];
-	byte     nslots;
-	__u8 short slot_tablelen;
-};
-
-
-struct atapi_slot {
-#if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 disc_present : 1;
-	__u8 reserved1    : 6;
-	__u8 change       : 1;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8 change       : 1;
-	__u8 reserved1    : 6;
-	__u8 disc_present : 1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-
-	byte reserved2[3];
-};
-
-struct atapi_changer_info {
-	struct atapi_mechstat_header hdr;
-	struct atapi_slot slots[0];
-};
-
-/* Extra per-device info for cdrom drives. */
-struct cdrom_info {
-
-	/* Buffer for table of contents.  NULL if we haven't allocated
-	   a TOC buffer for this device yet. */
-
-	struct atapi_toc *toc;
-
-	unsigned long	sector_buffered;
-	unsigned long	nsectors_buffered;
-	unsigned char	*buffer;
-
-	/* The result of the last successful request sense command
-	   on this device. */
-	struct request_sense sense_data;
-
-	struct request request_sense_request;
-	struct packet_command request_sense_pc;
-	int dma;
-	unsigned long last_block;
-	unsigned long start_seek;
-	/* Buffer to hold mechanism status and changer slot table. */
-	struct atapi_changer_info *changer_info;
-
-	struct ide_cd_config_flags	config_flags;
-	struct ide_cd_state_flags	state_flags;
-
-        /* Per-device info needed by cdrom.c generic driver. */
-        struct cdrom_device_info devinfo;
-};
-
-/****************************************************************************
- * Descriptions of ATAPI error codes.
- */
-
-#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
-
-/* This stuff should be in cdrom.h, since it is now generic... */
-
-/* ATAPI sense keys (from table 140 of ATAPI 2.6) */
-#define NO_SENSE                0x00
-#define RECOVERED_ERROR         0x01
-#define NOT_READY               0x02
-#define MEDIUM_ERROR            0x03
-#define HARDWARE_ERROR          0x04
-#define ILLEGAL_REQUEST         0x05
-#define UNIT_ATTENTION          0x06
-#define DATA_PROTECT            0x07
-#define ABORTED_COMMAND         0x0b
-#define MISCOMPARE              0x0e
-
- 
-
-/* This stuff should be in cdrom.h, since it is now generic... */
-#if VERBOSE_IDE_CD_ERRORS
-
- /* The generic packet command opcodes for CD/DVD Logical Units,
- * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ 
-const struct {
-	unsigned short packet_command;
-	const char * const text;
-} packet_command_texts[] = {
-	{ GPCMD_TEST_UNIT_READY, "Test Unit Ready" },
-	{ GPCMD_REQUEST_SENSE, "Request Sense" },
-	{ GPCMD_FORMAT_UNIT, "Format Unit" },
-	{ GPCMD_INQUIRY, "Inquiry" },
-	{ GPCMD_START_STOP_UNIT, "Start/Stop Unit" },
-	{ GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" },
-	{ GPCMD_READ_FORMAT_CAPACITIES, "Read Format Capacities" },
-	{ GPCMD_READ_CDVD_CAPACITY, "Read Cd/Dvd Capacity" },
-	{ GPCMD_READ_10, "Read 10" },
-	{ GPCMD_WRITE_10, "Write 10" },
-	{ GPCMD_SEEK, "Seek" },
-	{ GPCMD_WRITE_AND_VERIFY_10, "Write and Verify 10" },
-	{ GPCMD_VERIFY_10, "Verify 10" },
-	{ GPCMD_FLUSH_CACHE, "Flush Cache" },
-	{ GPCMD_READ_SUBCHANNEL, "Read Subchannel" },
-	{ GPCMD_READ_TOC_PMA_ATIP, "Read Table of Contents" },
-	{ GPCMD_READ_HEADER, "Read Header" },
-	{ GPCMD_PLAY_AUDIO_10, "Play Audio 10" },
-	{ GPCMD_GET_CONFIGURATION, "Get Configuration" },
-	{ GPCMD_PLAY_AUDIO_MSF, "Play Audio MSF" },
-	{ GPCMD_PLAYAUDIO_TI, "Play Audio TrackIndex" },
-	{ GPCMD_GET_EVENT_STATUS_NOTIFICATION, "Get Event Status Notification" },
-	{ GPCMD_PAUSE_RESUME, "Pause/Resume" },
-	{ GPCMD_STOP_PLAY_SCAN, "Stop Play/Scan" },
-	{ GPCMD_READ_DISC_INFO, "Read Disc Info" },
-	{ GPCMD_READ_TRACK_RZONE_INFO, "Read Track Rzone Info" },
-	{ GPCMD_RESERVE_RZONE_TRACK, "Reserve Rzone Track" },
-	{ GPCMD_SEND_OPC, "Send OPC" },
-	{ GPCMD_MODE_SELECT_10, "Mode Select 10" },
-	{ GPCMD_REPAIR_RZONE_TRACK, "Repair Rzone Track" },
-	{ GPCMD_MODE_SENSE_10, "Mode Sense 10" },
-	{ GPCMD_CLOSE_TRACK, "Close Track" },
-	{ GPCMD_BLANK, "Blank" },
-	{ GPCMD_SEND_EVENT, "Send Event" },
-	{ GPCMD_SEND_KEY, "Send Key" },
-	{ GPCMD_REPORT_KEY, "Report Key" },
-	{ GPCMD_LOAD_UNLOAD, "Load/Unload" },
-	{ GPCMD_SET_READ_AHEAD, "Set Read-ahead" },
-	{ GPCMD_READ_12, "Read 12" },
-	{ GPCMD_GET_PERFORMANCE, "Get Performance" },
-	{ GPCMD_SEND_DVD_STRUCTURE, "Send DVD Structure" },
-	{ GPCMD_READ_DVD_STRUCTURE, "Read DVD Structure" },
-	{ GPCMD_SET_STREAMING, "Set Streaming" },
-	{ GPCMD_READ_CD_MSF, "Read CD MSF" },
-	{ GPCMD_SCAN, "Scan" },
-	{ GPCMD_SET_SPEED, "Set Speed" },
-	{ GPCMD_PLAY_CD, "Play CD" },
-	{ GPCMD_MECHANISM_STATUS, "Mechanism Status" },
-	{ GPCMD_READ_CD, "Read CD" },
-};
-
-
-
-/* From Table 303 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
-const char * const sense_key_texts[16] = {
-	"No sense data",
-	"Recovered error",
-	"Not ready",
-	"Medium error",
-	"Hardware error",
-	"Illegal request",
-	"Unit attention",
-	"Data protect",
-	"(reserved)",
-	"(reserved)",
-	"(reserved)",
-	"Aborted command",
-	"(reserved)",
-	"(reserved)",
-	"Miscompare",
-	"(reserved)",
-};
-
-/* From Table 304 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
-const struct {
-	unsigned long asc_ascq;
-	const char * const text;
-} sense_data_texts[] = {
-	{ 0x000000, "No additional sense information" },
-	{ 0x000011, "Play operation in progress" },
-	{ 0x000012, "Play operation paused" },
-	{ 0x000013, "Play operation successfully completed" },
-	{ 0x000014, "Play operation stopped due to error" },
-	{ 0x000015, "No current audio status to return" },
-	{ 0x010c0a, "Write error - padding blocks added" },
-	{ 0x011700, "Recovered data with no error correction applied" },
-	{ 0x011701, "Recovered data with retries" },
-	{ 0x011702, "Recovered data with positive head offset" },
-	{ 0x011703, "Recovered data with negative head offset" },
-	{ 0x011704, "Recovered data with retries and/or CIRC applied" },
-	{ 0x011705, "Recovered data using previous sector ID" },
-	{ 0x011800, "Recovered data with error correction applied" },
-	{ 0x011801, "Recovered data with error correction and retries applied"},
-	{ 0x011802, "Recovered data - the data was auto-reallocated" },
-	{ 0x011803, "Recovered data with CIRC" },
-	{ 0x011804, "Recovered data with L-EC" },
-	{ 0x015d00, 
-	    "Failure prediction threshold exceeded - Predicted logical unit failure" },
-	{ 0x015d01, 
-	    "Failure prediction threshold exceeded - Predicted media failure" },
-	{ 0x015dff, "Failure prediction threshold exceeded - False" },
-	{ 0x017301, "Power calibration area almost full" },
-	{ 0x020400, "Logical unit not ready - cause not reportable" },
-	/* Following is misspelled in ATAPI 2.6, _and_ in Mt. Fuji */
-	{ 0x020401,
-	  "Logical unit not ready - in progress [sic] of becoming ready" },
-	{ 0x020402, "Logical unit not ready - initializing command required" },
-	{ 0x020403, "Logical unit not ready - manual intervention required" },
-	{ 0x020404, "In process of becoming ready - writing" },
-	{ 0x020600, "No reference position found (media may be upside down)" },
-	{ 0x023000, "Incompatible medium installed" },
-	{ 0x023a00, "Medium not present" },
-	{ 0x025300, "Media load or eject failed" },
-	{ 0x025700, "Unable to recover table of contents" },
-	{ 0x030300, "Peripheral device write fault" },
-	{ 0x030301, "No write current" },
-	{ 0x030302, "Excessive write errors" },
-	{ 0x030c00, "Write error" },
-	{ 0x030c01, "Write error - Recovered with auto reallocation" },
-	{ 0x030c02, "Write error - auto reallocation failed" },
-	{ 0x030c03, "Write error - recommend reassignment" },
-	{ 0x030c04, "Compression check miscompare error" },
-	{ 0x030c05, "Data expansion occurred during compress" },
-	{ 0x030c06, "Block not compressible" },
-	{ 0x030c07, "Write error - recovery needed" },
-	{ 0x030c08, "Write error - recovery failed" },
-	{ 0x030c09, "Write error - loss of streaming" },
-	{ 0x031100, "Unrecovered read error" },
-	{ 0x031106, "CIRC unrecovered error" },
-	{ 0x033101, "Format command failed" },
-	{ 0x033200, "No defect spare location available" },
-	{ 0x033201, "Defect list update failure" },
-	{ 0x035100, "Erase failure" },
-	{ 0x037200, "Session fixation error" },
-	{ 0x037201, "Session fixation error writin lead-in" },
-	{ 0x037202, "Session fixation error writin lead-out" },
-	{ 0x037300, "CD control error" },
-	{ 0x037302, "Power calibration area is full" },
-	{ 0x037303, "Power calibration area error" },
-	{ 0x037304, "Program memory area / RMA update failure" },
-	{ 0x037305, "Program memory area / RMA is full" },
-	{ 0x037306, "Program memory area / RMA is (almost) full" },
-
-	{ 0x040200, "No seek complete" },
-	{ 0x040300, "Write fault" },
-	{ 0x040900, "Track following error" },
-	{ 0x040901, "Tracking servo failure" },
-	{ 0x040902, "Focus servo failure" },
-	{ 0x040903, "Spindle servo failure" },
-	{ 0x041500, "Random positioning error" },
-	{ 0x041501, "Mechanical positioning or changer error" },
-	{ 0x041502, "Positioning error detected by read of medium" },
-	{ 0x043c00, "Mechanical positioning or changer error" },
-	{ 0x044000, "Diagnostic failure on component (ASCQ)" },
-	{ 0x044400, "Internal CD/DVD logical unit failure" },
-	{ 0x04b600, "Media load mechanism failed" },
-	{ 0x051a00, "Parameter list length error" },
-	{ 0x052000, "Invalid command operation code" },
-	{ 0x052c00, "Command sequence error" },
-	{ 0x052100, "Logical block address out of range" },
-	{ 0x052102, "Invalid address for write" },
-	{ 0x052400, "Invalid field in command packet" },
-	{ 0x052600, "Invalid field in parameter list" },
-	{ 0x052601, "Parameter not supported" },
-	{ 0x052602, "Parameter value invalid" },
-	{ 0x052700, "Write protected media" },
-	{ 0x052c00, "Command sequence error" },
-	{ 0x052c03, "Current program area is not empty" },
-	{ 0x052c04, "Current program area is empty" },
-	{ 0x053001, "Cannot read medium - unknown format" },
-	{ 0x053002, "Cannot read medium - incompatible format" },
-	{ 0x053900, "Saving parameters not supported" },
-	{ 0x054e00, "Overlapped commands attempted" },
-	{ 0x055302, "Medium removal prevented" },
-	{ 0x055500, "System resource failure" },
-	{ 0x056300, "End of user area encountered on this track" },
-	{ 0x056400, "Illegal mode for this track or incompatible medium" },
-	{ 0x056f00, "Copy protection key exchange failure - Authentication failure" },
-	{ 0x056f01, "Copy protection key exchange failure - Key not present" },
-	{ 0x056f02, "Copy protection key exchange failure - Key not established" },
-	{ 0x056f03, "Read of scrambled sector without authentication" },
-	{ 0x056f04, "Media region code is mismatched to logical unit" },
-	{ 0x056f05,  "Drive region must be permanent / region reset count error" },
-	{ 0x057203, "Session fixation error - incomplete track in session" },
-	{ 0x057204, "Empty or partially written reserved track" },
-	{ 0x057205, "No more RZONE reservations are allowed" },
-	{ 0x05bf00, "Loss of streaming" },
-	{ 0x062800, "Not ready to ready transition, medium may have changed" },
-	{ 0x062900, "Power on, reset or hardware reset occurred" },
-	{ 0x062a00, "Parameters changed" },
-	{ 0x062a01, "Mode parameters changed" },
-	{ 0x062e00, "Insufficient time for operation" },
-	{ 0x063f00, "Logical unit operating conditions have changed" },
-	{ 0x063f01, "Microcode has been changed" },
-	{ 0x065a00, "Operator request or state change input (unspecified)" },
-	{ 0x065a01, "Operator medium removal request" },
-	{ 0x0bb900, "Play operation aborted" },
-
-	/* Here we use 0xff for the key (not a valid key) to signify
-	 * that these can have _any_ key value associated with them... */
-	{ 0xff0401, "Logical unit is in process of becoming ready" },
-	{ 0xff0400, "Logical unit not ready, cause not reportable" },
-	{ 0xff0402, "Logical unit not ready, initializing command required" },
-	{ 0xff0403, "Logical unit not ready, manual intervention required" },
-	{ 0xff0500, "Logical unit does not respond to selection" },
-	{ 0xff0800, "Logical unit communication failure" },
-	{ 0xff0802, "Logical unit communication parity error" },
-	{ 0xff0801, "Logical unit communication time-out" },
-	{ 0xff2500, "Logical unit not supported" },
-	{ 0xff4c00, "Logical unit failed self-configuration" },
-	{ 0xff3e00, "Logical unit has not self-configured yet" },
-};
-#endif
-
-
-#endif /* _IDE_CD_H */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-cs.c linux/drivers/block/ide-cs.c
--- v2.3.51/linux/drivers/block/ide-cs.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/block/ide-cs.c	Wed Dec 31 16:00:00 1969
@@ -1,481 +0,0 @@
-/*======================================================================
-
-    A driver for PCMCIA IDE/ATA disk cards
-
-    ide_cs.c 1.26 1999/11/16 02:10:49
-
-    The contents of this file are subject to the Mozilla Public
-    License Version 1.1 (the "License"); you may not use this file
-    except in compliance with the License. You may obtain a copy of
-    the License at http://www.mozilla.org/MPL/
-
-    Software distributed under the License is distributed on an "AS
-    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
-    implied. See the License for the specific language governing
-    rights and limitations under the License.
-
-    The initial developer of the original code is David A. Hinds
-    <dhinds@pcmcia.sourceforge.org>.  Portions created by David A. Hinds
-    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
-
-    Alternatively, the contents of this file may be used under the
-    terms of the GNU Public License version 2 (the "GPL"), in which
-    case the provisions of the GPL are applicable instead of the
-    above.  If you wish to allow the use of your version of this file
-    only under the terms of the GPL and not to allow others to use
-    your version of this file under the MPL, indicate your decision
-    by deleting the provisions above and replace them with the notice
-    and other provisions required by the GPL.  If you do not delete
-    the provisions above, a recipient may use your version of this
-    file under either the MPL or the GPL.
-    
-======================================================================*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/ioport.h>
-#include <linux/hdreg.h>
-#include <linux/major.h>
-
-#include <asm/io.h>
-#include <asm/system.h>
-
-#include <pcmcia/version.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/cisreg.h>
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-MODULE_PARM(pc_debug, "i");
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"ide_cs.c 1.26 1999/11/16 02:10:49 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
-
-/*====================================================================*/
-
-/* Parameters that can be set with 'insmod' */
-
-/* Bit map of interrupts to choose from */
-static u_int irq_mask = 0xdeb8;
-static int irq_list[4] = { -1 };
-
-MODULE_PARM(irq_mask, "i");
-MODULE_PARM(irq_list, "1-4i");
-
-/*====================================================================*/
-
-static const char ide_major[] = {
-    IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR,
-#ifdef IDE4_MAJOR
-    IDE4_MAJOR, IDE5_MAJOR
-#endif
-};
-
-typedef struct ide_info_t {
-    dev_link_t	link;
-    int		ndev;
-    dev_node_t	node;
-    int		hd;
-} ide_info_t;
-
-static void ide_config(dev_link_t *link);
-static void ide_release(u_long arg);
-static int ide_event(event_t event, int priority,
-		     event_callback_args_t *args);
-
-static dev_info_t dev_info = "ide_cs";
-
-static dev_link_t *ide_attach(void);
-static void ide_detach(dev_link_t *);
-
-static dev_link_t *dev_list = NULL;
-
-/*====================================================================*/
-
-static void cs_error(client_handle_t handle, int func, int ret)
-{
-    error_info_t err = { func, ret };
-    CardServices(ReportError, handle, &err);
-}
-
-/*======================================================================
-
-    ide_attach() creates an "instance" of the driver, allocating
-    local data structures for one device.  The device is registered
-    with Card Services.
-
-======================================================================*/
-
-static dev_link_t *ide_attach(void)
-{
-    ide_info_t *info;
-    dev_link_t *link;
-    client_reg_t client_reg;
-    int i, ret;
-    
-    DEBUG(0, "ide_attach()\n");
-
-    /* Create new ide device */
-    info = kmalloc(sizeof(*info), GFP_KERNEL);
-    if (!info) return NULL;
-    memset(info, 0, sizeof(*info));
-    link = &info->link; link->priv = info;
-
-    link->release.function = &ide_release;
-    link->release.data = (u_long)link;
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
-    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
-    link->io.IOAddrLines = 3;
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
-    if (irq_list[0] == -1)
-	link->irq.IRQInfo2 = irq_mask;
-    else
-	for (i = 0; i < 4; i++)
-	    link->irq.IRQInfo2 |= 1 << irq_list[i];
-    link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
-    link->conf.IntType = INT_MEMORY_AND_IO;
-    
-    /* Register with Card Services */
-    link->next = dev_list;
-    dev_list = link;
-    client_reg.dev_info = &dev_info;
-    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
-    client_reg.EventMask =
-	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
-	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
-	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
-    client_reg.event_handler = &ide_event;
-    client_reg.Version = 0x0210;
-    client_reg.event_callback_args.client_data = link;
-    ret = CardServices(RegisterClient, &link->handle, &client_reg);
-    if (ret != CS_SUCCESS) {
-	cs_error(link->handle, RegisterClient, ret);
-	ide_detach(link);
-	return NULL;
-    }
-    
-    return link;
-} /* ide_attach */
-
-/*======================================================================
-
-    This deletes a driver "instance".  The device is de-registered
-    with Card Services.  If it has been released, all local data
-    structures are freed.  Otherwise, the structures will be freed
-    when the device is released.
-
-======================================================================*/
-
-static void ide_detach(dev_link_t *link)
-{
-    dev_link_t **linkp;
-    long flags;
-    int ret;
-
-    DEBUG(0, "ide_detach(0x%p)\n", link);
-    
-    /* Locate device structure */
-    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
-	if (*linkp == link) break;
-    if (*linkp == NULL)
-	return;
-
-    save_flags(flags);
-    cli();
-    if (link->state & DEV_RELEASE_PENDING) {
-	del_timer(&link->release);
-	link->state &= ~DEV_RELEASE_PENDING;
-    }
-    restore_flags(flags);
-    
-    if (link->state & DEV_CONFIG)
-	ide_release((u_long)link);
-    
-    if (link->handle) {
-	ret = CardServices(DeregisterClient, link->handle);
-	if (ret != CS_SUCCESS)
-	    cs_error(link->handle, DeregisterClient, ret);
-    }
-    
-    /* Unlink, free device structure */
-    *linkp = link->next;
-    kfree(link->priv);
-    
-} /* ide_detach */
-
-/*======================================================================
-
-    ide_config() is scheduled to run after a CARD_INSERTION event
-    is received, to configure the PCMCIA socket, and to make the
-    ide device available to the system.
-
-======================================================================*/
-
-#define CS_CHECK(fn, args...) \
-while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
-
-#define CFG_CHECK(fn, args...) \
-if (CardServices(fn, args) != 0) goto next_entry
-
-void ide_config(dev_link_t *link)
-{
-    client_handle_t handle = link->handle;
-    ide_info_t *info = link->priv;
-    tuple_t tuple;
-    u_short buf[128];
-    cisparse_t parse;
-    config_info_t conf;
-    cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
-    cistpl_cftable_entry_t dflt = { 0 };
-    int i, pass, last_ret, last_fn, hd, io_base, ctl_base;
-
-    DEBUG(0, "ide_config(0x%p)\n", link);
-    
-    tuple.TupleData = (cisdata_t *)buf;
-    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
-    tuple.Attributes = 0;
-    tuple.DesiredTuple = CISTPL_CONFIG;
-    CS_CHECK(GetFirstTuple, handle, &tuple);
-    CS_CHECK(GetTupleData, handle, &tuple);
-    CS_CHECK(ParseTuple, handle, &tuple, &parse);
-    link->conf.ConfigBase = parse.config.base;
-    link->conf.Present = parse.config.rmask[0];
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
-    /* Not sure if this is right... look up the current Vcc */
-    CS_CHECK(GetConfigurationInfo, handle, &conf);
-    link->conf.Vcc = conf.Vcc;
-    
-    pass = io_base = ctl_base = 0;
-    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-    tuple.Attributes = 0;
-    CS_CHECK(GetFirstTuple, handle, &tuple);
-    while (1) {
-	CFG_CHECK(GetTupleData, handle, &tuple);
-	CFG_CHECK(ParseTuple, handle, &tuple, &parse);
-
-	/* Check for matching Vcc, unless we're desperate */
-	if (!pass) {
-	    if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
-		if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
-		    goto next_entry;
-	    } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
-		if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000)
-		    goto next_entry;
-	    }
-	}
-	
-	if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
-	    link->conf.Vpp1 = link->conf.Vpp2 =
-		cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
-	else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
-	    link->conf.Vpp1 = link->conf.Vpp2 =
-		dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
-	
-	if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
-	    cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
-	    link->conf.ConfigIndex = cfg->index;
-	    link->io.BasePort1 = io->win[0].base;
-	    link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
-	    if (!(io->flags & CISTPL_IO_16BIT))
-		link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-	    if (io->nwin == 2) {
-		link->io.NumPorts1 = 8;
-		link->io.BasePort2 = io->win[1].base;
-		link->io.NumPorts2 = 1;
-		CFG_CHECK(RequestIO, link->handle, &link->io);
-		io_base = link->io.BasePort1;
-		ctl_base = link->io.BasePort2;
-	    } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
-		link->io.NumPorts1 = io->win[0].len;
-		link->io.NumPorts2 = 0;
-		CFG_CHECK(RequestIO, link->handle, &link->io);
-		io_base = link->io.BasePort1;
-		ctl_base = link->io.BasePort1+0x0e;
-	    } else goto next_entry;
-	    /* If we've got this far, we're done */
-	    break;
-	}
-	
-    next_entry:
-	if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
-	if (pass) {
-	    CS_CHECK(GetNextTuple, handle, &tuple);
-	} else if (CardServices(GetNextTuple, handle, &tuple) != 0) {
-	    CS_CHECK(GetFirstTuple, handle, &tuple);
-	    memset(&dflt, 0, sizeof(dflt));
-	    pass++;
-	}
-    }
-    
-    CS_CHECK(RequestIRQ, handle, &link->irq);
-    CS_CHECK(RequestConfiguration, handle, &link->conf);
-
-    /* deal with brain dead IDE resource management */
-    release_region(link->io.BasePort1, link->io.NumPorts1);
-    if (link->io.NumPorts2)
-	release_region(link->io.BasePort2, link->io.NumPorts2);
-
-    /* retry registration in case device is still spinning up */
-    for (i = 0; i < 10; i++) {
-	hd = ide_register(io_base, ctl_base, link->irq.AssignedIRQ);
-	if (hd >= 0) break;
-	if (link->io.NumPorts1 == 0x20) {
-	    hd = ide_register(io_base+0x10, ctl_base+0x10,
-			      link->irq.AssignedIRQ);
-	    if (hd >= 0) {
-		io_base += 0x10; ctl_base += 0x10;
-		break;
-	    }
-	}
-	__set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ/10);
-    }
-    
-    if (hd < 0) {
-	printk(KERN_NOTICE "ide_cs: ide_register() at 0x%3x & 0x%3x"
-	       ", irq %u failed\n", io_base, ctl_base,
-	       link->irq.AssignedIRQ);
-	goto failed;
-    }
-
-    MOD_INC_USE_COUNT;
-    info->ndev = 1;
-    sprintf(info->node.dev_name, "hd%c", 'a'+(hd*2));
-    info->node.major = ide_major[hd];
-    info->node.minor = 0;
-    info->hd = hd;
-    link->dev = &info->node;
-    printk(KERN_INFO "ide_cs: %s: Vcc = %d.%d, Vpp = %d.%d\n",
-	   info->node.dev_name, link->conf.Vcc/10, link->conf.Vcc%10,
-	   link->conf.Vpp1/10, link->conf.Vpp1%10);
-
-    link->state &= ~DEV_CONFIG_PENDING;
-    return;
-    
-cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
-failed:
-    ide_release((u_long)link);
-
-} /* ide_config */
-
-/*======================================================================
-
-    After a card is removed, ide_release() will unregister the net
-    device, and release the PCMCIA configuration.  If the device is
-    still open, this will be postponed until it is closed.
-    
-======================================================================*/
-
-void ide_release(u_long arg)
-{
-    dev_link_t *link = (dev_link_t *)arg;
-    ide_info_t *info = link->priv;
-    
-    DEBUG(0, "ide_release(0x%p)\n", link);
-
-    if (info->ndev) {
-	ide_unregister(info->hd);
-	MOD_DEC_USE_COUNT;
-    }
-    info->ndev = 0;
-    link->dev = NULL;
-    
-    CardServices(ReleaseConfiguration, link->handle);
-    CardServices(ReleaseIO, link->handle, &link->io);
-    CardServices(ReleaseIRQ, link->handle, &link->irq);
-    
-    link->state &= ~DEV_CONFIG;
-
-} /* ide_release */
-
-/*======================================================================
-
-    The card status event handler.  Mostly, this schedules other
-    stuff to run after an event is received.  A CARD_REMOVAL event
-    also sets some flags to discourage the ide drivers from
-    talking to the ports.
-    
-======================================================================*/
-
-int ide_event(event_t event, int priority,
-	      event_callback_args_t *args)
-{
-    dev_link_t *link = args->client_data;
-
-    DEBUG(1, "ide_event(0x%06x)\n", event);
-    
-    switch (event) {
-    case CS_EVENT_CARD_REMOVAL:
-	link->state &= ~DEV_PRESENT;
-	if (link->state & DEV_CONFIG) {
-	    link->release.expires = jiffies + HZ/20;
-	    link->state |= DEV_RELEASE_PENDING;
-	    add_timer(&link->release);
-	}
-	break;
-    case CS_EVENT_CARD_INSERTION:
-	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-	ide_config(link);
-	break;
-    case CS_EVENT_PM_SUSPEND:
-	link->state |= DEV_SUSPEND;
-	/* Fall through... */
-    case CS_EVENT_RESET_PHYSICAL:
-	if (link->state & DEV_CONFIG)
-	    CardServices(ReleaseConfiguration, link->handle);
-	break;
-    case CS_EVENT_PM_RESUME:
-	link->state &= ~DEV_SUSPEND;
-	/* Fall through... */
-    case CS_EVENT_CARD_RESET:
-	if (DEV_OK(link))
-	    CardServices(RequestConfiguration, link->handle, &link->conf);
-	break;
-    }
-    return 0;
-} /* ide_event */
-
-/*====================================================================*/
-
-static int __init init_ide_cs(void)
-{
-    servinfo_t serv;
-    DEBUG(0, "%s\n", version);
-    CardServices(GetCardServicesInfo, &serv);
-    if (serv.Revision != CS_RELEASE_CODE) {
-	printk(KERN_NOTICE "ide_cs: Card Services release "
-	       "does not match!\n");
-	return -1;
-    }
-    register_pccard_driver(&dev_info, &ide_attach, &ide_detach);
-    return 0;
-}
-
-static void __exit exit_ide_cs(void)
-{
-    DEBUG(0, "ide_cs: unloading\n");
-    unregister_pccard_driver(&dev_info);
-    while (dev_list != NULL)
-	ide_detach(dev_list);
-}
-
-module_init(init_ide_cs);
-module_exit(exit_ide_cs);
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.3.51/linux/drivers/block/ide-disk.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/ide-disk.c	Wed Dec 31 16:00:00 1969
@@ -1,906 +0,0 @@
-/*
- *  linux/drivers/block/ide-disk.c	Version 1.09	April 23, 1999
- *
- *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
- */
-
-/*
- *  Mostly written by Mark Lord <mlord@pobox.com>
- *                and  Gadi Oxman <gadio@netvision.net.il>
- *
- *  See linux/MAINTAINERS for address of current maintainer.
- *
- * This is the IDE/ATA disk driver, as evolved from hd.c and ide.c.
- *
- * Version 1.00		move disk only code from ide.c to ide-disk.c
- *			support optional byte-swapping of all data
- * Version 1.01		fix previous byte-swapping code
- * Version 1.02		remove ", LBA" from drive identification msgs
- * Version 1.03		fix display of id->buf_size for big-endian
- * Version 1.04		add /proc configurable settings and S.M.A.R.T support
- * Version 1.05		add capacity support for ATA3 >= 8GB
- * Version 1.06		get boot-up messages to show full cyl count
- * Version 1.07		disable door-locking if it fails
- * Version 1.08		fixed CHS/LBA translations for ATA4 > 8GB,
- *			process of adding new ATA4 compliance.
- *			fixed problems in allowing fdisk to see
- *			the entire disk.
- * Version 1.09		added increment of rq->sector in ide_multwrite
- *			added UDMA 3/4 reporting
- */
-
-#define IDEDISK_VERSION	"1.09"
-
-#undef REALLY_SLOW_IO		/* most systems can safely undef this */
-
-#define _IDE_DISK_C		/* Tell linux/hdsmart.h it's really us */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/genhd.h>
-#include <linux/malloc.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/byteorder.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
-#ifdef CONFIG_BLK_DEV_PDC4030
-#define IS_PDC4030_DRIVE (HWIF(drive)->chipset == ide_pdc4030)
-#else
-#define IS_PDC4030_DRIVE (0)	/* auto-NULLs out pdc4030 code */
-#endif
-
-static void idedisk_bswap_data (void *buffer, int wcount)
-{
-	u16 *p = buffer;
-
-	while (wcount--) {
-		*p++ = *p << 8 | *p >> 8;
-		*p++ = *p << 8 | *p >> 8;
-	}
-}
-
-static inline void idedisk_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
-{
-	ide_input_data(drive, buffer, wcount);
-	if (drive->bswap)
-		idedisk_bswap_data(buffer, wcount);
-}
-
-static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
-{
-	if (drive->bswap) {
-		idedisk_bswap_data(buffer, wcount);
-		ide_output_data(drive, buffer, wcount);
-		idedisk_bswap_data(buffer, wcount);
-	} else
-		ide_output_data(drive, buffer, wcount);
-}
-
-/*
- * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
- * value for this drive (from its reported identification information).
- *
- * Returns:	1 if lba_capacity looks sensible
- *		0 otherwise
- *
- * It is called only once for each drive.
- */
-static int lba_capacity_is_ok (struct hd_driveid *id)
-{
-	unsigned long lba_sects, chs_sects, head, tail;
-
-	/*
-	 * The ATA spec tells large drives to return
-	 * C/H/S = 16383/16/63 independent of their size.
-	 * Some drives can be jumpered to use 15 heads instead of 16.
-	 * Some drives can be jumpered to use 4092 cyls instead of 16383.
-	 */
-	if ((id->cyls == 16383
-	     || (id->cyls == 4092 && id->cur_cyls == 16383)) &&
-	    id->sectors == 63 &&
-	    (id->heads == 15 || id->heads == 16) &&
-	    id->lba_capacity >= 16383*63*id->heads)
-		return 1;
-
-	lba_sects   = id->lba_capacity;
-	chs_sects   = id->cyls * id->heads * id->sectors;
-
-	/* perform a rough sanity check on lba_sects:  within 10% is OK */
-	if ((lba_sects - chs_sects) < chs_sects/10)
-		return 1;
-
-	/* some drives have the word order reversed */
-	head = ((lba_sects >> 16) & 0xffff);
-	tail = (lba_sects & 0xffff);
-	lba_sects = (head | (tail << 16));
-	if ((lba_sects - chs_sects) < chs_sects/10) {
-		id->lba_capacity = lba_sects;
-		return 1;	/* lba_capacity is (now) good */
-	}
-
-	return 0;	/* lba_capacity value may be bad */
-}
-
-/*
- * read_intr() is the handler for disk read/multread interrupts
- */
-static ide_startstop_t read_intr (ide_drive_t *drive)
-{
-	byte stat;
-	int i;
-	unsigned int msect, nsect;
-	struct request *rq;
-#if 0
-	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-		return ide_error(drive, "read_intr", stat);
-	}
-#else	/* new way for dealing with premature shared PCI interrupts */
-	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-		if (stat & (ERR_STAT|DRQ_STAT)) {
-			return ide_error(drive, "read_intr", stat);
-		}
-		/* no data yet, so wait for another interrupt */
-		ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
-		return ide_started;
-	}
-#endif
-	msect = drive->mult_count;
-	
-read_next:
-	rq = HWGROUP(drive)->rq;
-	if (msect) {
-		if ((nsect = rq->current_nr_sectors) > msect)
-			nsect = msect;
-		msect -= nsect;
-	} else
-		nsect = 1;
-	idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
-#ifdef DEBUG
-	printk("%s:  read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
-		drive->name, rq->sector, rq->sector+nsect-1,
-		(unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
-#endif
-	rq->sector += nsect;
-	rq->buffer += nsect<<9;
-	rq->errors = 0;
-	i = (rq->nr_sectors -= nsect);
-	if (((long)(rq->current_nr_sectors -= nsect)) <= 0)
-		ide_end_request(1, HWGROUP(drive));
-	if (i > 0) {
-		if (msect)
-			goto read_next;
-		ide_set_handler (drive, &read_intr, WAIT_CMD, NULL);
-                return ide_started;
-	}
-        return ide_stopped;
-}
-
-/*
- * write_intr() is the handler for disk write interrupts
- */
-static ide_startstop_t write_intr (ide_drive_t *drive)
-{
-	byte stat;
-	int i;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	struct request *rq = hwgroup->rq;
-
-	if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
-		printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat);
-        } else {
-#ifdef DEBUG
-		printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
-			drive->name, rq->sector, (unsigned long) rq->buffer,
-			rq->nr_sectors-1);
-#endif
-		if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
-			rq->sector++;
-			rq->buffer += 512;
-			rq->errors = 0;
-			i = --rq->nr_sectors;
-			--rq->current_nr_sectors;
-			if (((long)rq->current_nr_sectors) <= 0)
-				ide_end_request(1, hwgroup);
-			if (i > 0) {
-				idedisk_output_data (drive, rq->buffer, SECTOR_WORDS);
-				ide_set_handler (drive, &write_intr, WAIT_CMD, NULL);
-                                return ide_started;
-			}
-                        return ide_stopped;
-		}
-		return ide_stopped;	/* the original code did this here (?) */
-	}
-	return ide_error(drive, "write_intr", stat);
-}
-
-/*
- * ide_multwrite() transfers a block of up to mcount sectors of data
- * to a drive as part of a disk multiple-sector write operation.
- *
- * Returns 0 if successful;  returns 1 if request had to be aborted due to corrupted buffer list.
- */
-int ide_multwrite (ide_drive_t *drive, unsigned int mcount)
-{
- 	ide_hwgroup_t	*hwgroup= HWGROUP(drive);
-
-	/*
-	 *	This may look a bit odd, but remember wrq is a copy of the
-	 *	request not the original. The pointers are real however so the
-	 *	bh's are not copies. Remember that or bad stuff will happen
-	 *
-	 *	At the point we are called the drive has asked us for the
-	 *	data, and its our job to feed it, walking across bh boundaries
-	 *	if need be.
-	 */
-
- 	struct request	*rq = &hwgroup->wrq;
-
-  	do {
- 		unsigned long flags;
-  		unsigned int nsect = rq->current_nr_sectors;
-		if (nsect > mcount)
-			nsect = mcount;
-		mcount -= nsect;
-
-		idedisk_output_data(drive, rq->buffer, nsect<<7);
-#ifdef DEBUG
-		printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
-			drive->name, rq->sector, (unsigned long) rq->buffer,
-			nsect, rq->nr_sectors - nsect);
-#endif
-		spin_lock_irqsave(&io_request_lock, flags);	/* Is this really necessary? */
-#ifdef CONFIG_BLK_DEV_PDC4030
-		rq->sector += nsect;
-#endif
-		if (((long)(rq->nr_sectors -= nsect)) <= 0) {
-#ifdef DEBUG
-			printk("%s: multwrite: count=%d, current=%ld\n",
-				drive->name, nsect, rq->nr_sectors);
-#endif
-			spin_unlock_irqrestore(&io_request_lock, flags);
-			break;
-		}
-		if ((rq->current_nr_sectors -= nsect) == 0) {
-			if ((rq->bh = rq->bh->b_reqnext) != NULL) {
-				rq->current_nr_sectors = rq->bh->b_size>>9;
-				rq->buffer             = rq->bh->b_data;
-			} else {
-				spin_unlock_irqrestore(&io_request_lock, flags);
-				printk("%s: buffer list corrupted (%ld, %ld, %d)\n",
-					drive->name, rq->current_nr_sectors,
-					rq->nr_sectors, nsect);
-				ide_end_request(0, hwgroup);
-				return 1;
-			}
-		} else {
-			/* Fix the pointer.. we ate data */
-			rq->buffer += nsect << 9;
-		}
-                spin_unlock_irqrestore(&io_request_lock, flags);
-	} while (mcount);
-        return 0;
-}
-
-/*
- * multwrite_intr() is the handler for disk multwrite interrupts
- */
-static ide_startstop_t multwrite_intr (ide_drive_t *drive)
-{
-	byte stat;
-	int i;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	struct request *rq = &hwgroup->wrq;
-
-	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
-		if (stat & DRQ_STAT) {
-			/*
-			 *	The drive wants data. Remember rq is the copy
-			 *	of the request
-			 */
-			if (rq->nr_sectors) {
-				if (ide_multwrite(drive, drive->mult_count))
-					return ide_stopped;
-				ide_set_handler (drive, &multwrite_intr, WAIT_CMD, NULL);
-				return ide_started;
-			}
-		} else {
-			/*
-			 *	If the copy has all the blocks completed then
-			 *	we can end the original request.
-			 */
-			if (!rq->nr_sectors) {	/* all done? */
-				rq = hwgroup->rq;
-				for (i = rq->nr_sectors; i > 0;){
-					i -= rq->current_nr_sectors;
-					ide_end_request(1, hwgroup);
-				}
-				return ide_stopped;
-			}
-		}
-		return ide_stopped;	/* the original code did this here (?) */
-	}
-	return ide_error(drive, "multwrite_intr", stat);
-}
-
-/*
- * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
- */
-static ide_startstop_t set_multmode_intr (ide_drive_t *drive)
-{
-	byte stat;
-
-	if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) {
-		drive->mult_count = drive->mult_req;
-	} else {
-		drive->mult_req = drive->mult_count = 0;
-		drive->special.b.recalibrate = 1;
-		(void) ide_dump_status(drive, "set_multmode", stat);
-	}
-	return ide_stopped;
-}
-
-/*
- * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
- */
-static ide_startstop_t set_geometry_intr (ide_drive_t *drive)
-{
-	byte stat;
-
-	if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT))
-		return ide_stopped;
-
-	if (stat & (ERR_STAT|DRQ_STAT))
-		return ide_error(drive, "set_geometry_intr", stat);
-
-	ide_set_handler(drive, &set_geometry_intr, WAIT_CMD, NULL);
-	return ide_started;	
-}
-
-/*
- * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
- */
-static ide_startstop_t recal_intr (ide_drive_t *drive)
-{
-	byte stat = GET_STAT();
-
-	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-		return ide_error(drive, "recal_intr", stat);
-	return ide_stopped;
-}
-
-/*
- * do_rw_disk() issues READ and WRITE commands to a disk,
- * using LBA if supported, or CHS otherwise, to address sectors.
- * It also takes care of issuing special DRIVE_CMDs.
- */
-static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
-{
-	if (IDE_CONTROL_REG)
-		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
-	OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
-#ifdef CONFIG_BLK_DEV_PDC4030
-	if (drive->select.b.lba || IS_PDC4030_DRIVE) {
-#else /* !CONFIG_BLK_DEV_PDC4030 */
-	if (drive->select.b.lba) {
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-#ifdef DEBUG
-		printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
-			drive->name, (rq->cmd==READ)?"read":"writ",
-			block, rq->nr_sectors, (unsigned long) rq->buffer);
-#endif
-		OUT_BYTE(block,IDE_SECTOR_REG);
-		OUT_BYTE(block>>=8,IDE_LCYL_REG);
-		OUT_BYTE(block>>=8,IDE_HCYL_REG);
-		OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
-	} else {
-		unsigned int sect,head,cyl,track;
-		track = block / drive->sect;
-		sect  = block % drive->sect + 1;
-		OUT_BYTE(sect,IDE_SECTOR_REG);
-		head  = track % drive->head;
-		cyl   = track / drive->head;
-		OUT_BYTE(cyl,IDE_LCYL_REG);
-		OUT_BYTE(cyl>>8,IDE_HCYL_REG);
-		OUT_BYTE(head|drive->select.all,IDE_SELECT_REG);
-#ifdef DEBUG
-		printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
-			drive->name, (rq->cmd==READ)?"read":"writ", cyl,
-			head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
-#endif
-	}
-#ifdef CONFIG_BLK_DEV_PDC4030
-	if (IS_PDC4030_DRIVE) {
-		extern ide_startstop_t do_pdc4030_io(ide_drive_t *, struct request *);
-		return do_pdc4030_io (drive, rq);
-	}
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-	if (rq->cmd == READ) {
-#ifdef CONFIG_BLK_DEV_IDEDMA
-		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
-			return ide_started;
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-		ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
-		OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG);
-		return ide_started;
-	}
-	if (rq->cmd == WRITE) {
-		ide_startstop_t startstop;
-#ifdef CONFIG_BLK_DEV_IDEDMA
-		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
-			return ide_started;
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-		OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG);
-		if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
-			printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name,
-				drive->mult_count ? "MULTWRITE" : "WRITE");
-			return startstop;
-		}
-		if (!drive->unmask)
-			__cli();	/* local CPU only */
-		if (drive->mult_count) {
-			ide_hwgroup_t *hwgroup = HWGROUP(drive);
-			/*
-			 * Ugh.. this part looks ugly because we MUST set up
-			 * the interrupt handler before outputting the first block
-			 * of data to be written.  If we hit an error (corrupted buffer list)
-			 * in ide_multwrite(), then we need to remove the handler/timer
-			 * before returning.  Fortunately, this NEVER happens (right?).
-			 *
-			 * Except when you get an error it seems...
-			 */
-			hwgroup->wrq = *rq; /* scratchpad */
-			ide_set_handler (drive, &multwrite_intr, WAIT_CMD, NULL);
-			if (ide_multwrite(drive, drive->mult_count)) {
-				unsigned long flags;
-				spin_lock_irqsave(&io_request_lock, flags);
-				hwgroup->handler = NULL;
-				del_timer(&hwgroup->timer);
-				spin_unlock_irqrestore(&io_request_lock, flags);
-				return ide_stopped;
-			}
-		} else {
-			ide_set_handler (drive, &write_intr, WAIT_CMD, NULL);
-			idedisk_output_data(drive, rq->buffer, SECTOR_WORDS);
-		}
-		return ide_started;
-	}
-	printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd);
-	ide_end_request(0, HWGROUP(drive));
-	return ide_stopped;
-}
-
-static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
-{
-	MOD_INC_USE_COUNT;
-	if (drive->removable && drive->usage == 1) {
-		check_disk_change(inode->i_rdev);
-		/*
-		 * Ignore the return code from door_lock,
-		 * since the open() has already succeeded,
-		 * and the door_lock is irrelevant at this point.
-		 */
-		if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORLOCK, 0, 0, 0, NULL))
-			drive->doorlocking = 0;
-	}
-	return 0;
-}
-
-static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
-{
-	if (drive->removable && !drive->usage) {
-		invalidate_buffers(inode->i_rdev);
-		if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORUNLOCK, 0, 0, 0, NULL))
-			drive->doorlocking = 0;
-	}
-	MOD_DEC_USE_COUNT;
-}
-
-static int idedisk_media_change (ide_drive_t *drive)
-{
-	return drive->removable;	/* if removable, always assume it was changed */
-}
-
-/*
- * Compute drive->capacity, the full capacity of the drive
- * Called with drive->id != NULL.
- */
-static void init_idedisk_capacity (ide_drive_t  *drive)
-{
-	struct hd_driveid *id = drive->id;
-	unsigned long capacity = drive->cyl * drive->head * drive->sect;
-
-	drive->select.b.lba = 0;
-
-	/* Determine capacity, and use LBA if the drive properly supports it */
-	if ((id->capability & 2) && lba_capacity_is_ok(id)) {
-		capacity = id->lba_capacity;
-		drive->cyl = capacity / (drive->head * drive->sect);
-		drive->select.b.lba = 1;
-	}
-	drive->capacity = capacity;
-}
-
-static unsigned long idedisk_capacity (ide_drive_t  *drive)
-{
-	return (drive->capacity - drive->sect0);
-}
-
-static ide_startstop_t idedisk_special (ide_drive_t *drive)
-{
-	special_t *s = &drive->special;
-
-	if (s->b.set_geometry) {
-		s->b.set_geometry = 0;
-		OUT_BYTE(drive->sect,IDE_SECTOR_REG);
-		OUT_BYTE(drive->cyl,IDE_LCYL_REG);
-		OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
-		OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
-		if (!IS_PDC4030_DRIVE)
-			ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
-	} else if (s->b.recalibrate) {
-		s->b.recalibrate = 0;
-		if (!IS_PDC4030_DRIVE)
-			ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
-	} else if (s->b.set_multmode) {
-		s->b.set_multmode = 0;
-		if (drive->id && drive->mult_req > drive->id->max_multsect)
-			drive->mult_req = drive->id->max_multsect;
-		if (!IS_PDC4030_DRIVE)
-			ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
-	} else if (s->all) {
-		int special = s->all;
-		s->all = 0;
-		printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
-		return ide_stopped;
-	}
-	return IS_PDC4030_DRIVE ? ide_stopped : ide_started;
-}
-
-static void idedisk_pre_reset (ide_drive_t *drive)
-{
-	drive->special.all = 0;
-	drive->special.b.set_geometry = 1;
-	drive->special.b.recalibrate  = 1;
-	if (OK_TO_RESET_CONTROLLER)
-		drive->mult_count = 0;
-	if (!drive->keep_settings && !drive->using_dma)
-		drive->mult_req = 0;
-	if (drive->mult_req != drive->mult_count)
-		drive->special.b.set_multmode = 1;
-}
-
-#ifdef CONFIG_PROC_FS
-
-static int smart_enable(ide_drive_t *drive)
-{
-	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL);
-}
-
-static int get_smart_values(ide_drive_t *drive, byte *buf)
-{
-	(void) smart_enable(drive);
-	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_VALUES, 1, buf);
-}
-
-static int get_smart_thresholds(ide_drive_t *drive, byte *buf)
-{
-	(void) smart_enable(drive);
-	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_THRESHOLDS, 1, buf);
-}
-
-static int proc_idedisk_read_cache
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	char		*out = page;
-	int		len;
-
-	if (drive->id)
-		len = sprintf(out,"%i\n", drive->id->buf_size / 2);
-	else
-		len = sprintf(out,"(none)\n");
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_idedisk_read_smart_thresholds
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *)data;
-	int		len = 0, i = 0;
-
-	if (!get_smart_thresholds(drive, page)) {
-		unsigned short *val = ((unsigned short *)page) + 2;
-		char *out = ((char *)val) + (SECTOR_WORDS * 4);
-		page = out;
-		do {
-			out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
-			val += 1;
-		} while (i < (SECTOR_WORDS * 2));
-		len = out - page;
-	}
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_idedisk_read_smart_values
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *)data;
-	int		len = 0, i = 0;
-
-	if (!get_smart_values(drive, page)) {
-		unsigned short *val = ((unsigned short *)page) + 2;
-		char *out = ((char *)val) + (SECTOR_WORDS * 4);
-		page = out;
-		do {
-			out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
-			val += 1;
-		} while (i < (SECTOR_WORDS * 2));
-		len = out - page;
-	}
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static ide_proc_entry_t idedisk_proc[] = {
-	{ "cache",		S_IFREG|S_IRUGO,	proc_idedisk_read_cache,		NULL },
-	{ "geometry",		S_IFREG|S_IRUGO,	proc_ide_read_geometry,			NULL },
-	{ "smart_values",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_values,		NULL },
-	{ "smart_thresholds",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_thresholds,	NULL },
-	{ NULL, 0, NULL, NULL }
-};
-
-#else
-
-#define	idedisk_proc	NULL
-
-#endif	/* CONFIG_PROC_FS */
-
-static int set_multcount(ide_drive_t *drive, int arg)
-{
-	struct request rq;
-
-	if (drive->special.b.set_multmode)
-		return -EBUSY;
-	ide_init_drive_cmd (&rq);
-	drive->mult_req = arg;
-	drive->special.b.set_multmode = 1;
-	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
-	return (drive->mult_count == arg) ? 0 : -EIO;
-}
-
-static int set_nowerr(ide_drive_t *drive, int arg)
-{
-	unsigned long flags;
-
-	if (ide_spin_wait_hwgroup(drive, &flags))
-		return -EBUSY;
-	drive->nowerr = arg;
-	drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
-	spin_unlock_irqrestore(&io_request_lock, flags);
-	return 0;
-}
-
-static void idedisk_add_settings(ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	int major = HWIF(drive)->major;
-	int minor = drive->select.b.unit << PARTN_BITS;
-
-	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	65535,				1,	1,	&drive->bios_cyl,		NULL);
-	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
-	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
-	ide_add_setting(drive,	"bswap",		SETTING_READ,					-1,			-1,			TYPE_BYTE,	0,	1,				1,	1,	&drive->bswap,			NULL);
-	ide_add_setting(drive,	"multcount",		id ? SETTING_RW : SETTING_READ,			HDIO_GET_MULTCOUNT,	HDIO_SET_MULTCOUNT,	TYPE_BYTE,	0,	id ? id->max_multsect : 0,	1,	2,	&drive->mult_count,		set_multcount);
-	ide_add_setting(drive,	"nowerr",		SETTING_RW,					HDIO_GET_NOWERR,	HDIO_SET_NOWERR,	TYPE_BYTE,	0,	1,				1,	1,	&drive->nowerr,			set_nowerr);
-	ide_add_setting(drive,	"breada_readahead",	SETTING_RW,					BLKRAGET,		BLKRASET,		TYPE_INT,	0,	255,				1,	2,	&read_ahead[major],		NULL);
-	ide_add_setting(drive,	"file_readahead",	SETTING_RW,					BLKFRAGET,		BLKFRASET,		TYPE_INTA,	0,	INT_MAX,			1,	1024,	&max_readahead[major][minor],	NULL);
-	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW,					BLKSECTGET,		BLKSECTSET,		TYPE_INTA,	1,	255,				1,	2,	&max_sectors[major][minor],	NULL);
-
-}
-
-/*
- *	IDE subdriver functions, registered with ide.c
- */
-static ide_driver_t idedisk_driver = {
-	"ide-disk",		/* name */
-	IDEDISK_VERSION,	/* version */
-	ide_disk,		/* media */
-	0,			/* busy */
-	1,			/* supports_dma */
-	0,			/* supports_dsc_overlap */
-	NULL,			/* cleanup */
-	do_rw_disk,		/* do_request */
-	NULL,			/* end_request */
-	NULL,			/* ioctl */
-	idedisk_open,		/* open */
-	idedisk_release,	/* release */
-	idedisk_media_change,	/* media_change */
-	idedisk_pre_reset,	/* pre_reset */
-	idedisk_capacity,	/* capacity */
-	idedisk_special,	/* special */
-	idedisk_proc		/* proc */
-};
-
-int idedisk_init (void);
-static ide_module_t idedisk_module = {
-	IDE_DRIVER_MODULE,
-	idedisk_init,
-	&idedisk_driver,
-	NULL
-};
-
-static int idedisk_cleanup (ide_drive_t *drive)
-{
-	return ide_unregister_subdriver(drive);
-}
-
-static void idedisk_setup (ide_drive_t *drive)
-{
-	int i;
-	
-	struct hd_driveid *id = drive->id;
-	unsigned long capacity;
-	
-	idedisk_add_settings(drive);
-
-	if (id == NULL)
-		return;
-
-	/*
-	 * CompactFlash cards and their brethern look just like hard drives
-	 * to us, but they are removable and don't have a doorlock mechanism.
-	 */
-	if (drive->removable && !drive_is_flashcard(drive)) {
-		/*
-		 * Removable disks (eg. SYQUEST); ignore 'WD' drives 
-		 */
-		if (id->model[0] != 'W' || id->model[1] != 'D') {
-			drive->doorlocking = 1;
-		}
-	}
-	for (i = 0; i < MAX_DRIVES; ++i) {
-		ide_hwif_t *hwif = HWIF(drive);
-
-		if (drive != &hwif->drives[i]) continue;
-		hwif->gd->de_arr[i] = drive->de;
-		if (drive->removable)
-			hwif->gd->flags[i] |= GENHD_FL_REMOVABLE;
-		break;
-	}
-
-	/* Extract geometry if we did not already have one for the drive */
-	if (!drive->cyl || !drive->head || !drive->sect) {
-		drive->cyl     = drive->bios_cyl  = id->cyls;
-		drive->head    = drive->bios_head = id->heads;
-		drive->sect    = drive->bios_sect = id->sectors;
-	}
-
-	/* Handle logical geometry translation by the drive */
-	if ((id->field_valid & 1) && id->cur_cyls &&
-	    id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
-		drive->cyl  = id->cur_cyls;
-		drive->head = id->cur_heads;
-		drive->sect = id->cur_sectors;
-	}
-
-	/* Use physical geometry if what we have still makes no sense */
-	if (drive->head > 16 && id->heads && id->heads <= 16) {
-		drive->cyl  = id->cyls;
-		drive->head = id->heads;
-		drive->sect = id->sectors;
-	}
-
-	/* calculate drive capacity, and select LBA if possible */
-	init_idedisk_capacity (drive);
-
-	/*
-	 * if possible, give fdisk access to more of the drive,
-	 * by correcting bios_cyls:
-	 */
-	capacity = idedisk_capacity (drive);
-	if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) &&
-	    (!drive->forced_geom) && drive->bios_sect && drive->bios_head)
-		drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
-
-#if 0	/* done instead for entire identify block in arch/ide.h stuff */
-	/* fix byte-ordering of buffer size field */
-	id->buf_size = le16_to_cpu(id->buf_size);
-#endif
-	printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
-			drive->name, id->model,
-			capacity/2048L, id->buf_size/2,
-			drive->bios_cyl, drive->bios_head, drive->bios_sect);
-
-	if (drive->using_dma) {
-		if ((id->field_valid & 4) && (id->hw_config & 0x2000) &&
-		    (HWIF(drive)->udma_four) &&
-		    (id->dma_ultra & (id->dma_ultra >> 11) & 3)) {
-			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
-		} else if ((id->field_valid & 4) &&
-			   (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
-			printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
-		} else if (id->field_valid & 4) {
-			printk(", (U)DMA");	/* Can be BIOS-enabled! */
-		} else {
-			printk(", DMA");
-		}
-	}
-	printk("\n");
-
-	drive->mult_count = 0;
-	if (id->max_multsect) {
-#ifdef CONFIG_IDEDISK_MULTI_MODE
-		id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
-		id->multsect_valid = id->multsect ? 1 : 0;
-		drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT;
-		drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
-#else	/* original, pre IDE-NFG, per request of AC */
-		drive->mult_req = INITIAL_MULT_COUNT;
-		if (drive->mult_req > id->max_multsect)
-			drive->mult_req = id->max_multsect;
-		if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
-			drive->special.b.set_multmode = 1;
-#endif
-	}
-	drive->no_io_32bit = id->dword_io ? 1 : 0;
-}
-
-int idedisk_init (void)
-{
-	ide_drive_t *drive;
-	int failed = 0;
-	
-	MOD_INC_USE_COUNT;
-	while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) {
-		if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) {
-			printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name);
-			continue;
-		}
-		idedisk_setup(drive);
-		if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
-			printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head);
-			(void) idedisk_cleanup(drive);
-			continue;
-		}
-		failed--;
-	}
-	ide_register_module(&idedisk_module);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-#ifdef MODULE
-int init_module (void)
-{
-	return idedisk_init();
-}
-
-void cleanup_module (void)
-{
-	ide_drive_t *drive;
-	int failed = 0;
-
-	while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, &idedisk_driver, failed)) != NULL) {
-		if (idedisk_cleanup (drive)) {
-			printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name);
-			failed++;
-		}
-		/* We must remove proc entries defined in this module.
-		   Otherwise we oops while accessing these entries */
-		if (drive->proc)
-			ide_remove_proc_entries(drive->proc, idedisk_proc);
-	}
-	ide_unregister_module(&idedisk_module);
-}
-#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c
--- v2.3.51/linux/drivers/block/ide-dma.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/ide-dma.c	Wed Dec 31 16:00:00 1969
@@ -1,580 +0,0 @@
-/*
- *  linux/drivers/block/ide-dma.c		Version 4.09	April 23, 1999
- *
- *  Copyright (c) 1999  Andre Hedrick
- *  May be copied or modified under the terms of the GNU General Public License
- */
-
-/*
- *  Special Thanks to Mark for his Six years of work.
- *
- *  Copyright (c) 1995-1998  Mark Lord
- *  May be copied or modified under the terms of the GNU General Public License
- */
-
-/*
- * This module provides support for the bus-master IDE DMA functions
- * of various PCI chipsets, including the Intel PIIX (i82371FB for
- * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and 
- * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset)
- * ("PIIX" stands for "PCI ISA IDE Xcellerator").
- *
- * Pretty much the same code works for other IDE PCI bus-mastering chipsets.
- *
- * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
- *
- * By default, DMA support is prepared for use, but is currently enabled only
- * for drives which already have DMA enabled (UltraDMA or mode 2 multi/single),
- * or which are recognized as "good" (see table below).  Drives with only mode0
- * or mode1 (multi/single) DMA should also work with this chipset/driver
- * (eg. MC2112A) but are not enabled by default.
- *
- * Use "hdparm -i" to view modes supported by a given drive.
- *
- * The hdparm-3.5 (or later) utility can be used for manually enabling/disabling
- * DMA support, but must be (re-)compiled against this kernel version or later.
- *
- * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
- * If problems arise, ide.c will disable DMA operation after a few retries.
- * This error recovery mechanism works and has been extremely well exercised.
- *
- * IDE drives, depending on their vintage, may support several different modes
- * of DMA operation.  The boot-time modes are indicated with a "*" in
- * the "hdparm -i" listing, and can be changed with *knowledgeable* use of
- * the "hdparm -X" feature.  There is seldom a need to do this, as drives
- * normally power-up with their "best" PIO/DMA modes enabled.
- *
- * Testing has been done with a rather extensive number of drives,
- * with Quantum & Western Digital models generally outperforming the pack,
- * and Fujitsu & Conner (and some Seagate which are really Conner) drives
- * showing more lackluster throughput.
- *
- * Keep an eye on /var/adm/messages for "DMA disabled" messages.
- *
- * Some people have reported trouble with Intel Zappa motherboards.
- * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
- * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
- * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
- *
- * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for
- * fixing the problem with the BIOS on some Acer motherboards.
- *
- * Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
- * "TX" chipset compatibility and for providing patches for the "TX" chipset.
- *
- * Thanks to Christian Brunner <chb@muc.de> for taking a good first crack
- * at generic DMA -- his patches were referred to when preparing this code.
- *
- * Most importantly, thanks to Robert Bringman <rob@mars.trion.com>
- * for supplying a Promise UDMA board & WD UDMA drive for this work!
- *
- * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
- *
- * ACARD ATP850UF Chipset "Modified SCSI Class" with other names
- *       AEC6210 U/UF
- *       SIIG's UltraIDE Pro CN-2449
- * TTI   HPT343 Chipset "Modified SCSI Class" but reports as an
- *       unknown storage device.
- * NEW	 check_drive_lists(ide_drive_t *drive, int good_bad)
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc);
-
-#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
-
-struct drive_list_entry {
-	char * id_model;
-	char * id_firmware;
-};
-
-struct drive_list_entry drive_whitelist [] = {
-
-	{ "Micropolis 2112A"	,       "ALL"		},
-	{ "CONNER CTMA 4000"	,       "ALL"		},
-	{ "CONNER CTT8000-A"	,       "ALL"		},
-	{ "ST34342A"		,	"ALL"		},
-	{ 0			,	0		}
-};
-
-struct drive_list_entry drive_blacklist [] = {
-
-	{ "WDC AC11000H"	,	"ALL"		},
-	{ "WDC AC22100H"	,	"ALL"		},
-	{ "WDC AC32500H"	,	"ALL"		},
-	{ "WDC AC33100H"	,	"ALL"		},
-	{ "WDC AC31600H"	,	"ALL"		},
-	{ "WDC AC32100H"	,	"24.09P07"	},
-	{ "WDC AC23200L"	,	"21.10N21"	},
-	{ 0			,	0		}
-
-};
-
-int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
-{
-	for ( ; drive_table->id_model ; drive_table++)
-		if ((!strcmp(drive_table->id_model, id->model)) &&
-		    ((!strstr(drive_table->id_firmware, id->fw_rev)) ||
-		     (!strcmp(drive_table->id_firmware, "ALL"))))
-			return 1;
-	return 0;
-}
-
-#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
-
-/*
- * good_dma_drives() lists the model names (from "hdparm -i")
- * of drives which do not support mode2 DMA but which are
- * known to work fine with this interface under Linux.
- */
-const char *good_dma_drives[] = {"Micropolis 2112A",
-				 "CONNER CTMA 4000",
-				 "CONNER CTT8000-A",
-				 "ST34342A",	/* for Sun Ultra */
-				 NULL};
-
-/*
- * bad_dma_drives() lists the model names (from "hdparm -i")
- * of drives which supposedly support (U)DMA but which are
- * known to corrupt data with this interface under Linux.
- *
- * This is an empirical list. Its generated from bug reports. That means
- * while it reflects actual problem distributions it doesn't answer whether
- * the drive or the controller, or cabling, or software, or some combination
- * thereof is the fault. If you don't happen to agree with the kernel's 
- * opinion of your drive - use hdparm to turn DMA on.
- */
-const char *bad_dma_drives[] = {"WDC AC11000H",
-				"WDC AC22100H",
-				"WDC AC32100H",
-				"WDC AC32500H",
-				"WDC AC33100H",
-				"WDC AC31600H",
- 				NULL};
-
-#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
-
-/*
- * Our Physical Region Descriptor (PRD) table should be large enough
- * to handle the biggest I/O request we are likely to see.  Since requests
- * can have no more than 256 sectors, and since the typical blocksize is
- * two or more sectors, we could get by with a limit of 128 entries here for
- * the usual worst case.  Most requests seem to include some contiguous blocks,
- * further reducing the number of table entries required.
- *
- * The driver reverts to PIO mode for individual requests that exceed
- * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
- * 100% of all crazy scenarios here is not necessary.
- *
- * As it turns out though, we must allocate a full 4KB page for this,
- * so the two PRD tables (ide0 & ide1) will each get half of that,
- * allowing each to have about 256 entries (8 bytes each) from this.
- */
-#define PRD_BYTES	8
-#define PRD_ENTRIES	(PAGE_SIZE / (2 * PRD_BYTES))
-
-/*
- * dma_intr() is the handler for disk read/write DMA interrupts
- */
-ide_startstop_t ide_dma_intr (ide_drive_t *drive)
-{
-	int i;
-	byte stat, dma_stat;
-
-	dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive);
-	stat = GET_STAT();			/* get drive status */
-	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
-		if (!dma_stat) {
-			struct request *rq = HWGROUP(drive)->rq;
-			rq = HWGROUP(drive)->rq;
-			for (i = rq->nr_sectors; i > 0;) {
-				i -= rq->current_nr_sectors;
-				ide_end_request(1, HWGROUP(drive));
-			}
-			return ide_stopped;
-		}
-		printk("%s: dma_intr: bad DMA status\n", drive->name);
-	}
-	return ide_error(drive, "dma_intr", stat);
-}
-
-static int ide_build_sglist (ide_hwif_t *hwif, struct request *rq)
-{
-	struct buffer_head *bh;
-	struct scatterlist *sg = hwif->sg_table;
-	int nents = 0;
-
-	if (rq->cmd == READ)
-		hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
-	else
-		hwif->sg_dma_direction = PCI_DMA_TODEVICE;
-	bh = rq->bh;
-	do {
-		unsigned char *virt_addr = bh->b_data;
-		unsigned int size = bh->b_size;
-
-		while ((bh = bh->b_reqnext) != NULL) {
-			if ((virt_addr + size) != (unsigned char *) bh->b_data)
-				break;
-			size += bh->b_size;
-		}
-		memset(&sg[nents], 0, sizeof(*sg));
-		sg[nents].address = virt_addr;
-		sg[nents].length = size;
-		nents++;
-	} while (bh != NULL);
-
-	return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction);
-}
-
-/*
- * ide_build_dmatable() prepares a dma request.
- * Returns 0 if all went okay, returns 1 otherwise.
- * May also be invoked from trm290.c
- */
-int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func)
-{
-	unsigned int *table = HWIF(drive)->dmatable_cpu;
-#ifdef CONFIG_BLK_DEV_TRM290
-	unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290);
-#else
-	const int is_trm290_chipset = 0;
-#endif
-	unsigned int count = 0;
-	int i;
-	struct scatterlist *sg;
-
-	HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq);
-
-	sg = HWIF(drive)->sg_table;
-	while (i && sg_dma_len(sg)) {
-		u32 cur_addr;
-		u32 cur_len;
-
-		cur_addr = sg_dma_address(sg);
-		cur_len = sg_dma_len(sg);
-
-		while (cur_len) {
-			if (++count >= PRD_ENTRIES) {
-				printk("%s: DMA table too small\n", drive->name);
-				pci_unmap_sg(HWIF(drive)->pci_dev,
-					     HWIF(drive)->sg_table,
-					     HWIF(drive)->sg_nents,
-					     HWIF(drive)->sg_dma_direction);
-				return 0; /* revert to PIO for this request */
-			} else {
-				u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
-
-				if (bcount > cur_len)
-					bcount = cur_len;
-				*table++ = cpu_to_le32(cur_addr);
-				xcount = bcount & 0xffff;
-				if (is_trm290_chipset)
-					xcount = ((xcount >> 2) - 1) << 16;
-				*table++ = cpu_to_le32(xcount);
-				cur_addr += bcount;
-				cur_len -= bcount;
-			}
-		}
-
-		sg++;
-		i--;
-	}
-
-	if (!count)
-		printk("%s: empty DMA table?\n", drive->name);
-	else if (!is_trm290_chipset)
-		*--table |= cpu_to_le32(0x80000000);
-
-	return count;
-}
-
-/* Teardown mappings after DMA has completed.  */
-void ide_destroy_dmatable (ide_drive_t *drive)
-{
-	struct pci_dev *dev = HWIF(drive)->pci_dev;
-	struct scatterlist *sg = HWIF(drive)->sg_table;
-	int nents = HWIF(drive)->sg_nents;
-
-	pci_unmap_sg(dev, sg, nents, HWIF(drive)->sg_dma_direction);
-}
-
-/*
- *  For both Blacklisted and Whitelisted drives.
- *  This is setup to be called as an extern for future support
- *  to other special driver code.
- */
-int check_drive_lists (ide_drive_t *drive, int good_bad)
-{
-	struct hd_driveid *id = drive->id;
-
-#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
-	if (good_bad) {
-		return in_drive_list(id, drive_whitelist);
-	} else {
-		int blacklist = in_drive_list(id, drive_blacklist);
-		if (blacklist)
-			printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
-		return(blacklist);
-	}
-#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
-	const char **list;
-
-	if (good_bad) {
-		/* Consult the list of known "good" drives */
-		list = good_dma_drives;
-		while (*list) {
-			if (!strcmp(*list++,id->model))
-				return 1;
-		}
-	} else {
-		/* Consult the list of known "bad" drives */
-		list = bad_dma_drives;
-		while (*list) {
-			if (!strcmp(*list++,id->model)) {
-				printk("%s: Disabling (U)DMA for %s\n",
-					drive->name, id->model);
-				return 1;
-			}
-		}
-	}
-#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
-	return 0;
-}
-
-static int config_drive_for_dma (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	ide_hwif_t *hwif = HWIF(drive);
-
-	if (id && (id->capability & 1) && hwif->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive))
-			return hwif->dmaproc(ide_dma_off, drive);
-
-		/* Enable DMA on any drive that has UltraDMA (mode 3/4) enabled */
-		if ((id->field_valid & 4) && (hwif->udma_four) && (id->hw_config & 0x2000))
-			if ((id->dma_ultra & (id->dma_ultra >> 11) & 3))
-				return hwif->dmaproc(ide_dma_on, drive);
-		/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
-		if (id->field_valid & 4)	/* UltraDMA */
-			if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
-				return hwif->dmaproc(ide_dma_on, drive);
-		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
-		if (id->field_valid & 2)	/* regular DMA */
-			if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
-				return hwif->dmaproc(ide_dma_on, drive);
-		/* Consult the list of known "good" drives */
-		if (ide_dmaproc(ide_dma_good_drive, drive))
-			return hwif->dmaproc(ide_dma_on, drive);
-	}
-	return hwif->dmaproc(ide_dma_off_quietly, drive);
-}
-
-/*
- * ide_dmaproc() initiates/aborts DMA read/write operations on a drive.
- *
- * The caller is assumed to have selected the drive and programmed the drive's
- * sector address using CHS or LBA.  All that remains is to prepare for DMA
- * and then issue the actual read/write DMA/PIO command to the drive.
- *
- * For ATAPI devices, we just prepare for DMA and return. The caller should
- * then issue the packet command to the drive and call us again with
- * ide_dma_begin afterwards.
- *
- * Returns 0 if all went well.
- * Returns 1 if DMA read/write could not be started, in which case
- * the caller should revert to PIO for the current request.
- * May also be invoked from trm290.c
- */
-int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	unsigned long dma_base = hwif->dma_base;
-	byte unit = (drive->select.b.unit & 0x01);
-	unsigned int count, reading = 0;
-	byte dma_stat;
-
-	switch (func) {
-		case ide_dma_off:
-			printk("%s: DMA disabled\n", drive->name);
-		case ide_dma_off_quietly:
-			outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
-		case ide_dma_on:
-			drive->using_dma = (func == ide_dma_on);
-			if (drive->using_dma)
-				outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-			return 0;
-		case ide_dma_check:
-			return config_drive_for_dma (drive);
-		case ide_dma_read:
-			reading = 1 << 3;
-		case ide_dma_write:
-			if (!(count = ide_build_dmatable(drive, func)))
-				return 1;	/* try PIO instead of DMA */
-			outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
-			outb(reading, dma_base);			/* specify r/w */
-			outb(inb(dma_base+2)|6, dma_base+2);		/* clear INTR & ERROR flags */
-			drive->waiting_for_dma = 1;
-			if (drive->media != ide_disk)
-				return 0;
-			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);	/* issue cmd to drive */
-			OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
-		case ide_dma_begin:
-			/* Note that this is done *after* the cmd has
-			 * been issued to the drive, as per the BM-IDE spec.
-			 * The Promise Ultra33 doesn't work correctly when
-			 * we do this part before issuing the drive cmd.
-			 */
-			outb(inb(dma_base)|1, dma_base);		/* start DMA */
-			return 0;
-		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-			drive->waiting_for_dma = 0;
-			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-			dma_stat = inb(dma_base+2);		/* get DMA status */
-			outb(dma_stat|6, dma_base+2);	/* clear the INTR & ERROR bits */
-			ide_destroy_dmatable(drive);	/* purge DMA mappings */
-			return (dma_stat & 7) != 4;	/* verify good DMA status */
-		case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
-			dma_stat = inb(dma_base+2);
-			return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
-		case ide_dma_bad_drive:
-		case ide_dma_good_drive:
-			return check_drive_lists(drive, (func == ide_dma_good_drive));
-		case ide_dma_lostirq:
-		case ide_dma_timeout:
-			printk("ide_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func),  func);
-			return 1;
-		default:
-			printk("ide_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func);
-			return 1;
-	}
-}
-
-/*
- * Needed for allowing full modular support of ide-driver
- */
-int ide_release_dma (ide_hwif_t *hwif)
-{
-	if (hwif->dmatable_cpu) {
-		pci_free_consistent(hwif->pci_dev,
-				    PRD_ENTRIES * PRD_BYTES,
-				    hwif->dmatable_cpu,
-				    hwif->dmatable_dma);
-		hwif->dmatable_cpu = NULL;
-	}
-	if (hwif->sg_table) {
-		kfree(hwif->sg_table);
-		hwif->sg_table = NULL;
-	}
-	if ((hwif->dma_extra) && (hwif->channel == 0))
-		release_region((hwif->dma_base + 16), hwif->dma_extra);
-	release_region(hwif->dma_base, 8);
-	return 1;
-}
-
-/*
- *	This can be called for a dynamically installed interface. Don't __init it
- */
- 
-void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports)
-{
-	printk("    %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1);
-	if (check_region(dma_base, num_ports)) {
-		printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
-		return;
-	}
-	request_region(dma_base, num_ports, hwif->name);
-	hwif->dma_base = dma_base;
-	hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
-						  PRD_ENTRIES * PRD_BYTES,
-						  &hwif->dmatable_dma);
-	if (hwif->dmatable_cpu == NULL)
-		goto dma_alloc_failure;
-
-	hwif->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
-				 GFP_KERNEL);
-	if (hwif->sg_table == NULL) {
-		pci_free_consistent(hwif->pci_dev, PRD_ENTRIES * PRD_BYTES,
-				    hwif->dmatable_cpu, hwif->dmatable_dma);
-		goto dma_alloc_failure;
-	}
-
-	hwif->dmaproc = &ide_dmaproc;
-
-	if (hwif->chipset != ide_trm290) {
-		byte dma_stat = inb(dma_base+2);
-		printk(", BIOS settings: %s:%s, %s:%s",
-		       hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
-		       hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
-	}
-	printk("\n");
-	return;
-
-dma_alloc_failure:
-	printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n");
-}
-
-/*
- * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space:
- */
-unsigned long __init ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name)
-{
-	unsigned long	dma_base = 0;
-	struct pci_dev	*dev = hwif->pci_dev;
-
-	if (hwif->mate && hwif->mate->dma_base) {
-		dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
-	} else {
-		dma_base = dev->resource[4].start;
-		if (!dma_base || dma_base == PCI_BASE_ADDRESS_IO_MASK) {
-			printk("%s: dma_base is invalid (0x%04lx)\n", name, dma_base);
-			dma_base = 0;
-		}
-	}
-	if (dma_base) {
-		if (extra) /* PDC20246, PDC20262, HPT343, & HPT366 */
-			request_region(dma_base+16, extra, name);
-		dma_base += hwif->channel ? 8 : 0;
-		hwif->dma_extra = extra;
-
-		switch(dev->device) {
-			case PCI_DEVICE_ID_AL_M5219:
-			case PCI_DEVICE_ID_AMD_VIPER_7409:
-			case PCI_DEVICE_ID_CMD_643:
-				outb(inb(dma_base+2) & 0x60, dma_base+2);
-				if (inb(dma_base+2) & 0x80) {
-					printk("%s: simplex device: DMA forced\n", name);
-				}
-				break;
-			default:
-				/*
-				 * If the device claims "simplex" DMA,
-				 * this means only one of the two interfaces
-				 * can be trusted with DMA at any point in time.
-				 * So we should enable DMA only on one of the
-				 * two interfaces.
-				 */
-				if ((inb(dma_base+2) & 0x80)) {	/* simplex device? */
-					if ((!hwif->drives[0].present && !hwif->drives[1].present) ||
-					    (hwif->mate && hwif->mate->dma_base)) {
-						printk("%s: simplex device:  DMA disabled\n", name);
-						dma_base = 0;
-					}
-				}
-		}
-	}
-	return dma_base;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-features.c linux/drivers/block/ide-features.c
--- v2.3.51/linux/drivers/block/ide-features.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/ide-features.c	Wed Dec 31 16:00:00 1969
@@ -1,312 +0,0 @@
-/*
- * linux/drivers/block/ide-features.c	Version 0.03	Feb. 10, 2000
- *
- *  Copyright (C) 1999-2000	Linus Torvalds & authors (see below)
- *  
- *  Copyright (C) 1999-2000	Andre Hedrick <andre@suse.com>
- *
- *  Extracts if ide.c to address the evolving transfer rate code for
- *  the SETFEATURES_XFER callouts.  Various parts of any given function
- *  are credited to previous ATA-IDE maintainers.
- *
- *  May be copied or modified under the terms of the GNU General Public License
- */
-
-#define __NO_VERSION__
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/genhd.h>
-#include <linux/blkpg.h>
-#include <linux/malloc.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/byteorder.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/bitops.h>
-
-#define SETFEATURES_CONTROL_REG		(0)	/* some arch's may need */
-
-/*
- * A Verbose noise maker for debugging on the attempted transfer rates.
- */
-char *ide_xfer_verbose (byte xfer_rate)
-{
-	switch(xfer_rate) {
-		case XFER_UDMA_7:	return("UDMA 7");
-		case XFER_UDMA_6:	return("UDMA 6");
-		case XFER_UDMA_5:	return("UDMA 5");
-		case XFER_UDMA_4:	return("UDMA 4");
-		case XFER_UDMA_3:	return("UDMA 3");
-		case XFER_UDMA_2:	return("UDMA 2");
-		case XFER_UDMA_1:	return("UDMA 1");
-		case XFER_UDMA_0:	return("UDMA 0");
-		case XFER_MW_DMA_2:	return("MW DMA 2");
-		case XFER_MW_DMA_1:	return("MW DMA 1");
-		case XFER_MW_DMA_0:	return("MW DMA 0");
-		case XFER_SW_DMA_2:	return("SW DMA 2");
-		case XFER_SW_DMA_1:	return("SW DMA 1");
-		case XFER_SW_DMA_0:	return("SW DMA 0");
-		case XFER_PIO_4:	return("PIO 4");
-		case XFER_PIO_3:	return("PIO 3");
-		case XFER_PIO_2:	return("PIO 2");
-		case XFER_PIO_1:	return("PIO 1");
-		case XFER_PIO_0:	return("PIO 0");
-		case XFER_PIO_SLOW:	return("PIO SLOW");
-		default:		return("XFER ERROR");
-	}
-}
-
-/*
- *
- */
-char *ide_media_verbose (ide_drive_t *drive)
-{
-	switch (drive->media) {
-		case ide_scsi:		return("scsi   ");
-		case ide_disk:		return("disk   ");
-		case ide_optical:	return("optical");
-		case ide_cdrom:		return("cdrom  ");
-		case ide_tape:		return("tape   ");
-		case ide_floppy:	return("floppy ");
-		default:		return("???????");
-	}
-}
-
-/*
- * A Verbose noise maker for debugging on the attempted dmaing calls.
- */
-char *ide_dmafunc_verbose (ide_dma_action_t dmafunc)
-{
-	switch (dmafunc) {
-		case ide_dma_read:		return("ide_dma_read");
-		case ide_dma_write:		return("ide_dma_write");
-		case ide_dma_begin:		return("ide_dma_begin");
-		case ide_dma_end:		return("ide_dma_end:");
-		case ide_dma_check:		return("ide_dma_check");
-		case ide_dma_on:		return("ide_dma_on");
-		case ide_dma_off:		return("ide_dma_off");
-		case ide_dma_off_quietly:	return("ide_dma_off_quietly");
-		case ide_dma_test_irq:		return("ide_dma_test_irq");
-		case ide_dma_bad_drive:		return("ide_dma_bad_drive");
-		case ide_dma_good_drive:	return("ide_dma_good_drive");
-		case ide_dma_lostirq:		return("ide_dma_lostirq");
-		case ide_dma_timeout:		return("ide_dma_timeout");
-		default:			return("unknown");
-	}
-}
-
-/*
- * Update the 
- */
-int ide_driveid_update (ide_drive_t *drive)
-{
-	/*
-	 * Re-read drive->id for possible DMA mode
-	 * change (copied from ide-probe.c)
-	 */
-	struct hd_driveid *id;
-	unsigned long timeout, irqs, flags;
-
-	probe_irq_off(probe_irq_on());
-	irqs = probe_irq_on();
-	if (IDE_CONTROL_REG)
-		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
-	ide_delay_50ms();
-	OUT_BYTE(WIN_IDENTIFY, IDE_COMMAND_REG);
-	timeout = jiffies + WAIT_WORSTCASE;
-	do {
-		if (0 < (signed long)(jiffies - timeout)) {
-			if (irqs)
-				(void) probe_irq_off(irqs);
-			return 0;	/* drive timed-out */
-		}
-		ide_delay_50ms();	/* give drive a breather */
-	} while (IN_BYTE(IDE_ALTSTATUS_REG) & BUSY_STAT);
-	ide_delay_50ms();	/* wait for IRQ and DRQ_STAT */
-	if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT))
-		return 0;
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only; some systems need this */
-	id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC);
-	ide_input_data(drive, id, SECTOR_WORDS);
-	(void) GET_STAT();	/* clear drive IRQ */
-	ide__sti();		/* local CPU only */
-	__restore_flags(flags);	/* local CPU only */
-	ide_fix_driveid(id);
-	if (id && id->cyls) {
-		drive->id->dma_ultra = id->dma_ultra;
-		drive->id->dma_mword = id->dma_mword;
-		drive->id->dma_1word = id->dma_1word;
-		/* anything more ? */
-#ifdef DEBUG
-		printk("%s: dma_ultra=%04X, dma_mword=%04X, dma_1word=%04X\n",
-			drive->name, id->dma_ultra, id->dma_mword, id->dma_1word);
-#endif
-		kfree(id);
-	}
-	return 1;
-}
-
-/*
- * Verify that we are doing an approved SETFEATURES_XFER with respect
- * to the hardware being able to support request.  Since some hardware
- * can improperly report capabilties, we check to see if the host adapter
- * in combination with the device (usually a disk) properly detect
- * and acknowledge each end of the ribbon.
- */
-int ide_ata66_check (ide_drive_t *drive, int cmd, int nsect, int feature)
-{
-	if ((cmd == WIN_SETFEATURES) &&
-	    (nsect > XFER_UDMA_2) &&
-	    (feature == SETFEATURES_XFER)) {
-		if (!HWIF(drive)->udma_four) {
-			printk("%s: Speed warnings UDMA 3/4 is not functional.\n", HWIF(drive)->name);
-			return 1;
-		}
-		if ((drive->id->hw_config & 0x2000) == 0) {
-			printk("%s: Speed warnings UDMA 3/4 is not functional.\n", drive->name);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-/*
- * Backside of HDIO_DRIVE_CMD call of SETFEATURES_XFER.
- * 1 : Safe to update drive->id DMA registers.
- * 0 : OOPs not allowed.
- */
-int set_transfer (ide_drive_t *drive, int cmd, int nsect, int feature)
-{
-	struct hd_driveid *id = drive->id;
-
-	if ((cmd == WIN_SETFEATURES) &&
-	    (nsect >= XFER_SW_DMA_0) &&
-	    (feature == SETFEATURES_XFER) &&
-	    (id->dma_ultra || id->dma_mword || id->dma_1word))
-		return 1;
-	return 0;
-}
-
-/*
- * Similar to ide_wait_stat(), except it never calls ide_error internally.
- * This is a kludge to handle the new ide_config_drive_speed() function,
- * and should not otherwise be used anywhere.  Eventually, the tuneproc's
- * should be updated to return ide_startstop_t, in which case we can get
- * rid of this abomination again.  :)   -ml
- *
- * It is gone..........
- *
- * const char *msg == consider adding for verbose errors.
- */
-int ide_config_drive_speed (ide_drive_t *drive, byte speed)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	int	i, error = 1;
-	byte unit = (drive->select.b.unit & 0x01);
-	byte stat;
-
-	/*
-	 * Don't use ide_wait_cmd here - it will
-	 * attempt to set_geometry and recalibrate,
-	 * but for some reason these don't work at
-	 * this point (lost interrupt).
-	 */
-        /*
-         * Select the drive, and issue the SETFEATURES command
-         */
-	disable_irq(hwif->irq);	/* disable_irq_nosync ?? */
-	udelay(1);
-	SELECT_DRIVE(HWIF(drive), drive);
-	udelay(1);
-	if (IDE_CONTROL_REG)
-		OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
-	OUT_BYTE(speed, IDE_NSECTOR_REG);
-	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
-	OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
-	if ((IDE_CONTROL_REG) && (SETFEATURES_CONTROL_REG))
-		OUT_BYTE(drive->ctl, IDE_CONTROL_REG);
-	udelay(1);
-	/*
-	 * Wait for drive to become non-BUSY
-	 */
-	if ((stat = GET_STAT()) & BUSY_STAT) {
-		unsigned long flags, timeout;
-		__save_flags(flags);	/* local CPU only */
-		ide__sti();		/* local CPU only -- for jiffies */
-		timeout = jiffies + WAIT_CMD;
-		while ((stat = GET_STAT()) & BUSY_STAT) {
-			if (0 < (signed long)(jiffies - timeout))
-				break;
-		}
-		__restore_flags(flags); /* local CPU only */
-	}
-
-	/*
-	 * Allow status to settle, then read it again.
-	 * A few rare drives vastly violate the 400ns spec here,
-	 * so we'll wait up to 10usec for a "good" status
-	 * rather than expensively fail things immediately.
-	 * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
-	 */
-	for (i = 0; i < 10; i++) {
-		udelay(1);
-		if (OK_STAT((stat = GET_STAT()), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) {
-			error = 0;
-			break;
-		}
-	}
-
-	enable_irq(hwif->irq);
-
-	if (error) {
-		(void) ide_dump_status(drive, "set_drive_speed_status", stat);
-		return error;
-	}
-
-	drive->id->dma_ultra &= ~0xFF00;
-	drive->id->dma_mword &= ~0x0F00;
-	drive->id->dma_1word &= ~0x0F00;
-
-	if (speed > XFER_PIO_4) {
-		outb(inb(hwif->dma_base+2)|(1<<(5+unit)), hwif->dma_base+2);
-	} else {
-		outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
-	}
-
-	switch(speed) {
-		case XFER_UDMA_7:   drive->id->dma_ultra |= 0x8080; break;
-		case XFER_UDMA_6:   drive->id->dma_ultra |= 0x4040; break;
-		case XFER_UDMA_5:   drive->id->dma_ultra |= 0x2020; break;
-		case XFER_UDMA_4:   drive->id->dma_ultra |= 0x1010; break;
-		case XFER_UDMA_3:   drive->id->dma_ultra |= 0x0808; break;
-		case XFER_UDMA_2:   drive->id->dma_ultra |= 0x0404; break;
-		case XFER_UDMA_1:   drive->id->dma_ultra |= 0x0202; break;
-		case XFER_UDMA_0:   drive->id->dma_ultra |= 0x0101; break;
-		case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break;
-		case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break;
-		case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break;
-		case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break;
-		case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break;
-		case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break;
-		default: break;
-	}
-	return error;
-}
-
-EXPORT_SYMBOL(ide_driveid_update);
-EXPORT_SYMBOL(ide_ata66_check);
-EXPORT_SYMBOL(set_transfer);
-EXPORT_SYMBOL(ide_config_drive_speed);
-
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.3.51/linux/drivers/block/ide-floppy.c	Sat Feb 26 22:31:43 2000
+++ linux/drivers/block/ide-floppy.c	Wed Dec 31 16:00:00 1969
@@ -1,1680 +0,0 @@
-/*
- * linux/drivers/block/ide-floppy.c	Version 0.9	Jul   4, 1999
- *
- * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il>
- */
-
-/*
- * IDE ATAPI floppy driver.
- *
- * The driver currently doesn't have any fancy features, just the bare
- * minimum read/write support.
- *
- * Many thanks to Lode Leroy <Lode.Leroy@www.ibase.be>, who tested so many
- * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive.
- *
- * Ver 0.1   Oct 17 96   Initial test version, mostly based on ide-tape.c.
- * Ver 0.2   Oct 31 96   Minor changes.
- * Ver 0.3   Dec  2 96   Fixed error recovery bug.
- * Ver 0.4   Jan 26 97   Add support for the HDIO_GETGEO ioctl.
- * Ver 0.5   Feb 21 97   Add partitions support.
- *                       Use the minimum of the LBA and CHS capacities.
- *                       Avoid hwgroup->rq == NULL on the last irq.
- *                       Fix potential null dereferencing with DEBUG_LOG.
- * Ver 0.8   Dec  7 97   Increase irq timeout from 10 to 50 seconds.
- *                       Add media write-protect detection.
- *                       Issue START command only if TEST UNIT READY fails.
- *                       Add work-around for IOMEGA ZIP revision 21.D.
- *                       Remove idefloppy_get_capabilities().
- * Ver 0.9   Jul  4 99   Fix a bug which might have caused the number of
- *                        bytes requested on each interrupt to be zero.
- *                        Thanks to <shanos@es.co.nz> for pointing this out.
- */
-
-#define IDEFLOPPY_VERSION "0.9"
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/genhd.h>
-#include <linux/malloc.h>
-#include <linux/cdrom.h>
-#include <linux/ide.h>
-
-#include <asm/byteorder.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/unaligned.h>
-#include <asm/bitops.h>
-
-/*
- *	The following are used to debug the driver.
- */
-#define IDEFLOPPY_DEBUG_LOG		0
-#define IDEFLOPPY_DEBUG_INFO		0
-#define IDEFLOPPY_DEBUG_BUGS		1
-
-/*
- *	Some drives require a longer irq timeout.
- */
-#define IDEFLOPPY_WAIT_CMD		(5 * WAIT_CMD)
-
-/*
- *	After each failed packet command we issue a request sense command
- *	and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
- */
-#define IDEFLOPPY_MAX_PC_RETRIES	3
-
-/*
- *	With each packet command, we allocate a buffer of
- *	IDEFLOPPY_PC_BUFFER_SIZE bytes.
- */
-#define IDEFLOPPY_PC_BUFFER_SIZE	256
-
-/*
- *	In various places in the driver, we need to allocate storage
- *	for packet commands and requests, which will remain valid while
- *	we leave the driver to wait for an interrupt or a timeout event.
- */
-#define IDEFLOPPY_PC_STACK		(10 + IDEFLOPPY_MAX_PC_RETRIES)
-
-/*
- *	Our view of a packet command.
- */
-typedef struct idefloppy_packet_command_s {
-	u8 c[12];				/* Actual packet bytes */
-	int retries;				/* On each retry, we increment retries */
-	int error;				/* Error code */
-	int request_transfer;			/* Bytes to transfer */
-	int actually_transferred;		/* Bytes actually transferred */
-	int buffer_size;			/* Size of our data buffer */
-	char *b_data;				/* Pointer which runs on the buffers */
-	int b_count;				/* Missing/Available data on the current buffer */
-	struct request *rq;			/* The corresponding request */
-	byte *buffer;				/* Data buffer */
-	byte *current_position;			/* Pointer into the above buffer */
-	void (*callback) (ide_drive_t *);	/* Called when this packet command is completed */
-	byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE];	/* Temporary buffer */
-	unsigned int flags;			/* Status/Action bit flags */
-} idefloppy_pc_t;
-
-/*
- *	Packet command flag bits.
- */
-#define	PC_ABORT			0	/* Set when an error is considered normal - We won't retry */
-#define PC_DMA_RECOMMENDED		2	/* 1 when we prefer to use DMA if possible */
-#define	PC_DMA_IN_PROGRESS		3	/* 1 while DMA in progress */
-#define	PC_DMA_ERROR			4	/* 1 when encountered problem during DMA */
-#define	PC_WRITING			5	/* Data direction */
-
-/*
- *	Removable Block Access Capabilities Page
- */
-typedef struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	unsigned	page_code	:6;	/* Page code - Should be 0x1b */
-	unsigned	reserved1_6	:1;	/* Reserved */
-	unsigned	ps		:1;	/* Should be 0 */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	unsigned	ps		:1;	/* Should be 0 */
-	unsigned	reserved1_6	:1;	/* Reserved */
-	unsigned	page_code	:6;	/* Page code - Should be 0x1b */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	u8		page_length;		/* Page Length - Should be 0xa */
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	unsigned	reserved2	:6;
-	unsigned	srfp		:1;	/* Supports reporting progress of format */
-	unsigned	sflp		:1;	/* System floppy type device */
-	unsigned	tlun		:3;	/* Total logical units supported by the device */
-	unsigned	reserved3	:3;
-	unsigned	sml		:1;	/* Single / Multiple lun supported */
-	unsigned	ncd		:1;	/* Non cd optical device */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	unsigned	sflp		:1;	/* System floppy type device */
-	unsigned	srfp		:1;	/* Supports reporting progress of format */
-	unsigned	reserved2	:6;
-	unsigned	ncd		:1;	/* Non cd optical device */
-	unsigned	sml		:1;	/* Single / Multiple lun supported */
-	unsigned	reserved3	:3;
-	unsigned	tlun		:3;	/* Total logical units supported by the device */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	u8		reserved[8];
-} idefloppy_capabilities_page_t;
-
-/*
- *	Flexible disk page.
- */
-typedef struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	unsigned	page_code	:6;	/* Page code - Should be 0x5 */
-	unsigned	reserved1_6	:1;	/* Reserved */
-	unsigned	ps		:1;	/* The device is capable of saving the page */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	unsigned	ps		:1;	/* The device is capable of saving the page */
-	unsigned	reserved1_6	:1;	/* Reserved */
-	unsigned	page_code	:6;	/* Page code - Should be 0x5 */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	u8		page_length;		/* Page Length - Should be 0x1e */
-	u16		transfer_rate;		/* In kilobits per second */
-	u8		heads, sectors;		/* Number of heads, Number of sectors per track */
-	u16		sector_size;		/* Byes per sector */
-	u16		cyls;			/* Number of cylinders */
-	u8		reserved10[10];
-	u8		motor_delay;		/* Motor off delay */
-	u8		reserved21[7];
-	u16		rpm;			/* Rotations per minute */
-	u8		reserved30[2];
-} idefloppy_flexible_disk_page_t;
- 
-/*
- *	Format capacity
- */
-typedef struct {
-	u8		reserved[3];
-	u8		length;			/* Length of the following descriptors in bytes */
-} idefloppy_capacity_header_t;
-
-typedef struct {
-	u32		blocks;			/* Number of blocks */
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	unsigned	dc		:2;	/* Descriptor Code */
-	unsigned	reserved	:6;
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	unsigned	reserved	:6;
-	unsigned	dc		:2;	/* Descriptor Code */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	u8		length_msb;		/* Block Length (MSB)*/
-	u16		length;			/* Block Length */
-} idefloppy_capacity_descriptor_t;
-
-#define CAPACITY_INVALID	0x00
-#define CAPACITY_UNFORMATTED	0x01
-#define CAPACITY_CURRENT	0x02
-#define CAPACITY_NO_CARTRIDGE	0x03
-
-/*
- *	Most of our global data which we need to save even as we leave the
- *	driver due to an interrupt or a timer event is stored in a variable
- *	of type idefloppy_floppy_t, defined below.
- */
-typedef struct {
-	ide_drive_t *drive;
-
-	idefloppy_pc_t *pc;			/* Current packet command */
-	idefloppy_pc_t *failed_pc; 		/* Last failed packet command */
-	idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */
-	int pc_stack_index;			/* Next free packet command storage space */
-	struct request rq_stack[IDEFLOPPY_PC_STACK];
-	int rq_stack_index;			/* We implement a circular array */
-
-	/*
-	 *	Last error information
-	 */
-	byte sense_key, asc, ascq;
-
-	/*
-	 *	Device information
-	 */
-	int blocks, block_size, bs_factor;			/* Current format */
-	idefloppy_capacity_descriptor_t capacity;		/* Last format capacity */
-	idefloppy_flexible_disk_page_t flexible_disk_page;	/* Copy of the flexible disk page */
-	int wp;							/* Write protect */
-
-	unsigned int flags;			/* Status/Action flags */
-} idefloppy_floppy_t;
-
-/*
- *	Floppy flag bits values.
- */
-#define IDEFLOPPY_DRQ_INTERRUPT		0	/* DRQ interrupt device */
-#define IDEFLOPPY_MEDIA_CHANGED		1	/* Media may have changed */
-#define IDEFLOPPY_USE_READ12		2	/* Use READ12/WRITE12 or READ10/WRITE10 */
-
-/*
- *	ATAPI floppy drive packet commands
- */
-#define IDEFLOPPY_FORMAT_UNIT_CMD	0x04
-#define IDEFLOPPY_INQUIRY_CMD		0x12
-#define IDEFLOPPY_MODE_SELECT_CMD	0x55
-#define IDEFLOPPY_MODE_SENSE_CMD	0x5a
-#define IDEFLOPPY_READ10_CMD		0x28
-#define IDEFLOPPY_READ12_CMD		0xa8
-#define IDEFLOPPY_READ_CAPACITY_CMD	0x23
-#define IDEFLOPPY_REQUEST_SENSE_CMD	0x03
-#define IDEFLOPPY_PREVENT_REMOVAL_CMD	0x1e
-#define IDEFLOPPY_SEEK_CMD		0x2b
-#define IDEFLOPPY_START_STOP_CMD	0x1b
-#define IDEFLOPPY_TEST_UNIT_READY_CMD	0x00
-#define IDEFLOPPY_VERIFY_CMD		0x2f
-#define IDEFLOPPY_WRITE10_CMD		0x2a
-#define IDEFLOPPY_WRITE12_CMD		0xaa
-#define IDEFLOPPY_WRITE_VERIFY_CMD	0x2e
-
-/*
- *	Defines for the mode sense command
- */
-#define MODE_SENSE_CURRENT		0x00
-#define MODE_SENSE_CHANGEABLE		0x01
-#define MODE_SENSE_DEFAULT		0x02 
-#define MODE_SENSE_SAVED		0x03
-
-/*
- *	Special requests for our block device strategy routine.
- */
-#define	IDEFLOPPY_FIRST_RQ		90
-
-/*
- * 	IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
- */
-#define	IDEFLOPPY_PC_RQ			90
-
-#define IDEFLOPPY_LAST_RQ		90
-
-/*
- *	A macro which can be used to check if a given request command
- *	originated in the driver or in the buffer cache layer.
- */
-#define IDEFLOPPY_RQ_CMD(cmd) 		((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
-
-/*
- *	Error codes which are returned in rq->errors to the higher part
- *	of the driver.
- */
-#define	IDEFLOPPY_ERROR_GENERAL		101
-
-/*
- *	The ATAPI Status Register.
- */
-typedef union {
-	unsigned all			:8;
-	struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-		unsigned check		:1;	/* Error occurred */
-		unsigned idx		:1;	/* Reserved */
-		unsigned corr		:1;	/* Correctable error occurred */
-		unsigned drq		:1;	/* Data is request by the device */
-		unsigned dsc		:1;	/* Media access command finished */
-		unsigned reserved5	:1;	/* Reserved */
-		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
-		unsigned bsy		:1;	/* The device has access to the command block */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-		unsigned bsy		:1;	/* The device has access to the command block */
-		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
-		unsigned reserved5	:1;	/* Reserved */
-		unsigned dsc		:1;	/* Media access command finished */
-		unsigned drq		:1;	/* Data is request by the device */
-		unsigned corr		:1;	/* Correctable error occurred */
-		unsigned idx		:1;	/* Reserved */
-		unsigned check		:1;	/* Error occurred */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	} b;
-} idefloppy_status_reg_t;
-
-/*
- *	The ATAPI error register.
- */
-typedef union {
-	unsigned all			:8;
-	struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-		unsigned ili		:1;	/* Illegal Length Indication */
-		unsigned eom		:1;	/* End Of Media Detected */
-		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
-		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
-		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
-		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
-		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
-		unsigned eom		:1;	/* End Of Media Detected */
-		unsigned ili		:1;	/* Illegal Length Indication */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	} b;
-} idefloppy_error_reg_t;
-
-/*
- *	ATAPI Feature Register
- */
-typedef union {
-	unsigned all			:8;
-	struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-		unsigned dma		:1;	/* Using DMA or PIO */
-		unsigned reserved321	:3;	/* Reserved */
-		unsigned reserved654	:3;	/* Reserved (Tag Type) */
-		unsigned reserved7	:1;	/* Reserved */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-		unsigned reserved7	:1;	/* Reserved */
-		unsigned reserved654	:3;	/* Reserved (Tag Type) */
-		unsigned reserved321	:3;	/* Reserved */
-		unsigned dma		:1;	/* Using DMA or PIO */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	} b;
-} idefloppy_feature_reg_t;
-
-/*
- *	ATAPI Byte Count Register.
- */
-typedef union {
-	unsigned all			:16;
-	struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-		unsigned low		:8;	/* LSB */
-		unsigned high		:8;	/* MSB */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-		unsigned high		:8;	/* MSB */
-		unsigned low		:8;	/* LSB */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	} b;
-} idefloppy_bcount_reg_t;
-
-/*
- *	ATAPI Interrupt Reason Register.
- */
-typedef union {
-	unsigned all			:8;
-	struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
-		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
-		unsigned reserved	:6;	/* Reserved */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-		unsigned reserved	:6;	/* Reserved */
-		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
-		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	} b;
-} idefloppy_ireason_reg_t;
-
-/*
- *	ATAPI floppy Drive Select Register
- */
-typedef union {	
-	unsigned all			:8;
-	struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-		unsigned sam_lun	:3;	/* Logical unit number */
-		unsigned reserved3	:1;	/* Reserved */
-		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
-		unsigned one5		:1;	/* Should be set to 1 */
-		unsigned reserved6	:1;	/* Reserved */
-		unsigned one7		:1;	/* Should be set to 1 */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-		unsigned one7		:1;	/* Should be set to 1 */
-		unsigned reserved6	:1;	/* Reserved */
-		unsigned one5		:1;	/* Should be set to 1 */
-		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
-		unsigned reserved3	:1;	/* Reserved */
-		unsigned sam_lun	:3;	/* Logical unit number */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	} b;
-} idefloppy_drivesel_reg_t;
-
-/*
- *	ATAPI Device Control Register
- */
-typedef union {			
-	unsigned all			:8;
-	struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-		unsigned zero0		:1;	/* Should be set to zero */
-		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
-		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
-		unsigned one3		:1;	/* Should be set to 1 */
-		unsigned reserved4567	:4;	/* Reserved */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-		unsigned reserved4567	:4;	/* Reserved */
-		unsigned one3		:1;	/* Should be set to 1 */
-		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
-		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
-		unsigned zero0		:1;	/* Should be set to zero */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	} b;
-} idefloppy_control_reg_t;
-
-/*
- *	The following is used to format the general configuration word of
- *	the ATAPI IDENTIFY DEVICE command.
- */
-struct idefloppy_id_gcw {	
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	unsigned packet_size		:2;	/* Packet Size */
-	unsigned reserved234		:3;	/* Reserved */
-	unsigned drq_type		:2;	/* Command packet DRQ type */
-	unsigned removable		:1;	/* Removable media */
-	unsigned device_type		:5;	/* Device type */
-	unsigned reserved13		:1;	/* Reserved */
-	unsigned protocol		:2;	/* Protocol type */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	unsigned protocol		:2;	/* Protocol type */
-	unsigned reserved13		:1;	/* Reserved */
-	unsigned device_type		:5;	/* Device type */
-	unsigned removable		:1;	/* Removable media */
-	unsigned drq_type		:2;	/* Command packet DRQ type */
-	unsigned reserved234		:3;	/* Reserved */
-	unsigned packet_size		:2;	/* Packet Size */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-};
-
-/*
- *	INQUIRY packet command - Data Format
- */
-typedef struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	unsigned	device_type	:5;	/* Peripheral Device Type */
-	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
-	unsigned	reserved1_6t0	:7;	/* Reserved */
-	unsigned	rmb		:1;	/* Removable Medium Bit */
-	unsigned	ansi_version	:3;	/* ANSI Version */
-	unsigned	ecma_version	:3;	/* ECMA Version */
-	unsigned	iso_version	:2;	/* ISO Version */
-	unsigned	response_format :4;	/* Response Data Format */
-	unsigned	reserved3_45	:2;	/* Reserved */
-	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
-	unsigned	reserved3_7	:1;	/* AENC - Reserved */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
-	unsigned	device_type	:5;	/* Peripheral Device Type */
-	unsigned	rmb		:1;	/* Removable Medium Bit */
-	unsigned	reserved1_6t0	:7;	/* Reserved */
-	unsigned	iso_version	:2;	/* ISO Version */
-	unsigned	ecma_version	:3;	/* ECMA Version */
-	unsigned	ansi_version	:3;	/* ANSI Version */
-	unsigned	reserved3_7	:1;	/* AENC - Reserved */
-	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
-	unsigned	reserved3_45	:2;	/* Reserved */
-	unsigned	response_format :4;	/* Response Data Format */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	u8		additional_length;	/* Additional Length (total_length-4) */
-	u8		rsv5, rsv6, rsv7;	/* Reserved */
-	u8		vendor_id[8];		/* Vendor Identification */
-	u8		product_id[16];		/* Product Identification */
-	u8		revision_level[4];	/* Revision Level */
-	u8		vendor_specific[20];	/* Vendor Specific - Optional */
-	u8		reserved56t95[40];	/* Reserved - Optional */
-						/* Additional information may be returned */
-} idefloppy_inquiry_result_t;
-
-/*
- *	REQUEST SENSE packet command result - Data Format.
- */
-typedef struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	unsigned	error_code	:7;	/* Current error (0x70) */
-	unsigned	valid		:1;	/* The information field conforms to SFF-8070i */
-	u8		reserved1	:8;	/* Reserved */
-	unsigned	sense_key	:4;	/* Sense Key */
-	unsigned	reserved2_4	:1;	/* Reserved */
-	unsigned	ili		:1;	/* Incorrect Length Indicator */
-	unsigned	reserved2_67	:2;
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	unsigned	valid		:1;	/* The information field conforms to SFF-8070i */
-	unsigned	error_code	:7;	/* Current error (0x70) */
-	u8		reserved1	:8;	/* Reserved */
-	unsigned	reserved2_67	:2;
-	unsigned	ili		:1;	/* Incorrect Length Indicator */
-	unsigned	reserved2_4	:1;	/* Reserved */
-	unsigned	sense_key	:4;	/* Sense Key */
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	u32		information __attribute__ ((packed));
-	u8		asl;			/* Additional sense length (n-7) */
-	u32		command_specific;	/* Additional command specific information */
-	u8		asc;			/* Additional Sense Code */
-	u8		ascq;			/* Additional Sense Code Qualifier */
-	u8		replaceable_unit_code;	/* Field Replaceable Unit Code */
-	u8		reserved[3];
-	u8		pad[2];			/* Padding to 20 bytes */
-} idefloppy_request_sense_result_t;
-
-/*
- *	Pages of the SELECT SENSE / MODE SENSE packet commands.
- */
-#define	IDEFLOPPY_CAPABILITIES_PAGE	0x1b
-#define IDEFLOPPY_FLEXIBLE_DISK_PAGE	0x05
-
-/*
- *	Mode Parameter Header for the MODE SENSE packet command
- */
-typedef struct {
-	u16		mode_data_length;	/* Length of the following data transfer */
-	u8		medium_type;		/* Medium Type */
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	unsigned	reserved3	:7;
-	unsigned	wp		:1;	/* Write protect */
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	unsigned	wp		:1;	/* Write protect */
-	unsigned	reserved3	:7;
-#else
-#error "Bitfield endianness not defined! Check your byteorder.h"
-#endif
-	u8		reserved[4];
-} idefloppy_mode_parameter_header_t;
-
-#define IDEFLOPPY_MIN(a,b)	((a)<(b) ? (a):(b))
-#define	IDEFLOPPY_MAX(a,b)	((a)>(b) ? (a):(b))
-
-/*
- *	Too bad. The drive wants to send us data which we are not ready to accept.
- *	Just throw it away.
- */
-static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount)
-{
-	while (bcount--)
-		IN_BYTE (IDE_DATA_REG);
-}
-
-#if IDEFLOPPY_DEBUG_BUGS
-static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount)
-{
-	while (bcount--)
-		OUT_BYTE (0, IDE_DATA_REG);
-}
-#endif /* IDEFLOPPY_DEBUG_BUGS */
-
-/*
- *	idefloppy_end_request is used to finish servicing a request.
- *
- *	For read/write requests, we will call ide_end_request to pass to the
- *	next buffer.
- */
-static void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
-{
-	ide_drive_t *drive = hwgroup->drive;
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	struct request *rq = hwgroup->rq;
-	int error;
-
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "Reached idefloppy_end_request\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	switch (uptodate) {
-		case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
-		case 1: error = 0; break;
-		default: error = uptodate;
-	}
-	if (error)
-		floppy->failed_pc = NULL;
-	/* Why does this happen? */
-	if (!rq)
-		return;
-	if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
-		ide_end_request (uptodate, hwgroup);
-		return;
-	}
-	rq->errors = error;
-	ide_end_drive_cmd (drive, 0, 0);
-}
-
-static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
-{
-	struct request *rq = pc->rq;
-	struct buffer_head *bh = rq->bh;
-	int count;
-
-	while (bcount) {
-		if (pc->b_count == bh->b_size) {
-			rq->sector += rq->current_nr_sectors;
-			rq->nr_sectors -= rq->current_nr_sectors;
-			idefloppy_end_request (1, HWGROUP(drive));
-			if ((bh = rq->bh) != NULL)
-				pc->b_count = 0;
-		}
-		if (bh == NULL) {
-			printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
-			idefloppy_discard_data (drive, bcount);
-			return;
-		}
-		count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount);
-		atapi_input_bytes (drive, bh->b_data + pc->b_count, count);
-		bcount -= count; pc->b_count += count;
-	}
-}
-
-static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
-{
-	struct request *rq = pc->rq;
-	struct buffer_head *bh = rq->bh;
-	int count;
-	
-	while (bcount) {
-		if (!pc->b_count) {
-			rq->sector += rq->current_nr_sectors;
-			rq->nr_sectors -= rq->current_nr_sectors;
-			idefloppy_end_request (1, HWGROUP(drive));
-			if ((bh = rq->bh) != NULL) {
-				pc->b_data = bh->b_data;
-				pc->b_count = bh->b_size;
-			}
-		}
-		if (bh == NULL) {
-			printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
-			idefloppy_write_zeros (drive, bcount);
-			return;
-		}
-		count = IDEFLOPPY_MIN (pc->b_count, bcount);
-		atapi_output_bytes (drive, pc->b_data, count);
-		bcount -= count; pc->b_data += count; pc->b_count -= count;
-	}
-}
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
-{
-	struct request *rq = pc->rq;
-	struct buffer_head *bh = rq->bh;
-
-	while ((bh = rq->bh) != NULL)
-		idefloppy_end_request (1, HWGROUP(drive));
-}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-/*
- *	idefloppy_queue_pc_head generates a new packet command request in front
- *	of the request queue, before the current request, so that it will be
- *	processed immediately, on the next pass through the driver.
- */
-static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq)
-{
-	ide_init_drive_cmd (rq);
-	rq->buffer = (char *) pc;
-	rq->cmd = IDEFLOPPY_PC_RQ;
-	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
-}
-
-static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-
-	if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK)
-		floppy->pc_stack_index=0;
-	return (&floppy->pc_stack[floppy->pc_stack_index++]);
-}
-
-static struct request *idefloppy_next_rq_storage (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-
-	if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK)
-		floppy->rq_stack_index=0;
-	return (&floppy->rq_stack[floppy->rq_stack_index++]);
-}
-
-/*
- *	idefloppy_analyze_error is called on each failed packet command retry
- *	to analyze the request sense.
- */
-static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-
-	floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq;
-#if IDEFLOPPY_DEBUG_LOG
-	if (floppy->failed_pc)
-		printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
-	else
-		printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
-#endif /* IDEFLOPPY_DEBUG_LOG */
-}
-
-static void idefloppy_request_sense_callback (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */
-	if (!floppy->pc->error) {
-		idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer);
-		idefloppy_end_request (1,HWGROUP (drive));
-	} else {
-		printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
-		idefloppy_end_request (0,HWGROUP (drive));
-	}
-}
-
-/*
- *	General packet command callback function.
- */
-static void idefloppy_pc_callback (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive));
-}
-
-/*
- *	idefloppy_init_pc initializes a packet command.
- */
-static void idefloppy_init_pc (idefloppy_pc_t *pc)
-{
-	memset (pc->c, 0, 12);
-	pc->retries = 0;
-	pc->flags = 0;
-	pc->request_transfer = 0;
-	pc->buffer = pc->pc_buffer;
-	pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
-	pc->b_data = NULL;
-	pc->callback = &idefloppy_pc_callback;
-}
-
-static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
-{
-	idefloppy_init_pc (pc);	
-	pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD;
-	pc->c[4] = 255;
-	pc->request_transfer = 18;
-	pc->callback = &idefloppy_request_sense_callback;
-}
-
-/*
- *	idefloppy_retry_pc is called when an error was detected during the
- *	last packet command. We queue a request sense packet command in
- *	the head of the request list.
- */
-static void idefloppy_retry_pc (ide_drive_t *drive)
-{
-	idefloppy_pc_t *pc;
-	struct request *rq;
-	idefloppy_error_reg_t error;
-
-	error.all = IN_BYTE (IDE_ERROR_REG);
-	pc = idefloppy_next_pc_storage (drive);
-	rq = idefloppy_next_rq_storage (drive);
-	idefloppy_create_request_sense_cmd (pc);
-	idefloppy_queue_pc_head (drive, pc, rq);
-}
-
-/*
- *	idefloppy_pc_intr is the usual interrupt handler which will be called
- *	during a packet command.
- */
-static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	idefloppy_status_reg_t status;
-	idefloppy_bcount_reg_t bcount;
-	idefloppy_ireason_reg_t ireason;
-	idefloppy_pc_t *pc=floppy->pc;
-	struct request *rq = pc->rq;
-	unsigned int temp;
-
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */	
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
-		if (HWIF(drive)->dmaproc(ide_dma_end, drive)) {
-			set_bit (PC_DMA_ERROR, &pc->flags);
-		} else {
-			pc->actually_transferred=pc->request_transfer;
-			idefloppy_update_buffers (drive, pc);
-		}
-#if IDEFLOPPY_DEBUG_LOG
-		printk (KERN_INFO "ide-floppy: DMA finished\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */
-	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-	status.all = GET_STAT();					/* Clear the interrupt */
-
-	if (!status.b.drq) {						/* No more interrupts */
-#if IDEFLOPPY_DEBUG_LOG
-		printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
-#endif /* IDEFLOPPY_DEBUG_LOG */
-		clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
-
-		ide__sti();	/* local CPU only */
-
-		if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) {	/* Error detected */
-#if IDEFLOPPY_DEBUG_LOG
-			printk (KERN_INFO "ide-floppy: %s: I/O error\n",drive->name);
-#endif /* IDEFLOPPY_DEBUG_LOG */
-			rq->errors++;
-			if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
-				printk (KERN_ERR "ide-floppy: I/O error in request sense command\n");
-				return ide_do_reset (drive);
-			}
-			idefloppy_retry_pc (drive);				/* Retry operation */
-			return ide_stopped; /* queued, but not started */
-		}
-		pc->error = 0;
-		if (floppy->failed_pc == pc)
-			floppy->failed_pc=NULL;
-		pc->callback(drive);			/* Command finished - Call the callback function */
-		return ide_stopped;
-	}
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
-		printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
-		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
-		return ide_do_reset (drive);
-	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
-	bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);			/* on this interrupt */
-	ireason.all=IN_BYTE (IDE_IREASON_REG);
-
-	if (ireason.b.cod) {
-		printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
-		return ide_do_reset (drive);
-	}
-	if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {	/* Hopefully, we will never get here */
-		printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read");
-		printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write");
-		return ide_do_reset (drive);
-	}
-	if (!test_bit (PC_WRITING, &pc->flags)) {			/* Reading - Check that we have enough space */
-		temp = pc->actually_transferred + bcount.all;
-		if ( temp > pc->request_transfer) {
-			if (temp > pc->buffer_size) {
-				printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
-				idefloppy_discard_data (drive,bcount.all);
-				ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD, NULL);
-				return ide_started;
-			}
-#if IDEFLOPPY_DEBUG_LOG
-			printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */
-		}
-	}
-	if (test_bit (PC_WRITING, &pc->flags)) {
-		if (pc->buffer != NULL)
-			atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
-		else
-			idefloppy_output_buffers (drive, pc, bcount.all);
-	} else {
-		if (pc->buffer != NULL)
-			atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
-		else
-			idefloppy_input_buffers (drive, pc, bcount.all);
-	}
-	pc->actually_transferred+=bcount.all;				/* Update the current position */
-	pc->current_position+=bcount.all;
-
-	ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD, NULL);		/* And set the interrupt handler again */
-	return ide_started;
-}
-
-static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive)
-{
-	ide_startstop_t startstop;
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	idefloppy_ireason_reg_t ireason;
-
-	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
-		printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
-		return startstop;
-	}
-	ireason.all=IN_BYTE (IDE_IREASON_REG);
-	if (!ireason.b.cod || ireason.b.io) {
-		printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n");
-		return ide_do_reset (drive);
-	}
-	ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL);	/* Set the interrupt routine */
-	atapi_output_bytes (drive, floppy->pc->c, 12);		/* Send the actual packet */
-	return ide_started;
-}
-
-/*
- *	Issue a packet command
- */
-static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	idefloppy_bcount_reg_t bcount;
-	int dma_ok = 0;
-
-#if IDEFLOPPY_DEBUG_BUGS
-	if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
-		printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n");
-	}
-#endif /* IDEFLOPPY_DEBUG_BUGS */
-
-	if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD)
-		floppy->failed_pc=pc;
-	floppy->pc=pc;							/* Set the current packet command */
-
-	if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
-		/*
-		 *	We will "abort" retrying a packet command in case
-		 *	a legitimate error code was received.
-		 */
-		if (!test_bit (PC_ABORT, &pc->flags)) {
-			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
-				drive->name, pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
-			pc->error = IDEFLOPPY_ERROR_GENERAL;		/* Giving up */
-		}
-		floppy->failed_pc=NULL;
-		pc->callback(drive);
-		return ide_stopped;
-	}
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "Retry number - %d\n",pc->retries);
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	pc->retries++;
-	pc->actually_transferred=0;					/* We haven't transferred any data yet */
-	pc->current_position=pc->buffer;
-	bcount.all = IDE_MIN(pc->request_transfer, 63 * 1024);
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
-		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
-	}
-	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
-		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-	if (IDE_CONTROL_REG)
-		OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
-	OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);			/* Use PIO/DMA */
-	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
-	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
-	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (dma_ok) {							/* Begin DMA, if necessary */
-		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
-		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
-	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-	if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
-		ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD, NULL);
-		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
-		return ide_started;
-	} else {
-		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
-		return idefloppy_transfer_pc (drive);
-	}
-}
-
-static void idefloppy_rw_callback (ide_drive_t *drive)
-{
-#if IDEFLOPPY_DEBUG_LOG	
-	printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	idefloppy_end_request(1, HWGROUP(drive));
-	return;
-}
-
-static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent)
-{
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent);
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	idefloppy_init_pc (pc);
-	pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD;
-	pc->c[4] = prevent;
-}
-
-static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc)
-{
-	idefloppy_init_pc (pc);
-	pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD;
-	pc->c[7] = 255;
-	pc->c[8] = 255;
-	pc->request_transfer = 255;
-}
-
-/*
- *	A mode sense command is used to "sense" floppy parameters.
- */
-static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type)
-{
-	unsigned short length = sizeof (idefloppy_mode_parameter_header_t);
-	
-	idefloppy_init_pc (pc);
-	pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD;
-	pc->c[1] = 0;
-	pc->c[2] = page_code + (type << 6);
-
-	switch (page_code) {
-		case IDEFLOPPY_CAPABILITIES_PAGE:
-			length += 12;
-			break;
-		case IDEFLOPPY_FLEXIBLE_DISK_PAGE:
-			length += 32;
-			break;
-		default:
-			printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n");
-	}
-	put_unaligned (htons (length), (unsigned short *) &pc->c[7]);
-	pc->request_transfer = length;
-}
-
-static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start)
-{
-	idefloppy_init_pc (pc);
-	pc->c[0] = IDEFLOPPY_START_STOP_CMD;
-	pc->c[4] = start;
-}
-
-static void idefloppy_create_test_unit_ready_cmd(idefloppy_pc_t *pc)
-{
-	idefloppy_init_pc(pc);
-	pc->c[0] = IDEFLOPPY_TEST_UNIT_READY_CMD;
-}
-
-static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
-{
-	int block = sector / floppy->bs_factor;
-	int blocks = rq->nr_sectors / floppy->bs_factor;
-	
-#if IDEFLOPPY_DEBUG_LOG
-	printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
-		2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks);
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	idefloppy_init_pc (pc);
-	if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) {
-		pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD;
-		put_unaligned (htonl (blocks), (unsigned int *) &pc->c[6]);
-	} else {
-		pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD;
-		put_unaligned (htons (blocks), (unsigned short *) &pc->c[7]);
-	}
-	put_unaligned (htonl (block), (unsigned int *) &pc->c[2]);
-	pc->callback = &idefloppy_rw_callback;
-	pc->rq = rq;
-	pc->b_data = rq->buffer;
-	pc->b_count = rq->cmd == READ ? 0 : rq->bh->b_size;
-	if (rq->cmd == WRITE)
-		set_bit (PC_WRITING, &pc->flags);
-	pc->buffer = NULL;
-	pc->request_transfer = pc->buffer_size = blocks * floppy->block_size;
-	set_bit (PC_DMA_RECOMMENDED, &pc->flags);
-}
-
-/*
- *	idefloppy_do_request is our request handling function.	
- */
-static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	idefloppy_pc_t *pc;
-
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
-	printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	if (rq->errors >= ERROR_MAX) {
-		if (floppy->failed_pc != NULL)
-			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
-				drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
-		else
-			printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name);
-		idefloppy_end_request (0, HWGROUP(drive));
-		return ide_stopped;
-	}
-	switch (rq->cmd) {
-		case READ:
-		case WRITE:
-			if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) {
-				printk ("%s: unsupported r/w request size\n", drive->name);
-				idefloppy_end_request (0, HWGROUP(drive));
-				return ide_stopped;
-			}
-			pc = idefloppy_next_pc_storage (drive);
-			idefloppy_create_rw_cmd (floppy, pc, rq, block);
-			break;
-		case IDEFLOPPY_PC_RQ:
-			pc = (idefloppy_pc_t *) rq->buffer;
-			break;
-		default:
-			printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd);
-			idefloppy_end_request (0,HWGROUP (drive));
-			return ide_stopped;
-	}
-	pc->rq = rq;
-	return idefloppy_issue_pc (drive, pc);
-}
-
-/*
- *	idefloppy_queue_pc_tail adds a special packet command request to the
- *	tail of the request queue, and waits for it to be serviced.
- */
-static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc)
-{
-	struct request rq;
-
-	ide_init_drive_cmd (&rq);
-	rq.buffer = (char *) pc;
-	rq.cmd = IDEFLOPPY_PC_RQ;
-	return ide_do_drive_cmd (drive, &rq, ide_wait);
-}
-
-/*
- *	Look at the flexible disk page parameters. We will ignore the CHS
- *	capacity parameters and use the LBA parameters instead.
- */
-static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	idefloppy_pc_t pc;
-	idefloppy_mode_parameter_header_t *header;
-	idefloppy_flexible_disk_page_t *page;
-	int capacity, lba_capacity;
-
-	idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT);
-	if (idefloppy_queue_pc_tail (drive,&pc)) {
-		printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n");
-		return 1;
-	}
-	header = (idefloppy_mode_parameter_header_t *) pc.buffer;
-	floppy->wp = header->wp;
-	page = (idefloppy_flexible_disk_page_t *) (header + 1);
-
-	page->transfer_rate = ntohs (page->transfer_rate);
-	page->sector_size = ntohs (page->sector_size);
-	page->cyls = ntohs (page->cyls);
-	page->rpm = ntohs (page->rpm);
-	capacity = page->cyls * page->heads * page->sectors * page->sector_size;
-	if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t)))
-		printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n",
-			drive->name, capacity / 1024, page->cyls, page->heads, page->sectors,
-			page->transfer_rate / 8, page->sector_size, page->rpm);
-
-	floppy->flexible_disk_page = *page;
-	drive->bios_cyl = page->cyls;
-	drive->bios_head = page->heads;
-	drive->bios_sect = page->sectors;
-	lba_capacity = floppy->blocks * floppy->block_size;
-	if (capacity != lba_capacity) {
-		printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
-			drive->name, capacity, lba_capacity);
-		capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
-		floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
-	}
-	return 0;
-}
-
-/*
- *	Determine if a media is present in the floppy drive, and if so,
- *	its LBA capacity.
- */
-static int idefloppy_get_capacity (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	idefloppy_pc_t pc;
-	idefloppy_capacity_header_t *header;
-	idefloppy_capacity_descriptor_t *descriptor;
-	int i, descriptors, rc = 1, blocks, length;
-	
-	drive->bios_cyl = 0;
-	drive->bios_head = drive->bios_sect = 0;
-	floppy->blocks = floppy->bs_factor = 0;
-	drive->part[0].nr_sects = 0;
-
-	idefloppy_create_read_capacity_cmd (&pc);
-	if (idefloppy_queue_pc_tail (drive, &pc)) {
-		printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n");
-		return 1;
-	}
-	header = (idefloppy_capacity_header_t *) pc.buffer;
-	descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t);
-	descriptor = (idefloppy_capacity_descriptor_t *) (header + 1);
-	for (i = 0; i < descriptors; i++, descriptor++) {
-		blocks = descriptor->blocks = ntohl (descriptor->blocks);
-		length = descriptor->length = ntohs (descriptor->length);
-		if (!i && descriptor->dc == CAPACITY_CURRENT) {
-			if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t))) {
-				printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size, %s \n",
-					drive->name, blocks * length / 1024, blocks, length,
-					drive->using_dma ? ", DMA":"");
-			}
-			floppy->capacity = *descriptor;
-			if (!length || length % 512)
-				printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length);
-			else {
-				floppy->blocks = blocks;
-				floppy->block_size = length;
-				if ((floppy->bs_factor = length / 512) != 1)
-					printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name);
-				rc = 0;
-			}
-		}
-#if IDEFLOPPY_DEBUG_INFO
-		if (!i) printk (KERN_INFO "Descriptor 0 Code: %d\n", descriptor->dc);
-		printk (KERN_INFO "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length);
-#endif /* IDEFLOPPY_DEBUG_INFO */
-	}
-	(void) idefloppy_get_flexible_disk_page (drive);
-	drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor;
-	return rc;
-}
-
-/*
- *	Our special ide-floppy ioctl's.
- *
- *	Currently there aren't any ioctl's.
- */
-static int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
-				 unsigned int cmd, unsigned long arg)
-{
-	idefloppy_pc_t pc;
-
-	if (cmd == CDROMEJECT) {
-		if (drive->usage > 1)
-			return -EBUSY;
-		idefloppy_create_prevent_cmd (&pc, 0);
-		(void) idefloppy_queue_pc_tail (drive, &pc);
-		idefloppy_create_start_stop_cmd (&pc, 2);
-		(void) idefloppy_queue_pc_tail (drive, &pc);
-		return 0;
-	}
- 	return -EIO;
-}
-
-/*
- *	Our open/release functions
- */
-static int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	idefloppy_pc_t pc;
-	
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "Reached idefloppy_open\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	MOD_INC_USE_COUNT;
-	if (drive->usage == 1) {
-		idefloppy_create_test_unit_ready_cmd(&pc);
-		if (idefloppy_queue_pc_tail(drive, &pc)) {
-			idefloppy_create_start_stop_cmd (&pc, 1);
-			(void) idefloppy_queue_pc_tail (drive, &pc);
-		}
-		if (idefloppy_get_capacity (drive)) {
-			drive->usage--;
-			MOD_DEC_USE_COUNT;
-			return -EIO;
-		}
-		if (floppy->wp && (filp->f_mode & 2)) {
-			drive->usage--;
-			MOD_DEC_USE_COUNT;
-			return -EROFS;
-		}		
-		set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
-		idefloppy_create_prevent_cmd (&pc, 1);
-		(void) idefloppy_queue_pc_tail (drive, &pc);
-		check_disk_change(inode->i_rdev);
-	}
-	return 0;
-}
-
-static void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
-{
-	idefloppy_pc_t pc;
-	
-#if IDEFLOPPY_DEBUG_LOG
-	printk (KERN_INFO "Reached idefloppy_release\n");
-#endif /* IDEFLOPPY_DEBUG_LOG */
-
-	if (!drive->usage) {
-		invalidate_buffers (inode->i_rdev);
-		idefloppy_create_prevent_cmd (&pc, 0);
-		(void) idefloppy_queue_pc_tail (drive, &pc);
-	}
-	MOD_DEC_USE_COUNT;
-}
-
-/*
- *	Check media change. Use a simple algorithm for now.
- */
-static int idefloppy_media_change (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	
-	return test_and_clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
-}
-
-/*
- *	Return the current floppy capacity to ide.c.
- */
-static unsigned long idefloppy_capacity (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-	unsigned long capacity = floppy->blocks * floppy->bs_factor;
-
-	return capacity;
-}
-
-/*
- *	idefloppy_identify_device checks if we can support a drive,
- *	based on the ATAPI IDENTIFY command results.
- */
-static int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
-{
-	struct idefloppy_id_gcw gcw;
-#if IDEFLOPPY_DEBUG_INFO
-	unsigned short mask,i;
-	char buffer[80];
-#endif /* IDEFLOPPY_DEBUG_INFO */
-
-	*((unsigned short *) &gcw) = id->config;
-
-#ifdef CONFIG_PPC
-	/* kludge for Apple PowerBook internal zip */
-	if ((gcw.device_type == 5) && !strstr(id->model, "CD-ROM")
-	    && strstr(id->model, "ZIP"))
-		gcw.device_type = 0;			
-#endif
-
-#if IDEFLOPPY_DEBUG_INFO
-	printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n");
-	switch (gcw.protocol) {
-		case 0: case 1: sprintf (buffer, "ATA");break;
-		case 2:	sprintf (buffer, "ATAPI");break;
-		case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break;
-	}
-	printk (KERN_INFO "Protocol Type: %s\n", buffer);
-	switch (gcw.device_type) {
-		case 0: sprintf (buffer, "Direct-access Device");break;
-		case 1: sprintf (buffer, "Streaming Tape Device");break;
-		case 2: case 3: case 4: sprintf (buffer, "Reserved");break;
-		case 5: sprintf (buffer, "CD-ROM Device");break;
-		case 6: sprintf (buffer, "Reserved");
-		case 7: sprintf (buffer, "Optical memory Device");break;
-		case 0x1f: sprintf (buffer, "Unknown or no Device type");break;
-		default: sprintf (buffer, "Reserved");
-	}
-	printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer);
-	printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No");	
-	switch (gcw.drq_type) {
-		case 0: sprintf (buffer, "Microprocessor DRQ");break;
-		case 1: sprintf (buffer, "Interrupt DRQ");break;
-		case 2: sprintf (buffer, "Accelerated DRQ");break;
-		case 3: sprintf (buffer, "Reserved");break;
-	}
-	printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer);
-	switch (gcw.packet_size) {
-		case 0: sprintf (buffer, "12 bytes");break;
-		case 1: sprintf (buffer, "16 bytes");break;
-		default: sprintf (buffer, "Reserved");break;
-	}
-	printk (KERN_INFO "Command Packet Size: %s\n", buffer);
-	printk (KERN_INFO "Model: %.40s\n",id->model);
-	printk (KERN_INFO "Firmware Revision: %.8s\n",id->fw_rev);
-	printk (KERN_INFO "Serial Number: %.20s\n",id->serial_no);
-	printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512);
-	printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
-	printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
-	printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
-	printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
-	printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
-	printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO);
-	printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA);
-	printk (KERN_INFO "Single Word DMA supported modes:\n");
-	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
-		if (id->dma_1word & mask)
-			printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : "");
-	}
-	printk (KERN_INFO "Multi Word DMA supported modes:\n");
-	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
-		if (id->dma_mword & mask)
-			printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : "");
-	}
-	if (id->field_valid & 0x0002) {
-		printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
-		if (id->eide_dma_min == 0)
-			sprintf (buffer, "Not supported");
-		else
-			sprintf (buffer, "%d ns",id->eide_dma_min);
-		printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer);
-		if (id->eide_dma_time == 0)
-			sprintf (buffer, "Not supported");
-		else
-			sprintf (buffer, "%d ns",id->eide_dma_time);
-		printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer);
-		if (id->eide_pio == 0)
-			sprintf (buffer, "Not supported");
-		else
-			sprintf (buffer, "%d ns",id->eide_pio);
-		printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer);
-		if (id->eide_pio_iordy == 0)
-			sprintf (buffer, "Not supported");
-		else
-			sprintf (buffer, "%d ns",id->eide_pio_iordy);
-		printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer);
-	} else
-		printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n");
-#endif /* IDEFLOPPY_DEBUG_INFO */
-
-	if (gcw.protocol != 2)
-		printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n");
-	else if (gcw.device_type != 0)
-		printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n");
-	else if (!gcw.removable)
-		printk (KERN_ERR "ide-floppy: The removable flag is not set\n");
-	else if (gcw.drq_type == 3) {
-		printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type);
-	} else if (gcw.packet_size != 0) {
-		printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n");
-	} else
-		return 1;
-	return 0;
-}
-
-static void idefloppy_add_settings(ide_drive_t *drive)
-{
-	int major = HWIF(drive)->major;
-	int minor = drive->select.b.unit << PARTN_BITS;
-
-	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	1023,				1,	1,	&drive->bios_cyl,		NULL);
-	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
-	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
-	ide_add_setting(drive,	"breada_readahead",	SETTING_RW,					BLKRAGET,		BLKRASET,		TYPE_INT,	0,	255,				1,	2,	&read_ahead[major],		NULL);
-	ide_add_setting(drive,	"file_readahead",	SETTING_RW,					BLKFRAGET,		BLKFRASET,		TYPE_INTA,	0,	INT_MAX,			1,	1024,	&max_readahead[major][minor],	NULL);
-	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW,					BLKSECTGET,		BLKSECTSET,		TYPE_INTA,	1,	255,				1,	2,	&max_sectors[major][minor],	NULL);
-
-}
-
-/*
- *	Driver initialization.
- */
-static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy)
-{
-	struct idefloppy_id_gcw gcw;
-	int major = HWIF(drive)->major, i;
-	int minor = drive->select.b.unit << PARTN_BITS;
-
-	*((unsigned short *) &gcw) = drive->id->config;
-	drive->driver_data = floppy;
-	drive->ready_stat = 0;
-	memset (floppy, 0, sizeof (idefloppy_floppy_t));
-	floppy->drive = drive;
-	floppy->pc = floppy->pc_stack;
-	if (gcw.drq_type == 1)
-		set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
-	/*
-	 *	We used to check revisions here. At this point however
-	 *	I'm giving up. Just assume they are all broken, its easier.
-	 *
-	 *	The actual reason for the workarounds was likely
-	 *	a driver bug after all rather than a firmware bug,
-	 *	and the workaround below used to hide it. It should
-	 *	be fixed as of version 1.9, but to be on the safe side
-	 *	we'll leave the limitation below for the 2.2.x tree.
-	 */
-
-	if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0)
-	{
-		for (i = 0; i < 1 << PARTN_BITS; i++)
-			max_sectors[major][minor + i] = 64;
-	}
-
-	(void) idefloppy_get_capacity (drive);
-	idefloppy_add_settings(drive);
-	for (i = 0; i < MAX_DRIVES; ++i) {
-		ide_hwif_t *hwif = HWIF(drive);
-
-		if (drive != &hwif->drives[i]) continue;
-		hwif->gd->de_arr[i] = drive->de;
-		if (drive->removable)
-			hwif->gd->flags[i] |= GENHD_FL_REMOVABLE;
-		break;
-	}
-}
-
-static int idefloppy_cleanup (ide_drive_t *drive)
-{
-	idefloppy_floppy_t *floppy = drive->driver_data;
-
-	if (ide_unregister_subdriver (drive))
-		return 1;
-	drive->driver_data = NULL;
-	kfree (floppy);
-	return 0;
-}
-
-#ifdef CONFIG_PROC_FS
-
-static ide_proc_entry_t idefloppy_proc[] = {
-	{ "geometry",	S_IFREG|S_IRUGO,	proc_ide_read_geometry,	NULL },
-	{ NULL, 0, NULL, NULL }
-};
-
-#else
-
-#define	idefloppy_proc	NULL
-
-#endif	/* CONFIG_PROC_FS */
-
-/*
- *	IDE subdriver functions, registered with ide.c
- */
-static ide_driver_t idefloppy_driver = {
-	"ide-floppy",		/* name */
-	IDEFLOPPY_VERSION,	/* version */
-	ide_floppy,		/* media */
-	0,			/* busy */
-	1,			/* supports_dma */
-	0,			/* supports_dsc_overlap */
-	idefloppy_cleanup,	/* cleanup */
-	idefloppy_do_request,	/* do_request */
-	idefloppy_end_request,	/* end_request */
-	idefloppy_ioctl,	/* ioctl */
-	idefloppy_open,		/* open */
-	idefloppy_release,	/* release */
-	idefloppy_media_change,	/* media_change */
-	NULL,			/* pre_reset */
-	idefloppy_capacity,	/* capacity */
-	NULL,			/* special */
-	idefloppy_proc		/* proc */
-};
-
-int idefloppy_init (void);
-static ide_module_t idefloppy_module = {
-	IDE_DRIVER_MODULE,
-	idefloppy_init,
-	&idefloppy_driver,
-	NULL
-};
-
-/*
- *	idefloppy_init will register the driver for each floppy.
- */
-int idefloppy_init (void)
-{
-	ide_drive_t *drive;
-	idefloppy_floppy_t *floppy;
-	int failed = 0;
-
-	MOD_INC_USE_COUNT;
-	while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) {
-		if (!idefloppy_identify_device (drive, drive->id)) {
-			printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
-			continue;
-		}
-		if (drive->scsi) {
-			printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name);
-			continue;
-		}
-		if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) {
-			printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name);
-			continue;
-		}
-		if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) {
-			printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name);
-			kfree (floppy);
-			continue;
-		}
-		idefloppy_setup (drive, floppy);
-		failed--;
-	}
-	ide_register_module(&idefloppy_module);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-#ifdef MODULE
-int init_module (void)
-{
-	return idefloppy_init ();
-}
-
-void cleanup_module (void)
-{
-	ide_drive_t *drive;
-	int failed = 0;
-
-	while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, &idefloppy_driver, failed)) != NULL) {
-		if (idefloppy_cleanup (drive)) {
-			printk ("%s: cleanup_module() called while still busy\n", drive->name);
-			failed++;
-		}
-		/* We must remove proc entries defined in this module.
-		   Otherwise we oops while accessing these entries */
-		if (drive->proc)
-			ide_remove_proc_entries(drive->proc, idefloppy_proc);
-	}
-	ide_unregister_module(&idefloppy_module);
-}
-#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-geometry.c linux/drivers/block/ide-geometry.c
--- v2.3.51/linux/drivers/block/ide-geometry.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/ide-geometry.c	Wed Dec 31 16:00:00 1969
@@ -1,214 +0,0 @@
-/*
- * linux/drivers/block/ide-geometry.c
- */
-#include <linux/config.h>
-
-#ifdef CONFIG_BLK_DEV_IDE
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-extern ide_drive_t * get_info_ptr(kdev_t);
-extern unsigned long current_capacity (ide_drive_t *);
-
-/*
- * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
- * controller that is BIOS compatible with ST-506, and thus showing up in our
- * BIOS table, but not register compatible, and therefore not present in CMOS.
- *
- * Furthermore, we will assume that our ST-506 drives <if any> are the primary
- * drives in the system -- the ones reflected as drive 1 or 2.  The first
- * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
- * nibble.  This will be either a 4 bit drive type or 0xf indicating use byte
- * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.  A non-zero value
- * means we have an AT controller hard disk for that drive.
- *
- * Of course, there is no guarantee that either drive is actually on the
- * "primary" IDE interface, but we don't bother trying to sort that out here.
- * If a drive is not actually on the primary interface, then these parameters
- * will be ignored.  This results in the user having to supply the logical
- * drive geometry as a boot parameter for each drive not on the primary i/f.
- */
-/*
- * The only "perfect" way to handle this would be to modify the setup.[cS] code
- * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
- * for us during initialization.  I have the necessary docs -- any takers?  -ml
- */
-/*
- * I did this, but it doesnt work - there is no reasonable way to find the
- * correspondence between the BIOS numbering of the disks and the Linux
- * numbering. -aeb
- *
- * The code below is bad. One of the problems is that drives 1 and 2
- * may be SCSI disks (even when IDE disks are present), so that
- * the geometry we read here from BIOS is attributed to the wrong disks.
- * Consequently, also the "drive->present = 1" below is a mistake.
- *
- * Eventually the entire routine below should be removed.
- */
-void probe_cmos_for_drives (ide_hwif_t *hwif)
-{
-#ifdef __i386__
-	extern struct drive_info_struct drive_info;
-	byte cmos_disks, *BIOS = (byte *) &drive_info;
-	int unit;
-
-#ifdef CONFIG_BLK_DEV_PDC4030
-	if (hwif->chipset == ide_pdc4030 && hwif->channel != 0)
-		return;
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-	outb_p(0x12,0x70);		/* specify CMOS address 0x12 */
-	cmos_disks = inb_p(0x71);	/* read the data from 0x12 */
-	/* Extract drive geometry from CMOS+BIOS if not already setup */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *drive = &hwif->drives[unit];
-
-		if ((cmos_disks & (0xf0 >> (unit*4)))
-		   && !drive->present && !drive->nobios) {
-			unsigned short cyl = *(unsigned short *)BIOS;
-			unsigned char head = *(BIOS+2);
-			unsigned char sect = *(BIOS+14);
-			if (cyl > 0 && head > 0 && sect > 0 && sect < 64) {
-				drive->cyl   = drive->bios_cyl  = cyl;
-				drive->head  = drive->bios_head = head;
-				drive->sect  = drive->bios_sect = sect;
-				drive->ctl   = *(BIOS+8);
-			} else {
-				printk("hd%d: C/H/S=%d/%d/%d from BIOS ignored\n", unit, cyl, head, sect);
-			}
-		}
-
-		BIOS += 16;
-	}
-#endif
-}
-
-
-/*
- * If heads is nonzero: find a translation with this many heads and S=63.
- * Otherwise: find out how OnTrack Disk Manager would translate the disk.
- */
-static void
-ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s) {
-	static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
-	const byte *headp = dm_head_vals;
-	unsigned long total;
-
-	/*
-	 * The specs say: take geometry as obtained from Identify,
-	 * compute total capacity C*H*S from that, and truncate to
-	 * 1024*255*63. Now take S=63, H the first in the sequence
-	 * 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total.
-	 * [Please tell aeb@cwi.nl in case this computes a
-	 * geometry different from what OnTrack uses.]
-	 */
-	total = DRIVER(drive)->capacity(drive);
-
-	*s = 63;
-
-	if (heads) {
-		*h = heads;
-		*c = total / (63 * heads);
-		return;
-	}
-
-	while (63 * headp[0] * 1024 < total && headp[1] != 0)
-		 headp++;
-	*h = headp[0];
-	*c = total / (63 * headp[0]);
-}
-
-/*
- * This routine is called from the partition-table code in pt/msdos.c.
- * It has two tasks:
- * (i) to handle Ontrack DiskManager by offsetting everything by 63 sectors,
- *  or to handle EZdrive by remapping sector 0 to sector 1.
- * (ii) to invent a translated geometry.
- * Part (i) is suppressed if the user specifies the "noremap" option
- * on the command line.
- * Part (ii) is suppressed if the user specifies an explicit geometry.
- *
- * The ptheads parameter is either 0 or tells about the number of
- * heads shown by the end of the first nonempty partition.
- * If this is either 16, 32, 64, 128, 240 or 255 we'll believe it.
- *
- * The xparm parameter has the following meaning:
- *	 0 = convert to CHS with fewer than 1024 cyls
- *	     using the same method as Ontrack DiskManager.
- *	 1 = same as "0", plus offset everything by 63 sectors.
- *	-1 = similar to "0", plus redirect sector 0 to sector 1.
- *	 2 = convert to a CHS geometry with "ptheads" heads.
- *
- * Returns 0 if the translation was not possible, if the device was not 
- * an IDE disk drive, or if a geometry was "forced" on the commandline.
- * Returns 1 if the geometry translation was successful.
- */
-int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg)
-{
-	ide_drive_t *drive;
-	const char *msg1 = "";
-	int heads = 0;
-	int c, h, s;
-	int transl = 1;		/* try translation */
-	int ret = 0;
-
-	drive = get_info_ptr(i_rdev);
-	if (!drive)
-		return 0;
-
-	/* remap? */
-	if (drive->remap_0_to_1 != 2) {
-		if (xparm == 1) {		/* DM */
-			drive->sect0 = 63;
-			msg1 = " [remap +63]";
-			ret = 1;
-		} else if (xparm == -1) {	/* EZ-Drive */
-			if (drive->remap_0_to_1 == 0) {
-				drive->remap_0_to_1 = 1;
-				msg1 = " [remap 0->1]";
-				ret = 1;
-			}
-		}
-	}
-
-	/* There used to be code here that assigned drive->id->CHS
-	   to drive->CHS and that to drive->bios_CHS. However,
-	   some disks have id->C/H/S = 4092/16/63 but are larger than 2.1 GB.
-	   In such cases that code was wrong.  Moreover,
-	   there seems to be no reason to do any of these things. */
-
-	/* translate? */
-	if (drive->forced_geom)
-		transl = 0;
-
-	/* does ptheads look reasonable? */
-	if (ptheads == 32 || ptheads == 64 || ptheads == 128 ||
-	    ptheads == 240 || ptheads == 255)
-		heads = ptheads;
-
-	if (xparm == 2) {
-		if (!heads ||
-		   (drive->bios_head >= heads && drive->bios_sect == 63))
-			transl = 0;
-	}
-	if (xparm == -1) {
-		if (drive->bios_head > 16)
-			transl = 0;     /* we already have a translation */
-	}
-
-	if (transl) {
-		ontrack(drive, heads, &c, &h, &s);
-		drive->bios_cyl = c;
-		drive->bios_head = h;
-		drive->bios_sect = s;
-		ret = 1;
-	}
-
-	drive->part[0].nr_sects = current_capacity(drive);
-
-	if (ret)
-		printk("%s%s [%d/%d/%d]", msg, msg1,
-		       drive->bios_cyl, drive->bios_head, drive->bios_sect);
-	return ret;
-}
-#endif /* CONFIG_BLK_DEV_IDE */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.3.51/linux/drivers/block/ide-pci.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/block/ide-pci.c	Wed Dec 31 16:00:00 1969
@@ -1,728 +0,0 @@
-/*
- *  linux/drivers/block/ide-pci.c		Version 1.04	July 27, 1999
- *
- *  Copyright (c) 1998-1999  Andre Hedrick
- *
- *  Copyright (c) 1995-1998  Mark Lord
- *  May be copied or modified under the terms of the GNU General Public License
- */
-
-/*
- *  This module provides support for automatic detection and
- *  configuration of all PCI IDE interfaces present in a system.  
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#define DEVID_PIIXa	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371FB_0})
-#define DEVID_PIIXb	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371FB_1})
-#define DEVID_PIIX3	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371SB_1})
-#define DEVID_PIIX4	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371AB})
-#define DEVID_PIIX4E	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AB_1})
-#define DEVID_PIIX4U	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AA_1})
-#define DEVID_PIIX4U2	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82372FB_1})
-#define DEVID_VIA_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C561})
-#define DEVID_VP_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C586_1})
-#define DEVID_PDC20246	((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246})
-#define DEVID_PDC20262	((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262})
-#define DEVID_RZ1000	((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH,  PCI_DEVICE_ID_PCTECH_RZ1000})
-#define DEVID_RZ1001	((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH,  PCI_DEVICE_ID_PCTECH_RZ1001})
-#define DEVID_SAMURAI	((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH,  PCI_DEVICE_ID_PCTECH_SAMURAI_IDE})
-#define DEVID_CMD640	((ide_pci_devid_t){PCI_VENDOR_ID_CMD,     PCI_DEVICE_ID_CMD_640})
-#define DEVID_CMD643	((ide_pci_devid_t){PCI_VENDOR_ID_CMD,     PCI_DEVICE_ID_CMD_643})
-#define DEVID_CMD646	((ide_pci_devid_t){PCI_VENDOR_ID_CMD,     PCI_DEVICE_ID_CMD_646})
-#define DEVID_CMD648	((ide_pci_devid_t){PCI_VENDOR_ID_CMD,     PCI_DEVICE_ID_CMD_648})
-#define DEVID_SIS5513	((ide_pci_devid_t){PCI_VENDOR_ID_SI,      PCI_DEVICE_ID_SI_5513})
-#define DEVID_OPTI621	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    PCI_DEVICE_ID_OPTI_82C621})
-#define DEVID_OPTI621V	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    PCI_DEVICE_ID_OPTI_82C558})
-#define DEVID_OPTI621X	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    PCI_DEVICE_ID_OPTI_82C825})
-#define DEVID_TRM290	((ide_pci_devid_t){PCI_VENDOR_ID_TEKRAM,  PCI_DEVICE_ID_TEKRAM_DC290})
-#define DEVID_NS87410	((ide_pci_devid_t){PCI_VENDOR_ID_NS,      PCI_DEVICE_ID_NS_87410})
-#define DEVID_NS87415	((ide_pci_devid_t){PCI_VENDOR_ID_NS,      PCI_DEVICE_ID_NS_87415})
-#define DEVID_HT6565	((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK,  PCI_DEVICE_ID_HOLTEK_6565})
-#define DEVID_AEC6210	((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP,   PCI_DEVICE_ID_ARTOP_ATP850UF})
-#define DEVID_W82C105	((ide_pci_devid_t){PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105})
-#define DEVID_UM8673F	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8673F})
-#define DEVID_UM8886A	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886A})
-#define DEVID_UM8886BF	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886BF})
-#define DEVID_HPT34X	((ide_pci_devid_t){PCI_VENDOR_ID_TTI,     PCI_DEVICE_ID_TTI_HPT343})
-#define DEVID_HPT366	((ide_pci_devid_t){PCI_VENDOR_ID_TTI,     PCI_DEVICE_ID_TTI_HPT366})
-#define DEVID_ALI15X3	((ide_pci_devid_t){PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M5229})
-#define DEVID_CY82C693	((ide_pci_devid_t){PCI_VENDOR_ID_CONTAQ,  PCI_DEVICE_ID_CONTAQ_82C693})
-#define DEVID_HINT	((ide_pci_devid_t){0x3388,                0x8013})
-#define DEVID_CS5530	((ide_pci_devid_t){PCI_VENDOR_ID_CYRIX,   PCI_DEVICE_ID_CYRIX_5530_IDE})
-#define DEVID_AMD7409	((ide_pci_devid_t){PCI_VENDOR_ID_AMD,     PCI_DEVICE_ID_AMD_VIPER_7409})
-
-#define	IDE_IGNORE	((void *)-1)
-
-#ifdef CONFIG_BLK_DEV_AEC6210
-extern unsigned int pci_init_aec6210(struct pci_dev *, const char *);
-extern void ide_init_aec6210(ide_hwif_t *);
-extern void ide_dmacapable_aec6210(ide_hwif_t *, unsigned long);
-#define PCI_AEC6210	&pci_init_aec6210
-#define INIT_AEC6210	&ide_init_aec6210
-#define DMA_AEC6210	&ide_dmacapable_aec6210
-#else
-#define PCI_AEC6210	NULL
-#define INIT_AEC6210	NULL
-#define DMA_AEC6210	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_ALI15X3
-extern unsigned int pci_init_ali15x3(struct pci_dev *, const char *);
-extern unsigned int ata66_ali15x3(ide_hwif_t *);
-extern void ide_init_ali15x3(ide_hwif_t *);
-extern void ide_dmacapable_ali15x3(ide_hwif_t *, unsigned long);
-#define PCI_ALI15X3	&pci_init_ali15x3
-#define ATA66_ALI15X3	&ata66_ali15x3
-#define INIT_ALI15X3	&ide_init_ali15x3
-#define DMA_ALI15X3	&ide_dmacapable_ali15x3
-#else
-#define PCI_ALI15X3	NULL
-#define ATA66_ALI15X3	NULL
-#define INIT_ALI15X3	NULL
-#define DMA_ALI15X3	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_AMD7409
-extern unsigned int pci_init_amd7409(struct pci_dev *, const char *);
-extern unsigned int ata66_amd7409(ide_hwif_t *);
-extern void ide_init_amd7409(ide_hwif_t *);
-extern void ide_dmacapable_amd7409(ide_hwif_t *, unsigned long);
-#define PCI_AMD7409	&pci_init_amd7409
-#define ATA66_AMD7409	&ata66_amd7409
-#define INIT_AMD7409	&ide_init_amd7409
-#define DMA_AMD7409	&ide_dmacapable_amd7409
-#else
-#define PCI_AMD7409	NULL
-#define ATA66_AMD7409	NULL
-#define INIT_AMD7409	NULL
-#define DMA_AMD7409	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_CMD64X
-extern unsigned int pci_init_cmd64x(struct pci_dev *, const char *);
-extern unsigned int ata66_cmd64x(ide_hwif_t *);
-extern void ide_init_cmd64x(ide_hwif_t *);
-extern void ide_dmacapable_cmd64x(ide_hwif_t *, unsigned long);
-#define PCI_CMD64X	&pci_init_cmd64x
-#define ATA66_CMD64X	&ata66_cmd64x
-#define INIT_CMD64X	&ide_init_cmd64x
-#else
-#define PCI_CMD64X	NULL
-#define ATA66_CMD64X	NULL
-#ifdef __sparc_v9__
-#define INIT_CMD64X	IDE_IGNORE
-#else
-#define INIT_CMD64X	NULL
-#endif
-#endif
-
-#ifdef CONFIG_BLK_DEV_CY82C693
-extern unsigned int pci_init_cy82c693(struct pci_dev *, const char *);
-extern void ide_init_cy82c693(ide_hwif_t *);
-#define PCI_CY82C693	&pci_init_cy82c693
-#define INIT_CY82C693	&ide_init_cy82c693
-#else
-#define PCI_CY82C693	NULL
-#define INIT_CY82C693	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_CS5530
-extern unsigned int pci_init_cs5530(struct pci_dev *, const char *);
-extern void ide_init_cs5530(ide_hwif_t *);
-#define PCI_CS5530	&pci_init_cs5530
-#define INIT_CS5530	&ide_init_cs5530
-#else
-#define PCI_CS5530	NULL
-#define INIT_CS5530	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_HPT34X
-extern unsigned int pci_init_hpt34x(struct pci_dev *, const char *);
-extern void ide_init_hpt34x(ide_hwif_t *);
-#define PCI_HPT34X	&pci_init_hpt34x
-#define INIT_HPT34X	&ide_init_hpt34x
-#else
-#define PCI_HPT34X	NULL
-#define INIT_HPT34X	IDE_IGNORE
-#endif
-
-#ifdef CONFIG_BLK_DEV_HPT366
-extern byte hpt363_shared_irq;
-extern byte hpt363_shared_pin;
-extern unsigned int pci_init_hpt366(struct pci_dev *, const char *);
-extern unsigned int ata66_hpt366(ide_hwif_t *);
-extern void ide_init_hpt366(ide_hwif_t *);
-extern void ide_dmacapable_hpt366(ide_hwif_t *, unsigned long);
-#define PCI_HPT366	&pci_init_hpt366
-#define ATA66_HPT366	&ata66_hpt366
-#define INIT_HPT366	&ide_init_hpt366
-#define DMA_HPT366	&ide_dmacapable_hpt366
-#else
-static byte hpt363_shared_irq = 0;
-static byte hpt363_shared_pin = 0;
-#define PCI_HPT366	NULL
-#define ATA66_HPT366	NULL
-#define INIT_HPT366	NULL
-#define DMA_HPT366	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_NS87415
-extern void ide_init_ns87415(ide_hwif_t *);
-#define INIT_NS87415	&ide_init_ns87415
-#else
-#define INIT_NS87415	IDE_IGNORE
-#endif
-
-#ifdef CONFIG_BLK_DEV_OPTI621
-extern void ide_init_opti621(ide_hwif_t *);
-#define INIT_OPTI621	&ide_init_opti621
-#else
-#define INIT_OPTI621	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_PDC202XX
-extern unsigned int pci_init_pdc202xx(struct pci_dev *, const char *);
-extern unsigned int ata66_pdc202xx(ide_hwif_t *);
-extern void ide_init_pdc202xx(ide_hwif_t *);
-#define PCI_PDC202XX	&pci_init_pdc202xx
-#define ATA66_PDC202XX	&ata66_pdc202xx
-#define INIT_PDC202XX	&ide_init_pdc202xx
-#else
-#define PCI_PDC202XX	NULL
-#define ATA66_PDC202XX	NULL
-#define INIT_PDC202XX	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_PIIX
-extern unsigned int pci_init_piix(struct pci_dev *, const char *);
-extern unsigned int ata66_piix(ide_hwif_t *);
-extern void ide_init_piix(ide_hwif_t *);
-#define PCI_PIIX	&pci_init_piix
-#define ATA66_PIIX	&ata66_piix
-#define INIT_PIIX	&ide_init_piix
-#else
-#define PCI_PIIX	NULL
-#define ATA66_PIIX	NULL
-#define INIT_PIIX	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_RZ1000
-extern void ide_init_rz1000(ide_hwif_t *);
-#define INIT_RZ1000	&ide_init_rz1000
-#else
-#define INIT_RZ1000	IDE_IGNORE
-#endif
-
-#define INIT_SAMURAI	NULL
-
-#ifdef CONFIG_BLK_DEV_SIS5513
-extern unsigned int pci_init_sis5513(struct pci_dev *, const char *);
-extern unsigned int ata66_sis5513(ide_hwif_t *);
-extern void ide_init_sis5513(ide_hwif_t *);
-#define PCI_SIS5513	&pci_init_sis5513
-#define ATA66_SIS5513	&ata66_sis5513
-#define INIT_SIS5513	&ide_init_sis5513
-#else
-#define PCI_SIS5513	NULL
-#define ATA66_SIS5513	NULL
-#define INIT_SIS5513	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_SL82C105
-extern void ide_init_sl82c105(ide_hwif_t *);
-extern void ide_dmacapable_sl82c105(ide_hwif_t *, unsigned long);
-#define INIT_W82C105	&ide_init_sl82c105
-#define DMA_W82C105	&ide_dmacapable_sl82c105
-#else
-#define INIT_W82C105	IDE_IGNORE
-#define DMA_W82C105	NULL
-#endif
-
-#ifdef CONFIG_BLK_DEV_TRM290
-extern void ide_init_trm290(ide_hwif_t *);
-#define INIT_TRM290	&ide_init_trm290
-#else
-#define INIT_TRM290	IDE_IGNORE
-#endif
-
-#ifdef CONFIG_BLK_DEV_VIA82CXXX
-extern unsigned int pci_init_via82cxxx(struct pci_dev *, const char *);
-extern unsigned int ata66_via82cxxx(ide_hwif_t *);
-extern void ide_init_via82cxxx(ide_hwif_t *);
-extern void ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long);
-#define PCI_VIA82CXXX	&pci_init_via82cxxx
-#define ATA66_VIA82CXXX	&ata66_via82cxxx
-#define INIT_VIA82CXXX	&ide_init_via82cxxx
-#define DMA_VIA82CXXX	&ide_dmacapable_via82cxxx
-#else
-#define PCI_VIA82CXXX	NULL
-#define ATA66_VIA82CXXX	NULL
-#define INIT_VIA82CXXX	NULL
-#define DMA_VIA82CXXX	NULL
-#endif
-
-typedef struct ide_pci_enablebit_s {
-	byte	reg;	/* byte pci reg holding the enable-bit */
-	byte	mask;	/* mask to isolate the enable-bit */
-	byte	val;	/* value of masked reg when "enabled" */
-} ide_pci_enablebit_t;
-
-typedef struct ide_pci_device_s {
-	ide_pci_devid_t		devid;
-	const char		*name;
-	unsigned int		(*init_chipset)(struct pci_dev *dev, const char *name);
-	unsigned int		(*ata66_check)(ide_hwif_t *hwif);
-	void 			(*init_hwif)(ide_hwif_t *hwif);
-	void			(*dma_init)(ide_hwif_t *hwif, unsigned long dmabase);
-	ide_pci_enablebit_t	enablebits[2];
-	byte			bootable;
-	unsigned int		extra;
-} ide_pci_device_t;
-
-static ide_pci_device_t ide_pci_chipsets[] __initdata = {
-	{DEVID_PIIXa,	"PIIX",		NULL,		NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
-	{DEVID_PIIXb,	"PIIX",		NULL,		NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
-	{DEVID_PIIX3,	"PIIX3",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
-	{DEVID_PIIX4,	"PIIX4",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
-	{DEVID_PIIX4E,	"PIIX4",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
-	{DEVID_PIIX4U,	"PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
-	{DEVID_PIIX4U2,	"PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
-	{DEVID_VIA_IDE,	"VIA_IDE",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_VP_IDE,	"VP_IDE",	PCI_VIA82CXXX,	ATA66_VIA82CXXX,INIT_VIA82CXXX,	DMA_VIA82CXXX,	{{0x40,0x02,0x02}, {0x40,0x01,0x01}}, 	ON_BOARD,	0 },
-	{DEVID_PDC20246,"PDC20246",	PCI_PDC202XX,	NULL,		INIT_PDC202XX,	NULL,		{{0x50,0x02,0x02}, {0x50,0x04,0x04}}, 	OFF_BOARD,	16 },
-	{DEVID_PDC20262,"PDC20262",	PCI_PDC202XX,	ATA66_PDC202XX,	INIT_PDC202XX,	NULL,		{{0x50,0x02,0x02}, {0x50,0x04,0x04}},	OFF_BOARD,	48 },
-	{DEVID_RZ1000,	"RZ1000",	NULL,		NULL,		INIT_RZ1000,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_RZ1001,	"RZ1001",	NULL,		NULL,		INIT_RZ1000,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_SAMURAI,	"SAMURAI",	NULL,		NULL,		INIT_SAMURAI,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_CMD640,	"CMD640",	NULL,		NULL,		IDE_IGNORE,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_NS87410,	"NS87410",	NULL,		NULL,		NULL,		NULL,		{{0x43,0x08,0x08}, {0x47,0x08,0x08}}, 	ON_BOARD,	0 },
-	{DEVID_SIS5513,	"SIS5513",	PCI_SIS5513,	ATA66_SIS5513,	INIT_SIS5513,	NULL,		{{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, 	ON_BOARD,	0 },
-	{DEVID_CMD643,	"CMD643",	PCI_CMD64X,	NULL,		INIT_CMD64X,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_CMD646,	"CMD646",	PCI_CMD64X,	NULL,		INIT_CMD64X,	NULL,		{{0x00,0x00,0x00}, {0x51,0x80,0x80}}, 	ON_BOARD,	0 },
-	{DEVID_CMD648,	"CMD648",	PCI_CMD64X,	ATA66_CMD64X,	INIT_CMD64X,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_HT6565,	"HT6565",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_OPTI621,	"OPTI621",	NULL,		NULL,		INIT_OPTI621,	NULL,		{{0x45,0x80,0x00}, {0x40,0x08,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_OPTI621X,"OPTI621X",	NULL,		NULL,		INIT_OPTI621,	NULL,		{{0x45,0x80,0x00}, {0x40,0x08,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_TRM290,	"TRM290",	NULL,		NULL,		INIT_TRM290,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_NS87415,	"NS87415",	NULL,		NULL,		INIT_NS87415,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_AEC6210,	"AEC6210",	PCI_AEC6210,	NULL,		INIT_AEC6210,	DMA_AEC6210,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	OFF_BOARD,	0 },
-	{DEVID_W82C105,	"W82C105",	NULL,		NULL,		INIT_W82C105,	DMA_W82C105,	{{0x40,0x01,0x01}, {0x40,0x10,0x10}}, 	ON_BOARD,	0 },
-	{DEVID_UM8673F,	"UM8673F",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_UM8886A,	"UM8886A",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_UM8886BF,"UM8886BF",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
-	{DEVID_HPT34X,	"HPT34X",	PCI_HPT34X,	NULL,		INIT_HPT34X,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	NEVER_BOARD,	16 },
-	{DEVID_HPT366,	"HPT366",	PCI_HPT366,	ATA66_HPT366,	INIT_HPT366,	DMA_HPT366,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	OFF_BOARD,	240 },
-	{DEVID_ALI15X3,	"ALI15X3",	PCI_ALI15X3,	ATA66_ALI15X3,	INIT_ALI15X3,	DMA_ALI15X3,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_CY82C693,"CY82C693",	PCI_CY82C693,	NULL,		INIT_CY82C693,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_HINT,	"HINT_IDE",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_CS5530,	"CS5530",	PCI_CS5530,	NULL,		INIT_CS5530,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
-	{DEVID_AMD7409,	"AMD7409",	PCI_AMD7409,	ATA66_AMD7409,	INIT_AMD7409,	DMA_AMD7409,	{{0x40,0x01,0x01}, {0x40,0x02,0x02}},	ON_BOARD,	0 },
-	{IDE_PCI_DEVID_NULL, "PCI_IDE",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 }};
-
-/*
- * This allows offboard ide-pci cards the enable a BIOS, verify interrupt
- * settings of split-mirror pci-config space, place chipset into init-mode,
- * and/or preserve an interrupt if the card is not native ide support.
- */
-static unsigned int __init ide_special_settings (struct pci_dev *dev, const char *name)
-{
-	switch(dev->device) {
-		case PCI_DEVICE_ID_TTI_HPT366:
-		case PCI_DEVICE_ID_PROMISE_20246:
-		case PCI_DEVICE_ID_PROMISE_20262:
-		case PCI_DEVICE_ID_ARTOP_ATP850UF:
-			return dev->irq;
-		default:
-			break;
-	}
-	return 0;
-}
-
-/*
- * Match a PCI IDE port against an entry in ide_hwifs[],
- * based on io_base port if possible.
- */
-static ide_hwif_t __init *ide_match_hwif (unsigned long io_base, byte bootable, const char *name)
-{
-	int h;
-	ide_hwif_t *hwif;
-
-	/*
-	 * Look for a hwif with matching io_base specified using
-	 * parameters to ide_setup().
-	 */
-	for (h = 0; h < MAX_HWIFS; ++h) {
-		hwif = &ide_hwifs[h];
-		if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
-			if (hwif->chipset == ide_generic)
-				return hwif; /* a perfect match */
-		}
-	}
-	/*
-	 * Look for a hwif with matching io_base default value.
-	 * If chipset is "ide_unknown", then claim that hwif slot.
-	 * Otherwise, some other chipset has already claimed it..  :(
-	 */
-	for (h = 0; h < MAX_HWIFS; ++h) {
-		hwif = &ide_hwifs[h];
-		if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
-			if (hwif->chipset == ide_unknown)
-				return hwif; /* match */
-			printk("%s: port 0x%04lx already claimed by %s\n", name, io_base, hwif->name);
-			return NULL;	/* already claimed */
-		}
-	}
-	/*
-	 * Okay, there is no hwif matching our io_base,
-	 * so we'll just claim an unassigned slot.
-	 * Give preference to claiming other slots before claiming ide0/ide1,
-	 * just in case there's another interface yet-to-be-scanned
-	 * which uses ports 1f0/170 (the ide0/ide1 defaults).
-	 *
-	 * Unless there is a bootable card that does not use the standard
-	 * ports 1f0/170 (the ide0/ide1 defaults). The (bootable) flag.
-	 */
-	if (bootable) {
-		for (h = 0; h < MAX_HWIFS; ++h) {
-			hwif = &ide_hwifs[h];
-			if (hwif->chipset == ide_unknown)
-				return hwif;	/* pick an unused entry */
-		}
-	} else {
-		for (h = 2; h < MAX_HWIFS; ++h) {
-			hwif = ide_hwifs + h;
-			if (hwif->chipset == ide_unknown)
-				return hwif;	/* pick an unused entry */
-		}
-	}
-	for (h = 0; h < 2; ++h) {
-		hwif = ide_hwifs + h;
-		if (hwif->chipset == ide_unknown)
-			return hwif;	/* pick an unused entry */
-	}
-	printk("%s: too many IDE interfaces, no room in table\n", name);
-	return NULL;
-}
-
-static int __init ide_setup_pci_baseregs (struct pci_dev *dev, const char *name)
-{
-	byte reg, progif = 0;
-
-	/*
-	 * Place both IDE interfaces into PCI "native" mode:
-	 */
-	if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) {
-		if ((progif & 0xa) != 0xa) {
-			printk("%s: device not capable of full native PCI mode\n", name);
-			return 1;
-		}
-		printk("%s: placing both ports into native PCI mode\n", name);
-		(void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5);
-		if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) {
-			printk("%s: rewrite of PROGIF failed, wanted 0x%04x, got 0x%04x\n", name, progif|5, progif);
-			return 1;
-		}
-	}
-	/*
-	 * Setup base registers for IDE command/control spaces for each interface:
-	 */
-	for (reg = 0; reg < 4; reg++) {
-		struct resource *res = dev->resource + reg;
-		if (!(res->flags & PCI_BASE_ADDRESS_SPACE_IO))
-			continue;
-		if (!res->start) {
-			printk("%s: Missing I/O address #%d\n", name, reg);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-/*
- * ide_setup_pci_device() looks at the primary/secondary interfaces
- * on a PCI IDE device and, if they are enabled, prepares the IDE driver
- * for use with them.  This generic code works for most PCI chipsets.
- *
- * One thing that is not standardized is the location of the
- * primary/secondary interface "enable/disable" bits.  For chipsets that
- * we "know" about, this information is in the ide_pci_device_t struct;
- * for all other chipsets, we just assume both interfaces are enabled.
- */
-static void __init ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d)
-{
-	unsigned int port, at_least_one_hwif_enabled = 0, autodma = 0, pciirq = 0;
-	unsigned short pcicmd = 0, tried_config = 0;
-	byte tmp = 0;
-	ide_hwif_t *hwif, *mate = NULL;
-
-#ifdef CONFIG_IDEDMA_AUTO
-	autodma = 1;
-#endif
-check_if_enabled:
-	if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) {
-		printk("%s: error accessing PCI regs\n", d->name);
-		return;
-	}
-	if (!(pcicmd & PCI_COMMAND_IO)) {	/* is device disabled? */
-		/*
-		 * PnP BIOS was *supposed* to have set this device up for us,
-		 * but we can do it ourselves, so long as the BIOS has assigned an IRQ
-		 *  (or possibly the device is using a "legacy header" for IRQs).
-		 * Maybe the user deliberately *disabled* the device,
-		 * but we'll eventually ignore it again if no drives respond.
-		 */
-		if (tried_config++
-		 || ide_setup_pci_baseregs(dev, d->name)
-		 || pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_IO)) {
-			printk("%s: device disabled (BIOS)\n", d->name);
-			return;
-		}
-		autodma = 0;	/* default DMA off if we had to configure it here */
-		goto check_if_enabled;
-	}
-	if (tried_config)
-		printk("%s: device enabled (Linux)\n", d->name);
-	/*
-	 * Can we trust the reported IRQ?
-	 */
-	pciirq = dev->irq;
-	if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) {
-		printk("%s: not 100%% native mode: will probe irqs later\n", d->name);
-		/*
-		 * This allows offboard ide-pci cards the enable a BIOS,
-		 * verify interrupt settings of split-mirror pci-config
-		 * space, place chipset into init-mode, and/or preserve
-		 * an interrupt if the card is not native ide support.
-		 */
-		pciirq = (d->init_chipset) ? d->init_chipset(dev, d->name) : ide_special_settings(dev, d->name);
-	} else if (tried_config) {
-		printk("%s: will probe irqs later\n", d->name);
-		pciirq = 0;
-	} else if (!pciirq) {
-		printk("%s: bad irq (%d): will probe later\n", d->name, pciirq);
-		pciirq = 0;
-	} else {
-		if (d->init_chipset)
-			(void) d->init_chipset(dev, d->name);
-#ifdef __sparc__
-		printk("%s: 100%% native mode on irq %s\n",
-		       d->name, __irq_itoa(pciirq));
-#else
-		printk("%s: 100%% native mode on irq %d\n", d->name, pciirq);
-#endif
-	}
-	if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X)) {
-		/* see comments in hpt34x.c on why..... */
-		d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD;
-	}
-	/*
-	 * Set up the IDE ports
-	 */
-	for (port = 0; port <= 1; ++port) {
-		unsigned long base = 0, ctl = 0;
-		ide_pci_enablebit_t *e = &(d->enablebits[port]);
-		if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val))
-			continue;	/* port not enabled */
-		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) && (port))
-			return;
-		if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE || (dev->class & (port ? 4 : 1)) != 0) {
-			ctl  = dev->resource[(2*port)+1].start;
-			base = dev->resource[2*port].start;
-			if (!(ctl & PCI_BASE_ADDRESS_IO_MASK) ||
-			    !(base & PCI_BASE_ADDRESS_IO_MASK)) {
-				printk("%s: IO baseregs (BIOS) are reported as MEM, report to <andre@suse.com>.\n", d->name);
-#if 0
-				/* FIXME! This really should check that it really gets the IO/MEM part right! */
-				continue;
-#endif
-			}
-		}
-		if ((ctl && !base) || (base && !ctl)) {
-			printk("%s: inconsistent baseregs (BIOS) for port %d, skipping\n", d->name, port);
-			continue;
-		}
-		if (!ctl)
-			ctl = port ? 0x374 : 0x3f4;	/* use default value */
-		if (!base)
-			base = port ? 0x170 : 0x1f0;	/* use default value */
-		if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL)
-			continue;	/* no room in ide_hwifs[] */
-		if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
-			ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL);
-			memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
-			hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
-		}
-		hwif->chipset = ide_pci;
-		hwif->pci_dev = dev;
-		hwif->pci_devid = d->devid;
-		hwif->channel = port;
-		if (!hwif->irq)
-			hwif->irq = pciirq;
-		if (mate) {
-			hwif->mate = mate;
-			mate->mate = hwif;
-			if (IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210)) {
-				hwif->serialized = 1;
-				mate->serialized = 1;
-			}
-		}
-		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886BF) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8673F)) {
-			hwif->irq = hwif->channel ? 15 : 14;
-			goto bypass_umc_dma;
-		}
-		if (hwif->udma_four) {
-			printk("%s: ATA-66 forced bit set (WARNING)!!\n", d->name);
-		} else {
-			hwif->udma_four = (d->ata66_check) ? d->ata66_check(hwif) : 0;
-		}
-#ifdef CONFIG_BLK_DEV_IDEDMA
-		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X))
-			autodma = 0;
-		if (autodma)
-			hwif->autodma = 1;
-		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530) ||
-		    IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) ||
-		    ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) {
-			unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name);
-			if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) {
-				/*
- 	 			 * Set up BM-DMA capability (PnP BIOS should have done this)
- 	 			 */
-				hwif->autodma = 0;	/* default DMA off if we had to configure it here */
-				(void) pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER);
-				if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || !(pcicmd & PCI_COMMAND_MASTER)) {
-					printk("%s: %s error updating PCICMD\n", hwif->name, d->name);
-					dma_base = 0;
-				}
-			}
-			if (dma_base) {
-				if (d->dma_init) {
-					d->dma_init(hwif, dma_base);
-				} else {
-					ide_setup_dma(hwif, dma_base, 8);
-				}
-			} else {
-				printk("%s: %s Bus-Master DMA disabled (BIOS)\n", hwif->name, d->name);
-			}
-		}
-#endif	/* CONFIG_BLK_DEV_IDEDMA */
-bypass_umc_dma:
-		if (d->init_hwif)  /* Call chipset-specific routine for each enabled hwif */
-			d->init_hwif(hwif);
-		mate = hwif;
-		at_least_one_hwif_enabled = 1;
-	}
-	if (!at_least_one_hwif_enabled)
-		printk("%s: neither IDE port enabled (BIOS)\n", d->name);
-}
-
-static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d)
-{
-	struct pci_dev *dev2 = NULL, *findev;
-	ide_pci_device_t *d2;
-	unsigned char pin1 = 0, pin2 = 0;
-
-	if (PCI_FUNC(dev->devfn) & 1)
-		return;
-	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1);
-	pci_for_each_dev(findev) {
-		if ((findev->vendor == dev->vendor) &&
-		    (findev->device == dev->device) &&
-		    ((findev->devfn - dev->devfn) == 1) &&
-		    (PCI_FUNC(findev->devfn) & 1)) {
-			dev2 = findev;
-			pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2);
-			hpt363_shared_pin = (pin1 != pin2) ? 1 : 0;
-			hpt363_shared_irq = (dev->irq == dev2->irq) ? 1 : 0;
-			if (hpt363_shared_pin && hpt363_shared_irq) {
-				d->bootable = ON_BOARD;
-				printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", d->name, pin1, pin2);
-			}
-			break;
-		}
-	}
-	printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
-	ide_setup_pci_device(dev, d);
-	if (!dev2)
-		return;
-	d2 = d;
-	printk("%s: IDE controller on PCI bus %02x dev %02x\n", d2->name, dev2->bus->number, dev2->devfn);
-	if (hpt363_shared_pin && !hpt363_shared_irq) {
-		printk("%s: IDE controller run unsupported mode three!!!\n", d2->name);
-#ifndef CONFIG_HPT366_MODE3
-		printk("%s: IDE controller report to <andre@suse.com>\n", d->name);
-		return;
-#else /* CONFIG_HPT366_MODE3 */
-		printk("%s: OVERRIDE IDE controller not advisable this mode!!!\n", d2->name);
-#endif /* CONFIG_HPT366_MODE3 */
-	}
-	ide_setup_pci_device(dev2, d2);
-}
-
-/*
- * ide_scan_pcibus() gets invoked at boot time from ide.c.
- * It finds all PCI IDE controllers and calls ide_setup_pci_device for them.
- */
-void __init ide_scan_pcidev (struct pci_dev *dev)
-{
-	ide_pci_devid_t		devid;
-	ide_pci_device_t	*d;
-
-	devid.vid = dev->vendor;
-	devid.did = dev->device;
-	for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d);
-	if (d->init_hwif == IDE_IGNORE)
-		printk("%s: ignored by ide_scan_pci_device() (uses own driver)\n", d->name);
-	else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_OPTI621V) && !(PCI_FUNC(dev->devfn) & 1))
-		return;
-	else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) && (!(PCI_FUNC(dev->devfn) & 1) || !((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)))
-		return;	/* CY82C693 is more than only a IDE controller */
-	else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) && !(PCI_FUNC(dev->devfn) & 1))
-		return;	/* UM8886A/BF pair */
-	else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366))
-		hpt366_device_order_fixup(dev, d);
-	else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
-		if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL))
-			printk("%s: unknown IDE controller on PCI bus %02x device %02x, VID=%04x, DID=%04x\n",
-			       d->name, dev->bus->number, dev->devfn, devid.vid, devid.did);
-		else
-			printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
-		ide_setup_pci_device(dev, d);
-	}
-}
-
-void __init ide_scan_pcibus (int scan_direction)
-{
-	struct pci_dev *dev;
-
-	if (!scan_direction) {
-		pci_for_each_dev(dev) {
-			ide_scan_pcidev(dev);
-		}
-	} else {
-		pci_for_each_dev_reverse(dev) {
-			ide_scan_pcidev(dev);
-		}
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-pmac.c linux/drivers/block/ide-pmac.c
--- v2.3.51/linux/drivers/block/ide-pmac.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/block/ide-pmac.c	Wed Dec 31 16:00:00 1969
@@ -1,969 +0,0 @@
-/*
- * Support for IDE interfaces on PowerMacs.
- * These IDE interfaces are memory-mapped and have a DBDMA channel
- * for doing DMA.
- *
- *  Copyright (C) 1998 Paul Mackerras.
- *
- *  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.
- *
- * Some code taken from drivers/block/ide-dma.c:
- *
- *  Copyright (c) 1995-1998  Mark Lord
- *
- * BenH: I began adding more complete timing setup code, mostly because DMA
- *       won't work on new machines unless timings are setup correctly. This
- *       code was mainly stolen from Cmd646 driver and should be completed to
- *       include real timing calc. instead of hard coded values. The format of
- *       the timing register can be found in Darwin's source code, except for
- *       Keylargo ATA-4 controller.
- */
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/prom.h>
-#include <asm/io.h>
-#include <asm/dbdma.h>
-#include <asm/ide.h>
-#include <asm/mediabay.h>
-#include <asm/feature.h>
-#ifdef CONFIG_PMAC_PBOOK
-#include <linux/adb.h>
-#include <linux/pmu.h>
-#include <asm/irq.h>
-#endif
-#include "ide_modes.h"
-
-#undef IDE_PMAC_DEBUG
-
-#define IDE_SYSCLK_NS	30
-
-struct pmac_ide_hwif {
-	ide_ioreg_t			regbase;
-	int				irq;
-	int				kind;
-	struct device_node*		node;
-	u32				timings[2];
-#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
-	volatile struct dbdma_regs*	dma_regs;
-	struct dbdma_cmd*		dma_table;
-#endif
-	
-} pmac_ide[MAX_HWIFS];
-
-static int pmac_ide_count;
-
-enum {
-	controller_ohare,	/* OHare based */
-	controller_heathrow,	/* Heathrow/Paddington */
-	controller_kl_ata3,	/* KeyLargo ATA-3 */
-	controller_kl_ata4	/* KeyLargo ATA-4 */
-};
-
-
-#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
-
-typedef struct {
-	int	accessTime;
-	int	cycleTime;
-} pmac_ide_timing;
-
-/* Multiword DMA timings */
-static pmac_ide_timing mdma_timings[] =
-{
-    { 215,    480 },	/* Mode 0 */
-    {  80,    150 },	/*      1 */
-    {  70,    120 }	/*      2 */
-};
-
-/* Ultra DMA timings (for use when I know how to calculate them */
-static pmac_ide_timing udma_timings[] =
-{
-    {   0,    114 },	/* Mode 0 */
-    {   0,     73 },	/*      1 */
-    {   0,     54 },	/*      2 */
-    {   0,     39 },	/*      3 */
-    {   0,     25 }	/*      4 */
-};
-
-#define MAX_DCMDS	256	/* allow up to 256 DBDMA commands per xfer */
-
-static void pmac_ide_setup_dma(struct device_node *np, int ix);
-static int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive);
-static int pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr);
-static void pmac_ide_tuneproc(ide_drive_t *drive, byte pio);
-static void pmac_ide_selectproc(ide_drive_t *drive);
-
-#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
-
-#ifdef CONFIG_PMAC_PBOOK
-static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when);
-struct pmu_sleep_notifier idepmac_sleep_notifier = {
-	idepmac_notify_sleep, SLEEP_LEVEL_BLOCK,
-};
-#endif /* CONFIG_PMAC_PBOOK */
-
-static int
-pmac_ide_find(ide_drive_t *drive)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	ide_ioreg_t base;
-	int i;
-	
-	for (i=0; i<pmac_ide_count; i++) {
-		base = pmac_ide[i].regbase;
-		if (base && base == hwif->io_ports[0])
-			return i;
-	}
-	return -1;
-}
-
-/*
- * N.B. this can't be an initfunc, because the media-bay task can
- * call ide_[un]register at any time.
- */
-void pmac_ide_init_hwif_ports(hw_regs_t *hw,
-			      ide_ioreg_t data_port, ide_ioreg_t ctrl_port,
-			      int *irq)
-{
-	int i, ix;
-
-	if (data_port == 0)
-		return;
-
-	for (ix = 0; ix < MAX_HWIFS; ++ix)
-		if (data_port == pmac_ide[ix].regbase)
-			break;
-
-	if (ix >= MAX_HWIFS) {
-		/* Probably a PCI interface... */
-		for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i)
-			hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET;
-		/* XXX is this right? */
-		hw->io_ports[IDE_CONTROL_OFFSET] = 0;
-		if (irq != 0)
-			*irq = 0;
-		return;
-	}
-
-	/* we check only for -EINVAL meaning that we have found a matching
-	   bay but with the wrong device type */ 
-	i = check_media_bay_by_base(data_port, MB_CD);
-	if (i == -EINVAL) {
-		hw->io_ports[IDE_DATA_OFFSET] = 0;
-		return;
-	}
-
-	for (i = 0; i < 8; ++i)
-		hw->io_ports[i] = data_port + i * 0x10;
-	hw->io_ports[8] = data_port + 0x160;
-
-	if (irq != NULL)
-		*irq = pmac_ide[ix].irq;
-
-	ide_hwifs[ix].tuneproc = pmac_ide_tuneproc;
-	ide_hwifs[ix].selectproc = pmac_ide_selectproc;
-	if (pmac_ide[ix].dma_regs && pmac_ide[ix].dma_table) {
-		ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc;
-#ifdef CONFIG_PMAC_IDEDMA_AUTO
-		ide_hwifs[ix].autodma = 1;
-#endif
-	}
-}
-
-#if 0
-/* This one could be later extended to handle CMD IDE and be used by some kind
- * of /proc interface. I want to be able to get the devicetree path of a block
- * device for yaboot configuration
- */
-struct device_node*
-pmac_ide_get_devnode(ide_drive_t *drive)
-{
-	int i = pmac_ide_find(drive);
-	if (i < 0)
-		return NULL;
-	return pmac_ide[i].node;
-}
-#endif
-
-/* Setup timings for the selected drive (master/slave). I still need to verify if this
- * is enough, I beleive selectproc will be called whenever an IDE command is started,
- * but... */
-static void
-pmac_ide_selectproc(ide_drive_t *drive)
-{
-	int i = pmac_ide_find(drive);
-	if (i < 0)
-		return;
-			
-	if (drive->select.all & 0x10)
-		out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[1]);
-	else
-		out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[0]);
-}
-
-/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */
-#define SYSCLK_TICKS(t)		(((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)
-
-static void
-pmac_ide_tuneproc(ide_drive_t *drive, byte pio)
-{
-	ide_pio_data_t d;
-	int i;
-	u32 *timings;
-	int accessTicks, recTicks;
-	
-	i = pmac_ide_find(drive);
-	if (i < 0)
-		return;
-		
-	/* The "ata-4" IDE controller of UMA machines is a bit different.
-	 * We don't do anything for PIO modes until we know how to do the
-	 * calculation.
-	 */
-	if (pmac_ide[i].kind == controller_kl_ata4)
-		return;
-		
-	pio = ide_get_best_pio_mode(drive, pio, 4, &d);
-	accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time);
-	if (accessTicks < 4)
-		accessTicks = 4;
-	recTicks = SYSCLK_TICKS(d.cycle_time) - accessTicks - 4;
-	if (recTicks < 1)
-		recTicks = 1;
-	if (drive->select.all & 0x10)
-		timings = &pmac_ide[i].timings[1];
-	else
-		timings = &pmac_ide[i].timings[0];
-	
-	*timings = ((*timings) & 0xFFFFFF800) | accessTicks | (recTicks << 5);	
-#ifdef IDE_PMAC_DEBUG
-	printk("ide_pmac: Set PIO timing for mode %d, reg: 0x%08x\n",
-		pio,  *timings);
-#endif	
-		
-	if (drive->select.all == IN_BYTE(IDE_SELECT_REG))
-		pmac_ide_selectproc(drive);
-}
-
-ide_ioreg_t
-pmac_ide_get_base(int index)
-{
-	return pmac_ide[index].regbase;
-}
-
-static int ide_majors[] = { 3, 22, 33, 34, 56, 57 };
-
-kdev_t __init
-pmac_find_ide_boot(char *bootdevice, int n)
-{
-	int i;
-	
-	/*
-	 * Look through the list of IDE interfaces for this one.
-	 */
-	for (i = 0; i < pmac_ide_count; ++i) {
-		char *name;
-		if (!pmac_ide[i].node || !pmac_ide[i].node->full_name)
-			continue;
-		name = pmac_ide[i].node->full_name;
-		if (memcmp(name, bootdevice, n) == 0 && name[n] == 0) {
-			/* XXX should cope with the 2nd drive as well... */
-			return MKDEV(ide_majors[i], 0);
-		}
-	}
-
-	return 0;
-}
-
-void __init
-pmac_ide_probe(void)
-{
-	struct device_node *np;
-	int i;
-	struct device_node *atas;
-	struct device_node *p, **pp, *removables, **rp;
-	unsigned long base;
-	int irq;
-	ide_hwif_t *hwif;
-
-	if (_machine != _MACH_Pmac)
-		return;
-	pp = &atas;
-	rp = &removables;
-	p = find_devices("ATA");
-	if (p == NULL)
-		p = find_devices("IDE");
-	if (p == NULL)
-		p = find_type_devices("ide");
-	if (p == NULL)
-		p = find_type_devices("ata");
-	/* Move removable devices such as the media-bay CDROM
-	   on the PB3400 to the end of the list. */
-	for (; p != NULL; p = p->next) {
-		if (p->parent && p->parent->type
-		    && strcasecmp(p->parent->type, "media-bay") == 0) {
-			*rp = p;
-			rp = &p->next;
-		} else {
-			*pp = p;
-			pp = &p->next;
-		}
-	}
-	*rp = NULL;
-	*pp = removables;
-
-	for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) {
-		struct device_node *tp;
-
-		/*
-		 * If this node is not under a mac-io or dbdma node,
-		 * leave it to the generic PCI driver.
-		 */
-		for (tp = np->parent; tp != 0; tp = tp->parent)
-			if (tp->type && (strcmp(tp->type, "mac-io") == 0
-					 || strcmp(tp->type, "dbdma") == 0))
-				break;
-		if (tp == 0)
-			continue;
-
-		if (np->n_addrs == 0) {
-			printk(KERN_WARNING "ide: no address for device %s\n",
-			       np->full_name);
-			continue;
-		}
-
-		/*
-		 * If this slot is taken (e.g. by ide-pci.c) try the next one.
-		 */
-		while (i < MAX_HWIFS
-		       && ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0)
-			++i;
-		if (i >= MAX_HWIFS)
-			break;
-
-		base = (unsigned long) ioremap(np->addrs[0].address, 0x200) - _IO_BASE;
-
-		/* XXX This is bogus. Should be fixed in the registry by checking
-		   the kind of host interrupt controller, a bit like gatwick
-		   fixes in irq.c
-		 */
-		if (np->n_intrs == 0) {
-			printk("ide: no intrs for device %s, using 13\n",
-			       np->full_name);
-			irq = 13;
-		} else {
-			irq = np->intrs[0].line;
-		}
-		pmac_ide[i].regbase = base;
-		pmac_ide[i].irq = irq;
-		pmac_ide[i].node = np;
-		if (device_is_compatible(np, "keylargo-ata")) {
-			if (strcmp(np->name, "ata-4") == 0)
-				pmac_ide[i].kind = controller_kl_ata4;
-			else
-				pmac_ide[i].kind = controller_kl_ata3;
-		} else if (device_is_compatible(np, "heathrow-ata"))
-			pmac_ide[i].kind = controller_heathrow;
-		else
-			pmac_ide[i].kind = controller_ohare;
-
-		if (np->parent && np->parent->name
-		    && strcasecmp(np->parent->name, "media-bay") == 0) {
-			media_bay_set_ide_infos(np->parent,base,irq,i);
-		} else if (pmac_ide[i].kind == controller_ohare) {
-			/* The code below is having trouble on some ohare machines
-			 * (timing related ?). Until I can put my hand on one of these
-			 * units, I keep the old way
-			 */
-			 feature_set(np, FEATURE_IDE0_enable);
-		} else {
-			/* This is necessary to enable IDE when net-booting */
-			int *bidp = (int *)get_property(np, "AAPL,bus-id", NULL);
-			int bid = bidp ? *bidp : 0;
-			printk("pmac_ide: enabling IDE bus ID %d\n", bid);
-			switch(bid) {
-			    case 0:
-				feature_set(np, FEATURE_IDE0_reset);
-				feature_set(np, FEATURE_IOBUS_enable);
-				mdelay(10);
- 				feature_set(np, FEATURE_IDE0_enable);
-				mdelay(10);
-				feature_clear(np, FEATURE_IDE0_reset);
-				break;
-			    case 1:
-				feature_set(np, FEATURE_Mediabay_IDE_reset);
-				mdelay(10);
- 				feature_set(np, FEATURE_Mediabay_IDE_enable);
-				mdelay(10);
-				feature_clear(np, FEATURE_Mediabay_IDE_reset);
-				break;
-			    case 2:
-			    	/* This one exists only for KL, I don't know about any
-			    	   enable bit */
-				feature_set(np, FEATURE_IDE2_reset);
-				mdelay(10);
-				feature_clear(np, FEATURE_IDE2_reset);
-				break;
-			}
-			mdelay(1000);
-		}
-
-		hwif = &ide_hwifs[i];
-		pmac_ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->irq);
-		memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
-		hwif->chipset = ide_pmac;
-		hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
-
-#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
-		if (np->n_addrs >= 2) {
-			/* has a DBDMA controller channel */
-			pmac_ide_setup_dma(np, i);
-		}
-#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
-
-		++i;
-	}
-	pmac_ide_count = i;
-
-#ifdef CONFIG_PMAC_PBOOK
-	pmu_register_sleep_notifier(&idepmac_sleep_notifier);
-#endif /* CONFIG_PMAC_PBOOK */
-}
-
-#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
-
-static void __init 
-pmac_ide_setup_dma(struct device_node *np, int ix)
-{
-	pmac_ide[ix].dma_regs =
-		(volatile struct dbdma_regs*)ioremap(np->addrs[1].address, 0x200);
-
-	/*
-	 * Allocate space for the DBDMA commands.
-	 * The +2 is +1 for the stop command and +1 to allow for
-	 * aligning the start address to a multiple of 16 bytes.
-	 */
-	pmac_ide[ix].dma_table = (struct dbdma_cmd*)
-	       kmalloc((MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), GFP_KERNEL);
-	if (pmac_ide[ix].dma_table == 0) {
-		printk(KERN_ERR "%s: unable to allocate DMA command list\n",
-		       ide_hwifs[ix].name);
-		return;
-	}
-
-	ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc;
-#ifdef CONFIG_PMAC_IDEDMA_AUTO
-	ide_hwifs[ix].autodma = 1;
-#endif
-}
-
-/*
- * pmac_ide_build_dmatable builds the DBDMA command list
- * for a transfer and sets the DBDMA channel to point to it.
- */
-static int
-pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr)
-{
-	struct dbdma_cmd *table, *tstart;
-	int count = 0;
-	struct request *rq = HWGROUP(drive)->rq;
-	struct buffer_head *bh = rq->bh;
-	unsigned int size, addr;
-	volatile struct dbdma_regs *dma = pmac_ide[ix].dma_regs;
-
-	table = tstart = (struct dbdma_cmd *) DBDMA_ALIGN(pmac_ide[ix].dma_table);
-	out_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
-	while (in_le32(&dma->status) & RUN)
-		udelay(1);
-
-	do {
-		/*
-		 * Determine addr and size of next buffer area.  We assume that
-		 * individual virtual buffers are always composed linearly in
-		 * physical memory.  For example, we assume that any 8kB buffer
-		 * is always composed of two adjacent physical 4kB pages rather
-		 * than two possibly non-adjacent physical 4kB pages.
-		 */
-		if (bh == NULL) {  /* paging requests have (rq->bh == NULL) */
-			addr = virt_to_bus(rq->buffer);
-			size = rq->nr_sectors << 9;
-		} else {
-			/* group sequential buffers into one large buffer */
-			addr = virt_to_bus(bh->b_data);
-			size = bh->b_size;
-			while ((bh = bh->b_reqnext) != NULL) {
-				if ((addr + size) != virt_to_bus(bh->b_data))
-					break;
-				size += bh->b_size;
-			}
-		}
-
-		/*
-		 * Fill in the next DBDMA command block.
-		 * Note that one DBDMA command can transfer
-		 * at most 65535 bytes.
-		 */
-		while (size) {
-			unsigned int tc = (size < 0xfe00)? size: 0xfe00;
-
-			if (++count >= MAX_DCMDS) {
-				printk("%s: DMA table too small\n",
-				       drive->name);
-				return 0; /* revert to PIO for this request */
-			}
-			st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE);
-			st_le16(&table->req_count, tc);
-			st_le32(&table->phy_addr, addr);
-			table->cmd_dep = 0;
-			table->xfer_status = 0;
-			table->res_count = 0;
-			addr += tc;
-			size -= tc;
-			++table;
-		}
-	} while (bh != NULL);
-
-	/* convert the last command to an input/output last command */
-	if (count)
-		st_le16(&table[-1].command, wr? OUTPUT_LAST: INPUT_LAST);
-	else
-		printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name);
-
-	/* add the stop command to the end of the list */
-	memset(table, 0, sizeof(struct dbdma_cmd));
-	out_le16(&table->command, DBDMA_STOP);
-
-	out_le32(&dma->cmdptr, virt_to_bus(tstart));
-	return 1;
-}
-
-
-/* This is fun.  -DaveM */
-#define IDE_SETXFER		0x03
-#define IDE_SETFEATURE		0xef
-#define IDE_DMA2_ENABLE		0x22
-#define IDE_DMA1_ENABLE		0x21
-#define IDE_DMA0_ENABLE		0x20
-#define IDE_UDMA4_ENABLE	0x44
-#define IDE_UDMA3_ENABLE	0x43
-#define IDE_UDMA2_ENABLE	0x42
-#define IDE_UDMA1_ENABLE	0x41
-#define IDE_UDMA0_ENABLE	0x40
-
-static __inline__ unsigned char
-dma_bits_to_command(unsigned char bits)
-{
-	if(bits & 0x04)
-		return IDE_DMA2_ENABLE;
-	if(bits & 0x02)
-		return IDE_DMA1_ENABLE;
-	return IDE_DMA0_ENABLE;
-}
-
-static __inline__ unsigned char
-udma_bits_to_command(unsigned char bits)
-{
-	if(bits & 0x10)
-		return IDE_UDMA4_ENABLE;
-	if(bits & 0x08)
-		return IDE_UDMA3_ENABLE;
-	if(bits & 0x04)
-		return IDE_UDMA2_ENABLE;
-	if(bits & 0x02)
-		return IDE_UDMA1_ENABLE;
-	if(bits & 0x01)
-		return IDE_UDMA0_ENABLE;
-	return 0;
-}
-
-static __inline__ int
-wait_for_ready(ide_drive_t *drive)
-{
-	/* Timeout bumped for some powerbooks */
-	int timeout = 2000;
-	byte stat;
-
-	while(--timeout) {
-		stat = GET_STAT();
-		if(!(stat & BUSY_STAT)) {
-			if (drive->ready_stat == 0)
-				break;
-			else if((stat & drive->ready_stat) || (stat & ERR_STAT))
-				break;
-		}
-		mdelay(1);
-	}
-	if((stat & ERR_STAT) || timeout <= 0) {
-		if (stat & ERR_STAT) {
-			printk("ide_pmace: wait_for_ready, error status: %x\n", stat);
-		}
-		return 1;
-	}
-	return 0;
-}
-
-static int
-pmac_ide_do_setfeature(ide_drive_t *drive, byte command)
-{
-	unsigned long flags;
-	byte old_select;
-	int result = 1;
-
-	save_flags(flags);
-	cli();
-	old_select = IN_BYTE(IDE_SELECT_REG);
-	OUT_BYTE(drive->select.all, IDE_SELECT_REG);
-	udelay(10);
-	OUT_BYTE(IDE_SETXFER, IDE_FEATURE_REG);
-	OUT_BYTE(command, IDE_NSECTOR_REG);
-	if(wait_for_ready(drive)) {
-		printk("pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n");
-		goto out;
-	}
-	OUT_BYTE(IDE_SETFEATURE, IDE_COMMAND_REG);
-	result = wait_for_ready(drive);
-	if (result)
-		printk("pmac_ide_do_setfeature disk not ready after SET_FEATURE !\n");
-out:
-	OUT_BYTE(old_select, IDE_SELECT_REG);
-	restore_flags(flags);
-	
-	return result;
-}
-
-static int
-pmac_ide_mdma_enable(ide_drive_t *drive, int idx)
-{
-	byte bits = drive->id->dma_mword & 0x07;
-	byte feature = dma_bits_to_command(bits);
-	u32 *timings;
-	int cycleTime, accessTime;
-	int accessTicks, recTicks;
-	struct hd_driveid *id = drive->id;
-	
-	/* For now, we don't know these values */
-	if (pmac_ide[idx].kind == controller_kl_ata4 && feature != IDE_DMA2_ENABLE)
-		return 0;
-	if (pmac_ide[idx].kind != controller_kl_ata4 && feature == IDE_DMA0_ENABLE)
-		return 0;
-				
-	/* Set feature on drive */
-    	printk("%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf);
-	if (pmac_ide_do_setfeature(drive, feature)) {
-	    	printk("%s: Failed !\n", drive->name);
-	    	return 0;
-	}
-	
-	/* which drive is it ? */
-	if (drive->select.all & 0x10)
-		timings = &pmac_ide[idx].timings[1];
-	else
-		timings = &pmac_ide[idx].timings[0];
-
-	/* Calculate accesstime and cycle time */
-	cycleTime = mdma_timings[feature & 0xf].cycleTime;
-	accessTime = mdma_timings[feature & 0xf].accessTime;
-	if ((id->field_valid & 2) && (id->eide_dma_time))
-		cycleTime = id->eide_dma_time;
-	if ((pmac_ide[idx].kind == controller_ohare) && (cycleTime < 150))
-		cycleTime = 150;
-
-	/* For ata-4 controller, we don't know the calculation */
-	if (pmac_ide[idx].kind == controller_kl_ata4) {
-		*timings = 0x00019465;	/* MDMA2 */
-	} else {
-		int halfTick = 0;
-		int origAccessTime = accessTime;
-		int origCycleTime = cycleTime;
-		
-		accessTicks = SYSCLK_TICKS(accessTime);
-		if (accessTicks < 1)
-			accessTicks = 1;
-		accessTime = accessTicks * IDE_SYSCLK_NS;
-		recTicks = SYSCLK_TICKS(cycleTime - accessTime) - 1;
-		if (recTicks < 1)
-			recTicks = 1;
-		cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS;
-
-		if ((accessTicks > 1) &&
-			((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
-			((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) {
-            			halfTick    = 1;
-				accessTicks--;
-		}
-		*timings = ((*timings) & 0x7FF) |
-			(accessTicks | (recTicks << 5) | (halfTick << 10)) << 11;
-	}
-#ifdef IDE_PMAC_DEBUG
-	printk("ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n",
-		feature & 0xf, *timings);
-#endif	
-	return 1;
-}
-
-static int
-pmac_ide_udma_enable(ide_drive_t *drive, int idx)
-{
-	byte bits = drive->id->dma_ultra & 0x1f;
-	byte feature = udma_bits_to_command(bits);
-	u32 timings;
-	
-	/* We support only those values */
-	if (feature != IDE_UDMA4_ENABLE && feature != IDE_UDMA2_ENABLE)
-		return 0;
-		
-	/* Set feature on drive */
-    	printk("%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf);
-	if (pmac_ide_do_setfeature(drive, feature)) {
-		printk("%s: Failed !\n", drive->name);
-		return 0;
-	}
-
-	/* Put this channel into UDMA mode.
-	 * This value is set by MacOS on the iBook for U/DMA2
-	 */
-	switch(feature) {	
-		case IDE_UDMA4_ENABLE:
-			timings = 0x0cd00065;
-			break;
-		case IDE_UDMA2_ENABLE:
-			timings = 0x11100065;
-			break;
-	}
-	
-	if (drive->select.all & 0x10)
-		pmac_ide[idx].timings[1] = timings;
-	else
-		pmac_ide[idx].timings[0] = timings;
-	
-	return 1;
-}
-
-static int
-pmac_ide_dma_onoff(ide_drive_t *drive, int enable)
-{
-	int ata4, udma, idx;
-	struct hd_driveid *id = drive->id;
-
-	drive->using_dma = 0;
-	
-	idx = pmac_ide_find(drive);
-	if (idx < 0)
-		return 0;
-		
-	if (drive->media == ide_floppy)
-		enable = 0;
-	if (((id->capability & 1) == 0) && !check_drive_lists(drive, GOOD_DMA_DRIVE))
-		enable = 0;
-	if (check_drive_lists(drive, BAD_DMA_DRIVE))
-		enable = 0;
-
-	udma = 0;
-	ata4 = (pmac_ide[idx].kind == controller_kl_ata4);
-			
-	if(enable) {
-		if (ata4 && (drive->media == ide_disk) &&
-		    (id->field_valid & 0x0004) && (id->dma_ultra & 0x17)) {
-			/* UltraDMA modes. */
-			drive->using_dma = pmac_ide_udma_enable(drive, idx);
-		}
-		if (!drive->using_dma && (id->dma_mword & 0x0007)) {
-			/* Normal MultiWord DMA modes. */
-			drive->using_dma = pmac_ide_mdma_enable(drive, idx);
-		}
-		/* Without this, strange things will happen on Keylargo-based
-		 * machines
-		 */
-		OUT_BYTE(0, IDE_CONTROL_REG);
-		if (drive->select.all == IN_BYTE(IDE_SELECT_REG))
-			pmac_ide_selectproc(drive);
-	}
-	return 0;
-}
-
-int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	int ix, dstat;
-	volatile struct dbdma_regs *dma;
-
-	/* Can we stuff a pointer to our intf structure in config_data
-	 * or select_data in hwif ?
-	 */
-	ix = pmac_ide_find(drive);
-	if (ix < 0)
-		return 0;		
-	dma = pmac_ide[ix].dma_regs;
-
-	switch (func) {
-	case ide_dma_on:
-	case ide_dma_off:
-	case ide_dma_off_quietly:
-		pmac_ide_dma_onoff(drive, (func == ide_dma_on));
-		break;
-	case ide_dma_check:
-		if (hwif->autodma)
-			pmac_ide_dma_onoff(drive, 1);
-		break;
-	case ide_dma_read:
-	case ide_dma_write:
-		if (!pmac_ide_build_dmatable(drive, ix, func==ide_dma_write))
-			return 1;
-		drive->waiting_for_dma = 1;
-		if (drive->media != ide_disk)
-			return 0;
-		ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
-		OUT_BYTE(func==ide_dma_write? WIN_WRITEDMA: WIN_READDMA,
-			 IDE_COMMAND_REG);
-	case ide_dma_begin:
-		out_le32(&dma->control, (RUN << 16) | RUN);
-		break;
-	case ide_dma_end:
-		drive->waiting_for_dma = 0;
-		dstat = in_le32(&dma->status);
-		out_le32(&dma->control, ((RUN|WAKE|DEAD) << 16));
-		/* verify good dma status */
-		return (dstat & (RUN|DEAD|ACTIVE)) != RUN;
-	case ide_dma_test_irq:
-		return (in_le32(&dma->status) & (RUN|ACTIVE)) == RUN;
-	default:
-		printk(KERN_ERR "pmac_ide_dmaproc: bad func %d\n", func);
-	}
-	return 0;
-}
-#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
-
-#ifdef CONFIG_PMAC_PBOOK
-static void idepmac_sleep_disk(int i, unsigned long base)
-{
-	struct device_node* np = pmac_ide[i].node;
-	int j;
-
-	/* FIXME: We only handle the master IDE */
-	if (ide_hwifs[i].drives[0].media == ide_disk) {
-		/* Spin down the drive */
-		outb(0xa0, base+0x60);
-		outb(0x0, base+0x30);
-		outb(0x0, base+0x20);
-		outb(0x0, base+0x40);
-		outb(0x0, base+0x50);
-		outb(0xe0, base+0x70);
-		outb(0x2, base+0x160);   
-		for (j = 0; j < 10; j++) {
-			int status;
-			mdelay(100);
-			status = inb(base+0x70);
-			if (!(status & BUSY_STAT) && (status & DRQ_STAT))
-				break;
-		}
-	}
-	feature_set(np, FEATURE_IDE0_reset);
-	feature_clear(np, FEATURE_IOBUS_enable);
-	feature_clear(np, FEATURE_IDE0_enable);
-	pmac_ide[i].timings[0] = 0;
-	pmac_ide[i].timings[1] = 0;
-}
-
-static void idepmac_wake_disk(int i, unsigned long base)
-{
-	struct device_node* np = pmac_ide[i].node;
-	int j;
-
-	/* Revive IDE disk and controller */
-	feature_set(np, FEATURE_IOBUS_enable);
-	mdelay(10);
-	feature_set(np, FEATURE_IDE0_enable);
-	mdelay(10);
-	feature_clear(np, FEATURE_IDE0_reset);
-	mdelay(100);
-
-	/* Reset timings */
-	pmac_ide_selectproc(&ide_hwifs[i].drives[0]);
-	mdelay(10);
-
-	/* Wait up to 10 seconds (enough for recent drives) */
-	for (j = 0; j < 100; j++) {
-		int status;
-		mdelay(100);
-		status = inb(base + 0x70);
-		if (!(status & BUSY_STAT))
-			break;
-	}
-}
-
-/* Here we handle media bay devices */
-static void
-idepmac_wake_bay(int i, unsigned long base)
-{
-	int timeout;
-
-	/* Reset timings */
-	pmac_ide_selectproc(&ide_hwifs[i].drives[0]);
-	mdelay(10);
-
-	timeout = 10000;
-	while ((inb(base + 0x70) & BUSY_STAT) && timeout) {
-		mdelay(1);
-		--timeout;
-	}
-}
-
-/* Note: We support only master drives for now. This will have to be
- * improved if we want to handle sleep on the iMacDV where the CD-ROM
- * is a slave
- */
-static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when)
-{
-	int i, ret;
-	unsigned long base;
-
-	switch (when) {
-	case PBOOK_SLEEP_REQUEST:
-		break;
-	case PBOOK_SLEEP_REJECT:
-		break;
-	case PBOOK_SLEEP_NOW:
-		for (i = 0; i < pmac_ide_count; ++i) {
-			if ((base = pmac_ide[i].regbase) == 0)
-				continue;
-			/* Disable irq during sleep */
-			disable_irq(pmac_ide[i].irq);
-			ret = check_media_bay_by_base(base, MB_CD);
-			if (ret == -ENODEV)
-				/* not media bay - put the disk to sleep */
-				idepmac_sleep_disk(i, base);
-		}
-		break;
-	case PBOOK_WAKE:
-		for (i = 0; i < pmac_ide_count; ++i) {
-			ide_hwif_t *hwif;
-			if ((base = pmac_ide[i].regbase) == 0)
-				continue;
-			hwif = &ide_hwifs[i];
-		        /* We don't handle media bay devices this way */
-			ret = check_media_bay_by_base(base, MB_CD);
-			if (ret == -ENODEV)
-				idepmac_wake_disk(i, base);
-			else if (ret == 0)
-				idepmac_wake_bay(i, base);
-			enable_irq(pmac_ide[i].irq);
-
-#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
-			if (hwif->drives[0].present && hwif->drives[0].using_dma)
-				pmac_ide_dma_onoff(&hwif->drives[0], 1);
-#endif				
-		}
-		break;
-	}
-	return PBOOK_SLEEP_OK;
-}
-#endif /* CONFIG_PMAC_PBOOK */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-pnp.c linux/drivers/block/ide-pnp.c
--- v2.3.51/linux/drivers/block/ide-pnp.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/ide-pnp.c	Wed Dec 31 16:00:00 1969
@@ -1,158 +0,0 @@
-/*
- * linux/drivers/block/ide-pnp.c
- *
- * This file provides autodetection for ISA PnP IDE interfaces.
- * It was tested with "ESS ES1868 Plug and Play AudioDrive" IDE interface.
- *
- * Copyright (C) 2000 Andrey Panin <pazke@orbita.don.sitek.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example /usr/src/linux/COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
- */
-
-#include <linux/ide.h>
-#include <linux/isapnp.h>
-
-#ifndef PREPARE_FUNC
-#define PREPARE_FUNC(dev)  (dev->prepare)
-#define ACTIVATE_FUNC(dev)  (dev->activate)
-#define DEACTIVATE_FUNC(dev)  (dev->deactivate)
-#endif
-
-#define DEV_IO(dev, index) (dev->resource[index].start)
-#define DEV_IRQ(dev, index) (dev->irq_resource[index].start)
-
-#define DEV_NAME(dev) (dev->bus->name ? dev->bus->name : "ISA PnP")
-
-#define GENERIC_HD_DATA		0
-#define GENERIC_HD_ERROR	1
-#define GENERIC_HD_NSECTOR	2
-#define GENERIC_HD_SECTOR	3
-#define GENERIC_HD_LCYL		4
-#define GENERIC_HD_HCYL		5
-#define GENERIC_HD_SELECT	6
-#define GENERIC_HD_STATUS	7
-
-static int generic_ide_offsets[IDE_NR_PORTS] __initdata = {
-	GENERIC_HD_DATA, GENERIC_HD_ERROR, GENERIC_HD_NSECTOR, 
-	GENERIC_HD_SECTOR, GENERIC_HD_LCYL, GENERIC_HD_HCYL,
-	GENERIC_HD_SELECT, GENERIC_HD_STATUS, -1, -1
-};
-
-/* ISA PnP device table entry */
-struct pnp_dev_t {
-	unsigned int vendor, device;
-	int (*init_fn)(struct pci_dev *dev, int enable);
-};
-
-/* Generic initialisation function for ISA PnP IDE interface */
-static int __init pnpide_generic_init(struct pci_dev *dev, int enable)
-{
-	hw_regs_t hw;
-	int index;
-
-	if (!enable)
-		return 0;
-
-	if (!(DEV_IO(dev, 0) && DEV_IO(dev, 1) && DEV_IRQ(dev, 0)))
-		return 1;
-
-	ide_setup_ports(&hw, (ide_ioreg_t) DEV_IO(dev, 0),
-			generic_ide_offsets, (ide_ioreg_t) DEV_IO(dev, 1),
-			0, NULL, DEV_IRQ(dev, 0));
-
-	index = ide_register_hw(&hw, NULL);
-
-	if (index != -1) {
-	    	printk("ide%d: %s IDE interface\n", index, DEV_NAME(dev));
-		return 0;
-	}
-
-	return 1;
-}
-
-/* Add your devices here :)) */
-struct pnp_dev_t idepnp_devices[] __initdata = {
-	/* Generic ESDI/IDE/ATA compatible hard disk controller
 */
-	{	ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0600),
-		pnpide_generic_init },
-	{	0 }
-};
-
-#ifdef MODULE
-#define NR_PNP_DEVICES 8
-struct pnp_dev_inst {
-	struct pci_dev *dev;
-	struct pnp_dev_t *dev_type;
-};
-static struct pnp_dev_inst devices[NR_PNP_DEVICES];
-static int pnp_ide_dev_idx = 0;
-#endif
-
-/*
- * Probe for ISA PnP IDE interfaces.
- */
-void pnpide_init(int enable)
-{
-	struct pci_dev *dev = NULL;
-	struct pnp_dev_t *dev_type;
-
-	if (!isapnp_present())
-		return;
-
-#ifdef MODULE
-	/* Module unload, deactivate all registered devices. */
-	if (!enable) {
-		int i;
-		for (i = 0; i < pnp_ide_dev_idx; i++) {
-			devices[i].dev_type->init_fn(dev, 0);
-
-			if (DEACTIVATE_FUNC(devices[i].dev))
-				DEACTIVATE_FUNC(devices[i].dev)(devices[i].dev);
-		}
-		return;
-	}
-#endif
-	for (dev_type = idepnp_devices; dev_type->vendor; dev_type++) {
-		while ((dev = isapnp_find_dev(NULL, dev_type->vendor,
-			dev_type->device, dev))) {
-
-			if (dev->active)
-				continue;
-
-       			if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) {
-				printk("ide: %s prepare failed\n", DEV_NAME(dev));
-				continue;
-			}
-
-			if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) {
-				printk("ide: %s activate failed\n", DEV_NAME(dev));
-				continue;
-			}
-
-			/* Call device initialization function */
-			if (dev_type->init_fn(dev, 1)) {
-				if (DEACTIVATE_FUNC(dev))
-					DEACTIVATE_FUNC(dev)(dev);
-			} else {
-#ifdef MODULE
-				/*
-				 * Register device in the array to
-				 * deactivate it on a module unload.
-				 */
-				if (pnp_ide_dev_idx >= NR_PNP_DEVICES)
-					return;
-				devices[pnp_ide_dev_idx].dev = dev;
-				devices[pnp_ide_dev_idx].dev_type = dev_type;
-				pnp_ide_dev_idx++;
-#endif
-			}
-		}
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.3.51/linux/drivers/block/ide-probe.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/ide-probe.c	Wed Dec 31 16:00:00 1969
@@ -1,929 +0,0 @@
-/*
- *  linux/drivers/block/ide-probe.c	Version 1.05	July 3, 1999
- *
- *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
- */
-
-/*
- *  Mostly written by Mark Lord <mlord@pobox.com>
- *                and Gadi Oxman <gadio@netvision.net.il>
- *
- *  See linux/MAINTAINERS for address of current maintainer.
- *
- * This is the IDE probe module, as evolved from hd.c and ide.c.
- *
- * Version 1.00		move drive probing code from ide.c to ide-probe.c
- * Version 1.01		fix compilation problem for m68k
- * Version 1.02		increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot
- *			 by Andrea Arcangeli
- * Version 1.03		fix for (hwif->chipset == ide_4drives)
- * Version 1.04		fixed buggy treatments of known flash memory cards
- *
- * Version 1.05		fix for (hwif->chipset == ide_pdc4030)
- *			added ide6/7/8/9
- *			allowed for secondary flash card to be detectable
- *			 with new flag : drive->ata_flash : 1;
- */
-
-#undef REALLY_SLOW_IO		/* most systems can safely undef this */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/genhd.h>
-#include <linux/malloc.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/byteorder.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
-static inline void do_identify (ide_drive_t *drive, byte cmd)
-{
-	int bswap = 1;
-	struct hd_driveid *id;
-
-	id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_ATOMIC);	/* called with interrupts disabled! */
-	ide_input_data(drive, id, SECTOR_WORDS);		/* read 512 bytes of id info */
-	ide__sti();	/* local CPU only */
-	ide_fix_driveid(id);
-	if (!drive->forced_lun)
-		drive->last_lun = id->last_lun & 0x7;
-#if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA)
-	/*
-	 * EATA SCSI controllers do a hardware ATA emulation:
-	 * Ignore them if there is a driver for them available.
-	 */
-	if ((id->model[0] == 'P' && id->model[1] == 'M')
-	 || (id->model[0] == 'S' && id->model[1] == 'K')) {
-		printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
-		drive->present = 0;
-		return;
-	}
-#endif /* CONFIG_SCSI_EATA_DMA || CONFIG_SCSI_EATA_PIO */
-
-	/*
-	 *  WIN_IDENTIFY returns little-endian info,
-	 *  WIN_PIDENTIFY *usually* returns little-endian info.
-	 */
-	if (cmd == WIN_PIDENTIFY) {
-		if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
-		 || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
-		 || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
-			bswap ^= 1;	/* Vertos drives may still be weird */
-	}
-	ide_fixstring (id->model,     sizeof(id->model),     bswap);
-	ide_fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
-	ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
-
-	if (strstr(id->model, "E X A B Y T E N E S T"))
-		return;
-
-	id->model[sizeof(id->model)-1] = '\0';	/* we depend on this a lot! */
-	printk("%s: %s, ", drive->name, id->model);
-	drive->present = 1;
-
-	/*
-	 * Check for an ATAPI device
-	 */
-	if (cmd == WIN_PIDENTIFY) {
-		byte type = (id->config >> 8) & 0x1f;
-		printk("ATAPI ");
-#ifdef CONFIG_BLK_DEV_PDC4030
-		if (HWIF(drive)->channel == 1 && HWIF(drive)->chipset == ide_pdc4030) {
-			printk(" -- not supported on 2nd Promise port\n");
-			drive->present = 0;
-			return;
-		}
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-		switch (type) {
-			case ide_floppy:
-				if (!strstr(id->model, "CD-ROM")) {
-					if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
-						printk("cdrom or floppy?, assuming ");
-					if (drive->media != ide_cdrom) {
-						printk ("FLOPPY");
-						break;
-					}
-				}
-				type = ide_cdrom;	/* Early cdrom models used zero */
-			case ide_cdrom:
-				drive->removable = 1;
-#ifdef CONFIG_PPC
-				/* kludge for Apple PowerBook internal zip */
-				if (!strstr(id->model, "CD-ROM") && strstr(id->model, "ZIP")) {
-					printk ("FLOPPY");
-					type = ide_floppy;
-					break;
-				}
-#endif
-				printk ("CDROM");
-				break;
-			case ide_tape:
-				printk ("TAPE");
-				break;
-			case ide_optical:
-				printk ("OPTICAL");
-				drive->removable = 1;
-				break;
-			default:
-				printk("UNKNOWN (type %d)", type);
-				break;
-		}
-		printk (" drive\n");
-		drive->media = type;
-		return;
-	}
-
-	/*
-	 * Not an ATAPI device: looks like a "regular" hard disk
-	 */
-	if (id->config & (1<<7))
-		drive->removable = 1;
-	/*
-	 * Prevent long system lockup probing later for non-existant
-	 * slave drive if the hwif is actually a flash memory card of some variety:
-	 */
-	if (drive_is_flashcard(drive)) {
-		ide_drive_t *mate = &HWIF(drive)->drives[1^drive->select.b.unit];
-		if (!mate->ata_flash) {
-			mate->present = 0;
-			mate->noprobe = 1;
-		}
-	}
-	drive->media = ide_disk;
-	printk("ATA DISK drive\n");
-	return;
-}
-
-/*
- * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
- * and waits for a response.  It also monitors irqs while this is
- * happening, in hope of automatically determining which one is
- * being used by the interface.
- *
- * Returns:	0  device was identified
- *		1  device timed-out (no response to identify request)
- *		2  device aborted the command (refused to identify itself)
- */
-static int try_to_identify (ide_drive_t *drive, byte cmd)
-{
-	int rc;
-	ide_ioreg_t hd_status;
-	unsigned long timeout;
-	unsigned long irqs = 0;
-	byte s, a;
-
-	if (IDE_CONTROL_REG) {
-		if (!HWIF(drive)->irq) {		/* already got an IRQ? */
-			probe_irq_off(probe_irq_on());	/* clear dangling irqs */
-			irqs = probe_irq_on();		/* start monitoring irqs */
-			OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* enable device irq */
-		}
-
-		ide_delay_50ms();				/* take a deep breath */
-		a = IN_BYTE(IDE_ALTSTATUS_REG);
-		s = IN_BYTE(IDE_STATUS_REG);
-		if ((a ^ s) & ~INDEX_STAT) {
-			printk("%s: probing with STATUS(0x%02x) instead of ALTSTATUS(0x%02x)\n", drive->name, s, a);
-			hd_status = IDE_STATUS_REG;	/* ancient Seagate drives, broken interfaces */
-		} else {
-			hd_status = IDE_ALTSTATUS_REG;	/* use non-intrusive polling */
-		}
-	} else {
-		ide_delay_50ms();
-		hd_status = IDE_STATUS_REG;
-	}
-
-#if CONFIG_BLK_DEV_PDC4030
-	if (HWIF(drive)->chipset == ide_pdc4030) {
-		/* DC4030 hosted drives need their own identify... */
-		extern int pdc4030_identify(ide_drive_t *);
-		if (pdc4030_identify(drive)) {
-			if (irqs)
-				(void) probe_irq_off(irqs);
-			return 1;
-		}
-	} else
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-		OUT_BYTE(cmd,IDE_COMMAND_REG);		/* ask drive for ID */
-	timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
-	timeout += jiffies;
-	do {
-		if (0 < (signed long)(jiffies - timeout)) {
-			if (irqs)
-				(void) probe_irq_off(irqs);
-			return 1;	/* drive timed-out */
-		}
-		ide_delay_50ms();		/* give drive a breather */
-	} while (IN_BYTE(hd_status) & BUSY_STAT);
-
-	ide_delay_50ms();		/* wait for IRQ and DRQ_STAT */
-	if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
-		unsigned long flags;
-		__save_flags(flags);	/* local CPU only */
-		__cli();		/* local CPU only; some systems need this */
-		do_identify(drive, cmd); /* drive returned ID */
-		rc = 0;			/* drive responded with ID */
-		(void) GET_STAT();	/* clear drive IRQ */
-		__restore_flags(flags);	/* local CPU only */
-	} else
-		rc = 2;			/* drive refused ID */
-	if (IDE_CONTROL_REG && !HWIF(drive)->irq) {
-		irqs = probe_irq_off(irqs);	/* get our irq number */
-		if (irqs > 0) {
-			HWIF(drive)->irq = irqs; /* save it for later */
-			irqs = probe_irq_on();
-			OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
-			udelay(5);
-			(void) probe_irq_off(irqs);
-			(void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */
-			(void) GET_STAT();	/* clear drive IRQ */
-
-		} else {	/* Mmmm.. multiple IRQs.. don't know which was ours */
-			printk("%s: IRQ probe failed (%ld)\n", drive->name, irqs);
-#ifdef CONFIG_BLK_DEV_CMD640
-#ifdef CMD640_DUMP_REGS
-			if (HWIF(drive)->chipset == ide_cmd640) {
-				printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
-				CMD640_DUMP_REGS;
-			}
-#endif /* CMD640_DUMP_REGS */
-#endif /* CONFIG_BLK_DEV_CMD640 */
-		}
-	}
-	return rc;
-}
-
-/*
- * do_probe() has the difficult job of finding a drive if it exists,
- * without getting hung up if it doesn't exist, without trampling on
- * ethernet cards, and without leaving any IRQs dangling to haunt us later.
- *
- * If a drive is "known" to exist (from CMOS or kernel parameters),
- * but does not respond right away, the probe will "hang in there"
- * for the maximum wait time (about 30 seconds), otherwise it will
- * exit much more quickly.
- *
- * Returns:	0  device was identified
- *		1  device timed-out (no response to identify request)
- *		2  device aborted the command (refused to identify itself)
- *		3  bad status from device (possible for ATAPI drives)
- *		4  probe was not attempted because failure was obvious
- */
-static int do_probe (ide_drive_t *drive, byte cmd)
-{
-	int rc;
-	ide_hwif_t *hwif = HWIF(drive);
-	if (drive->present) {	/* avoid waiting for inappropriate probes */
-		if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
-			return 4;
-	}
-#ifdef DEBUG
-	printk("probing for %s: present=%d, media=%d, probetype=%s\n",
-		drive->name, drive->present, drive->media,
-		(cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
-#endif
-	ide_delay_50ms();	/* needed for some systems (e.g. crw9624 as drive0 with disk as slave) */
-	SELECT_DRIVE(hwif,drive);
-	ide_delay_50ms();
-	if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
-		if (drive->select.b.unit != 0) {
-			SELECT_DRIVE(hwif,&hwif->drives[0]);	/* exit with drive0 selected */
-			ide_delay_50ms();		/* allow BUSY_STAT to assert & clear */
-		}
-		return 3;    /* no i/f present: mmm.. this should be a 4 -ml */
-	}
-
-	if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
-	 || drive->present || cmd == WIN_PIDENTIFY)
-	{
-		if ((rc = try_to_identify(drive,cmd)))   /* send cmd and wait */
-			rc = try_to_identify(drive,cmd); /* failed: try again */
-		if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) {
-			unsigned long timeout;
-			printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT());
-			ide_delay_50ms();
-			OUT_BYTE (drive->select.all, IDE_SELECT_REG);
-			ide_delay_50ms();
-			OUT_BYTE(WIN_SRST, IDE_COMMAND_REG);
-			timeout = jiffies;
-			while ((GET_STAT() & BUSY_STAT) && time_before(jiffies, timeout + WAIT_WORSTCASE))
-				ide_delay_50ms();
-			rc = try_to_identify(drive, cmd);
-		}
-		if (rc == 1)
-			printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
-		(void) GET_STAT();		/* ensure drive irq is clear */
-	} else {
-		rc = 3;				/* not present or maybe ATAPI */
-	}
-	if (drive->select.b.unit != 0) {
-		SELECT_DRIVE(hwif,&hwif->drives[0]);	/* exit with drive0 selected */
-		ide_delay_50ms();
-		(void) GET_STAT();		/* ensure drive irq is clear */
-	}
-	return rc;
-}
-
-/*
- *
- */
-static void enable_nest (ide_drive_t *drive)
-{
-	unsigned long timeout;
-
-	printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model);
-	SELECT_DRIVE(HWIF(drive), drive);
-	ide_delay_50ms();
-	OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG);
-	timeout = jiffies + WAIT_WORSTCASE;
-	do {
-		if (jiffies > timeout) {
-			printk("failed (timeout)\n");
-			return;
-		}
-		ide_delay_50ms();
-	} while (GET_STAT() & BUSY_STAT);
-	ide_delay_50ms();
-	if (!OK_STAT(GET_STAT(), 0, BAD_STAT))
-		printk("failed (status = 0x%02x)\n", GET_STAT());
-	else
-		printk("success\n");
-	if (do_probe(drive, WIN_IDENTIFY) >= 2) {	/* if !(success||timed-out) */
-		(void) do_probe(drive, WIN_PIDENTIFY);	/* look for ATAPI device */
-	}
-}
-
-/*
- * probe_for_drive() tests for existence of a given drive using do_probe().
- *
- * Returns:	0  no device was found
- *		1  device was found (note: drive->present might still be 0)
- */
-static inline byte probe_for_drive (ide_drive_t *drive)
-{
-	if (drive->noprobe)			/* skip probing? */
-		return drive->present;
-	if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
-		(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
-	}
-	if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T"))
-		enable_nest(drive);
-	if (!drive->present)
-		return 0;			/* drive not found */
-	if (drive->id == NULL) {		/* identification failed? */
-		if (drive->media == ide_disk) {
-			printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
-			 drive->name, drive->cyl, drive->head, drive->sect);
-		} else if (drive->media == ide_cdrom) {
-			printk("%s: ATAPI cdrom (?)\n", drive->name);
-		} else {
-			drive->present = 0;	/* nuke it */
-		}
-	}
-	return 1;	/* drive was found */
-}
-
-/*
- * Calculate the region that this interface occupies,
- * handling interfaces where the registers may not be
- * ordered sanely.  We deal with the CONTROL register
- * separately.
- */
-static int hwif_check_regions (ide_hwif_t *hwif)
-{
-	int region_errors = 0;
-
-	hwif->straight8 = 0;
-	region_errors  = ide_check_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_LCYL_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
-
-	if (hwif->io_ports[IDE_CONTROL_OFFSET])
-		region_errors += ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
-
-	if (hwif->io_ports[IDE_IRQ_OFFSET])
-		region_errors += ide_check_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
-
-	/*
-	 * If any errors are return, we drop the hwif interface.
-	 */
-	return(region_errors);
-}
-
-static void hwif_register (ide_hwif_t *hwif)
-{
-	if ((hwif->io_ports[IDE_DATA_OFFSET] | 7) ==
-	    (hwif->io_ports[IDE_STATUS_OFFSET])) {
-		ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name);
-		hwif->straight8 = 1;
-		goto jump_straight8;
-	}
-
-	if (hwif->io_ports[IDE_DATA_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 1, hwif->name);
-	if (hwif->io_ports[IDE_ERROR_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_ERROR_OFFSET], 1, hwif->name);
-	if (hwif->io_ports[IDE_NSECTOR_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1, hwif->name);
-	if (hwif->io_ports[IDE_SECTOR_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1, hwif->name);
-	if (hwif->io_ports[IDE_LCYL_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_LCYL_OFFSET], 1, hwif->name);
-	if (hwif->io_ports[IDE_HCYL_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_HCYL_OFFSET], 1, hwif->name);
-	if (hwif->io_ports[IDE_SELECT_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_SELECT_OFFSET], 1, hwif->name);
-	if (hwif->io_ports[IDE_STATUS_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_STATUS_OFFSET], 1, hwif->name);
-
-jump_straight8:
-	if (hwif->io_ports[IDE_CONTROL_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
-	if (hwif->io_ports[IDE_IRQ_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_IRQ_OFFSET], 1, hwif->name);
-}
-
-/*
- * This routine only knows how to look for drive units 0 and 1
- * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
- */
-static void probe_hwif (ide_hwif_t *hwif)
-{
-	unsigned int unit;
-	unsigned long flags;
-
-	if (hwif->noprobe)
-		return;
-	if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) {
-		extern void probe_cmos_for_drives(ide_hwif_t *);
-
-		probe_cmos_for_drives (hwif);
-	}
-
-	if ((hwif->chipset != ide_4drives || !hwif->mate->present) &&
-#if CONFIG_BLK_DEV_PDC4030
-	    (hwif->chipset != ide_pdc4030 || hwif->channel == 0) &&
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-	    (hwif_check_regions(hwif))) {
-		int msgout = 0;
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			ide_drive_t *drive = &hwif->drives[unit];
-			if (drive->present) {
-				drive->present = 0;
-				printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
-				msgout = 1;
-			}
-		}
-		if (!msgout)
-			printk("%s: ports already in use, skipping probe\n", hwif->name);
-		return;	
-	}
-
-	__save_flags(flags);	/* local CPU only */
-	__sti();		/* local CPU only; needed for jiffies and irq probing */
-	/*
-	 * Second drive should only exist if first drive was found,
-	 * but a lot of cdrom drives are configured as single slaves.
-	 */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *drive = &hwif->drives[unit];
-		(void) probe_for_drive (drive);
-		if (drive->present && !hwif->present) {
-			hwif->present = 1;
-			if (hwif->chipset != ide_4drives || !hwif->mate->present) {
-				hwif_register(hwif);
-			}
-		}
-	}
-	if (hwif->io_ports[IDE_CONTROL_OFFSET] && hwif->reset) {
-		unsigned long timeout = jiffies + WAIT_WORSTCASE;
-		byte stat;
-
-		printk("%s: reset\n", hwif->name);
-		OUT_BYTE(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
-		udelay(10);
-		OUT_BYTE(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
-		do {
-			ide_delay_50ms();
-			stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
-		} while ((stat & BUSY_STAT) && 0 < (signed long)(timeout - jiffies));
-
-	}
-	__restore_flags(flags);	/* local CPU only */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *drive = &hwif->drives[unit];
-		if (drive->present) {
-			ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
-			if (tuneproc != NULL && drive->autotune == 1)
-				tuneproc(drive, 255);	/* auto-tune PIO mode */
-		}
-	}
-}
-
-#if MAX_HWIFS > 1
-/*
- * save_match() is used to simplify logic in init_irq() below.
- *
- * A loophole here is that we may not know about a particular
- * hwif's irq until after that hwif is actually probed/initialized..
- * This could be a problem for the case where an hwif is on a
- * dual interface that requires serialization (eg. cmd640) and another
- * hwif using one of the same irqs is initialized beforehand.
- *
- * This routine detects and reports such situations, but does not fix them.
- */
-static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
-{
-	ide_hwif_t *m = *match;
-
-	if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
-		if (!new->hwgroup)
-			return;
-		printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name);
-	}
-	if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
-		*match = new;
-}
-#endif /* MAX_HWIFS > 1 */
-
-/*
- * This routine sets up the irq for an ide interface, and creates a new
- * hwgroup for the irq/hwif if none was previously assigned.
- *
- * Much of the code is for correctly detecting/handling irq sharing
- * and irq serialization situations.  This is somewhat complex because
- * it handles static as well as dynamic (PCMCIA) IDE interfaces.
- *
- * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
- * interrupts completely disabled.  This can be bad for interrupt latency,
- * but anything else has led to problems on some machines.  We re-enable
- * interrupts as much as we can safely do in most places.
- */
-static int init_irq (ide_hwif_t *hwif)
-{
-	unsigned long flags;
-	unsigned int index;
-	ide_hwgroup_t *hwgroup;
-	ide_hwif_t *match = NULL;
-
-	save_flags(flags);	/* all CPUs */
-	cli();			/* all CPUs */
-
-	hwif->hwgroup = NULL;
-#if MAX_HWIFS > 1
-	/*
-	 * Group up with any other hwifs that share our irq(s).
-	 */
-	for (index = 0; index < MAX_HWIFS; index++) {
-		ide_hwif_t *h = &ide_hwifs[index];
-		if (h->hwgroup) {  /* scan only initialized hwif's */
-			if (hwif->irq == h->irq) {
-				hwif->sharing_irq = h->sharing_irq = 1;
-				if (hwif->chipset != ide_pci || h->chipset != ide_pci) {
-					save_match(hwif, h, &match);
-				}
-			}
-			if (hwif->serialized) {
-				if (hwif->mate && hwif->mate->irq == h->irq)
-					save_match(hwif, h, &match);
-			}
-			if (h->serialized) {
-				if (h->mate && hwif->irq == h->mate->irq)
-					save_match(hwif, h, &match);
-			}
-		}
-	}
-#endif /* MAX_HWIFS > 1 */
-	/*
-	 * If we are still without a hwgroup, then form a new one
-	 */
-	if (match) {
-		hwgroup = match->hwgroup;
-	} else {
-		hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
-		memset(hwgroup, 0, sizeof(ide_hwgroup_t));
-		hwgroup->hwif     = hwif->next = hwif;
-		hwgroup->rq       = NULL;
-		hwgroup->handler  = NULL;
-		hwgroup->drive    = NULL;
-		hwgroup->busy     = 0;
-		init_timer(&hwgroup->timer);
-		hwgroup->timer.function = &ide_timer_expiry;
-		hwgroup->timer.data = (unsigned long) hwgroup;
-	}
-
-	/*
-	 * Allocate the irq, if not already obtained for another hwif
-	 */
-	if (!match || match->irq != hwif->irq) {
-#ifdef CONFIG_IDEPCI_SHARE_IRQ
-		int sa = (hwif->chipset == ide_pci) ? SA_SHIRQ : SA_INTERRUPT;
-#else /* !CONFIG_IDEPCI_SHARE_IRQ */
-		int sa = (hwif->chipset == ide_pci) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT;
-#endif /* CONFIG_IDEPCI_SHARE_IRQ */
-		if (ide_request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwgroup)) {
-			if (!match)
-				kfree(hwgroup);
-			restore_flags(flags);	/* all CPUs */
-			return 1;
-		}
-	}
-
-	/*
-	 * Everything is okay, so link us into the hwgroup
-	 */
-	hwif->hwgroup = hwgroup;
-	hwif->next = hwgroup->hwif->next;
-	hwgroup->hwif->next = hwif;
-
-	for (index = 0; index < MAX_DRIVES; ++index) {
-		ide_drive_t *drive = &hwif->drives[index];
-		if (!drive->present)
-			continue;
-		if (!hwgroup->drive)
-			hwgroup->drive = drive;
-		drive->next = hwgroup->drive->next;
-		hwgroup->drive->next = drive;
-	}
-	if (!hwgroup->hwif) {
-		hwgroup->hwif = HWIF(hwgroup->drive);
-#ifdef DEBUG
-		printk("%s : Adding missed hwif to hwgroup!!\n", hwif->name);
-#endif
-	}
-	restore_flags(flags);	/* all CPUs; safe now that hwif->hwgroup is set up */
-
-#if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__)
-	printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
-		hwif->io_ports[IDE_DATA_OFFSET],
-		hwif->io_ports[IDE_DATA_OFFSET]+7,
-		hwif->io_ports[IDE_CONTROL_OFFSET], hwif->irq);
-#elif defined(__sparc__)
-	printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %s", hwif->name,
-		hwif->io_ports[IDE_DATA_OFFSET],
-		hwif->io_ports[IDE_DATA_OFFSET]+7,
-		hwif->io_ports[IDE_CONTROL_OFFSET], __irq_itoa(hwif->irq));
-#else
-	printk("%s at %p on irq 0x%08x", hwif->name,
-		hwif->io_ports[IDE_DATA_OFFSET], hwif->irq);
-#endif /* __mc68000__ && CONFIG_APUS */
-	if (match)
-		printk(" (%sed with %s)",
-			hwif->sharing_irq ? "shar" : "serializ", match->name);
-	printk("\n");
-	return 0;
-}
-
-/*
- * init_gendisk() (as opposed to ide_geninit) is called for each major device,
- * after probing for drives, to allocate partition tables and other data
- * structures needed for the routines in genhd.c.  ide_geninit() gets called
- * somewhat later, during the partition check.
- */
-static void init_gendisk (ide_hwif_t *hwif)
-{
-	struct gendisk *gd, **gdp;
-	unsigned int unit, units, minors;
-	int *bs, *max_sect, *max_ra;
-	extern devfs_handle_t ide_devfs_handle;
-
-	/* figure out maximum drive number on the interface */
-	for (units = MAX_DRIVES; units > 0; --units) {
-		if (hwif->drives[units-1].present)
-			break;
-	}
-	minors    = units * (1<<PARTN_BITS);
-	gd        = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
-	gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
-	gd->part  = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
-	bs        = kmalloc (minors*sizeof(int), GFP_KERNEL);
-	max_sect  = kmalloc (minors*sizeof(int), GFP_KERNEL);
-	max_ra    = kmalloc (minors*sizeof(int), GFP_KERNEL);
-
-	memset(gd->part, 0, minors * sizeof(struct hd_struct));
-
-	/* cdroms and msdos f/s are examples of non-1024 blocksizes */
-	blksize_size[hwif->major] = bs;
-	max_sectors[hwif->major] = max_sect;
-	max_readahead[hwif->major] = max_ra;
-	for (unit = 0; unit < minors; ++unit) {
-		*bs++ = BLOCK_SIZE;
-#ifdef CONFIG_BLK_DEV_PDC4030
-		*max_sect++ = ((hwif->chipset == ide_pdc4030) ? 127 : MAX_SECTORS);
-#else
-		*max_sect++ = MAX_SECTORS;
-#endif
-		*max_ra++ = MAX_READAHEAD;
-	}
-
-	for (unit = 0; unit < units; ++unit)
-		hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
-
-	gd->major	= hwif->major;		/* our major device number */
-	gd->major_name	= IDE_MAJOR_NAME;	/* treated special in genhd.c */
-	gd->minor_shift	= PARTN_BITS;		/* num bits for partitions */
-	gd->max_p	= 1<<PARTN_BITS;	/* 1 + max partitions / drive */
-	gd->nr_real	= units;		/* current num real drives */
-	gd->real_devices= hwif;			/* ptr to internal data */
-	gd->next	= NULL;			/* linked list of major devs */
-	gd->fops        = ide_fops;             /* file operations */
-	gd->de_arr	= kmalloc (sizeof *gd->de_arr * units, GFP_KERNEL);
-	gd->flags	= kmalloc (sizeof *gd->flags * units, GFP_KERNEL);
-	if (gd->de_arr)
-		memset (gd->de_arr, 0, sizeof *gd->de_arr * units);
-	if (gd->flags)
-		memset (gd->flags, 0, sizeof *gd->flags * units);
-
-	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
-	hwif->gd = *gdp = gd;			/* link onto tail of list */
-
-	for (unit = 0; unit < units; ++unit) {
-		if (hwif->drives[unit].present) {
-			char name[64];
-
-			ide_add_generic_settings(hwif->drives + unit);
-			sprintf (name, "host%d/bus%d/target%d/lun%d",
-				 (hwif->channel && hwif->mate) ? hwif->mate->index : hwif->index,
-				 hwif->channel, unit, 0);
-			hwif->drives[unit].de =
-				devfs_mk_dir (ide_devfs_handle, name, 0, NULL);
-		}
-	}
-}
-
-static int hwif_init (ide_hwif_t *hwif)
-{
-	ide_drive_t *drive;
-	void (*rfn)(request_queue_t *);
-	
-	if (!hwif->present)
-		return 0;
-	if (!hwif->irq) {
-		if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET])))
-		{
-			printk("%s: DISABLED, NO IRQ\n", hwif->name);
-			return (hwif->present = 0);
-		}
-	}
-#ifdef CONFIG_BLK_DEV_HD
-	if (hwif->irq == HD_IRQ && hwif->io_ports[IDE_DATA_OFFSET] != HD_DATA) {
-		printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
-		return (hwif->present = 0);
-	}
-#endif /* CONFIG_BLK_DEV_HD */
-	
-	hwif->present = 0; /* we set it back to 1 if all is ok below */
-	switch (hwif->major) {
-	case IDE0_MAJOR: rfn = &do_ide0_request; break;
-#if MAX_HWIFS > 1
-	case IDE1_MAJOR: rfn = &do_ide1_request; break;
-#endif
-#if MAX_HWIFS > 2
-	case IDE2_MAJOR: rfn = &do_ide2_request; break;
-#endif
-#if MAX_HWIFS > 3
-	case IDE3_MAJOR: rfn = &do_ide3_request; break;
-#endif
-#if MAX_HWIFS > 4
-	case IDE4_MAJOR: rfn = &do_ide4_request; break;
-#endif
-#if MAX_HWIFS > 5
-	case IDE5_MAJOR: rfn = &do_ide5_request; break;
-#endif
-#if MAX_HWIFS > 6
-	case IDE6_MAJOR: rfn = &do_ide6_request; break;
-#endif
-#if MAX_HWIFS > 7
-	case IDE7_MAJOR: rfn = &do_ide7_request; break;
-#endif
-#if MAX_HWIFS > 8
-	case IDE8_MAJOR: rfn = &do_ide8_request; break;
-#endif
-#if MAX_HWIFS > 9
-	case IDE9_MAJOR: rfn = &do_ide9_request; break;
-#endif
-	default:
-		printk("%s: request_fn NOT DEFINED\n", hwif->name);
-		return (hwif->present = 0);
-	}
-	if (devfs_register_blkdev (hwif->major, hwif->name, ide_fops)) {
-		printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
-		return (hwif->present = 0);
-	}
-	
-	if (init_irq(hwif)) {
-		int i = hwif->irq;
-		/*
-		 *	It failed to initialise. Find the default IRQ for 
-		 *	this port and try that.
-		 */
-		if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) {
-			printk("%s: Disabled unable to get IRQ %d.\n", hwif->name, i);
-			(void) unregister_blkdev (hwif->major, hwif->name);
-			return (hwif->present = 0);
-		}
-		if (init_irq(hwif)) {
-			printk("%s: probed IRQ %d and default IRQ %d failed.\n",
-				hwif->name, i, hwif->irq);
-			(void) unregister_blkdev (hwif->major, hwif->name);
-			return (hwif->present = 0);
-		}
-		printk("%s: probed IRQ %d failed, using default.\n",
-			hwif->name, hwif->irq);
-	}
-	
-	init_gendisk(hwif);
-	blk_dev[hwif->major].data = hwif;
-	blk_dev[hwif->major].queue = ide_get_queue;
-	read_ahead[hwif->major] = 8;	/* (4kB) */
-	hwif->present = 1;	/* success */
-
-	/*
-	 * FIXME(eric) - This needs to be tested.  I *think* that this
-	 * is correct.   Also, I believe that there is no longer any
-	 * reason to have multiple functions (do_ide[0-7]_request)
-	 * functions - the queuedata field could be used to indicate
-	 * the correct hardware group - either this, or we could add
-	 * a new field to request_queue_t to hold this information.
-	 */
-	drive = &hwif->drives[0];
-	blk_init_queue(&drive->queue, rfn);
-
-	drive = &hwif->drives[1];
-	blk_init_queue(&drive->queue, rfn);
-
-#if (DEBUG_SPINLOCK > 0)
-{
-	static int done = 0;
-	if (!done++)
-		printk("io_request_lock is %p\n", &io_request_lock);    /* FIXME */
-}
-#endif
-	return hwif->present;
-}
-
-int ideprobe_init (void);
-static ide_module_t ideprobe_module = {
-	IDE_PROBE_MODULE,
-	ideprobe_init,
-	NULL
-};
-
-int ideprobe_init (void)
-{
-	unsigned int index;
-	int probe[MAX_HWIFS];
-	
-	MOD_INC_USE_COUNT;
-	memset(probe, 0, MAX_HWIFS * sizeof(int));
-	for (index = 0; index < MAX_HWIFS; ++index)
-		probe[index] = !ide_hwifs[index].present;
-
-	/*
-	 * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
-	 */
-	for (index = 0; index < MAX_HWIFS; ++index)
-		if (probe[index])
-			probe_hwif(&ide_hwifs[index]);
-	for (index = 0; index < MAX_HWIFS; ++index)
-		if (probe[index])
-			hwif_init(&ide_hwifs[index]);
-	if (!ide_probe)
-		ide_probe = &ideprobe_module;
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-#ifdef MODULE
-int init_module (void)
-{
-	unsigned int index;
-	
-	for (index = 0; index < MAX_HWIFS; ++index)
-		ide_unregister(index);
-	ideprobe_init();
-	create_proc_ide_interfaces();
-	return 0;
-}
-
-void cleanup_module (void)
-{
-	ide_probe = NULL;
-}
-#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c
--- v2.3.51/linux/drivers/block/ide-proc.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/ide-proc.c	Wed Dec 31 16:00:00 1969
@@ -1,904 +0,0 @@
-/*
- *  linux/drivers/block/ide-proc.c	Version 1.03	January  2, 1998
- *
- *  Copyright (C) 1997-1998	Mark Lord
- */
-
-/*
- * This is the /proc/ide/ filesystem implementation.
- *
- * The major reason this exists is to provide sufficient access
- * to driver and config data, such that user-mode programs can
- * be developed to handle chipset tuning for most PCI interfaces.
- * This should provide better utilities, and less kernel bloat.
- *
- * The entire pci config space for a PCI interface chipset can be
- * retrieved by just reading it.  e.g.    "cat /proc/ide3/config"
- *
- * To modify registers *safely*, do something like:
- *   echo "P40:88" >/proc/ide/ide3/config
- * That expression writes 0x88 to pci config register 0x40
- * on the chip which controls ide3.  Multiple tuples can be issued,
- * and the writes will be completed as an atomic set:
- *   echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
- *
- * All numbers must be specified using pairs of ascii hex digits.
- * It is important to note that these writes will be performed
- * after waiting for the IDE controller (both interfaces)
- * to be completely idle, to ensure no corruption of I/O in progress.
- *
- * Non-PCI registers can also be written, using "R" in place of "P"
- * in the above examples.  The size of the port transfer is determined
- * by the number of pairs of hex digits given for the data.  If a two
- * digit value is given, the write will be a byte operation; if four
- * digits are used, the write will be performed as a 16-bit operation;
- * and if eight digits are specified, a 32-bit "dword" write will be
- * performed.  Odd numbers of digits are not permitted.
- *
- * If there is an error *anywhere* in the string of registers/data
- * then *none* of the writes will be performed.
- *
- * Drive/Driver settings can be retrieved by reading the drive's
- * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
- * To write a new value "val" into a specific setting "name", use:
- *   echo "name:val" >/proc/ide/ide0/hda/settings
- *
- * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
- * smart_thresholds, capabilities]" will issue an IDENTIFY /
- * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
- * SENSE CAPABILITIES command to /dev/hda, and then dump out the
- * returned data as 256 16-bit words.  The "hdparm" utility will
- * be updated someday soon to use this mechanism.
- *
- * Feel free to develop and distribute fancy GUI configuration
- * utilities for your favorite PCI chipsets.  I'll be working on
- * one for the Promise 20246 someday soon.  -ml
- *
- */
-
-#include <linux/config.h>
-#include <asm/uaccess.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-#include <linux/ctype.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#ifndef MIN
-#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-#endif
-
-#ifdef CONFIG_BLK_DEV_AEC6210
-extern byte aec6210_proc;
-int (*aec6210_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_AEC6210 */
-#ifdef CONFIG_BLK_DEV_ALI15X3
-extern byte ali_proc;
-int (*ali_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_ALI15X3 */
-#ifdef CONFIG_BLK_DEV_AMD7409
-extern byte amd7409_proc;
-int (*amd7409_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_AMD7409 */
-#ifdef CONFIG_BLK_DEV_CMD64X
-extern byte cmd64x_proc;
-int (*cmd64x_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_CMD64X */
-#ifdef CONFIG_BLK_DEV_CS5530
-extern byte cs5530_proc;
-int (*cs5530_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_CS5530 */
-#ifdef CONFIG_BLK_DEV_HPT34X
-extern byte hpt34x_proc;
-int (*hpt34x_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_HPT34X */
-#ifdef CONFIG_BLK_DEV_HPT366
-extern byte hpt366_proc;
-int (*hpt366_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_HPT366 */
-#ifdef CONFIG_BLK_DEV_PDC202XX
-extern byte pdc202xx_proc;
-int (*pdc202xx_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_PDC202XX */
-#ifdef CONFIG_BLK_DEV_PIIX
-extern byte piix_proc;
-int (*piix_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_PIIX */
-#ifdef CONFIG_BLK_DEV_SIS5513
-extern byte sis_proc;
-int (*sis_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_SIS5513 */
-#ifdef CONFIG_BLK_DEV_VIA82CXXX
-extern byte via_proc;
-int (*via_display_info)(char *, char **, off_t, int) = NULL;
-#endif /* CONFIG_BLK_DEV_VIA82CXXX */
-
-static int ide_getxdigit(char c)
-{
-	int digit;
-	if (isdigit(c))
-		digit = c - '0';
-	else if (isxdigit(c))
-		digit = tolower(c) - 'a' + 10;
-	else
-		digit = -1;
-	return digit;
-}
-
-static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
-{
-	char errbuf[16];
-	int i;
-	if (len >= sizeof(errbuf))
-		len = sizeof(errbuf) - 1;
-	for (i = 0; i < len; ++i) {
-		char c = data[i];
-		if (!c || c == '\n')
-			c = '\0';
-		else if (iscntrl(c))
-			c = '?';
-		errbuf[i] = c;
-	}
-	errbuf[i] = '\0';
-	printk("proc_ide: error: %s: '%s'\n", msg, errbuf);
-	return -EINVAL;
-}
-
-static struct proc_dir_entry * proc_ide_root = NULL;
-
-static int proc_ide_write_config
-	(struct file *file, const char *buffer, unsigned long count, void *data)
-{
-	ide_hwif_t	*hwif = (ide_hwif_t *)data;
-	int		for_real = 0;
-	unsigned long	startn = 0, n, flags;
-	const char	*start = NULL, *msg = NULL;
-
-	if (!capable(CAP_SYS_ADMIN))
-		return -EACCES;
-	/*
-	 * Skip over leading whitespace
-	 */
-	while (count && isspace(*buffer)) {
-		--count;
-		++buffer;
-	}
-	/*
-	 * Do one full pass to verify all parameters,
-	 * then do another to actually write the regs.
-	 */
-	save_flags(flags);	/* all CPUs */
-	do {
-		const char *p;
-		if (for_real) {
-			unsigned long timeout = jiffies + (3 * HZ);
-			ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
-			ide_hwgroup_t *mategroup = NULL;
-			if (hwif->mate && hwif->mate->hwgroup)
-				mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
-			cli();	/* all CPUs; ensure all writes are done together */
-			while (mygroup->busy || (mategroup && mategroup->busy)) {
-				sti();	/* all CPUs */
-				if (0 < (signed long)(jiffies - timeout)) {
-					printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name);
-					restore_flags(flags);	/* all CPUs */
-					return -EBUSY;
-				}
-				cli();	/* all CPUs */
-			}
-		}
-		p = buffer;
-		n = count;
-		while (n > 0) {
-			int d, digits;
-			unsigned int reg = 0, val = 0, is_pci;
-			start = p;
-			startn = n--;
-			switch (*p++) {
-				case 'R':	is_pci = 0;
-						break;
-				case 'P':	is_pci = 1;
-#ifdef CONFIG_BLK_DEV_IDEPCI
-						if (hwif->pci_dev && !IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
-							break;
-#endif	/* CONFIG_BLK_DEV_IDEPCI */
-						msg = "not a PCI device";
-						goto parse_error;
-				default:	msg = "expected 'R' or 'P'";
-						goto parse_error;
-			}
-			digits = 0;
-			while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
-				reg = (reg << 4) | d;
-				--n;
-				++p;
-				++digits;
-			}
-			if (!digits || (digits > 4) || (is_pci && reg > 0xff)) {
-				msg = "bad/missing register number";
-				goto parse_error;
-			}
-			if (n-- == 0 || *p++ != ':') {
-				msg = "missing ':'";
-				goto parse_error;
-			}
-			digits = 0;
-			while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
-				val = (val << 4) | d;
-				--n;
-				++p;
-				++digits;
-			}
-			if (digits != 2 && digits != 4 && digits != 8) {
-				msg = "bad data, 2/4/8 digits required";
-				goto parse_error;
-			}
-			if (n > 0 && !isspace(*p)) {
-				msg = "expected whitespace after data";
-				goto parse_error;
-			}
-			while (n > 0 && isspace(*p)) {
-				--n;
-				++p;
-			}
-#ifdef CONFIG_BLK_DEV_IDEPCI
-			if (is_pci && (reg & ((digits >> 1) - 1))) {
-				msg = "misaligned access";
-				goto parse_error;
-			}
-#endif	/* CONFIG_BLK_DEV_IDEPCI */
-			if (for_real) {
-#if 0
-				printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? "PCI" : "non-PCI", reg, val, digits);
-#endif
-				if (is_pci) {
-#ifdef CONFIG_BLK_DEV_IDEPCI
-					int rc = 0;
-					struct pci_dev *dev = hwif->pci_dev;
-					switch (digits) {
-						case 2:	msg = "byte";
-							rc = pci_write_config_byte(dev, reg, val);
-							break;
-						case 4:	msg = "word";
-							rc = pci_write_config_word(dev, reg, val);
-							break;
-						case 8:	msg = "dword";
-							rc = pci_write_config_dword(dev, reg, val);
-							break;
-					}
-					if (rc) {
-						restore_flags(flags);	/* all CPUs */
-						printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n",
-							msg, dev->bus->number, dev->devfn, reg, val);
-						printk("proc_ide_write_config: error %d\n", rc);
-						return -EIO;
-					}
-#endif	/* CONFIG_BLK_DEV_IDEPCI */
-				} else {	/* not pci */
-#if !defined(__mc68000__) && !defined(CONFIG_APUS)
-
-/*
- * Geert Uytterhoeven
- *
- * unless you can explain me what it really does.
- * On m68k, we don't have outw() and outl() yet,
- * and I need a good reason to implement it.
- * 
- * BTW, IMHO the main remaining portability problem with the IDE driver 
- * is that it mixes IO (ioport) and MMIO (iomem) access on different platforms.
- * 
- * I think all accesses should be done using
- * 
- *     ide_in[bwl](ide_device_instance, offset)
- *     ide_out[bwl](ide_device_instance, value, offset)
- * 
- * so the architecture specific code can #define ide_{in,out}[bwl] to the
- * appropriate function.
- * 
- */
-					switch (digits) {
-						case 2:	outb(val, reg);
-							break;
-						case 4:	outw(val, reg);
-							break;
-						case 8:	outl(val, reg);
-							break;
-					}
-#endif /* !__mc68000__ && !CONFIG_APUS */
-				}
-			}
-		}
-	} while (!for_real++);
-	restore_flags(flags);	/* all CPUs */
-	return count;
-parse_error:
-	restore_flags(flags);	/* all CPUs */
-	printk("parse error\n");
-	return xx_xx_parse_error(start, startn, msg);
-}
-
-static int proc_ide_read_config
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	char		*out = page;
-	int		len;
-
-#ifdef CONFIG_BLK_DEV_IDEPCI
-	ide_hwif_t	*hwif = (ide_hwif_t *)data;
-	struct pci_dev	*dev = hwif->pci_dev;
-	if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL) && dev && dev->bus) {
-		int reg = 0;
-
-		out += sprintf(out, "pci bus %02x device %02x vid %04x did %04x channel %d\n",
-			dev->bus->number, dev->devfn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
-		do {
-			byte val;
-			int rc = pci_read_config_byte(dev, reg, &val);
-			if (rc) {
-				printk("proc_ide_read_config: error %d reading bus %02x dev %02x reg 0x%02x\n",
-					rc, dev->bus->number, dev->devfn, reg);
-				out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
-			} else
-				out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n');
-		} while (reg < 0x100);
-	} else
-#endif	/* CONFIG_BLK_DEV_IDEPCI */
-		out += sprintf(out, "(none)\n");
-	len = out - page;
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-
-static int ide_getdigit(char c)
-{
-	int digit;
-	if (isdigit(c))
-		digit = c - '0';
-	else
-		digit = -1;
-	return digit;
-}
-
-static int proc_ide_read_drivers
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	char		*out = page;
-	int		len;
-	ide_module_t	*p = ide_modules;
-	ide_driver_t	*driver;
-
-	while (p) {
-		driver = (ide_driver_t *) p->info;
-		if (driver)
-			out += sprintf(out, "%s version %s\n", driver->name, driver->version);
-		p = p->next;
-	}
-	len = out - page;
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_ide_read_imodel
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_hwif_t	*hwif = (ide_hwif_t *) data;
-	int		len;
-	const char	*name;
-
-	switch (hwif->chipset) {
-		case ide_unknown:	name = "(none)";	break;
-		case ide_generic:	name = "generic";	break;
-		case ide_pci:		name = "pci";		break;
-		case ide_cmd640:	name = "cmd640";	break;
-		case ide_dtc2278:	name = "dtc2278";	break;
-		case ide_ali14xx:	name = "ali14xx";	break;
-		case ide_qd6580:	name = "qd6580";	break;
-		case ide_umc8672:	name = "umc8672";	break;
-		case ide_ht6560b:	name = "ht6560b";	break;
-		case ide_pdc4030:	name = "pdc4030";	break;
-		case ide_rz1000:	name = "rz1000";	break;
-		case ide_trm290:	name = "trm290";	break;
-		case ide_cmd646:	name = "cmd646";	break;
-		case ide_cy82c693:	name = "cy82c693";	break;
-		case ide_4drives:	name = "4drives";	break;
-		case ide_pmac:		name = "mac-io";	break;
-		default:		name = "(unknown)";	break;
-	}
-	len = sprintf(page, "%s\n", name);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_ide_read_mate
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_hwif_t	*hwif = (ide_hwif_t *) data;
-	int		len;
-
-	if (hwif && hwif->mate && hwif->mate->present)
-		len = sprintf(page, "%s\n", hwif->mate->name);
-	else
-		len = sprintf(page, "(none)\n");
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_ide_read_channel
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_hwif_t	*hwif = (ide_hwif_t *) data;
-	int		len;
-
-	page[0] = hwif->channel ? '1' : '0';
-	page[1] = '\n';
-	len = 2;
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_ide_get_identify(ide_drive_t *drive, byte *buf)
-{
-	return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf);
-}
-
-static int proc_ide_read_identify
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *)data;
-	int		len = 0, i = 0;
-
-	if (drive && !proc_ide_get_identify(drive, page)) {
-		unsigned short *val = ((unsigned short *)page) + 2;
-		char *out = ((char *)val) + (SECTOR_WORDS * 4);
-		page = out;
-		do {
-			out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
-			val += 1;
-		} while (i < (SECTOR_WORDS * 2));
-		len = out - page;
-	}
-	else
-		len = sprintf(page, "\n");
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_ide_read_settings
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	ide_settings_t	*setting = (ide_settings_t *) drive->settings;
-	char		*out = page;
-	int		len, rc, mul_factor, div_factor;
-
-	out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
-	out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
-	while(setting) {
-		mul_factor = setting->mul_factor;
-		div_factor = setting->div_factor;
-		out += sprintf(out, "%-24s", setting->name);
-		if ((rc = ide_read_setting(drive, setting)) >= 0)
-			out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
-		else
-			out += sprintf(out, "%-16s", "write-only");
-		out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
-		if (setting->rw & SETTING_READ)
-			out += sprintf(out, "r");
-		if (setting->rw & SETTING_WRITE)
-			out += sprintf(out, "w");
-		out += sprintf(out, "\n");
-		setting = setting->next;
-	}
-	len = out - page;
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-#define MAX_LEN	30
-
-static int proc_ide_write_settings
-	(struct file *file, const char *buffer, unsigned long count, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	char		name[MAX_LEN + 1];
-	int		for_real = 0, len;
-	unsigned long	n;
-	const char	*start = NULL;
-	ide_settings_t	*setting;
-
-	if (!capable(CAP_SYS_ADMIN))
-		return -EACCES;
-	/*
-	 * Skip over leading whitespace
-	 */
-	while (count && isspace(*buffer)) {
-		--count;
-		++buffer;
-	}
-	/*
-	 * Do one full pass to verify all parameters,
-	 * then do another to actually write the new settings.
-	 */
-	do {
-		const char *p;
-		p = buffer;
-		n = count;
-		while (n > 0) {
-			int d, digits;
-			unsigned int val = 0;
-			start = p;
-
-			while (n > 0 && *p != ':') {
-				--n;
-				p++;
-			}
-			if (*p != ':')
-				goto parse_error;
-			len = IDE_MIN(p - start, MAX_LEN);
-			strncpy(name, start, IDE_MIN(len, MAX_LEN));
-			name[len] = 0;
-
-			if (n > 0) {
-				--n;
-				p++;
-			} else
-				goto parse_error;
-			
-			digits = 0;
-			while (n > 0 && (d = ide_getdigit(*p)) >= 0) {
-				val = (val * 10) + d;
-				--n;
-				++p;
-				++digits;
-			}
-			if (n > 0 && !isspace(*p))
-				goto parse_error;
-			while (n > 0 && isspace(*p)) {
-				--n;
-				++p;
-			}
-			setting = ide_find_setting_by_name(drive, name);
-			if (!setting)
-				goto parse_error;
-
-			if (for_real)
-				ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
-		}
-	} while (!for_real++);
-	return count;
-parse_error:
-	printk("proc_ide_write_settings(): parse error\n");
-	return -EINVAL;
-}
-
-int proc_ide_read_capacity
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	ide_driver_t    *driver = (ide_driver_t *) drive->driver;
-	int		len;
-
-	if (!driver)
-		len = sprintf(page, "(none)\n");
-        else
-		len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-int proc_ide_read_geometry
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	char		*out = page;
-	int		len;
-
-	out += sprintf(out,"physical     %d/%d/%d\n", drive->cyl, drive->head, drive->sect);
-	out += sprintf(out,"logical      %d/%d/%d\n", drive->bios_cyl, drive->bios_head, drive->bios_sect);
-	len = out - page;
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_ide_read_dmodel
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	struct hd_driveid *id = drive->id;
-	int		len;
-
-	len = sprintf(page, "%.40s\n", (id && id->model[0]) ? (char *)id->model : "(none)");
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_ide_read_driver
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	ide_driver_t	*driver = (ide_driver_t *) drive->driver;
-	int		len;
-
-	if (!driver)
-		len = sprintf(page, "(none)\n");
-	else
-		len = sprintf(page, "%s version %s\n", driver->name, driver->version);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static int proc_ide_write_driver
-	(struct file *file, const char *buffer, unsigned long count, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-
-	if (!capable(CAP_SYS_ADMIN))
-		return -EACCES;
-	if (ide_replace_subdriver(drive, buffer))
-		return -EINVAL;
-	return count;
-}
-
-static int proc_ide_read_media
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	const char	*media;
-	int		len;
-
-	switch (drive->media) {
-		case ide_disk:	media = "disk\n";
-				break;
-		case ide_cdrom:	media = "cdrom\n";
-				break;
-		case ide_tape:	media = "tape\n";
-				break;
-		case ide_floppy:media = "floppy\n";
-				break;
-		default:	media = "UNKNOWN\n";
-				break;
-	}
-	strcpy(page,media);
-	len = strlen(media);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static ide_proc_entry_t generic_drive_entries[] = {
-	{ "driver",	S_IFREG|S_IRUGO,	proc_ide_read_driver,	proc_ide_write_driver },
-	{ "identify",	S_IFREG|S_IRUSR,	proc_ide_read_identify,	NULL },
-	{ "media",	S_IFREG|S_IRUGO,	proc_ide_read_media,	NULL },
-	{ "model",	S_IFREG|S_IRUGO,	proc_ide_read_dmodel,	NULL },
-	{ "settings",	S_IFREG|S_IRUSR|S_IWUSR,proc_ide_read_settings,	proc_ide_write_settings },
-	{ NULL,	0, NULL, NULL }
-};
-
-void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
-{
-	struct proc_dir_entry *ent;
-
-	if (!dir || !p)
-		return;
-	while (p->name != NULL) {
-		ent = create_proc_entry(p->name, p->mode, dir);
-		if (!ent) return;
-		ent->nlink = 1;
-		ent->data = data;
-		ent->read_proc = p->read_proc;
-		ent->write_proc = p->write_proc;
-		p++;
-	}
-}
-
-void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
-{
-	if (!dir || !p)
-		return;
-	while (p->name != NULL) {
-		remove_proc_entry(p->name, dir);
-		p++;
-	}
-}
-
-static void create_proc_ide_drives(ide_hwif_t *hwif)
-{
-	int	d;
-	struct proc_dir_entry *ent;
-	struct proc_dir_entry *parent = hwif->proc;
-	char name[64];
-
-	for (d = 0; d < MAX_DRIVES; d++) {
-		ide_drive_t *drive = &hwif->drives[d];
-		ide_driver_t *driver = drive->driver;
-
-		if (!drive->present)
-			continue;
-		if (drive->proc)
-			continue;
-
-		drive->proc = proc_mkdir(drive->name, parent);
-		if (drive->proc) {
-			ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
-			if (driver) {
-				ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
-				ide_add_proc_entries(drive->proc, driver->proc, drive);
-			}
-		}
-		sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name);
-		ent = proc_symlink(drive->name, proc_ide_root, name);
-		if (!ent) return;
-	}
-}
-
-void destroy_proc_ide_drives(ide_hwif_t *hwif)
-{
-	int	d;
-
-	for (d = 0; d < MAX_DRIVES; d++) {
-		ide_drive_t *drive = &hwif->drives[d];
-		ide_driver_t *driver = drive->driver;
-
-		if (!drive->proc)
-			continue;
-		if (driver)
-			ide_remove_proc_entries(drive->proc, driver->proc);
-		ide_remove_proc_entries(drive->proc, generic_drive_entries);
-		remove_proc_entry(drive->name, proc_ide_root);
-		remove_proc_entry(drive->name, hwif->proc);
-		drive->proc = NULL;
-	}
-}
-
-static ide_proc_entry_t hwif_entries[] = {
-	{ "channel",	S_IFREG|S_IRUGO,	proc_ide_read_channel,	NULL },
-	{ "config",	S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config,	proc_ide_write_config },
-	{ "mate",	S_IFREG|S_IRUGO,	proc_ide_read_mate,	NULL },
-	{ "model",	S_IFREG|S_IRUGO,	proc_ide_read_imodel,	NULL },
-	{ NULL,	0, NULL, NULL }
-};
-
-void create_proc_ide_interfaces(void)
-{
-	int	h;
-
-	for (h = 0; h < MAX_HWIFS; h++) {
-		ide_hwif_t *hwif = &ide_hwifs[h];
-
-		if (!hwif->present)
-			continue;
-		if (!hwif->proc) {
-			hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
-			if (!hwif->proc)
-				return;
-			ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
-		}
-		create_proc_ide_drives(hwif);
-	}
-}
-
-static void destroy_proc_ide_interfaces(void)
-{
-	int	h;
-
-	for (h = 0; h < MAX_HWIFS; h++) {
-		ide_hwif_t *hwif = &ide_hwifs[h];
-		int exist = (hwif->proc != NULL);
-#if 0
-		if (!hwif->present)
-			continue;
-#endif
-		if (exist) {
-			destroy_proc_ide_drives(hwif);
-			ide_remove_proc_entries(hwif->proc, hwif_entries);
-			remove_proc_entry(hwif->name, proc_ide_root);
-			hwif->proc = NULL;
-		} else
-			continue;
-	}
-}
-
-void proc_ide_create(void)
-{
-	proc_ide_root = proc_mkdir("ide", 0);
-	if (!proc_ide_root) return;
-
-	create_proc_ide_interfaces();
-
-	create_proc_read_entry("drivers", 0, proc_ide_root,
-				proc_ide_read_drivers, NULL);
-
-#ifdef CONFIG_BLK_DEV_AEC6210
-	if ((aec6210_display_info) && (aec6210_proc))
-		create_proc_info_entry("aec6210", 0, proc_ide_root, aec6210_display_info);
-#endif /* CONFIG_BLK_DEV_AEC6210 */
-#ifdef CONFIG_BLK_DEV_ALI15X3
-	if ((ali_display_info) && (ali_proc))
-		create_proc_info_entry("ali", 0, proc_ide_root, ali_display_info);
-#endif /* CONFIG_BLK_DEV_ALI15X3 */
-#ifdef CONFIG_BLK_DEV_AMD7409
-	if ((amd7409_display_info) && (amd7409_proc))
-		create_proc_info_entry("amd7409", 0, proc_ide_root, amd7409_display_info);
-#endif /* CONFIG_BLK_DEV_AMD7409 */
-#ifdef CONFIG_BLK_DEV_CMD64X
-	if ((cmd64x_display_info) && (cmd64x_proc))
-		create_proc_info_entry("cmd64x", 0, proc_ide_root, cmd64x_display_info);
-#endif /* CONFIG_BLK_DEV_CMD64X */
-#ifdef CONFIG_BLK_DEV_CS5530
-	if ((cs5530_display_info) && (cs5530_proc))
-		create_proc_info_entry("cs5530", 0, proc_ide_root, cs5530_display_info);
-#endif /* CONFIG_BLK_DEV_CS5530 */
-#ifdef CONFIG_BLK_DEV_HPT34X
-	if ((hpt34x_display_info) && (hpt34x_proc))
-		create_proc_info_entry("hpt34x", 0, proc_ide_root, hpt34x_display_info);
-#endif /* CONFIG_BLK_DEV_HPT34X */
-#ifdef CONFIG_BLK_DEV_HPT366
-	if ((hpt366_display_info) && (hpt366_proc))
-		create_proc_info_entry("hpt366", 0, proc_ide_root, hpt366_display_info);
-#endif /* CONFIG_BLK_DEV_HPT366 */
-#ifdef CONFIG_BLK_DEV_PDC202XX
-	if ((pdc202xx_display_info) && (pdc202xx_proc))
-		create_proc_info_entry("pdc202xx", 0, proc_ide_root, pdc202xx_display_info);
-#endif /* CONFIG_BLK_DEV_PDC202XX */
-#ifdef CONFIG_BLK_DEV_PIIX
-	if ((piix_display_info) && (piix_proc))
-		create_proc_info_entry("piix", 0, proc_ide_root, piix_display_info);
-#endif /* CONFIG_BLK_DEV_PIIX */
-#ifdef CONFIG_BLK_DEV_SIS5513
-	if ((sis_display_info) && (sis_proc))
-		create_proc_info_entry("sis", 0, proc_ide_root, sis_display_info);
-#endif /* CONFIG_BLK_DEV_SIS5513 */
-#ifdef CONFIG_BLK_DEV_VIA82CXXX
-	if ((via_display_info) && (via_proc))
-		create_proc_info_entry("via", 0, proc_ide_root, via_display_info);
-#endif /* CONFIG_BLK_DEV_VIA82CXXX */
-}
-
-void proc_ide_destroy(void)
-{
-	/*
-	 * Mmmm.. does this free up all resources,
-	 * or do we need to do a more proper cleanup here ??
-	 */
-#ifdef CONFIG_BLK_DEV_AEC6210
-	if ((aec6210_display_info) && (aec6210_proc))
-		remove_proc_entry("ide/aec6210",0);
-#endif /* CONFIG_BLK_DEV_AEC6210 */
-#ifdef CONFIG_BLK_DEV_ALI15X3
-	if ((ali_display_info) && (ali_proc))
-		remove_proc_entry("ide/ali",0);
-#endif /* CONFIG_BLK_DEV_ALI15X3 */
-#ifdef CONFIG_BLK_DEV_AMD7409
-	if ((amd7409_display_info) && (amd7409_proc))
-		remove_proc_entry("ide/amd7409",0);
-#endif /* CONFIG_BLK_DEV_AMD7409 */
-#ifdef CONFIG_BLK_DEV_CMD64X
-	if ((cmd64x_display_info) && (cmd64x_proc))
-		remove_proc_entry("ide/cmd64x",0);
-#endif /* CONFIG_BLK_DEV_CMD64X */
-#ifdef CONFIG_BLK_DEV_CS5530
-	if ((cs5530_display_info) && (cs5530_proc))
-		remove_proc_entry("ide/cs5530",0);
-#endif /* CONFIG_BLK_DEV_CS5530 */
-#ifdef CONFIG_BLK_DEV_HPT34X
-	if ((hpt34x_display_info) && (hpt34x_proc))
-		remove_proc_entry("ide/hpt34x",0);
-#endif /* CONFIG_BLK_DEV_HPT34X */
-#ifdef CONFIG_BLK_DEV_HPT366
-	if ((hpt366_display_info) && (hpt366_proc))
-		remove_proc_entry("ide/hpt366",0);
-#endif /* CONFIG_BLK_DEV_HPT366 */
-#ifdef CONFIG_BLK_DEV_PDC202XX
-	if ((pdc202xx_display_info) && (pdc202xx_proc))
-		remove_proc_entry("ide/pdc202xx",0);
-#endif /* CONFIG_BLK_DEV_PDC202XX */
-#ifdef CONFIG_BLK_DEV_PIIX
-	if ((piix_display_info) && (piix_proc))
-		remove_proc_entry("ide/piix",0);
-#endif /* CONFIG_BLK_DEV_PIIX */
-#ifdef CONFIG_BLK_DEV_SIS5513
-	if ((sis_display_info) && (sis_proc))
-		remove_proc_entry("ide/sis", 0);
-#endif /* CONFIG_BLK_DEV_SIS5513 */
-#ifdef CONFIG_BLK_DEV_VIA82CXXX
-	if ((via_display_info) && (via_proc))
-		remove_proc_entry("ide/via",0);
-#endif /* CONFIG_BLK_DEV_VIA82CXXX */
-
-	remove_proc_entry("ide/drivers", 0);
-	destroy_proc_ide_interfaces();
-	remove_proc_entry("ide", 0);
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.3.51/linux/drivers/block/ide-tape.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/ide-tape.c	Wed Dec 31 16:00:00 1969
@@ -1,6031 +0,0 @@
-/*
- * linux/drivers/block/ide-tape.c		Version 1.16f	Dec  15, 1999
- *
- * Copyright (C) 1995 - 1999 Gadi Oxman <gadio@netvision.net.il>
- *
- * This driver was constructed as a student project in the software laboratory
- * of the faculty of electrical engineering in the Technion - Israel's
- * Institute Of Technology, with the guide of Avner Lottem and Dr. Ilana David.
- *
- * It is hereby placed under the terms of the GNU general public license.
- * (See linux/COPYING).
- */
- 
-/*
- * IDE ATAPI streaming tape driver.
- *
- * This driver is a part of the Linux ide driver and works in co-operation
- * with linux/drivers/block/ide.c.
- *
- * The driver, in co-operation with ide.c, basically traverses the 
- * request-list for the block device interface. The character device
- * interface, on the other hand, creates new requests, adds them
- * to the request-list of the block device, and waits for their completion.
- *
- * Pipelined operation mode is now supported on both reads and writes.
- *
- * The block device major and minor numbers are determined from the
- * tape's relative position in the ide interfaces, as explained in ide.c.
- *
- * The character device interface consists of the following devices:
- *
- * ht0		major 37, minor 0	first  IDE tape, rewind on close.
- * ht1		major 37, minor 1	second IDE tape, rewind on close.
- * ...
- * nht0		major 37, minor 128	first  IDE tape, no rewind on close.
- * nht1		major 37, minor 129	second IDE tape, no rewind on close.
- * ...
- *
- * Run linux/scripts/MAKEDEV.ide to create the above entries.
- *
- * The general magnetic tape commands compatible interface, as defined by
- * include/linux/mtio.h, is accessible through the character device.
- *
- * General ide driver configuration options, such as the interrupt-unmask
- * flag, can be configured by issuing an ioctl to the block device interface,
- * as any other ide device.
- *
- * Our own ide-tape ioctl's can be issued to either the block device or
- * the character device interface.
- *
- * Maximal throughput with minimal bus load will usually be achieved in the
- * following scenario:
- *
- *	1.	ide-tape is operating in the pipelined operation mode.
- *	2.	No buffering is performed by the user backup program.
- *
- * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
- * 
- * Ver 0.1   Nov  1 95   Pre-working code :-)
- * Ver 0.2   Nov 23 95   A short backup (few megabytes) and restore procedure
- *                        was successful ! (Using tar cvf ... on the block
- *                        device interface).
- *                       A longer backup resulted in major swapping, bad
- *                        overall Linux performance and eventually failed as
- *                        we received non serial read-ahead requests from the
- *                        buffer cache.
- * Ver 0.3   Nov 28 95   Long backups are now possible, thanks to the
- *                        character device interface. Linux's responsiveness
- *                        and performance doesn't seem to be much affected
- *                        from the background backup procedure.
- *                       Some general mtio.h magnetic tape operations are
- *                        now supported by our character device. As a result,
- *                        popular tape utilities are starting to work with
- *                        ide tapes :-)
- *                       The following configurations were tested:
- *                       	1. An IDE ATAPI TAPE shares the same interface
- *                       	   and irq with an IDE ATAPI CDROM.
- *                        	2. An IDE ATAPI TAPE shares the same interface
- *                          	   and irq with a normal IDE disk.
- *                        Both configurations seemed to work just fine !
- *                        However, to be on the safe side, it is meanwhile
- *                        recommended to give the IDE TAPE its own interface
- *                        and irq.
- *                       The one thing which needs to be done here is to
- *                        add a "request postpone" feature to ide.c,
- *                        so that we won't have to wait for the tape to finish
- *                        performing a long media access (DSC) request (such
- *                        as a rewind) before we can access the other device
- *                        on the same interface. This effect doesn't disturb
- *                        normal operation most of the time because read/write
- *                        requests are relatively fast, and once we are
- *                        performing one tape r/w request, a lot of requests
- *                        from the other device can be queued and ide.c will
- *			  service all of them after this single tape request.
- * Ver 1.0   Dec 11 95   Integrated into Linux 1.3.46 development tree.
- *                       On each read / write request, we now ask the drive
- *                        if we can transfer a constant number of bytes
- *                        (a parameter of the drive) only to its buffers,
- *                        without causing actual media access. If we can't,
- *                        we just wait until we can by polling the DSC bit.
- *                        This ensures that while we are not transferring
- *                        more bytes than the constant referred to above, the
- *                        interrupt latency will not become too high and
- *                        we won't cause an interrupt timeout, as happened
- *                        occasionally in the previous version.
- *                       While polling for DSC, the current request is
- *                        postponed and ide.c is free to handle requests from
- *                        the other device. This is handled transparently to
- *                        ide.c. The hwgroup locking method which was used
- *                        in the previous version was removed.
- *                       Use of new general features which are provided by
- *                        ide.c for use with atapi devices.
- *                        (Programming done by Mark Lord)
- *                       Few potential bug fixes (Again, suggested by Mark)
- *                       Single character device data transfers are now
- *                        not limited in size, as they were before.
- *                       We are asking the tape about its recommended
- *                        transfer unit and send a larger data transfer
- *                        as several transfers of the above size.
- *                        For best results, use an integral number of this
- *                        basic unit (which is shown during driver
- *                        initialization). I will soon add an ioctl to get
- *                        this important parameter.
- *                       Our data transfer buffer is allocated on startup,
- *                        rather than before each data transfer. This should
- *                        ensure that we will indeed have a data buffer.
- * Ver 1.1   Dec 14 95   Fixed random problems which occurred when the tape
- *                        shared an interface with another device.
- *                        (poll_for_dsc was a complete mess).
- *                       Removed some old (non-active) code which had
- *                        to do with supporting buffer cache originated
- *                        requests.
- *                       The block device interface can now be opened, so
- *                        that general ide driver features like the unmask
- *                        interrupts flag can be selected with an ioctl.
- *                        This is the only use of the block device interface.
- *                       New fast pipelined operation mode (currently only on
- *                        writes). When using the pipelined mode, the
- *                        throughput can potentially reach the maximum
- *                        tape supported throughput, regardless of the
- *                        user backup program. On my tape drive, it sometimes
- *                        boosted performance by a factor of 2. Pipelined
- *                        mode is enabled by default, but since it has a few
- *                        downfalls as well, you may want to disable it.
- *                        A short explanation of the pipelined operation mode
- *                        is available below.
- * Ver 1.2   Jan  1 96   Eliminated pipelined mode race condition.
- *                       Added pipeline read mode. As a result, restores
- *                        are now as fast as backups.
- *                       Optimized shared interface behavior. The new behavior
- *                        typically results in better IDE bus efficiency and
- *                        higher tape throughput.
- *                       Pre-calculation of the expected read/write request
- *                        service time, based on the tape's parameters. In
- *                        the pipelined operation mode, this allows us to
- *                        adjust our polling frequency to a much lower value,
- *                        and thus to dramatically reduce our load on Linux,
- *                        without any decrease in performance.
- *                       Implemented additional mtio.h operations.
- *                       The recommended user block size is returned by
- *                        the MTIOCGET ioctl.
- *                       Additional minor changes.
- * Ver 1.3   Feb  9 96   Fixed pipelined read mode bug which prevented the
- *                        use of some block sizes during a restore procedure.
- *                       The character device interface will now present a
- *                        continuous view of the media - any mix of block sizes
- *                        during a backup/restore procedure is supported. The
- *                        driver will buffer the requests internally and
- *                        convert them to the tape's recommended transfer
- *                        unit, making performance almost independent of the
- *                        chosen user block size.
- *                       Some improvements in error recovery.
- *                       By cooperating with ide-dma.c, bus mastering DMA can
- *                        now sometimes be used with IDE tape drives as well.
- *                        Bus mastering DMA has the potential to dramatically
- *                        reduce the CPU's overhead when accessing the device,
- *                        and can be enabled by using hdparm -d1 on the tape's
- *                        block device interface. For more info, read the
- *                        comments in ide-dma.c.
- * Ver 1.4   Mar 13 96   Fixed serialize support.
- * Ver 1.5   Apr 12 96   Fixed shared interface operation, broken in 1.3.85.
- *                       Fixed pipelined read mode inefficiency.
- *                       Fixed nasty null dereferencing bug.
- * Ver 1.6   Aug 16 96   Fixed FPU usage in the driver.
- *                       Fixed end of media bug.
- * Ver 1.7   Sep 10 96   Minor changes for the CONNER CTT8000-A model.
- * Ver 1.8   Sep 26 96   Attempt to find a better balance between good
- *                        interactive response and high system throughput.
- * Ver 1.9   Nov  5 96   Automatically cross encountered filemarks rather
- *                        than requiring an explicit FSF command.
- *                       Abort pending requests at end of media.
- *                       MTTELL was sometimes returning incorrect results.
- *                       Return the real block size in the MTIOCGET ioctl.
- *                       Some error recovery bug fixes.
- * Ver 1.10  Nov  5 96   Major reorganization.
- *                       Reduced CPU overhead a bit by eliminating internal
- *                        bounce buffers.
- *                       Added module support.
- *                       Added multiple tape drives support.
- *                       Added partition support.
- *                       Rewrote DSC handling.
- *                       Some portability fixes.
- *                       Removed ide-tape.h.
- *                       Additional minor changes.
- * Ver 1.11  Dec  2 96   Bug fix in previous DSC timeout handling.
- *                       Use ide_stall_queue() for DSC overlap.
- *                       Use the maximum speed rather than the current speed
- *                        to compute the request service time.
- * Ver 1.12  Dec  7 97   Fix random memory overwriting and/or last block data
- *                        corruption, which could occur if the total number
- *                        of bytes written to the tape was not an integral
- *                        number of tape blocks.
- *                       Add support for INTERRUPT DRQ devices.
- * Ver 1.13  Jan  2 98   Add "speed == 0" work-around for HP COLORADO 5GB
- * Ver 1.14  Dec 30 98   Partial fixes for the Sony/AIWA tape drives.
- *                       Replace cli()/sti() with hwgroup spinlocks.
- * Ver 1.15  Mar 25 99   Fix SMP race condition by replacing hwgroup
- *                        spinlock with private per-tape spinlock.
- * Ver 1.16  Sep  1 99   Add OnStream tape support.
- *                       Abort read pipeline on EOD.
- *                       Wait for the tape to become ready in case it returns
- *                        "in the process of becoming ready" on open().
- *                       Fix zero padding of the last written block in
- *                        case the tape block size is larger than PAGE_SIZE.
- *                       Decrease the default disconnection time to tn.
- * Ver 1.16e Oct  3 99   Minor fixes.
- * Ver 1.16e1 Oct 13 99  Patches by Arnold Niessen,
- *                          niessen@iae.nl / arnold.niessen@philips.com
- *                   GO-1)  Undefined code in idetape_read_position
- *				according to Gadi's email
- *                   AJN-1) Minor fix asc == 11 should be asc == 0x11
- *                               in idetape_issue_packet_command (did effect
- *                               debugging output only)
- *                   AJN-2) Added more debugging output, and
- *                              added ide-tape: where missing. I would also
- *				like to add tape->name where possible
- *                   AJN-3) Added different debug_level's 
- *                              via /proc/ide/hdc/settings
- * 				"debug_level" determines amount of debugging output;
- * 				can be changed using /proc/ide/hdx/settings
- * 				0 : almost no debugging output
- * 				1 : 0+output errors only
- * 				2 : 1+output all sensekey/asc
- * 				3 : 2+follow all chrdev related procedures
- * 				4 : 3+follow all procedures
- * 				5 : 4+include pc_stack rq_stack info
- * 				6 : 5+USE_COUNT updates
- *                   AJN-4) Fixed timeout for retension in idetape_queue_pc_tail
- *				from 5 to 10 minutes
- *                   AJN-5) Changed maximum number of blocks to skip when
- *                              reading tapes with multiple consecutive write
- *                              errors from 100 to 1000 in idetape_get_logical_blk
- *                   Proposed changes to code:
- *                   1) output "logical_blk_num" via /proc
- *                   2) output "current_operation" via /proc
- *                   3) Either solve or document the fact that `mt rewind' is
- *                      required after reading from /dev/nhtx to be
- *			able to rmmod the idetape module;
- *			Also, sometimes an application finishes but the
- *			device remains `busy' for some time. Same cause ?
- *                   Proposed changes to release-notes:
- *		     4) write a simple `quickstart' section in the
- *                      release notes; I volunteer if you don't want to
- * 		     5) include a pointer to video4linux in the doc
- *                      to stimulate video applications
- *                   6) release notes lines 331 and 362: explain what happens
- *			if the application data rate is higher than 1100 KB/s; 
- *			similar approach to lower-than-500 kB/s ?
- *		     7) 6.6 Comparison; wouldn't it be better to allow different 
- *			strategies for read and write ?
- *			Wouldn't it be better to control the tape buffer
- *			contents instead of the bandwidth ?
- *		     8) line 536: replace will by would (if I understand
- *			this section correctly, a hypothetical and unwanted situation
- *			 is being described)
- * Ver 1.16f Dec 15 99   Change place of the secondary OnStream header frames.
- *
- *
- * Here are some words from the first releases of hd.c, which are quoted
- * in ide.c and apply here as well:
- *
- * | Special care is recommended.  Have Fun!
- *
- */
-
-/*
- * An overview of the pipelined operation mode.
- *
- * In the pipelined write mode, we will usually just add requests to our
- * pipeline and return immediately, before we even start to service them. The
- * user program will then have enough time to prepare the next request while
- * we are still busy servicing previous requests. In the pipelined read mode,
- * the situation is similar - we add read-ahead requests into the pipeline,
- * before the user even requested them.
- *
- * The pipeline can be viewed as a "safety net" which will be activated when
- * the system load is high and prevents the user backup program from keeping up
- * with the current tape speed. At this point, the pipeline will get
- * shorter and shorter but the tape will still be streaming at the same speed.
- * Assuming we have enough pipeline stages, the system load will hopefully
- * decrease before the pipeline is completely empty, and the backup program
- * will be able to "catch up" and refill the pipeline again.
- * 
- * When using the pipelined mode, it would be best to disable any type of
- * buffering done by the user program, as ide-tape already provides all the
- * benefits in the kernel, where it can be done in a more efficient way.
- * As we will usually not block the user program on a request, the most
- * efficient user code will then be a simple read-write-read-... cycle.
- * Any additional logic will usually just slow down the backup process.
- *
- * Using the pipelined mode, I get a constant over 400 KBps throughput,
- * which seems to be the maximum throughput supported by my tape.
- *
- * However, there are some downfalls:
- *
- *	1.	We use memory (for data buffers) in proportional to the number
- *		of pipeline stages (each stage is about 26 KB with my tape).
- *	2.	In the pipelined write mode, we cheat and postpone error codes
- *		to the user task. In read mode, the actual tape position
- *		will be a bit further than the last requested block.
- *
- * Concerning (1):
- *
- *	1.	We allocate stages dynamically only when we need them. When
- *		we don't need them, we don't consume additional memory. In
- *		case we can't allocate stages, we just manage without them
- *		(at the expense of decreased throughput) so when Linux is
- *		tight in memory, we will not pose additional difficulties.
- *
- *	2.	The maximum number of stages (which is, in fact, the maximum
- *		amount of memory) which we allocate is limited by the compile
- *		time parameter IDETAPE_MAX_PIPELINE_STAGES.
- *
- *	3.	The maximum number of stages is a controlled parameter - We
- *		don't start from the user defined maximum number of stages
- *		but from the lower IDETAPE_MIN_PIPELINE_STAGES (again, we
- *		will not even allocate this amount of stages if the user
- *		program can't handle the speed). We then implement a feedback
- *		loop which checks if the pipeline is empty, and if it is, we
- *		increase the maximum number of stages as necessary until we
- *		reach the optimum value which just manages to keep the tape
- *		busy with minimum allocated memory or until we reach
- *		IDETAPE_MAX_PIPELINE_STAGES.
- *
- * Concerning (2):
- *
- *	In pipelined write mode, ide-tape can not return accurate error codes
- *	to the user program since we usually just add the request to the
- *      pipeline without waiting for it to be serviced. In case an error
- *      occurs, I will report it on the next user request.
- *
- *	In the pipelined read mode, subsequent read requests or forward
- *	filemark spacing will perform correctly, as we preserve all blocks
- *	and filemarks which we encountered during our excess read-ahead.
- * 
- *	For accurate tape positioning and error reporting, disabling
- *	pipelined mode might be the best option.
- *
- * You can enable/disable/tune the pipelined operation mode by adjusting
- * the compile time parameters below.
- */
-
-/*
- *	Possible improvements.
- *
- *	1.	Support for the ATAPI overlap protocol.
- *
- *		In order to maximize bus throughput, we currently use the DSC
- *		overlap method which enables ide.c to service requests from the
- *		other device while the tape is busy executing a command. The
- *		DSC overlap method involves polling the tape's status register
- *		for the DSC bit, and servicing the other device while the tape
- *		isn't ready.
- *
- *		In the current QIC development standard (December 1995),
- *		it is recommended that new tape drives will *in addition* 
- *		implement the ATAPI overlap protocol, which is used for the
- *		same purpose - efficient use of the IDE bus, but is interrupt
- *		driven and thus has much less CPU overhead.
- *
- *		ATAPI overlap is likely to be supported in most new ATAPI
- *		devices, including new ATAPI cdroms, and thus provides us
- *		a method by which we can achieve higher throughput when
- *		sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
- */
-
-#define IDETAPE_VERSION "1.16f"
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/major.h>
-#include <linux/devfs_fs_kernel.h>
-#include <linux/errno.h>
-#include <linux/genhd.h>
-#include <linux/malloc.h>
-#include <linux/pci.h>
-#include <linux/ide.h>
-
-#include <asm/byteorder.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/unaligned.h>
-#include <asm/bitops.h>
-
-
-#define NO_LONGER_REQUIRED	(1)
-
-/*
- *	OnStream support
- */
-#define ONSTREAM_DEBUG		(0)
-#define OS_CONFIG_PARTITION	(0xff)
-#define OS_DATA_PARTITION	(0)
-#define OS_PARTITION_VERSION	(1)
-
-/*
- * partition
- */
-typedef struct os_partition_s {
-	__u8	partition_num;
-	__u8	par_desc_ver;
-	__u16	wrt_pass_cntr;
-	__u32	first_frame_addr;
-	__u32	last_frame_addr;
-	__u32	eod_frame_addr;
-} os_partition_t;
-
-/*
- * DAT entry
- */
-typedef struct os_dat_entry_s {
-	__u32	blk_sz;
-	__u16	blk_cnt;
-	__u8	flags;
-	__u8	reserved;
-} os_dat_entry_t;
-
-/*
- * DAT
- */
-#define OS_DAT_FLAGS_DATA	(0xc)
-#define OS_DAT_FLAGS_MARK	(0x1)
-
-typedef struct os_dat_s {
-	__u8		dat_sz;
-	__u8		reserved1;
-	__u8		entry_cnt;
-	__u8		reserved3;
-	os_dat_entry_t	dat_list[16];
-} os_dat_t;
-
-/*
- * Frame types
- */
-#define OS_FRAME_TYPE_FILL	(0)
-#define OS_FRAME_TYPE_EOD	(1 << 0)
-#define OS_FRAME_TYPE_MARKER	(1 << 1)
-#define OS_FRAME_TYPE_HEADER	(1 << 3)
-#define OS_FRAME_TYPE_DATA	(1 << 7)
-
-/*
- * AUX
- */
-typedef struct os_aux_s {
-	__u32		format_id;		/* hardware compability AUX is based on */
-	char		application_sig[4];	/* driver used to write this media */
-	__u32		hdwr;			/* reserved */
-	__u32		update_frame_cntr;	/* for configuration frame */
-	__u8		frame_type;
-	__u8		frame_type_reserved;
-	__u8		reserved_18_19[2];
-	os_partition_t	partition;
-	__u8		reserved_36_43[8];
-	__u32		frame_seq_num;
-	__u32		logical_blk_num_high;
-	__u32		logical_blk_num;
-	os_dat_t	dat;
-	__u8		reserved188_191[4];
-	__u32		filemark_cnt;
-	__u32		phys_fm;
-	__u32		last_mark_addr;
-	__u8		reserved204_223[20];
-
-	/*
-	 * __u8		app_specific[32];
-	 *
-	 * Linux specific fields:
-	 */
-	 __u32		next_mark_addr;		/* when known, points to next marker */
-	 __u8		linux_specific[28];
-
-	__u8		reserved_256_511[256];
-} os_aux_t;
-
-typedef struct os_header_s {
-	char		ident_str[8];
-	__u8		major_rev;
-	__u8		minor_rev;
-	__u8		reserved10_15[6];
-	__u8		par_num;
-	__u8		reserved1_3[3];
-	os_partition_t	partition;
-} os_header_t;
-
-/*
- * OnStream ADRL frame
- */
-#define OS_FRAME_SIZE	(32 * 1024 + 512)
-#define OS_DATA_SIZE	(32 * 1024)
-#define OS_AUX_SIZE	(512)
-
-#include <linux/mtio.h>
-
-/**************************** Tunable parameters *****************************/
-
-
-/*
- *	Pipelined mode parameters.
- *
- *	We try to use the minimum number of stages which is enough to
- *	keep the tape constantly streaming. To accomplish that, we implement
- *	a feedback loop around the maximum number of stages:
- *
- *	We start from MIN maximum stages (we will not even use MIN stages
- *      if we don't need them), increment it by RATE*(MAX-MIN)
- *	whenever we sense that the pipeline is empty, until we reach
- *	the optimum value or until we reach MAX.
- *
- *	Setting the following parameter to 0 will disable the pipelined mode.
- */
-#define IDETAPE_MIN_PIPELINE_STAGES	200
-#define IDETAPE_MAX_PIPELINE_STAGES	400
-#define IDETAPE_INCREASE_STAGES_RATE	 20
-
-/*
- *	The following are used to debug the driver:
- *
- *	Setting IDETAPE_DEBUG_INFO to 1 will report device capabilities.
- *	Setting IDETAPE_DEBUG_LOG to 1 will log driver flow control.
- *	Setting IDETAPE_DEBUG_BUGS to 1 will enable self-sanity checks in
- *	some places.
- *
- *	Setting them to 0 will restore normal operation mode:
- *
- *		1.	Disable logging normal successful operations.
- *		2.	Disable self-sanity checks.
- *		3.	Errors will still be logged, of course.
- *
- *	All the #if DEBUG code will be removed some day, when the driver
- *	is verified to be stable enough. This will make it much more
- *	esthetic.
- */
-#define IDETAPE_DEBUG_INFO		0
-#define IDETAPE_DEBUG_LOG		1
-#define IDETAPE_DEBUG_LOG_VERBOSE	0
-#define IDETAPE_DEBUG_BUGS		1
-
-/*
- *	After each failed packet command we issue a request sense command
- *	and retry the packet command IDETAPE_MAX_PC_RETRIES times.
- *
- *	Setting IDETAPE_MAX_PC_RETRIES to 0 will disable retries.
- */
-#define IDETAPE_MAX_PC_RETRIES		3
-
-/*
- *	With each packet command, we allocate a buffer of
- *	IDETAPE_PC_BUFFER_SIZE bytes. This is used for several packet
- *	commands (Not for READ/WRITE commands).
- */
-#define IDETAPE_PC_BUFFER_SIZE		256
-
-/*
- *	In various places in the driver, we need to allocate storage
- *	for packet commands and requests, which will remain valid while
- *	we leave the driver to wait for an interrupt or a timeout event.
- */
-#define IDETAPE_PC_STACK		(10 + IDETAPE_MAX_PC_RETRIES)
-
-/*
- *	Some tape drives require a long irq timeout
- */
-#define IDETAPE_WAIT_CMD		(60*HZ)
-
-/*
- *	The following parameter is used to select the point in the internal
- *	tape fifo in which we will start to refill the buffer. Decreasing
- *	the following parameter will improve the system's latency and
- *	interactive response, while using a high value might improve sytem
- *	throughput.
- */
-#define IDETAPE_FIFO_THRESHOLD 		2
-
-/*
- *	DSC polling parameters.
- *
- *	Polling for DSC (a single bit in the status register) is a very
- *	important function in ide-tape. There are two cases in which we
- *	poll for DSC:
- *
- *	1.	Before a read/write packet command, to ensure that we
- *		can transfer data from/to the tape's data buffers, without
- *		causing an actual media access. In case the tape is not
- *		ready yet, we take out our request from the device
- *		request queue, so that ide.c will service requests from
- *		the other device on the same interface meanwhile.
- *
- *	2.	After the successful initialization of a "media access
- *		packet command", which is a command which can take a long
- *		time to complete (it can be several seconds or even an hour).
- *
- *		Again, we postpone our request in the middle to free the bus
- *		for the other device. The polling frequency here should be
- *		lower than the read/write frequency since those media access
- *		commands are slow. We start from a "fast" frequency -
- *		IDETAPE_DSC_MA_FAST (one second), and if we don't receive DSC
- *		after IDETAPE_DSC_MA_THRESHOLD (5 minutes), we switch it to a
- *		lower frequency - IDETAPE_DSC_MA_SLOW (1 minute).
- *
- *	We also set a timeout for the timer, in case something goes wrong.
- *	The timeout should be longer then the maximum execution time of a
- *	tape operation.
- */
- 
-/*
- *	DSC timings.
- */
-#define IDETAPE_DSC_RW_MIN		5*HZ/100	/* 50 msec */
-#define IDETAPE_DSC_RW_MAX		40*HZ/100	/* 400 msec */
-#define IDETAPE_DSC_RW_TIMEOUT		2*60*HZ		/* 2 minutes */
-#define IDETAPE_DSC_MA_FAST		2*HZ		/* 2 seconds */
-#define IDETAPE_DSC_MA_THRESHOLD	5*60*HZ		/* 5 minutes */
-#define IDETAPE_DSC_MA_SLOW		30*HZ		/* 30 seconds */
-#define IDETAPE_DSC_MA_TIMEOUT		2*60*60*HZ	/* 2 hours */
-
-/*************************** End of tunable parameters ***********************/
-
-/*
- *	Debugging/Performance analysis
- *
- *	I/O trace support
- */
-#define USE_IOTRACE	0
-#if USE_IOTRACE
-#include <linux/io_trace.h>
-#define IO_IDETAPE_FIFO	500
-#endif
-
-/*
- *	Read/Write error simulation
- */
-#define SIMULATE_ERRORS			0
-
-/*
- *	For general magnetic tape device compatibility.
- */
-typedef enum {
-	idetape_direction_none,
-	idetape_direction_read,
-	idetape_direction_write
-} idetape_chrdev_direction_t;
-
-/*
- *	Our view of a packet command.
- */
-typedef struct idetape_packet_command_s {
-	u8 c[12];				/* Actual packet bytes */
-	int retries;				/* On each retry, we increment retries */
-	int error;				/* Error code */
-	int request_transfer;			/* Bytes to transfer */
-	int actually_transferred;		/* Bytes actually transferred */
-	int buffer_size;			/* Size of our data buffer */
-	struct buffer_head *bh;
-	char *b_data;
-	int b_count;
-	byte *buffer;				/* Data buffer */
-	byte *current_position;			/* Pointer into the above buffer */
-	ide_startstop_t (*callback) (ide_drive_t *);	/* Called when this packet command is completed */
-	byte pc_buffer[IDETAPE_PC_BUFFER_SIZE];	/* Temporary buffer */
-	unsigned int flags;			/* Status/Action bit flags */
-} idetape_pc_t;
-
-/*
- *	Packet command flag bits.
- */
-#define	PC_ABORT			0	/* Set when an error is considered normal - We won't retry */
-#define PC_WAIT_FOR_DSC			1	/* 1 When polling for DSC on a media access command */
-#define PC_DMA_RECOMMENDED		2	/* 1 when we prefer to use DMA if possible */
-#define	PC_DMA_IN_PROGRESS		3	/* 1 while DMA in progress */
-#define	PC_DMA_ERROR			4	/* 1 when encountered problem during DMA */
-#define	PC_WRITING			5	/* Data direction */
-
-/*
- *	Capabilities and Mechanical Status Page
- */
-typedef struct {
-	unsigned	page_code	:6;	/* Page code - Should be 0x2a */
-	__u8		reserved0_6	:1;
-	__u8		ps		:1;	/* parameters saveable */
-	__u8		page_length;		/* Page Length - Should be 0x12 */
-	__u8		reserved2, reserved3;
-	unsigned	ro		:1;	/* Read Only Mode */
-	unsigned	reserved4_1234	:4;
-	unsigned	sprev		:1;	/* Supports SPACE in the reverse direction */
-	unsigned	reserved4_67	:2;
-	unsigned	reserved5_012	:3;
-	unsigned	efmt		:1;	/* Supports ERASE command initiated formatting */
-	unsigned	reserved5_4	:1;
-	unsigned	qfa		:1;	/* Supports the QFA two partition formats */
-	unsigned	reserved5_67	:2;
-	unsigned	lock		:1;	/* Supports locking the volume */
-	unsigned	locked		:1;	/* The volume is locked */
-	unsigned	prevent		:1;	/* The device defaults in the prevent state after power up */	
-	unsigned	eject		:1;	/* The device can eject the volume */
-	__u8		disconnect	:1;	/* The device can break request > ctl */	
-	__u8		reserved6_5	:1;
-	unsigned	ecc		:1;	/* Supports error correction */
-	unsigned	cmprs		:1;	/* Supports data compression */
-	unsigned	reserved7_0	:1;
-	unsigned	blk512		:1;	/* Supports 512 bytes block size */
-	unsigned	blk1024		:1;	/* Supports 1024 bytes block size */
-	unsigned	reserved7_3_6	:4;
-	unsigned	blk32768	:1;	/* slowb - the device restricts the byte count for PIO */
-						/* transfers for slow buffer memory ??? */
-						/* Also 32768 block size in some cases */
-	__u16		max_speed;		/* Maximum speed supported in KBps */
-	__u8		reserved10, reserved11;
-	__u16		ctl;			/* Continuous Transfer Limit in blocks */
-	__u16		speed;			/* Current Speed, in KBps */
-	__u16		buffer_size;		/* Buffer Size, in 512 bytes */
-	__u8		reserved18, reserved19;
-} idetape_capabilities_page_t;
-
-/*
- *	Block Size Page
- */
-typedef struct {
-	unsigned	page_code	:6;	/* Page code - Should be 0x30 */
-	unsigned	reserved1_6	:1;
-	unsigned	ps		:1;
-	__u8		page_length;		/* Page Length - Should be 2 */
-	__u8		reserved2;
-	unsigned	play32		:1;
-	unsigned	play32_5	:1;
-	unsigned	reserved2_23	:2;
-	unsigned	record32	:1;
-	unsigned	record32_5	:1;
-	unsigned	reserved2_6	:1;
-	unsigned	one		:1;
-} idetape_block_size_page_t;
-
-/*
- *	A pipeline stage.
- */
-typedef struct idetape_stage_s {
-	struct request rq;			/* The corresponding request */
-	struct buffer_head *bh;			/* The data buffers */
-	struct idetape_stage_s *next;		/* Pointer to the next stage */
-	os_aux_t *aux;				/* OnStream aux ptr */
-} idetape_stage_t;
-
-/*
- *	REQUEST SENSE packet command result - Data Format.
- */
-typedef struct {
-	unsigned	error_code	:7;	/* Current of deferred errors */
-	unsigned	valid		:1;	/* The information field conforms to QIC-157C */
-	__u8		reserved1	:8;	/* Segment Number - Reserved */
-	unsigned	sense_key	:4;	/* Sense Key */
-	unsigned	reserved2_4	:1;	/* Reserved */
-	unsigned	ili		:1;	/* Incorrect Length Indicator */
-	unsigned	eom		:1;	/* End Of Medium */
-	unsigned	filemark 	:1;	/* Filemark */
-	__u32		information __attribute__ ((packed));
-	__u8		asl;			/* Additional sense length (n-7) */
-	__u32		command_specific;	/* Additional command specific information */
-	__u8		asc;			/* Additional Sense Code */
-	__u8		ascq;			/* Additional Sense Code Qualifier */
-	__u8		replaceable_unit_code;	/* Field Replaceable Unit Code */
-	unsigned	sk_specific1 	:7;	/* Sense Key Specific */
-	unsigned	sksv		:1;	/* Sense Key Specific information is valid */
-	__u8		sk_specific2;		/* Sense Key Specific */
-	__u8		sk_specific3;		/* Sense Key Specific */
-	__u8		pad[2];			/* Padding to 20 bytes */
-} idetape_request_sense_result_t;
-
-
-/*
- *	Most of our global data which we need to save even as we leave the
- *	driver due to an interrupt or a timer event is stored in a variable
- *	of type idetape_tape_t, defined below.
- */
-typedef struct {
-	ide_drive_t *drive;
-	devfs_handle_t de_r, de_n;
-
-	/*
-	 *	Since a typical character device operation requires more
-	 *	than one packet command, we provide here enough memory
-	 *	for the maximum of interconnected packet commands.
-	 *	The packet commands are stored in the circular array pc_stack.
-	 *	pc_stack_index points to the last used entry, and warps around
-	 *	to the start when we get to the last array entry.
-	 *
-	 *	pc points to the current processed packet command.
-	 *
-	 *	failed_pc points to the last failed packet command, or contains
-	 *	NULL if we do not need to retry any packet command. This is
-	 *	required since an additional packet command is needed before the
-	 *	retry, to get detailed information on what went wrong.
-	 */
-	idetape_pc_t *pc;			/* Current packet command */
-	idetape_pc_t *failed_pc; 		/* Last failed packet command */
-	idetape_pc_t pc_stack[IDETAPE_PC_STACK];/* Packet command stack */
-	int pc_stack_index;			/* Next free packet command storage space */
-	struct request rq_stack[IDETAPE_PC_STACK];
-	int rq_stack_index;			/* We implement a circular array */
-
-	/*
-	 *	DSC polling variables.
-	 *
-	 *	While polling for DSC we use postponed_rq to postpone the
-	 *	current request so that ide.c will be able to service
-	 *	pending requests on the other device. Note that at most
-	 *	we will have only one DSC (usually data transfer) request
-	 *	in the device request queue. Additional requests can be
-	 *	queued in our internal pipeline, but they will be visible
-	 *	to ide.c only one at a time.
-	 */
-	struct request *postponed_rq;
-	unsigned long dsc_polling_start;	/* The time in which we started polling for DSC */
-	struct timer_list dsc_timer;		/* Timer used to poll for dsc */
-	unsigned long best_dsc_rw_frequency;	/* Read/Write dsc polling frequency */
-	unsigned long dsc_polling_frequency;	/* The current polling frequency */
-	unsigned long dsc_timeout;		/* Maximum waiting time */
-
-	/*
-	 *	Read position information
-	 */
-	byte partition;
-	unsigned int first_frame_position;		/* Current block */
-	unsigned int last_frame_position;
-	unsigned int blocks_in_buffer;
-
-	/*
-	 *	Last error information
-	 */
-	byte sense_key, asc, ascq;
-
-	/*
-	 *	Character device operation
-	 */
-	unsigned int minor;
-	char name[4];					/* device name */
-	idetape_chrdev_direction_t chrdev_direction;	/* Current character device data transfer direction */
-
-	/*
-	 *	Device information
-	 */
-	unsigned short tape_block_size;			/* Usually 512 or 1024 bytes */
-	int user_bs_factor;
-	idetape_capabilities_page_t capabilities;	/* Copy of the tape's Capabilities and Mechanical Page */
-
-	/*
-	 *	Active data transfer request parameters.
-	 *
-	 *	At most, there is only one ide-tape originated data transfer
-	 *	request in the device request queue. This allows ide.c to
-	 *	easily service requests from the other device when we
-	 *	postpone our active request. In the pipelined operation
-	 *	mode, we use our internal pipeline structure to hold
-	 *	more data requests.
-	 *
-	 *	The data buffer size is chosen based on the tape's
-	 *	recommendation.
-	 */
-	struct request *active_data_request;	/* Pointer to the request which is waiting in the device request queue */
-	int stage_size;				/* Data buffer size (chosen based on the tape's recommendation */
-	idetape_stage_t *merge_stage;
-	int merge_stage_size;
-	struct buffer_head *bh;
-	char *b_data;
-	int b_count;
-	
-	/*
-	 *	Pipeline parameters.
-	 *
-	 *	To accomplish non-pipelined mode, we simply set the following
-	 *	variables to zero (or NULL, where appropriate).
-	 */
-	int nr_stages;				/* Number of currently used stages */
-	int nr_pending_stages;			/* Number of pending stages */
-	int max_stages, min_pipeline, max_pipeline; /* We will not allocate more than this number of stages */
-	idetape_stage_t *first_stage;		/* The first stage which will be removed from the pipeline */
-	idetape_stage_t *active_stage;		/* The currently active stage */
-	idetape_stage_t *next_stage;		/* Will be serviced after the currently active request */
-	idetape_stage_t *last_stage;		/* New requests will be added to the pipeline here */
-	idetape_stage_t *cache_stage;		/* Optional free stage which we can use */
-	int pages_per_stage;
-	int excess_bh_size;			/* Wasted space in each stage */
-
-	unsigned int flags;			/* Status/Action flags */
-	spinlock_t spinlock;			/* protects the ide-tape queue */
-
-	/*
-	 * Measures average tape speed
-	 */
-	unsigned long avg_time;
-	int avg_size;
-	int avg_speed;
-
-	idetape_request_sense_result_t sense;	/* last sense information */
-
-	char vendor_id[10];
-	char product_id[18];
-	char firmware_revision[6];
-	int firmware_revision_num;
-
-	int door_locked;			/* the door is currently locked */
-
-	/*
-	 * OnStream flags
-	 */
-	int onstream;				/* the tape is an OnStream tape */
-	int raw;				/* OnStream raw access (32.5KB block size) */
-	int cur_frames;				/* current number of frames in internal buffer */
-	int max_frames;				/* max number of frames in internal buffer */
-	int logical_blk_num;			/* logical block number */
-	__u16 wrt_pass_cntr;			/* write pass counter */
-	__u32 update_frame_cntr;		/* update frame counter */
-	struct semaphore *sem;
-	int onstream_write_error;		/* write error recovery active */
-	int header_ok;				/* header frame verified ok */
-	int linux_media;			/* reading linux-specifc media */
-	int linux_media_version;
-	char application_sig[5];		/* application signature */
-	int filemark_cnt;
-	int first_mark_addr;
-	int last_mark_addr;
-	int eod_frame_addr;
-	unsigned long cmd_start_time;
-	unsigned long max_cmd_time;
-
-	/*
-	 * Optimize the number of "buffer filling"
-	 * mode sense commands.
-	 */
-	unsigned long last_buffer_fill;		/* last time in which we issued fill cmd */
-	int req_buffer_fill;			/* buffer fill command requested */
-	int writes_since_buffer_fill;
-	int reads_since_buffer_fill;
-
-	/*
-	 * Limit the number of times a request can
-	 * be postponed, to avoid an infinite postpone
-	 * deadlock.
-	 */
-	int postpone_cnt;			/* request postpone count limit */
-
-	/*
-	 * Measures number of frames:
-	 *
-	 * 1. written/read to/from the driver pipeline (pipeline_head).
-	 * 2. written/read to/from the tape buffers (buffer_head).
-	 * 3. written/read by the tape to/from the media (tape_head).
-	 */
-	int pipeline_head;
-	int buffer_head;
-	int tape_head;
-	int last_tape_head;
-
-	/*
-	 * Speed control at the tape buffers input/output
-	 */
-	unsigned long insert_time;
-	int insert_size;
-	int insert_speed;
-	int max_insert_speed;
-	int measure_insert_time;
-
-	/*
-	 * Measure tape still time, in milliseconds
-	 */
-	unsigned long tape_still_time_begin;
-	int tape_still_time;
-
-	/*
-	 * Speed regulation negative feedback loop
-	 */
-	int speed_control;
-	int pipeline_head_speed, controlled_pipeline_head_speed, uncontrolled_pipeline_head_speed;
-	int controlled_last_pipeline_head, uncontrolled_last_pipeline_head;
-	unsigned long uncontrolled_pipeline_head_time, controlled_pipeline_head_time;
-	int controlled_previous_pipeline_head, uncontrolled_previous_pipeline_head;
-	unsigned long controlled_previous_head_time, uncontrolled_previous_head_time;
-	int restart_speed_control_req;
-
-        /*
-         * Debug_level determines amount of debugging output;
-         * can be changed using /proc/ide/hdx/settings
-         * 0 : almost no debugging output
-         * 1 : 0+output errors only
-         * 2 : 1+output all sensekey/asc
-         * 3 : 2+follow all chrdev related procedures
-         * 4 : 3+follow all procedures
-         * 5 : 4+include pc_stack rq_stack info
-         * 6 : 5+USE_COUNT updates
-         */
-         int debug_level; 
-} idetape_tape_t;
-
-/*
- *	Tape door status
- */
-#define DOOR_UNLOCKED			0
-#define DOOR_LOCKED			1
-#define DOOR_EXPLICITLY_LOCKED		2
-
-/*
- *	Tape flag bits values.
- */
-#define IDETAPE_IGNORE_DSC		0
-#define IDETAPE_ADDRESS_VALID		1	/* 0 When the tape position is unknown */
-#define IDETAPE_BUSY			2	/* Device already opened */
-#define IDETAPE_PIPELINE_ERROR		3	/* Error detected in a pipeline stage */
-#define IDETAPE_DETECT_BS		4	/* Attempt to auto-detect the current user block size */
-#define IDETAPE_FILEMARK		5	/* Currently on a filemark */
-#define IDETAPE_DRQ_INTERRUPT		6	/* DRQ interrupt device */
-#define IDETAPE_READ_ERROR		7
-#define IDETAPE_PIPELINE_ACTIVE		8	/* pipeline active */
-
-/*
- *	Supported ATAPI tape drives packet commands
- */
-#define IDETAPE_TEST_UNIT_READY_CMD	0x00
-#define IDETAPE_REWIND_CMD		0x01
-#define IDETAPE_REQUEST_SENSE_CMD	0x03
-#define IDETAPE_READ_CMD		0x08
-#define IDETAPE_WRITE_CMD		0x0a
-#define IDETAPE_WRITE_FILEMARK_CMD	0x10
-#define IDETAPE_SPACE_CMD		0x11
-#define IDETAPE_INQUIRY_CMD		0x12
-#define IDETAPE_ERASE_CMD		0x19
-#define IDETAPE_MODE_SENSE_CMD		0x1a
-#define IDETAPE_MODE_SELECT_CMD		0x15
-#define IDETAPE_LOAD_UNLOAD_CMD		0x1b
-#define IDETAPE_PREVENT_CMD		0x1e
-#define IDETAPE_LOCATE_CMD		0x2b
-#define IDETAPE_READ_POSITION_CMD	0x34
-#define IDETAPE_READ_BUFFER_CMD		0x3c
-#define IDETAPE_SET_SPEED_CMD		0xbb
-
-/*
- *	Some defines for the READ BUFFER command
- */
-#define IDETAPE_RETRIEVE_FAULTY_BLOCK	6
-
-/*
- *	Some defines for the SPACE command
- */
-#define IDETAPE_SPACE_OVER_FILEMARK	1
-#define IDETAPE_SPACE_TO_EOD		3
-
-/*
- *	Some defines for the LOAD UNLOAD command
- */
-#define IDETAPE_LU_LOAD_MASK		1
-#define IDETAPE_LU_RETENSION_MASK	2
-#define IDETAPE_LU_EOT_MASK		4
-
-/*
- *	Special requests for our block device strategy routine.
- *
- *	In order to service a character device command, we add special
- *	requests to the tail of our block device request queue and wait
- *	for their completion.
- *
- */
-#define IDETAPE_FIRST_RQ		90
-
-/*
- * 	IDETAPE_PC_RQ is used to queue a packet command in the request queue.
- */
-#define IDETAPE_PC_RQ1			90
-#define IDETAPE_PC_RQ2			91
-
-/*
- *	IDETAPE_READ_RQ and IDETAPE_WRITE_RQ are used by our
- *	character device interface to request read/write operations from
- *	our block device interface.
- */
-#define IDETAPE_READ_RQ			92
-#define IDETAPE_WRITE_RQ		93
-#define IDETAPE_ABORTED_WRITE_RQ	94
-#define IDETAPE_ABORTED_READ_RQ		95
-#define IDETAPE_READ_BUFFER_RQ		96
-
-#define IDETAPE_LAST_RQ			96
-
-/*
- *	A macro which can be used to check if a we support a given
- *	request command.
- */
-#define IDETAPE_RQ_CMD(cmd) 		((cmd >= IDETAPE_FIRST_RQ) && (cmd <= IDETAPE_LAST_RQ))
-
-/*
- *	Error codes which are returned in rq->errors to the higher part
- *	of the driver.
- */
-#define	IDETAPE_ERROR_GENERAL		101
-#define	IDETAPE_ERROR_FILEMARK		102
-#define	IDETAPE_ERROR_EOD		103
-
-/*
- *	The ATAPI Status Register.
- */
-typedef union {
-	unsigned all			:8;
-	struct {
-		unsigned check		:1;	/* Error occurred */
-		unsigned idx		:1;	/* Reserved */
-		unsigned corr		:1;	/* Correctable error occurred */
-		unsigned drq		:1;	/* Data is request by the device */
-		unsigned dsc		:1;	/* Buffer availability / Media access command finished */
-		unsigned reserved5	:1;	/* Reserved */
-		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
-		unsigned bsy		:1;	/* The device has access to the command block */
-	} b;
-} idetape_status_reg_t;
-
-/*
- *	The ATAPI error register.
- */
-typedef union {
-	unsigned all			:8;
-	struct {
-		unsigned ili		:1;	/* Illegal Length Indication */
-		unsigned eom		:1;	/* End Of Media Detected */
-		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
-		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
-		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
-	} b;
-} idetape_error_reg_t;
-
-/*
- *	ATAPI Feature Register
- */
-typedef union {
-	unsigned all			:8;
-	struct {
-		unsigned dma		:1;	/* Using DMA of PIO */
-		unsigned reserved321	:3;	/* Reserved */
-		unsigned reserved654	:3;	/* Reserved (Tag Type) */
-		unsigned reserved7	:1;	/* Reserved */
-	} b;
-} idetape_feature_reg_t;
-
-/*
- *	ATAPI Byte Count Register.
- */
-typedef union {
-	unsigned all			:16;
-	struct {
-		unsigned low		:8;	/* LSB */
-		unsigned high		:8;	/* MSB */
-	} b;
-} idetape_bcount_reg_t;
-
-/*
- *	ATAPI Interrupt Reason Register.
- */
-typedef union {
-	unsigned all			:8;
-	struct {
-		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
-		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
-		unsigned reserved	:6;	/* Reserved */
-	} b;
-} idetape_ireason_reg_t;
-
-/*
- *	ATAPI Drive Select Register
- */
-typedef union {	
-	unsigned all			:8;
-	struct {
-		unsigned sam_lun	:4;	/* Should be zero with ATAPI (not used) */
-		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
-		unsigned one5		:1;	/* Should be set to 1 */
-		unsigned reserved6	:1;	/* Reserved */
-		unsigned one7		:1;	/* Should be set to 1 */
-	} b;
-} idetape_drivesel_reg_t;
-
-/*
- *	ATAPI Device Control Register
- */
-typedef union {			
-	unsigned all			:8;
-	struct {
-		unsigned zero0		:1;	/* Should be set to zero */
-		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
-		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
-		unsigned one3		:1;	/* Should be set to 1 */
-		unsigned reserved4567	:4;	/* Reserved */
-	} b;
-} idetape_control_reg_t;
-
-/*
- *	idetape_chrdev_t provides the link between out character device
- *	interface and our block device interface and the corresponding
- *	ide_drive_t structure.
- */
-typedef struct {
-	ide_drive_t *drive;
-} idetape_chrdev_t;
-
-/*
- *	The following is used to format the general configuration word of
- *	the ATAPI IDENTIFY DEVICE command.
- */
-struct idetape_id_gcw {	
-	unsigned packet_size		:2;	/* Packet Size */
-	unsigned reserved234		:3;	/* Reserved */
-	unsigned drq_type		:2;	/* Command packet DRQ type */
-	unsigned removable		:1;	/* Removable media */
-	unsigned device_type		:5;	/* Device type */
-	unsigned reserved13		:1;	/* Reserved */
-	unsigned protocol		:2;	/* Protocol type */
-};
-
-/*
- *	INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
- */
-typedef struct {
-	unsigned	device_type	:5;	/* Peripheral Device Type */
-	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
-	unsigned	reserved1_6t0	:7;	/* Reserved */
-	unsigned	rmb		:1;	/* Removable Medium Bit */
-	unsigned	ansi_version	:3;	/* ANSI Version */
-	unsigned	ecma_version	:3;	/* ECMA Version */
-	unsigned	iso_version	:2;	/* ISO Version */
-	unsigned	response_format :4;	/* Response Data Format */
-	unsigned	reserved3_45	:2;	/* Reserved */
-	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
-	unsigned	reserved3_7	:1;	/* AENC - Reserved */
-	__u8		additional_length;	/* Additional Length (total_length-4) */
-	__u8		rsv5, rsv6, rsv7;	/* Reserved */
-	__u8		vendor_id[8];		/* Vendor Identification */
-	__u8		product_id[16];		/* Product Identification */
-	__u8		revision_level[4];	/* Revision Level */
-	__u8		vendor_specific[20];	/* Vendor Specific - Optional */
-	__u8		reserved56t95[40];	/* Reserved - Optional */
-						/* Additional information may be returned */
-} idetape_inquiry_result_t;
-
-/*
- *	READ POSITION packet command - Data Format (From Table 6-57)
- */
-typedef struct {
-	unsigned	reserved0_10	:2;	/* Reserved */
-	unsigned	bpu		:1;	/* Block Position Unknown */	
-	unsigned	reserved0_543	:3;	/* Reserved */
-	unsigned	eop		:1;	/* End Of Partition */
-	unsigned	bop		:1;	/* Beginning Of Partition */
-	u8		partition;		/* Partition Number */
-	u8		reserved2, reserved3;	/* Reserved */
-	u32		first_block;		/* First Block Location */
-	u32		last_block;		/* Last Block Location (Optional) */
-	u8		reserved12;		/* Reserved */
-	u8		blocks_in_buffer[3];	/* Blocks In Buffer - (Optional) */
-	u32		bytes_in_buffer;	/* Bytes In Buffer (Optional) */
-} idetape_read_position_result_t;
-
-/*
- *	Follows structures which are related to the SELECT SENSE / MODE SENSE
- *	packet commands. Those packet commands are still not supported
- *	by ide-tape.
- */
-#define	IDETAPE_CAPABILITIES_PAGE	0x2a
-#define IDETAPE_BLOCK_SIZE_PAGE		0x30
-
-/*
- *	Mode Parameter Header for the MODE SENSE packet command
- */
-typedef struct {
-	__u8	mode_data_length;	/* Length of the following data transfer */
-	__u8	medium_type;		/* Medium Type */
-	__u8	dsp;			/* Device Specific Parameter */
-	__u8	bdl;			/* Block Descriptor Length */
-#if 0
-	/* data transfer page */
-	__u8	page_code	:6;
-	__u8	reserved0_6	:1;
-	__u8	ps		:1;	/* parameters saveable */
-	__u8	page_length;		/* page Length == 0x02 */
-	__u8	reserved2;
-	__u8	read32k		:1;	/* 32k blk size (data only) */
-	__u8	read32k5	:1;	/* 32.5k blk size (data&AUX) */
-	__u8	reserved3_23	:2;
-	__u8	write32k	:1;	/* 32k blk size (data only) */
-	__u8	write32k5	:1;	/* 32.5k blk size (data&AUX) */
-	__u8	reserved3_6	:1;
-	__u8	streaming	:1;	/* streaming mode enable */
-#endif
-} idetape_mode_parameter_header_t;
-
-/*
- *	Mode Parameter Block Descriptor the MODE SENSE packet command
- *
- *	Support for block descriptors is optional.
- */
-typedef struct {
-	__u8		density_code;		/* Medium density code */
-	__u8		blocks[3];		/* Number of blocks */
-	__u8		reserved4;		/* Reserved */
-	__u8		length[3];		/* Block Length */
-} idetape_parameter_block_descriptor_t;
-
-/*
- *	The Data Compression Page, as returned by the MODE SENSE packet command.
- */
-typedef struct {
-	unsigned	page_code	:6;	/* Page Code - Should be 0xf */
-	unsigned	reserved0	:1;	/* Reserved */
-	unsigned	ps		:1;
-	__u8		page_length;		/* Page Length - Should be 14 */
-	unsigned	reserved2	:6;	/* Reserved */
-	unsigned	dcc		:1;	/* Data Compression Capable */
-	unsigned	dce		:1;	/* Data Compression Enable */
-	unsigned	reserved3	:5;	/* Reserved */
-	unsigned	red		:2;	/* Report Exception on Decompression */
-	unsigned	dde		:1;	/* Data Decompression Enable */
-	__u32		ca;			/* Compression Algorithm */
-	__u32		da;			/* Decompression Algorithm */
-	__u8		reserved[4];		/* Reserved */
-} idetape_data_compression_page_t;
-
-/*
- *	The Medium Partition Page, as returned by the MODE SENSE packet command.
- */
-typedef struct {
-	unsigned	page_code	:6;	/* Page Code - Should be 0x11 */
-	unsigned	reserved1_6	:1;	/* Reserved */
-	unsigned	ps		:1;
-	__u8		page_length;		/* Page Length - Should be 6 */
-	__u8		map;			/* Maximum Additional Partitions - Should be 0 */
-	__u8		apd;			/* Additional Partitions Defined - Should be 0 */
-	unsigned	reserved4_012	:3;	/* Reserved */
-	unsigned	psum		:2;	/* Should be 0 */
-	unsigned	idp		:1;	/* Should be 0 */
-	unsigned	sdp		:1;	/* Should be 0 */
-	unsigned	fdp		:1;	/* Fixed Data Partitions */
-	__u8		mfr;			/* Medium Format Recognition */
-	__u8		reserved[2];		/* Reserved */
-} idetape_medium_partition_page_t;
-
-/*
- *	Run time configurable parameters.
- */
-typedef struct {
-	int	dsc_rw_frequency;
-	int	dsc_media_access_frequency;
-	int	nr_stages;
-} idetape_config_t;
-
-/*
- *	The variables below are used for the character device interface.
- *	Additional state variables are defined in our ide_drive_t structure.
- */
-static idetape_chrdev_t idetape_chrdevs[MAX_HWIFS * MAX_DRIVES];
-static int idetape_chrdev_present = 0;
-
-#if IDETAPE_DEBUG_LOG_VERBOSE
-
-/*
- * DO NOT REMOVE, BUILDING A VERBOSE DEBUG SCHEME FOR ATAPI
- */
-
-char *idetape_sense_key_verbose (byte idetape_sense_key)
-{
-	switch (idetape_sense_key) {
-		default: {
-			char buf[22];
-			sprintf(buf, "IDETAPE_SENSE (0x%02x)", idetape_sense_key);
-			return(buf);
-		}
-
-	}
-}
-
-char *idetape_command_key_verbose (byte idetape_command_key)
-{
-	switch (idetape_command_key) {
-		case IDETAPE_TEST_UNIT_READY_CMD:	return("TEST_UNIT_READY_CMD");
-		case IDETAPE_REWIND_CMD:		return("REWIND_CMD");
-		case IDETAPE_REQUEST_SENSE_CMD:		return("REQUEST_SENSE_CMD");
-		case IDETAPE_READ_CMD:			return("READ_CMD");
-		case IDETAPE_WRITE_CMD:			return("WRITE_CMD");
-		case IDETAPE_WRITE_FILEMARK_CMD:	return("WRITE_FILEMARK_CMD");
-		case IDETAPE_SPACE_CMD:			return("SPACE_CMD");
-		case IDETAPE_INQUIRY_CMD:		return("INQUIRY_CMD");
-		case IDETAPE_ERASE_CMD:			return("ERASE_CMD")
-		case IDETAPE_MODE_SENSE_CMD:		return("MODE_SENSE_CMD");
-		case IDETAPE_MODE_SELECT_CMD:		return("MODE_SELECT_CMD");
-		case IDETAPE_LOAD_UNLOAD_CMD:		return("LOAD_UNLOAD_CMD");
-		case IDETAPE_PREVENT_CMD:		return("PREVENT_CMD");
-		case IDETAPE_LOCATE_CMD:		return("LOCATE_CMD");
-		case IDETAPE_READ_POSITION_CMD:		return("READ_POSITION_CMD");
-		case IDETAPE_READ_BUFFER_CMD:		return("READ_BUFFER_CMD");
-		case IDETAPE_SET_SPEED_CMD:		return("SET_SPEED_CMD");
-		default: {
-				char buf[20];
-				sprintf(buf, "CMD (0x%02x)", idetape_command_key);
-				return(buf);
-			}
-	}
-}
-#endif /* IDETAPE_DEBUG_LOG_VERBOSE */
-
-/*
- *	Too bad. The drive wants to send us data which we are not ready to accept.
- *	Just throw it away.
- */
-static void idetape_discard_data (ide_drive_t *drive, unsigned int bcount)
-{
-	while (bcount--)
-		IN_BYTE (IDE_DATA_REG);
-}
-
-static void idetape_input_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount)
-{
-	struct buffer_head *bh = pc->bh;
-	int count;
-
-	while (bcount) {
-#if IDETAPE_DEBUG_BUGS
-		if (bh == NULL) {
-			printk (KERN_ERR "ide-tape: bh == NULL in idetape_input_buffers\n");
-			idetape_discard_data (drive, bcount);
-			return;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-		count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), bcount);
-		atapi_input_bytes (drive, bh->b_data + atomic_read(&bh->b_count), count);
-		bcount -= count; atomic_add(count, &bh->b_count);
-		if (atomic_read(&bh->b_count) == bh->b_size) {
-			bh = bh->b_reqnext;
-			if (bh)
-				atomic_set(&bh->b_count, 0);
-		}
-	}
-	pc->bh = bh;
-}
-
-static void idetape_output_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount)
-{
-	struct buffer_head *bh = pc->bh;
-	int count;
-
-	while (bcount) {
-#if IDETAPE_DEBUG_BUGS
-		if (bh == NULL) {
-			printk (KERN_ERR "ide-tape: bh == NULL in idetape_output_buffers\n");
-			return;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-		count = IDE_MIN (pc->b_count, bcount);
-		atapi_output_bytes (drive, pc->b_data, count);
-		bcount -= count; pc->b_data += count; pc->b_count -= count;
-		if (!pc->b_count) {
-			pc->bh = bh = bh->b_reqnext;
-			if (bh) {
-				pc->b_data = bh->b_data;
-				pc->b_count = atomic_read(&bh->b_count);
-			}
-		}
-	}
-}
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-static void idetape_update_buffers (idetape_pc_t *pc)
-{
-	struct buffer_head *bh = pc->bh;
-	int count, bcount = pc->actually_transferred;
-
-	if (test_bit (PC_WRITING, &pc->flags))
-		return;
-	while (bcount) {
-#if IDETAPE_DEBUG_BUGS
-		if (bh == NULL) {
-			printk (KERN_ERR "ide-tape: bh == NULL in idetape_update_buffers\n");
-			return;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-		count = IDE_MIN (bh->b_size, bcount);
-		atomic_set(&bh->b_count, count);
-		if (atomic_read(&bh->b_count) == bh->b_size)
-			bh = bh->b_reqnext;
-		bcount -= count;
-	}
-	pc->bh = bh;
-}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-/*
- *	idetape_next_pc_storage returns a pointer to a place in which we can
- *	safely store a packet command, even though we intend to leave the
- *	driver. A storage space for a maximum of IDETAPE_PC_STACK packet
- *	commands is allocated at initialization time.
- */
-static idetape_pc_t *idetape_next_pc_storage (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 5)
-		printk (KERN_INFO "ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
-#endif /* IDETAPE_DEBUG_LOG */
-	if (tape->pc_stack_index==IDETAPE_PC_STACK)
-		tape->pc_stack_index=0;
-	return (&tape->pc_stack[tape->pc_stack_index++]);
-}
-
-/*
- *	idetape_next_rq_storage is used along with idetape_next_pc_storage.
- *	Since we queue packet commands in the request queue, we need to
- *	allocate a request, along with the allocation of a packet command.
- */
- 
-/**************************************************************
- *                                                            *
- *  This should get fixed to use kmalloc(GFP_ATOMIC, ..)      *
- *  followed later on by kfree().   -ml                       *
- *                                                            *
- **************************************************************/
- 
-static struct request *idetape_next_rq_storage (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 5)
-		printk (KERN_INFO "ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
-#endif /* IDETAPE_DEBUG_LOG */
-	if (tape->rq_stack_index==IDETAPE_PC_STACK)
-		tape->rq_stack_index=0;
-	return (&tape->rq_stack[tape->rq_stack_index++]);
-}
-
-/*
- *	idetape_init_pc initializes a packet command.
- */
-static void idetape_init_pc (idetape_pc_t *pc)
-{
-	memset (pc->c, 0, 12);
-	pc->retries = 0;
-	pc->flags = 0;
-	pc->request_transfer = 0;
-	pc->buffer = pc->pc_buffer;
-	pc->buffer_size = IDETAPE_PC_BUFFER_SIZE;
-	pc->bh = NULL;
-	pc->b_data = NULL;
-}
-
-/*
- *	idetape_analyze_error is called on each failed packet command retry
- *	to analyze the request sense. We currently do not utilize this
- *	information.
- */
-static void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t *pc = tape->failed_pc;
-
-	tape->sense = *result;
-	tape->sense_key = result->sense_key; tape->asc = result->asc; tape->ascq = result->ascq;
-#if IDETAPE_DEBUG_LOG
-	/*
-	 *	Without debugging, we only log an error if we decided to
-	 *	give up retrying.
-	 */
-	if (tape->debug_level >= 1)
-		printk (KERN_INFO "ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq);
-#if IDETAPE_DEBUG_LOG_VERBOSE
-	if (tape->debug_level >= 1)
-		printk (KERN_INFO "ide-tape: pc = %s, sense key = %x, asc = %x, ascq = %x\n",
-			idetape_command_key_verbose((byte) pc->c[0]),
-			result->sense_key,
-			result->asc,
-			result->ascq);
-#endif /* IDETAPE_DEBUG_LOG_VERBOSE */
-#endif /* IDETAPE_DEBUG_LOG */
-
-	if (tape->onstream && result->sense_key == 2 && result->asc == 0x53 && result->ascq == 2) {
-		clear_bit(PC_DMA_ERROR, &pc->flags);
-		ide_stall_queue(drive, HZ / 2);
-		return;
-	}
-#ifdef CONFIG_BLK_DEV_IDEDMA
-
-	/*
-	 *	Correct pc->actually_transferred by asking the tape.
-	 */
-	if (test_bit (PC_DMA_ERROR, &pc->flags)) {
-		pc->actually_transferred = pc->request_transfer - tape->tape_block_size * ntohl (get_unaligned (&result->information));
-		idetape_update_buffers (pc);
-	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-	if (pc->c[0] == IDETAPE_READ_CMD && result->filemark) {
-		pc->error = IDETAPE_ERROR_FILEMARK;
-		set_bit (PC_ABORT, &pc->flags);
-	}
-	if (pc->c[0] == IDETAPE_WRITE_CMD) {
-		if (result->eom || (result->sense_key == 0xd && result->asc == 0x0 && result->ascq == 0x2)) {
-			pc->error = IDETAPE_ERROR_EOD;
-			set_bit (PC_ABORT, &pc->flags);
-		}
-	}
-	if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) {
-		if (result->sense_key == 8) {
-			pc->error = IDETAPE_ERROR_EOD;
-			set_bit (PC_ABORT, &pc->flags);
-		}
-		if (!test_bit (PC_ABORT, &pc->flags) && (tape->onstream || pc->actually_transferred))
-			pc->retries = IDETAPE_MAX_PC_RETRIES + 1;
-	}
-}
-
-static void idetape_abort_pipeline (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *stage = tape->next_stage;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk(KERN_INFO "ide-tape: %s: idetape_abort_pipeline called\n", tape->name);
-#endif
-	while (stage) {
-		if (stage->rq.cmd == IDETAPE_WRITE_RQ)
-			stage->rq.cmd = IDETAPE_ABORTED_WRITE_RQ;
-		else if (stage->rq.cmd == IDETAPE_READ_RQ)
-			stage->rq.cmd = IDETAPE_ABORTED_READ_RQ;
-		stage = stage->next;
-	}
-}
-
-/*
- *	idetape_active_next_stage will declare the next stage as "active".
- */
-static void idetape_active_next_stage (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *stage=tape->next_stage;
-	struct request *rq = &stage->rq;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_active_next_stage\n");
-#endif /* IDETAPE_DEBUG_LOG */
-#if IDETAPE_DEBUG_BUGS
-	if (stage == NULL) {
-		printk (KERN_ERR "ide-tape: bug: Trying to activate a non existing stage\n");
-		return;
-	}
-#endif /* IDETAPE_DEBUG_BUGS */	
-
-	rq->buffer = NULL;
-	rq->bh = stage->bh;
-	tape->active_data_request=rq;
-	tape->active_stage=stage;
-	tape->next_stage=stage->next;
-}
-
-/*
- *	idetape_increase_max_pipeline_stages is a part of the feedback
- *	loop which tries to find the optimum number of stages. In the
- *	feedback loop, we are starting from a minimum maximum number of
- *	stages, and if we sense that the pipeline is empty, we try to
- *	increase it, until we reach the user compile time memory limit.
- */
-static void idetape_increase_max_pipeline_stages (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int increase = (tape->max_pipeline - tape->min_pipeline) / 10;
-	
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_increase_max_pipeline_stages\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-	tape->max_stages += increase;
-	tape->max_stages = IDE_MAX(tape->max_stages, tape->min_pipeline);
-	tape->max_stages = IDE_MIN(tape->max_stages, tape->max_pipeline);
-}
-
-/*
- *	idetape_kfree_stage calls kfree to completely free a stage, along with
- *	its related buffers.
- */
-static void __idetape_kfree_stage (idetape_stage_t *stage)
-{
-	struct buffer_head *prev_bh, *bh = stage->bh;
-	int size;
-
-	while (bh != NULL) {
-		if (bh->b_data != NULL) {
-			size = (int) bh->b_size;
-			while (size > 0) {
-				free_page ((unsigned long) bh->b_data);
-				size -= PAGE_SIZE;
-				bh->b_data += PAGE_SIZE;
-			}
-		}
-		prev_bh = bh;
-		bh = bh->b_reqnext;
-		kfree (prev_bh);
-	}
-	kfree (stage);
-}
-
-static void idetape_kfree_stage (idetape_tape_t *tape, idetape_stage_t *stage)
-{
-	__idetape_kfree_stage (stage);
-}
-
-/*
- *	idetape_remove_stage_head removes tape->first_stage from the pipeline.
- *	The caller should avoid race conditions.
- */
-static void idetape_remove_stage_head (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *stage;
-	
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_remove_stage_head\n");
-#endif /* IDETAPE_DEBUG_LOG */
-#if IDETAPE_DEBUG_BUGS
-	if (tape->first_stage == NULL) {
-		printk (KERN_ERR "ide-tape: bug: tape->first_stage is NULL\n");
-		return;		
-	}
-	if (tape->active_stage == tape->first_stage) {
-		printk (KERN_ERR "ide-tape: bug: Trying to free our active pipeline stage\n");
-		return;
-	}
-#endif /* IDETAPE_DEBUG_BUGS */
-	stage=tape->first_stage;
-	tape->first_stage=stage->next;
-	idetape_kfree_stage (tape, stage);
-	tape->nr_stages--;
-	if (tape->first_stage == NULL) {
-		tape->last_stage=NULL;
-#if IDETAPE_DEBUG_BUGS
-		if (tape->next_stage != NULL)
-			printk (KERN_ERR "ide-tape: bug: tape->next_stage != NULL\n");
-		if (tape->nr_stages)
-			printk (KERN_ERR "ide-tape: bug: nr_stages should be 0 now\n");
-#endif /* IDETAPE_DEBUG_BUGS */
-	}
-}
-
-/*
- *	idetape_end_request is used to finish servicing a request, and to
- *	insert a pending pipeline request into the main device queue.
- */
-static void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
-{
-	ide_drive_t *drive = hwgroup->drive;
-	struct request *rq = hwgroup->rq;
-	idetape_tape_t *tape = drive->driver_data;
-	unsigned long flags;
-	int error;
-	int remove_stage = 0;
-#if ONSTREAM_DEBUG
-	idetape_stage_t *stage;
-	os_aux_t *aux;
-	unsigned char *p;
-#endif
-
-#if IDETAPE_DEBUG_LOG
-        if (tape->debug_level >= 4)
-	printk (KERN_INFO "ide-tape: Reached idetape_end_request\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-	switch (uptodate) {
-		case 0:	error = IDETAPE_ERROR_GENERAL; break;
-		case 1: error = 0; break;
-		default: error = uptodate;
-	}
-	rq->errors = error;
-	if (error)
-		tape->failed_pc = NULL;
-
-	spin_lock_irqsave(&tape->spinlock, flags);
-	if (tape->active_data_request == rq) {		/* The request was a pipelined data transfer request */
-		tape->active_stage = NULL;
-		tape->active_data_request = NULL;
-		tape->nr_pending_stages--;
-		if (rq->cmd == IDETAPE_WRITE_RQ) {
-#if ONSTREAM_DEBUG
-			if (tape->debug_level >= 2) {
-				if (tape->onstream) {
-					stage = tape->first_stage;
-					aux = stage->aux;
-					p = stage->bh->b_data;
-					if (ntohl(aux->logical_blk_num) < 11300 && ntohl(aux->logical_blk_num) > 11100)
-						printk(KERN_INFO "ide-tape: finished writing logical blk %u (data %x %x %x %x)\n", ntohl(aux->logical_blk_num), *p++, *p++, *p++, *p++);
-				}
-			}
-#endif
-			if (tape->onstream && !tape->raw) {
-				if (tape->first_frame_position == 0xba4) {
-#if ONSTREAM_DEBUG
-				if (tape->debug_level >= 2)
-					printk("ide-tape: %s: skipping over config parition..\n", tape->name);
-#endif
-					tape->onstream_write_error = 2;
-					if (tape->sem)
-						up(tape->sem);
-				}
-			}
-			remove_stage = 1;
-			if (error) {
-				set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
-				if (error == IDETAPE_ERROR_EOD)
-					idetape_abort_pipeline (drive);
-				if (tape->onstream && !tape->raw && error == IDETAPE_ERROR_GENERAL && tape->sense.sense_key == 3) {
-					clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
-					printk(KERN_ERR "ide-tape: %s: write error, enabling error recovery\n", tape->name);
-					tape->onstream_write_error = 1;
-					remove_stage = 0;
-					tape->nr_pending_stages++;
-					tape->next_stage = tape->first_stage;
-					rq->current_nr_sectors = rq->nr_sectors;
-					if (tape->sem)
-						up(tape->sem);
-				}
-			}
-		} else if (rq->cmd == IDETAPE_READ_RQ) {
-			if (error == IDETAPE_ERROR_EOD) {
-				set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
-				idetape_abort_pipeline(drive);
-			}
-		}
-		if (tape->next_stage != NULL && !tape->onstream_write_error) {
-			idetape_active_next_stage (drive);
-
-			/*
-			 *	Insert the next request into the request queue.
-			 */
-			(void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
-		} else if (!error) {
-			if (!tape->onstream)
-				idetape_increase_max_pipeline_stages (drive);
-		}
-	}
-	ide_end_drive_cmd (drive, 0, 0);
-	if (remove_stage)
-		idetape_remove_stage_head (drive);
-	if (tape->active_data_request == NULL)
-		clear_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
-	spin_unlock_irqrestore(&tape->spinlock, flags);
-}
-
-static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_request_sense_callback\n");
-#endif /* IDETAPE_DEBUG_LOG */
-	if (!tape->pc->error) {
-		idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
-		idetape_end_request (1,HWGROUP (drive));
-	} else {
-		printk (KERN_ERR "ide-tape: Error in REQUEST SENSE itself - Aborting request!\n");
-		idetape_end_request (0,HWGROUP (drive));
-	}
-	return ide_stopped;
-}
-
-static void idetape_create_request_sense_cmd (idetape_pc_t *pc)
-{
-	idetape_init_pc (pc);	
-	pc->c[0] = IDETAPE_REQUEST_SENSE_CMD;
-	pc->c[4] = 20;
-	pc->request_transfer = 18;
-	pc->callback = &idetape_request_sense_callback;
-}
-
-/*
- *	idetape_queue_pc_head generates a new packet command request in front
- *	of the request queue, before the current request, so that it will be
- *	processed immediately, on the next pass through the driver.
- *
- *	idetape_queue_pc_head is called from the request handling part of
- *	the driver (the "bottom" part). Safe storage for the request should
- *	be allocated with idetape_next_pc_storage and idetape_next_rq_storage
- *	before calling idetape_queue_pc_head.
- *
- *	Memory for those requests is pre-allocated at initialization time, and
- *	is limited to IDETAPE_PC_STACK requests. We assume that we have enough
- *	space for the maximum possible number of inter-dependent packet commands.
- *
- *	The higher level of the driver - The ioctl handler and the character
- *	device handling functions should queue request to the lower level part
- *	and wait for their completion using idetape_queue_pc_tail or
- *	idetape_queue_rw_tail.
- */
-static void idetape_queue_pc_head (ide_drive_t *drive,idetape_pc_t *pc,struct request *rq)
-{
-	ide_init_drive_cmd (rq);
-	rq->buffer = (char *) pc;
-	rq->cmd = IDETAPE_PC_RQ1;
-	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
-}
-
-/*
- *	idetape_retry_pc is called when an error was detected during the
- *	last packet command. We queue a request sense packet command in
- *	the head of the request list.
- */
-static ide_startstop_t idetape_retry_pc (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t *pc;
-	struct request *rq;
-	idetape_error_reg_t error;
-
-	error.all = IN_BYTE (IDE_ERROR_REG);
-	pc = idetape_next_pc_storage (drive);
-	rq = idetape_next_rq_storage (drive);
-	idetape_create_request_sense_cmd (pc);
-	set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
-	idetape_queue_pc_head (drive, pc, rq);
-	return ide_stopped;
-}
-
-/*
- *	idetape_postpone_request postpones the current request so that
- *	ide.c will be able to service requests from another device on
- *	the same hwgroup while we are polling for DSC.
- */
-static void idetape_postpone_request (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk(KERN_INFO "ide-tape: idetape_postpone_request\n");
-#endif
-	tape->postponed_rq = HWGROUP(drive)->rq;
-	ide_stall_queue(drive, tape->dsc_polling_frequency);
-}
-
-/*
- *	idetape_pc_intr is the usual interrupt handler which will be called
- *	during a packet command. We will transfer some of the data (as
- *	requested by the drive) and will re-point interrupt handler to us.
- *	When data transfer is finished, we will act according to the
- *	algorithm described before idetape_issue_packet_command.
- *
- */
-static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_status_reg_t status;
-	idetape_bcount_reg_t bcount;
-	idetape_ireason_reg_t ireason;
-	idetape_pc_t *pc=tape->pc;
-
-	unsigned int temp;
-	unsigned long cmd_time;
-#if SIMULATE_ERRORS
-	static int error_sim_count = 0;
-#endif
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_pc_intr interrupt handler\n");
-#endif /* IDETAPE_DEBUG_LOG */	
-
-	status.all = GET_STAT();					/* Clear the interrupt */
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
-		if (HWIF(drive)->dmaproc(ide_dma_end, drive)) {
-			/*
-			 * A DMA error is sometimes expected. For example,
-			 * if the tape is crossing a filemark during a
-			 * READ command, it will issue an irq and position
-			 * itself before the filemark, so that only a partial
-			 * data transfer will occur (which causes the DMA
-			 * error). In that case, we will later ask the tape
-			 * how much bytes of the original request were
-			 * actually transferred (we can't receive that
-			 * information from the DMA engine on most chipsets).
-			 */
-			set_bit (PC_DMA_ERROR, &pc->flags);
-		} else if (!status.b.check) {
-			pc->actually_transferred=pc->request_transfer;
-			idetape_update_buffers (pc);
-		}
-#if IDETAPE_DEBUG_LOG
-		if (tape->debug_level >= 4)
-			printk (KERN_INFO "ide-tape: DMA finished\n");
-#endif /* IDETAPE_DEBUG_LOG */
-	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-	if (!status.b.drq) {						/* No more interrupts */
-		cmd_time = (jiffies - tape->cmd_start_time) * 1000 / HZ;
-		tape->max_cmd_time = IDE_MAX(cmd_time, tape->max_cmd_time);
-#if IDETAPE_DEBUG_LOG
-		if (tape->debug_level >= 2)
-			printk (KERN_INFO "ide-tape: Packet command completed, %d bytes transferred\n", pc->actually_transferred);
-#endif /* IDETAPE_DEBUG_LOG */
-		clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
-
-		ide__sti();	/* local CPU only */
-
-#if SIMULATE_ERRORS
-		if ((pc->c[0] == IDETAPE_WRITE_CMD || pc->c[0] == IDETAPE_READ_CMD) && (++error_sim_count % 100) == 0) {
-			printk(KERN_INFO "ide-tape: %s: simulating error\n", tape->name);
-			status.b.check = 1;
-		}
-#endif
-		if (status.b.check && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD)
-			status.b.check = 0;
-		if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) {	/* Error detected */
-#if IDETAPE_DEBUG_LOG
-			if (tape->debug_level >= 1)
-				printk (KERN_INFO "ide-tape: %s: I/O error, ",tape->name);
-#endif /* IDETAPE_DEBUG_LOG */
-			if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
-				printk (KERN_ERR "ide-tape: I/O error in request sense command\n");
-				return ide_do_reset (drive);
-			}
-#if IDETAPE_DEBUG_LOG
-			if (tape->debug_level >= 1)
-				printk(KERN_INFO "ide-tape: [cmd %x]: check condition\n", pc->c[0]);
-#endif
-			return idetape_retry_pc (drive);				/* Retry operation */
-		}
-		pc->error = 0;
-		if (!tape->onstream && test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) {	/* Media access command */
-			tape->dsc_polling_start = jiffies;
-			tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST;
-			tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT;
-			idetape_postpone_request (drive);		/* Allow ide.c to handle other requests */
-			return ide_stopped;
-		}
-		if (tape->failed_pc == pc)
-			tape->failed_pc=NULL;
-		return pc->callback(drive);			/* Command finished - Call the callback function */
-	}
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
-		printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n");
-		printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
-		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
-		return ide_do_reset (drive);
-	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
-	bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);				/* on this interrupt */
-	ireason.all=IN_BYTE (IDE_IREASON_REG);
-
-	if (ireason.b.cod) {
-		printk (KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n");
-		return ide_do_reset (drive);
-	}
-	if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {	/* Hopefully, we will never get here */
-		printk (KERN_ERR "ide-tape: We wanted to %s, ", ireason.b.io ? "Write":"Read");
-		printk (KERN_ERR "ide-tape: but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write");
-		return ide_do_reset (drive);
-	}
-	if (!test_bit (PC_WRITING, &pc->flags)) {			/* Reading - Check that we have enough space */
-		temp = pc->actually_transferred + bcount.all;
-		if ( temp > pc->request_transfer) {
-			if (temp > pc->buffer_size) {
-				printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n");
-				idetape_discard_data (drive,bcount.all);
-				ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD,NULL);
-				return ide_started;
-			}
-#if IDETAPE_DEBUG_LOG
-			if (tape->debug_level >= 2)
-				printk (KERN_NOTICE "ide-tape: The tape wants to send us more data than expected - allowing transfer\n");
-#endif /* IDETAPE_DEBUG_LOG */
-		}
-	}
-	if (test_bit (PC_WRITING, &pc->flags)) {
-		if (pc->bh != NULL)
-			idetape_output_buffers (drive, pc, bcount.all);
-		else
-			atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
-	} else {
-		if (pc->bh != NULL)
-			idetape_input_buffers (drive, pc, bcount.all);
-		else
-			atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
-	}
-	pc->actually_transferred+=bcount.all;					/* Update the current position */
-	pc->current_position+=bcount.all;
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 2)
-		printk(KERN_INFO "ide-tape: [cmd %x] transferred %d bytes on that interrupt\n", pc->c[0], bcount.all);
-#endif
-	ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD,NULL);		/* And set the interrupt handler again */
-	return ide_started;
-}
-
-/*
- *	Packet Command Interface
- *
- *	The current Packet Command is available in tape->pc, and will not
- *	change until we finish handling it. Each packet command is associated
- *	with a callback function that will be called when the command is
- *	finished.
- *
- *	The handling will be done in three stages:
- *
- *	1.	idetape_issue_packet_command will send the packet command to the
- *		drive, and will set the interrupt handler to idetape_pc_intr.
- *
- *	2.	On each interrupt, idetape_pc_intr will be called. This step
- *		will be repeated until the device signals us that no more
- *		interrupts will be issued.
- *
- *	3.	ATAPI Tape media access commands have immediate status with a
- *		delayed process. In case of a successful initiation of a
- *		media access packet command, the DSC bit will be set when the
- *		actual execution of the command is finished. 
- *		Since the tape drive will not issue an interrupt, we have to
- *		poll for this event. In this case, we define the request as
- *		"low priority request" by setting rq_status to
- *		IDETAPE_RQ_POSTPONED, 	set a timer to poll for DSC and exit
- *		the driver.
- *
- *		ide.c will then give higher priority to requests which
- *		originate from the other device, until will change rq_status
- *		to RQ_ACTIVE.
- *
- *	4.	When the packet command is finished, it will be checked for errors.
- *
- *	5.	In case an error was found, we queue a request sense packet command
- *		in front of the request queue and retry the operation up to
- *		IDETAPE_MAX_PC_RETRIES times.
- *
- *	6.	In case no error was found, or we decided to give up and not
- *		to retry again, the callback function will be called and then
- *		we will handle the next request.
- *
- */
-static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t *pc = tape->pc;
-	idetape_ireason_reg_t ireason;
-	int retries = 100;
-	ide_startstop_t startstop;
-
-	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
-		printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
-		return startstop;
-	}
-	ireason.all=IN_BYTE (IDE_IREASON_REG);
-	while (retries-- && (!ireason.b.cod || ireason.b.io)) {
-		printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, retrying\n");
-		udelay(100);
-		ireason.all = IN_BYTE(IDE_IREASON_REG);
-		if (retries == 0) {
-			printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, ignoring\n");
-			ireason.b.cod = 1;
-			ireason.b.io = 0;
-		}
-	}
-	if (!ireason.b.cod || ireason.b.io) {
-		printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
-		return ide_do_reset (drive);
-	}
-	tape->cmd_start_time = jiffies;
-	ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL);	/* Set the interrupt routine */
-	atapi_output_bytes (drive,pc->c,12);			/* Send the actual packet */
-	return ide_started;
-}
-
-static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_bcount_reg_t bcount;
-	int dma_ok=0;
-
-#if IDETAPE_DEBUG_BUGS
-	if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
-		printk (KERN_ERR "ide-tape: possible ide-tape.c bug - Two request sense in serial were issued\n");
-	}
-#endif /* IDETAPE_DEBUG_BUGS */
-
-	if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
-		tape->failed_pc=pc;
-	tape->pc=pc;							/* Set the current packet command */
-
-	if (pc->retries > IDETAPE_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
-		/*
-		 *	We will "abort" retrying a packet command in case
-		 *	a legitimate error code was received (crossing a
-		 *	filemark, or DMA error in the end of media, for
-		 *	example).
-		 */
-		if (!test_bit (PC_ABORT, &pc->flags)) {
-			if (!(pc->c[0] == 0 && tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8))) {
-				printk (KERN_ERR "ide-tape: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
-					tape->name, pc->c[0], tape->sense_key, tape->asc, tape->ascq);
-				if (tape->onstream && pc->c[0] == 8 && tape->sense_key == 3 && tape->asc == 0x11)  /* AJN-1: 11 should be 0x11 */
-					printk(KERN_ERR "ide-tape: %s: enabling read error recovery\n", tape->name);
-			}
-			pc->error = IDETAPE_ERROR_GENERAL;		/* Giving up */
-		}
-		tape->failed_pc=NULL;
-		return pc->callback(drive);
-	}
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 2)
-		printk (KERN_INFO "ide-tape: Retry number - %d\n",pc->retries);
-#endif /* IDETAPE_DEBUG_LOG */
-
-	pc->retries++;
-	pc->actually_transferred=0;					/* We haven't transferred any data yet */
-	pc->current_position=pc->buffer;
-	bcount.all=pc->request_transfer;				/* Request to transfer the entire buffer at once */
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
-		printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n");
-		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
-	}
-	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
-		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-	if (IDE_CONTROL_REG)
-		OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
-	OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);			/* Use PIO/DMA */
-	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
-	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
-	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (dma_ok) {						/* Begin DMA, if necessary */
-		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
-		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
-	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-	if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) {
-		ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL);
-		OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
-		return ide_started;
-	} else {
-		OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
-		return idetape_transfer_pc(drive);
-	}
-}
-
-/*
- *	General packet command callback function.
- */
-static ide_startstop_t idetape_pc_callback (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_pc_callback\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-	idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive));
-	return ide_stopped;
-}
-
-/*
- *	A mode sense command is used to "sense" tape parameters.
- */
-static void idetape_create_mode_sense_cmd (idetape_pc_t *pc, byte page_code)
-{
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_MODE_SENSE_CMD;
-	pc->c[1] = 8;				/* DBD = 1 - Don't return block descriptors for now */
-	pc->c[2] = page_code;
-	pc->c[3] = 255;				/* Don't limit the returned information */
-	pc->c[4] = 255;				/* (We will just discard data in that case) */
-	if (page_code == IDETAPE_CAPABILITIES_PAGE)
-		pc->request_transfer = 24;
-	else
-		pc->request_transfer = 50;
-	pc->callback = &idetape_pc_callback;
-}
-
-static ide_startstop_t idetape_onstream_buffer_fill_callback (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	tape->max_frames = tape->pc->buffer[4 + 2];
-	tape->cur_frames = tape->pc->buffer[4 + 3];
-	if (tape->chrdev_direction == idetape_direction_write)
-		tape->tape_head = tape->buffer_head - tape->cur_frames;
-	else
-		tape->tape_head = tape->buffer_head + tape->cur_frames;
-	if (tape->tape_head != tape->last_tape_head) {
-		tape->last_tape_head = tape->tape_head;
-		tape->tape_still_time_begin = jiffies;
-		if (tape->tape_still_time > 200)
-			tape->measure_insert_time = 1;
-	}
-	tape->tape_still_time = (jiffies - tape->tape_still_time_begin) * 1000 / HZ;
-#if USE_IOTRACE
-	IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 1)
-		printk(KERN_INFO "ide-tape: buffer fill callback, %d/%d\n", tape->cur_frames, tape->max_frames);
-#endif
-	idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive));
-	return ide_stopped;
-}
-
-static void idetape_queue_onstream_buffer_fill (ide_drive_t *drive)
-{
-	idetape_pc_t *pc;
-	struct request *rq;
-
-	pc = idetape_next_pc_storage (drive);
-	rq = idetape_next_rq_storage (drive);
-	idetape_create_mode_sense_cmd (pc, 0x33);
-	pc->callback = idetape_onstream_buffer_fill_callback;
-	idetape_queue_pc_head (drive, pc, rq);
-}
-
-static void calculate_speeds(ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int full = 125, empty = 75;
-
-	if (jiffies > tape->controlled_pipeline_head_time + 120 * HZ) {
-		tape->controlled_previous_pipeline_head = tape->controlled_last_pipeline_head;
-		tape->controlled_previous_head_time = tape->controlled_pipeline_head_time;
-		tape->controlled_last_pipeline_head = tape->pipeline_head;
-		tape->controlled_pipeline_head_time = jiffies;
-	}
-	if (jiffies > tape->controlled_pipeline_head_time + 60 * HZ)
-		tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_last_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_pipeline_head_time);
-	else if (jiffies > tape->controlled_previous_head_time)
-		tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_previous_head_time);
-
-	if (tape->nr_pending_stages < tape->max_stages /*- 1 */) { /* -1 for read mode error recovery */
-		if (jiffies > tape->uncontrolled_previous_head_time + 10 * HZ) {
-			tape->uncontrolled_pipeline_head_time = jiffies;
-			tape->uncontrolled_pipeline_head_speed = (tape->pipeline_head - tape->uncontrolled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->uncontrolled_previous_head_time);
-		}
-	} else {
-		tape->uncontrolled_previous_head_time = jiffies;
-		tape->uncontrolled_previous_pipeline_head = tape->pipeline_head;
-		if (jiffies > tape->uncontrolled_pipeline_head_time + 30 * HZ) {
-			tape->uncontrolled_pipeline_head_time = jiffies;
-		}
-	}
-	tape->pipeline_head_speed = IDE_MAX(tape->uncontrolled_pipeline_head_speed, tape->controlled_pipeline_head_speed);
-	if (tape->speed_control == 0) {
-		tape->max_insert_speed = 5000;
-	} else if (tape->speed_control == 1) {
-		if (tape->nr_pending_stages >= tape->max_stages / 2)
-			tape->max_insert_speed = tape->pipeline_head_speed +
-				(1100 - tape->pipeline_head_speed) * 2 * (tape->nr_pending_stages - tape->max_stages / 2) / tape->max_stages;
-		else
-			tape->max_insert_speed = 500 +
-				(tape->pipeline_head_speed - 500) * 2 * tape->nr_pending_stages / tape->max_stages;
-		if (tape->nr_pending_stages >= tape->max_stages * 99 / 100)
-			tape->max_insert_speed = 5000;
-	} else if (tape->speed_control == 2) {
-		tape->max_insert_speed = tape->pipeline_head_speed * empty / 100 +
-			(tape->pipeline_head_speed * full / 100 - tape->pipeline_head_speed * empty / 100) * tape->nr_pending_stages / tape->max_stages;
-	} else
-		tape->max_insert_speed = tape->speed_control;
-	tape->max_insert_speed = IDE_MAX(tape->max_insert_speed, 500);
-}
-
-static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t *pc = tape->pc;
-	idetape_status_reg_t status;
-
-	if (tape->onstream)
-		printk(KERN_INFO "ide-tape: bug: onstream, media_access_finished\n");
-	status.all = GET_STAT();
-	if (status.b.dsc) {
-		if (status.b.check) {					/* Error detected */
-			printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name);
-			return idetape_retry_pc (drive);			/* Retry operation */
-		}
-		pc->error = 0;
-		if (tape->failed_pc == pc)
-			tape->failed_pc = NULL;
-	} else {
-		pc->error = IDETAPE_ERROR_GENERAL;
-		tape->failed_pc = NULL;
-	}
-	return pc->callback (drive);
-}
-
-static ide_startstop_t idetape_rw_callback (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	struct request *rq = HWGROUP(drive)->rq;
-	int blocks = tape->pc->actually_transferred / tape->tape_block_size;
-
-	tape->avg_size += blocks * tape->tape_block_size;
-	tape->insert_size += blocks * tape->tape_block_size;
-	if (tape->insert_size > 1024 * 1024)
-		tape->measure_insert_time = 1;
-	if (tape->measure_insert_time) {
-		tape->measure_insert_time = 0;
-		tape->insert_time = jiffies;
-		tape->insert_size = 0;
-	}
-	if (jiffies > tape->insert_time)
-		tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
-	if (jiffies - tape->avg_time >= HZ) {
-		tape->avg_speed = tape->avg_size * HZ / (jiffies - tape->avg_time) / 1024;
-		tape->avg_size = 0;
-		tape->avg_time = jiffies;
-	}
-
-#if IDETAPE_DEBUG_LOG	
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_rw_callback\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-	tape->first_frame_position += blocks;
-	rq->current_nr_sectors -= blocks;
-
-	if (!tape->pc->error)
-		idetape_end_request (1, HWGROUP (drive));
-	else
-		idetape_end_request (tape->pc->error, HWGROUP (drive));
-	return ide_stopped;
-}
-
-static void idetape_create_read_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
-{
-	struct buffer_head *p = bh;
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_READ_CMD;
-	put_unaligned (htonl (length), (unsigned int *) &pc->c[1]);
-	pc->c[1] = 1;
-	pc->callback = &idetape_rw_callback;
-	pc->bh = bh;
-	atomic_set(&bh->b_count, 0);
-	pc->buffer = NULL;
-	if (tape->onstream) {
-		while (p) {
-			atomic_set(&p->b_count, 0);
-			p = p->b_reqnext;
-		}
-	}
-	if (!tape->onstream) {
-		pc->request_transfer = pc->buffer_size = length * tape->tape_block_size;
-		if (pc->request_transfer == tape->stage_size)
-			set_bit (PC_DMA_RECOMMENDED, &pc->flags);
-	} else  {
-		if (length) {
-			pc->request_transfer = pc->buffer_size = 32768 + 512;
-			set_bit (PC_DMA_RECOMMENDED, &pc->flags);
-		} else
-			pc->request_transfer = 0;
-	}
-}
-
-static void idetape_create_read_buffer_cmd(idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
-{
-	int size = 32768;
-
-	struct buffer_head *p = bh;
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_READ_BUFFER_CMD;
-	pc->c[1] = IDETAPE_RETRIEVE_FAULTY_BLOCK;
-	pc->c[7] = size >> 8;
-	pc->c[8] = size & 0xff;
-	pc->callback = &idetape_pc_callback;
-	pc->bh = bh;
-	atomic_set(&bh->b_count, 0);
-	pc->buffer = NULL;
-	while (p) {
-		atomic_set(&p->b_count, 0);
-		p = p->b_reqnext;
-	}
-	pc->request_transfer = pc->buffer_size = size;
-}
-
-static void idetape_create_write_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
-{
-	struct buffer_head *p = bh;
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_WRITE_CMD;
-	put_unaligned (htonl (length), (unsigned int *) &pc->c[1]);
-	pc->c[1] = 1;
-	pc->callback = &idetape_rw_callback;
-	set_bit (PC_WRITING, &pc->flags);
-	if (tape->onstream) {
-		while (p) {
-			atomic_set(&p->b_count, p->b_size);
-			p = p->b_reqnext;
-		}
-	}
-	pc->bh = bh;
-	pc->b_data = bh->b_data;
-	pc->b_count = atomic_read(&bh->b_count);
-	pc->buffer = NULL;
-	if (!tape->onstream) {
-		pc->request_transfer = pc->buffer_size = length * tape->tape_block_size;
-		if (pc->request_transfer == tape->stage_size)
-			set_bit (PC_DMA_RECOMMENDED, &pc->flags);
-	} else  {
-		if (length) {
-			pc->request_transfer = pc->buffer_size = 32768 + 512;
-			set_bit (PC_DMA_RECOMMENDED, &pc->flags);
-		} else
-			pc->request_transfer = 0;
-	}
-}
-
-/*
- *	idetape_do_request is our request handling function.	
- */
-static ide_startstop_t idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t *pc;
-	struct request *postponed_rq = tape->postponed_rq;
-	idetape_status_reg_t status;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 5)
-		printk (KERN_INFO "ide-tape: rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
-	if (tape->debug_level >= 2)
-		printk (KERN_INFO "ide-tape: sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
-#endif /* IDETAPE_DEBUG_LOG */
-
-	if (!IDETAPE_RQ_CMD (rq->cmd)) {
-		/*
-		 *	We do not support buffer cache originated requests.
-		 */
-		printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue (%d)\n", drive->name, rq->cmd);
-		ide_end_request (0,HWGROUP (drive));			/* Let the common code handle it */
-		return ide_stopped;
-	}
-
-	/*
-	 *	Retry a failed packet command
-	 */
-	if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
-		return idetape_issue_packet_command (drive, tape->failed_pc);
-	}
-#if IDETAPE_DEBUG_BUGS
-	if (postponed_rq != NULL)
-		if (rq != postponed_rq) {
-			printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
-			idetape_end_request (0,HWGROUP (drive));
-			return ide_stopped;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-
-	tape->postponed_rq = NULL;
-
-	/*
-	 *	If the tape is still busy, postpone our request and service
-	 *	the other device meanwhile.
-	 */
-	status.all = GET_STAT();
-
-	/*
-	 * The OnStream tape drive doesn't support DSC. Assume
-	 * that DSC is always set.
-	 */
-	if (tape->onstream)
-		status.b.dsc = 1;
-	if (!drive->dsc_overlap && rq->cmd != IDETAPE_PC_RQ2)
-		set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
-
-	/*
-	 * For the OnStream tape, check the current status of the tape
-	 * internal buffer using data gathered from the buffer fill
-	 * mode page, and postpone our request, effectively "disconnecting"
-	 * from the IDE bus, in case the buffer is full (writing) or
-	 * empty (reading), and there is a danger that our request will
-	 * hold the IDE bus during actual media access.
-	 */
-	if (tape->tape_still_time > 100 && tape->tape_still_time < 200)
-		tape->measure_insert_time = 1;
-	if (tape->req_buffer_fill && (rq->cmd == IDETAPE_WRITE_RQ || rq->cmd == IDETAPE_READ_RQ)) {
-		tape->req_buffer_fill = 0;
-		tape->writes_since_buffer_fill = 0;
-		tape->reads_since_buffer_fill = 0;
-		tape->last_buffer_fill = jiffies;
-		idetape_queue_onstream_buffer_fill(drive);
-		if (jiffies > tape->insert_time)
-			tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
-		return ide_stopped;
-	}
-	if (jiffies > tape->insert_time)
-		tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
-	calculate_speeds(drive);
-	if (tape->onstream && tape->max_frames &&
-	   ((rq->cmd == IDETAPE_WRITE_RQ && (tape->cur_frames == tape->max_frames || (tape->speed_control && tape->cur_frames > 5 && (tape->insert_speed > tape->max_insert_speed || (0 /* tape->cur_frames > 30 && tape->tape_still_time > 200 */))))) ||
-	    (rq->cmd == IDETAPE_READ_RQ && (tape->cur_frames == 0 || (tape->speed_control && (tape->cur_frames < tape->max_frames - 5) && tape->insert_speed > tape->max_insert_speed)) && rq->nr_sectors))) {
-#if IDETAPE_DEBUG_LOG
-		if (tape->debug_level >= 4)
-			printk(KERN_INFO "ide-tape: postponing request, cmd %d, cur %d, max %d\n",
-				rq->cmd, tape->cur_frames, tape->max_frames);
-#endif
-		if (tape->postpone_cnt++ < 500) {
-			status.b.dsc = 0;
-			tape->req_buffer_fill = 1;
-		}
-#if ONSTREAM_DEBUG
-		else if (tape->debug_level >= 4) 
-			printk(KERN_INFO "ide-tape: %s: postpone_cnt %d\n", tape->name, tape->postpone_cnt);
-#endif
-	}
-	if (!test_and_clear_bit (IDETAPE_IGNORE_DSC, &tape->flags) && !status.b.dsc) {
-		if (postponed_rq == NULL) {
-			tape->dsc_polling_start = jiffies;
-			tape->dsc_polling_frequency = tape->best_dsc_rw_frequency;
-			tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT;
-		} else if ((signed long) (jiffies - tape->dsc_timeout) > 0) {
-			printk (KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name);
-			if (rq->cmd == IDETAPE_PC_RQ2) {
-				idetape_media_access_finished (drive);
-				return ide_stopped;
-			} else {
-				return ide_do_reset (drive);
-			}
-		} else if (jiffies - tape->dsc_polling_start > IDETAPE_DSC_MA_THRESHOLD)
-			tape->dsc_polling_frequency = IDETAPE_DSC_MA_SLOW;
-		idetape_postpone_request (drive);
-		return ide_stopped;
-	}
-	switch (rq->cmd) {
-		case IDETAPE_READ_RQ:
-			tape->buffer_head++;
-#if USE_IOTRACE
-			IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
-			tape->postpone_cnt = 0;
-			tape->reads_since_buffer_fill++;
-			if (tape->onstream) {
-				if (tape->cur_frames - tape->reads_since_buffer_fill <= 0)
-					tape->req_buffer_fill = 1;
-				if (jiffies > tape->last_buffer_fill + 5 * HZ / 100)
-					tape->req_buffer_fill = 1;
-			}
-			pc=idetape_next_pc_storage (drive);
-			idetape_create_read_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
-			break;
-		case IDETAPE_WRITE_RQ:
-			tape->buffer_head++;
-#if USE_IOTRACE
-			IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
-			tape->postpone_cnt = 0;
-			tape->writes_since_buffer_fill++;
-			if (tape->onstream) {
-				if (tape->cur_frames + tape->writes_since_buffer_fill >= tape->max_frames)
-					tape->req_buffer_fill = 1;
-				if (jiffies > tape->last_buffer_fill + 5 * HZ / 100)
-					tape->req_buffer_fill = 1;
-				calculate_speeds(drive);
-			}
-			pc=idetape_next_pc_storage (drive);
-			idetape_create_write_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
-			break;
-		case IDETAPE_READ_BUFFER_RQ:
-			tape->postpone_cnt = 0;
-			pc=idetape_next_pc_storage (drive);
-			idetape_create_read_buffer_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
-			break;
-		case IDETAPE_ABORTED_WRITE_RQ:
-			rq->cmd = IDETAPE_WRITE_RQ;
-			idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive));
-			return ide_stopped;
-		case IDETAPE_ABORTED_READ_RQ:
-#if IDETAPE_DEBUG_LOG
-			if (tape->debug_level >= 2)
-				printk(KERN_INFO "ide-tape: %s: detected aborted read rq\n", tape->name);
-#endif
-			rq->cmd = IDETAPE_READ_RQ;
-			idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive));
-			return ide_stopped;
-		case IDETAPE_PC_RQ1:
-			pc=(idetape_pc_t *) rq->buffer;
-			rq->cmd = IDETAPE_PC_RQ2;
-			break;
-		case IDETAPE_PC_RQ2:
-			idetape_media_access_finished (drive);
-			return ide_stopped;
-		default:
-			printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n");
-			idetape_end_request (0,HWGROUP (drive));
-			return ide_stopped;
-	}
-	return idetape_issue_packet_command (drive, pc);
-}
-
-/*
- *	Pipeline related functions
- */
-static inline int idetape_pipeline_active (idetape_tape_t *tape)
-{
-	int rc1, rc2;
-
-	rc1 = test_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
-	rc2 = (tape->active_data_request != NULL);
-	return rc1;
-}
-
-/*
- *	idetape_kmalloc_stage uses __get_free_page to allocate a pipeline
- *	stage, along with all the necessary small buffers which together make
- *	a buffer of size tape->stage_size (or a bit more). We attempt to
- *	combine sequential pages as much as possible.
- *
- *	Returns a pointer to the new allocated stage, or NULL if we
- *	can't (or don't want to) allocate a stage.
- *
- *	Pipeline stages are optional and are used to increase performance.
- *	If we can't allocate them, we'll manage without them.
- */
-static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape, int full, int clear)
-{
-	idetape_stage_t *stage;
-	struct buffer_head *prev_bh, *bh;
-	int pages = tape->pages_per_stage;
-	char *b_data;
-
-	if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL)
-		return NULL;
-	stage->next = NULL;
-
-	bh = stage->bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL);
-	if (bh == NULL)
-		goto abort;
-	bh->b_reqnext = NULL;
-	if ((bh->b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL)
-		goto abort;
-	if (clear)
-		memset(bh->b_data, 0, PAGE_SIZE);
-	bh->b_size = PAGE_SIZE;
-	atomic_set(&bh->b_count, full ? bh->b_size : 0);
-	set_bit (BH_Lock, &bh->b_state);
-
-	while (--pages) {
-		if ((b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL)
-			goto abort;
-		if (clear)
-			memset(b_data, 0, PAGE_SIZE);
-		if (bh->b_data == b_data + PAGE_SIZE) {
-			bh->b_size += PAGE_SIZE;
-			bh->b_data -= PAGE_SIZE;
-			if (full)
-				atomic_add(PAGE_SIZE, &bh->b_count);
-			continue;
-		}
-		if (b_data == bh->b_data + bh->b_size) {
-			bh->b_size += PAGE_SIZE;
-			if (full)
-				atomic_add(PAGE_SIZE, &bh->b_count);
-			continue;
-		}
-		prev_bh = bh;
-		if ((bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL)) == NULL) {
-			free_page ((unsigned long) b_data);
-			goto abort;
-		}
-		bh->b_reqnext = NULL;
-		bh->b_data = b_data;
-		bh->b_size = PAGE_SIZE;
-		atomic_set(&bh->b_count, full ? bh->b_size : 0);
-		set_bit (BH_Lock, &bh->b_state);
-		prev_bh->b_reqnext = bh;
-	}
-	bh->b_size -= tape->excess_bh_size;
-	if (full)
-		atomic_sub(tape->excess_bh_size, &bh->b_count);
-	if (tape->onstream)
-		stage->aux = (os_aux_t *) (bh->b_data + bh->b_size - OS_AUX_SIZE);
-	return stage;
-abort:
-	__idetape_kfree_stage (stage);
-	return NULL;
-}
-
-static idetape_stage_t *idetape_kmalloc_stage (idetape_tape_t *tape)
-{
-	idetape_stage_t *cache_stage = tape->cache_stage;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_kmalloc_stage\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-	if (tape->nr_stages >= tape->max_stages)
-		return NULL;
-	if (cache_stage != NULL) {
-		tape->cache_stage = NULL;
-		return cache_stage;
-	}
-	return __idetape_kmalloc_stage (tape, 0, 0);
-}
-
-static void idetape_copy_stage_from_user (idetape_tape_t *tape, idetape_stage_t *stage, const char *buf, int n)
-{
-	struct buffer_head *bh = tape->bh;
-	int count;
-
-	while (n) {
-#if IDETAPE_DEBUG_BUGS
-		if (bh == NULL) {
-			printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_from_user\n");
-			return;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-		count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), n);
-		copy_from_user (bh->b_data + atomic_read(&bh->b_count), buf, count);
-		n -= count; atomic_add(count, &bh->b_count); buf += count;
-		if (atomic_read(&bh->b_count) == bh->b_size) {
-			bh = bh->b_reqnext;
-			if (bh)
-				atomic_set(&bh->b_count, 0);
-		}
-	}
-	tape->bh = bh;
-}
-
-static void idetape_copy_stage_to_user (idetape_tape_t *tape, char *buf, idetape_stage_t *stage, int n)
-{
-	struct buffer_head *bh = tape->bh;
-	int count;
-
-	while (n) {
-#if IDETAPE_DEBUG_BUGS
-		if (bh == NULL) {
-			printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_to_user\n");
-			return;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-		count = IDE_MIN (tape->b_count, n);
-		copy_to_user (buf, tape->b_data, count);
-		n -= count; tape->b_data += count; tape->b_count -= count; buf += count;
-		if (!tape->b_count) {
-			tape->bh = bh = bh->b_reqnext;
-			if (bh) {
-				tape->b_data = bh->b_data;
-				tape->b_count = atomic_read(&bh->b_count);
-			}
-		}
-	}
-}
-
-static void idetape_init_merge_stage (idetape_tape_t *tape)
-{
-	struct buffer_head *bh = tape->merge_stage->bh;
-	
-	tape->bh = bh;
-	if (tape->chrdev_direction == idetape_direction_write)
-		atomic_set(&bh->b_count, 0);
-	else {
-		tape->b_data = bh->b_data;
-		tape->b_count = atomic_read(&bh->b_count);
-	}
-}
-
-static void idetape_switch_buffers (idetape_tape_t *tape, idetape_stage_t *stage)
-{
-	struct buffer_head *tmp;
-	os_aux_t *tmp_aux;
-
-	tmp = stage->bh; tmp_aux = stage->aux;
-	stage->bh = tape->merge_stage->bh; stage->aux = tape->merge_stage->aux;
-	tape->merge_stage->bh = tmp; tape->merge_stage->aux = tmp_aux;
-	idetape_init_merge_stage (tape);
-}
-
-/*
- *	idetape_add_stage_tail adds a new stage at the end of the pipeline.
- */
-static void idetape_add_stage_tail (ide_drive_t *drive,idetape_stage_t *stage)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	unsigned long flags;
-	
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_add_stage_tail\n");
-#endif /* IDETAPE_DEBUG_LOG */
-	spin_lock_irqsave(&tape->spinlock, flags);
-	stage->next=NULL;
-	if (tape->last_stage != NULL)
-		tape->last_stage->next=stage;
-	else
-		tape->first_stage=tape->next_stage=stage;
-	tape->last_stage=stage;
-	if (tape->next_stage == NULL)
-		tape->next_stage=tape->last_stage;
-	tape->nr_stages++;
-	tape->nr_pending_stages++;
-	spin_unlock_irqrestore(&tape->spinlock, flags);
-}
-
-/*
- * Initialize the OnStream AUX
- */
-static void idetape_init_stage (ide_drive_t *drive, idetape_stage_t *stage, int frame_type, int logical_blk_num)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	os_aux_t *aux = stage->aux;
-	os_partition_t *par = &aux->partition;
-	os_dat_t *dat = &aux->dat;
-
-	if (!tape->onstream || tape->raw)
-		return;
-	memset(aux, 0, sizeof(*aux));
-	aux->format_id = htonl(0);
-	memcpy(aux->application_sig, "LIN3", 4);
-	aux->hdwr = htonl(0);
-	aux->frame_type = frame_type;
-
-	if (frame_type == OS_FRAME_TYPE_HEADER) {
-		aux->update_frame_cntr = htonl(tape->update_frame_cntr);
-		par->partition_num = OS_CONFIG_PARTITION;
-		par->par_desc_ver = OS_PARTITION_VERSION;
-		par->wrt_pass_cntr = htons(0xffff);
-		par->first_frame_addr = htonl(0);
-		par->last_frame_addr = htonl(0xbb7);
-	} else {
-		aux->update_frame_cntr = htonl(0);
-		par->partition_num = OS_DATA_PARTITION;
-		par->par_desc_ver = OS_PARTITION_VERSION;
-		par->wrt_pass_cntr = htons(tape->wrt_pass_cntr);
-		par->first_frame_addr = htonl(0x14);
-		par->last_frame_addr = htonl(19239 * 24);
-	}
-	if (frame_type != OS_FRAME_TYPE_HEADER) {
-		aux->frame_seq_num = htonl(logical_blk_num);
-		aux->logical_blk_num_high = htonl(0);
-		aux->logical_blk_num = htonl(logical_blk_num);
-	} else {
-		aux->frame_seq_num = htonl(0);
-		aux->logical_blk_num_high = htonl(0);
-		aux->logical_blk_num = htonl(0);
-	}
-
-	if (frame_type != OS_FRAME_TYPE_HEADER) {
-		dat->dat_sz = 8;
-		dat->reserved1 = 0;
-		dat->entry_cnt = 1;
-		dat->reserved3 = 0;
-		if (frame_type == OS_FRAME_TYPE_DATA)
-			dat->dat_list[0].blk_sz = htonl(32 * 1024);
-		else
-			dat->dat_list[0].blk_sz = 0;
-		dat->dat_list[0].blk_cnt = htons(1);
-		if (frame_type == OS_FRAME_TYPE_MARKER)
-			dat->dat_list[0].flags = OS_DAT_FLAGS_MARK;
-		else
-			dat->dat_list[0].flags = OS_DAT_FLAGS_DATA;
-		dat->dat_list[0].reserved = 0;
-	} else
-		aux->next_mark_addr = htonl(tape->first_mark_addr);
-	aux->filemark_cnt = ntohl(tape->filemark_cnt);
-	aux->phys_fm = ntohl(0xffffffff);
-	aux->last_mark_addr = ntohl(tape->last_mark_addr);
-}
-
-/*
- *	idetape_wait_for_request installs a semaphore in a pending request
- *	and sleeps until it is serviced.
- *
- *	The caller should ensure that the request will not be serviced
- *	before we install the semaphore (usually by disabling interrupts).
- */
-static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq)
-{
-	DECLARE_MUTEX_LOCKED(sem);
-	idetape_tape_t *tape = drive->driver_data;
-
-#if IDETAPE_DEBUG_BUGS
-	if (rq == NULL || !IDETAPE_RQ_CMD (rq->cmd)) {
-		printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n");
-		return;
-	}
-#endif /* IDETAPE_DEBUG_BUGS */
-	rq->sem = &sem;
-	tape->sem = &sem;
-	spin_unlock(&tape->spinlock);
-	down(&sem);
-	rq->sem = NULL;
-	tape->sem = NULL;
-	spin_lock_irq(&tape->spinlock);
-}
-
-static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_read_position_result_t *result;
-	
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_read_position_callback\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-	if (!tape->pc->error) {
-		result = (idetape_read_position_result_t *) tape->pc->buffer;
-#if IDETAPE_DEBUG_LOG
-		if (tape->debug_level >= 2)
-			printk (KERN_INFO "ide-tape: BOP - %s\n",result->bop ? "Yes":"No");
-		if (tape->debug_level >= 2)
-			printk (KERN_INFO "ide-tape: EOP - %s\n",result->eop ? "Yes":"No");
-#endif /* IDETAPE_DEBUG_LOG */
-		if (result->bpu) {
-			printk (KERN_INFO "ide-tape: Block location is unknown to the tape\n");
-			clear_bit (IDETAPE_ADDRESS_VALID, &tape->flags);
-			idetape_end_request (0,HWGROUP (drive));
-		} else {
-#if IDETAPE_DEBUG_LOG
-			if (tape->debug_level >= 2)
-				printk (KERN_INFO "ide-tape: Block Location - %u\n", ntohl (result->first_block));
-#endif /* IDETAPE_DEBUG_LOG */
-			tape->partition = result->partition;
-			tape->first_frame_position = ntohl (result->first_block);
-			tape->last_frame_position = ntohl (result->last_block);
-			tape->blocks_in_buffer = result->blocks_in_buffer[2];
-			set_bit (IDETAPE_ADDRESS_VALID, &tape->flags);
-			idetape_end_request (1,HWGROUP (drive));
-		}
-	} else {
-		idetape_end_request (0,HWGROUP (drive));
-	}
-	return ide_stopped;
-}
-
-/*
- *	idetape_create_write_filemark_cmd will:
- *
- *		1.	Write a filemark if write_filemark=1.
- *		2.	Flush the device buffers without writing a filemark
- *			if write_filemark=0.
- *
- */
-static void idetape_create_write_filemark_cmd (ide_drive_t *drive, idetape_pc_t *pc,int write_filemark)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD;
-	if (tape->onstream)
-		pc->c[1] = 1;
-	pc->c[4] = write_filemark;
-	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
-	pc->callback = &idetape_pc_callback;
-}
-
-static void idetape_create_test_unit_ready_cmd(idetape_pc_t *pc)
-{
-	idetape_init_pc(pc);
-	pc->c[0] = IDETAPE_TEST_UNIT_READY_CMD;
-	pc->callback = &idetape_pc_callback;
-}
-
-/*
- *	idetape_queue_pc_tail is based on the following functions:
- *
- *	ide_do_drive_cmd from ide.c
- *	cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c
- *
- *	We add a special packet command request to the tail of the request queue,
- *	and wait for it to be serviced.
- *
- *	This is not to be called from within the request handling part
- *	of the driver ! We allocate here data in the stack, and it is valid
- *	until the request is finished. This is not the case for the bottom
- *	part of the driver, where we are always leaving the functions to wait
- *	for an interrupt or a timer event.
- *
- *	From the bottom part of the driver, we should allocate safe memory
- *	using idetape_next_pc_storage and idetape_next_rq_storage, and add
- *	the request to the request list without waiting for it to be serviced !
- *	In that case, we usually use idetape_queue_pc_head.
- */
-static int __idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc)
-{
-	struct request rq;
-
-	ide_init_drive_cmd (&rq);
-	rq.buffer = (char *) pc;
-	rq.cmd = IDETAPE_PC_RQ1;
-	return ide_do_drive_cmd (drive, &rq, ide_wait);
-}
-
-static void idetape_create_load_unload_cmd (ide_drive_t *drive, idetape_pc_t *pc,int cmd)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_LOAD_UNLOAD_CMD;
-	pc->c[4] = cmd;
-	if (tape->onstream)
-		pc->c[1] = 1;
-	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
-	pc->callback = &idetape_pc_callback;
-}
-
-static int idetape_wait_ready (ide_drive_t *drive, unsigned long long timeout)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t pc;
-
-	/*
-	 * Wait for the tape to become ready
-	 */
-	timeout += jiffies;
-	while (jiffies < timeout) {
-		idetape_create_test_unit_ready_cmd(&pc);
-		if (!__idetape_queue_pc_tail(drive, &pc))
-			return 0;
-		if (tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2) {
-			idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK);
-			__idetape_queue_pc_tail(drive,&pc);
-			idetape_create_test_unit_ready_cmd(&pc);
-			if (!__idetape_queue_pc_tail(drive, &pc))
-				return 0;
-		}
-		if (!(tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8)))
-			break;
-		current->state = TASK_INTERRUPTIBLE;
-  		schedule_timeout(HZ / 10);
-	}
-	return -EIO;
-}
-
-static int idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int rc;
-
-	rc = __idetape_queue_pc_tail(drive, pc);
-	if (rc)	return rc;
-	if (tape->onstream && test_bit(PC_WAIT_FOR_DSC, &pc->flags))
-		rc = idetape_wait_ready(drive, 60 * 10 * HZ);   /* AJN-4: Changed from 5 to 10 minutes;
-                          because retension takes approx. 8:20 with Onstream 30GB tape */
-	return rc;
-}
-
-static int idetape_flush_tape_buffers (ide_drive_t *drive)
-{
-	idetape_pc_t pc;
-	int rc;
-
-	idetape_create_write_filemark_cmd(drive, &pc, 0);
-	if ((rc = idetape_queue_pc_tail (drive,&pc)))
-		return rc;
-	idetape_wait_ready(drive, 60 * 5 * HZ);
-	return 0;
-}
-
-static void idetape_create_read_position_cmd (idetape_pc_t *pc)
-{
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_READ_POSITION_CMD;
-	pc->request_transfer = 20;
-	pc->callback = &idetape_read_position_callback;
-}
-
-static int idetape_read_position (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t pc;
-	int position;
-
-#ifdef NO_LONGER_REQUIRED
-	idetape_flush_tape_buffers(drive);
-#endif
-	idetape_create_read_position_cmd(&pc);
-	if (idetape_queue_pc_tail (drive,&pc))
-		return -1;
-	position = tape->first_frame_position;
-#ifdef NO_LONGER_REQUIRED
-	if (tape->onstream) {
-		if ((position != tape->last_frame_position - tape->blocks_in_buffer) &&
-		    (position != tape->last_frame_position + tape->blocks_in_buffer)) {
-			if (tape->blocks_in_buffer == 0) {
-				printk("ide-tape: %s: correcting read position %d, %d, %d\n", tape->name, position, tape->last_frame_position, tape->blocks_in_buffer);
-				position = tape->last_frame_position;
-				tape->first_frame_position = position;
-			}
-		}
-	}
-#endif
-	return position;
-}
-
-static void idetape_create_locate_cmd (ide_drive_t *drive, idetape_pc_t *pc, unsigned int block, byte partition, int skip)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_LOCATE_CMD;
-	if (tape->onstream)
-		pc->c[1] = 1;
-	else
-		pc->c[1] = 2;
-	put_unaligned (htonl (block), (unsigned int *) &pc->c[3]);
-	pc->c[8] = partition;
-	if (tape->onstream)
-		pc->c[9] = skip << 7;
-	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
-	pc->callback = &idetape_pc_callback;
-}
-
-static void idetape_create_prevent_cmd (ide_drive_t *drive, idetape_pc_t *pc, int prevent)
-{
-	idetape_init_pc(pc);
-	pc->c[0] = IDETAPE_PREVENT_CMD;
-	pc->c[4] = prevent;
-	pc->callback = &idetape_pc_callback;
-}
-
-static int __idetape_discard_read_pipeline (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	unsigned long flags;
-	int cnt;
-
-	if (tape->chrdev_direction != idetape_direction_read)
-		return 0;
-	tape->merge_stage_size = 0;
-	if (tape->merge_stage != NULL) {
-		__idetape_kfree_stage (tape->merge_stage);
-		tape->merge_stage = NULL;
-	}
-	tape->chrdev_direction = idetape_direction_none;
-	
-	if (tape->first_stage == NULL)
-		return 0;
-
-	spin_lock_irqsave(&tape->spinlock, flags);
-	tape->next_stage = NULL;
-	if (idetape_pipeline_active (tape))
-		idetape_wait_for_request(drive, tape->active_data_request);
-	spin_unlock_irqrestore(&tape->spinlock, flags);
-
-	cnt = tape->nr_stages - tape->nr_pending_stages;
-	while (tape->first_stage != NULL)
-		idetape_remove_stage_head (drive);
-	tape->nr_pending_stages = 0;
-	tape->max_stages = tape->min_pipeline;
-	return cnt;
-}
-
-/*
- *	idetape_position_tape positions the tape to the requested block
- *	using the LOCATE packet command. A READ POSITION command is then
- *	issued to check where we are positioned.
- *
- *	Like all higher level operations, we queue the commands at the tail
- *	of the request queue and wait for their completion.
- *	
- */
-static int idetape_position_tape (ide_drive_t *drive, unsigned int block, byte partition, int skip)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int retval;
-	idetape_pc_t pc;
-
-	if (tape->chrdev_direction == idetape_direction_read)
-		__idetape_discard_read_pipeline(drive);
-	idetape_wait_ready(drive, 60 * 5 * HZ);
-	idetape_create_locate_cmd (drive, &pc, block, partition, skip);
-	retval=idetape_queue_pc_tail (drive,&pc);
-	if (retval) return (retval);
-
-	idetape_create_read_position_cmd (&pc);
-	return (idetape_queue_pc_tail (drive,&pc));
-}
-
-static void idetape_discard_read_pipeline (ide_drive_t *drive, int restore_position)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int cnt;
-	int seek, position;
-
-	cnt = __idetape_discard_read_pipeline(drive);
-	if (restore_position) {
-		position = idetape_read_position(drive);
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 2)
-			printk(KERN_INFO "ide-tape: address %u, nr_stages %d\n", position, cnt);
-#endif
-		seek = position > cnt ? position - cnt : 0;
-		if (idetape_position_tape(drive, seek, 0, 0)) {
-			printk(KERN_INFO "ide-tape: %s: position_tape failed in discard_pipeline()\n", tape->name);
-			return;
-		}
-	}
-}
-
-static void idetape_update_stats (ide_drive_t *drive)
-{
-	idetape_pc_t pc;
-
-	idetape_create_mode_sense_cmd (&pc, 0x33);
-	pc.callback = idetape_onstream_buffer_fill_callback;
-	(void) idetape_queue_pc_tail(drive, &pc);
-}
-
-/*
- *	idetape_queue_rw_tail generates a read/write request for the block
- *	device interface and wait for it to be serviced.
- */
-static int idetape_queue_rw_tail (ide_drive_t *drive, int cmd, int blocks, struct buffer_head *bh)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	struct request rq;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 2)
-		printk (KERN_INFO "ide-tape: idetape_queue_rw_tail: cmd=%d\n",cmd);
-#endif /* IDETAPE_DEBUG_LOG */
-#if IDETAPE_DEBUG_BUGS
-	if (idetape_pipeline_active (tape)) {
-		printk (KERN_ERR "ide-tape: bug: the pipeline is active in idetape_queue_rw_tail\n");
-		return (0);
-	}
-#endif /* IDETAPE_DEBUG_BUGS */	
-
-	ide_init_drive_cmd (&rq);
-	rq.bh = bh;
-	rq.cmd = cmd;
-	rq.sector = tape->first_frame_position;
-	rq.nr_sectors = rq.current_nr_sectors = blocks;
-	if (tape->onstream)
-		tape->postpone_cnt = 600;
-	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
-
-	if (cmd != IDETAPE_READ_RQ && cmd != IDETAPE_WRITE_RQ)
-		return 0;
-
-	if (tape->merge_stage)
-		idetape_init_merge_stage (tape);
-	if (rq.errors == IDETAPE_ERROR_GENERAL)
-		return -EIO;
-	return (tape->tape_block_size * (blocks-rq.current_nr_sectors));
-}
-
-/*
- * Read back the drive's internal buffer contents, as a part
- * of the write error recovery mechanism for old OnStream
- * firmware revisions.
- */
-static void idetape_onstream_read_back_buffer (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int frames, i, logical_blk_num;
-	idetape_stage_t *stage, *first = NULL, *last = NULL;
-	os_aux_t *aux;
-	struct request *rq;
-	unsigned char *p;
-	unsigned long flags;
-
-	idetape_update_stats(drive);
-	frames = tape->cur_frames;
-	logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num) - frames;
-	printk(KERN_INFO "ide-tape: %s: reading back %d frames from the drive's internal buffer\n", tape->name, frames);
-	for (i = 0; i < frames; i++) {
-		stage = __idetape_kmalloc_stage(tape, 0, 0);
-		if (!first)
-			first = stage;
-		aux = stage->aux;
-		p = stage->bh->b_data;
-		idetape_queue_rw_tail(drive, IDETAPE_READ_BUFFER_RQ, tape->capabilities.ctl, stage->bh);
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 2)
-			printk(KERN_INFO "ide-tape: %s: read back logical block %d, data %x %x %x %x\n", tape->name, logical_blk_num, *p++, *p++, *p++, *p++);
-#endif
-		rq = &stage->rq;
-		ide_init_drive_cmd (rq);
-		rq->cmd = IDETAPE_WRITE_RQ;
-		rq->sector = tape->first_frame_position;
-		rq->nr_sectors = rq->current_nr_sectors = tape->capabilities.ctl;
-		idetape_init_stage(drive, stage, OS_FRAME_TYPE_DATA, logical_blk_num++);
-		stage->next = NULL;
-		if (last)
-			last->next = stage;
-		last = stage;
-	}
-	if (frames) {
-		spin_lock_irqsave(&tape->spinlock, flags);
-		last->next = tape->first_stage;
-		tape->next_stage = tape->first_stage = first;
-		tape->nr_stages += frames;
-		tape->nr_pending_stages += frames;
-		spin_unlock_irqrestore(&tape->spinlock, flags);
-	}
-	idetape_update_stats(drive);
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 2)
-		printk(KERN_INFO "ide-tape: %s: frames left in buffer: %d\n", tape->name, tape->cur_frames);
-#endif
-}
-
-/*
- * Error recovery algorithm for the OnStream tape.
- */
-static void idetape_onstream_write_error_recovery (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	unsigned int block;
-
-	if (tape->onstream_write_error == 1) {
-		printk(KERN_ERR "ide-tape: %s: detected physical bad block at %u\n", tape->name, ntohl(tape->sense.information));
-		block = ntohl(tape->sense.information) + 80;
-		idetape_update_stats(drive);
-		printk(KERN_ERR "ide-tape: %s: relocating %d buffered logical blocks to physical block %u\n", tape->name, tape->cur_frames, block);
-		idetape_update_stats(drive);
-		if (tape->firmware_revision_num >= 106)
-			idetape_position_tape(drive, block, 0, 1);
-		else {
-			idetape_onstream_read_back_buffer(drive);
-			idetape_position_tape(drive, block, 0, 0);
-		}
-		idetape_read_position(drive);
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 1)
-			printk(KERN_ERR "ide-tape: %s: positioning complete, cur_frames %d, pos %d, tape pos %d\n", tape->name, tape->cur_frames, tape->first_frame_position, tape->last_frame_position);
-#endif
-	} else if (tape->onstream_write_error == 2) {
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 1)
-			printk(KERN_INFO "ide-tape: %s: skipping over config partition\n", tape->name);
-#endif
-		idetape_flush_tape_buffers(drive);
-		block = idetape_read_position(drive);
-		if (block != 0xba4) 
-			printk(KERN_ERR "ide-tape: warning, current position %d, expected %d\n", block, 0xba4);
-		idetape_position_tape(drive, 0xbb8, 0, 0);
-	}
-	tape->onstream_write_error = 0;
-}
-
-/*
- *	idetape_insert_pipeline_into_queue is used to start servicing the
- *	pipeline stages, starting from tape->next_stage.
- */
-static void idetape_insert_pipeline_into_queue (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	if (tape->next_stage == NULL)
-		return;
-	if (!idetape_pipeline_active (tape)) {
-		if (tape->onstream_write_error)
-			idetape_onstream_write_error_recovery(drive);
-		set_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
-		idetape_active_next_stage (drive);
-		(void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
-	}
-}
-
-static void idetape_create_inquiry_cmd (idetape_pc_t *pc)
-{
-	idetape_init_pc(pc);
-	pc->c[0] = IDETAPE_INQUIRY_CMD;
-	pc->c[4] = pc->request_transfer = 254;
-	pc->callback = &idetape_pc_callback;
-}
-
-static void idetape_create_rewind_cmd (ide_drive_t *drive, idetape_pc_t *pc)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_REWIND_CMD;
-	if (tape->onstream)
-		pc->c[1] = 1;
-	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
-	pc->callback = &idetape_pc_callback;
-}
-
-static void idetape_create_mode_select_cmd (idetape_pc_t *pc, int length)
-{
-	idetape_init_pc (pc);
-	set_bit (PC_WRITING, &pc->flags);
-	pc->c[0] = IDETAPE_MODE_SELECT_CMD;
-	pc->c[1] = 0x10;
-	put_unaligned (htons(length), (unsigned short *) &pc->c[3]);
-	pc->request_transfer = 255;
-	pc->callback = &idetape_pc_callback;
-}
-
-static void idetape_create_erase_cmd (idetape_pc_t *pc)
-{
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_ERASE_CMD;
-	pc->c[1] = 1;
-	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
-	pc->callback = &idetape_pc_callback;
-}
-
-static void idetape_create_space_cmd (idetape_pc_t *pc,int count,byte cmd)
-{
-	idetape_init_pc (pc);
-	pc->c[0] = IDETAPE_SPACE_CMD;
-	put_unaligned (htonl (count), (unsigned int *) &pc->c[1]);
-	pc->c[1] = cmd;
-	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
-	pc->callback = &idetape_pc_callback;
-}
-
-/*
- * Verify that we have the correct tape frame
- */
-static int idetape_verify_stage (ide_drive_t *drive, idetape_stage_t *stage, int logical_blk_num, int quiet)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	os_aux_t *aux = stage->aux;
-	os_partition_t *par = &aux->partition;
-	struct request *rq = &stage->rq;
-	struct buffer_head *bh;
-
-	if (!tape->onstream)
-		return 1;
-	if (tape->raw) {
-		if (rq->errors) {
-			bh = stage->bh;
-			while (bh) {
-				memset(bh->b_data, 0, bh->b_size);
-				bh = bh->b_reqnext;
-			}
-			strcpy(stage->bh->b_data, "READ ERROR ON FRAME");
-		}
-		return 1;
-	}
-	if (rq->errors == IDETAPE_ERROR_GENERAL) {
-		printk(KERN_INFO "ide-tape: %s: skipping frame, read error\n", tape->name);
-		return 0;
-	}
-	if (rq->errors == IDETAPE_ERROR_EOD) {
-		printk(KERN_INFO "ide-tape: %s: skipping frame, eod\n", tape->name);
-		return 0;
-	}
-	if (ntohl(aux->format_id) != 0) {
-		printk(KERN_INFO "ide-tape: %s: skipping frame, format_id %u\n", tape->name, ntohl(aux->format_id));
-		return 0;
-	}
-	if (memcmp(aux->application_sig, tape->application_sig, 4) != 0) {
-		printk(KERN_INFO "ide-tape: %s: skipping frame, incorrect application signature\n", tape->name);
-		return 0;
-	}
-	if (aux->frame_type != OS_FRAME_TYPE_DATA &&
-	    aux->frame_type != OS_FRAME_TYPE_EOD &&
-	    aux->frame_type != OS_FRAME_TYPE_MARKER) {
-		printk(KERN_INFO "ide-tape: %s: skipping frame, frame type %x\n", tape->name, aux->frame_type);
-		return 0;
-	}
-	if (par->partition_num != OS_DATA_PARTITION) {
-		if (!tape->linux_media || tape->linux_media_version != 2) {
-			printk(KERN_INFO "ide-tape: %s: skipping frame, partition num %d\n", tape->name, par->partition_num);
-			return 0;
-		}
-	}
-	if (par->par_desc_ver != OS_PARTITION_VERSION) {
-		printk(KERN_INFO "ide-tape: %s: skipping frame, partition version %d\n", tape->name, par->par_desc_ver);
-		return 0;
-	}
-	if (ntohs(par->wrt_pass_cntr) != tape->wrt_pass_cntr) {
-		printk(KERN_INFO "ide-tape: %s: skipping frame, wrt_pass_cntr %d (expected %d)(logical_blk_num %u)\n", tape->name, ntohs(par->wrt_pass_cntr), tape->wrt_pass_cntr, ntohl(aux->logical_blk_num));
-		return 0;
-	}
-	if (aux->frame_seq_num != aux->logical_blk_num) {
-		printk(KERN_INFO "ide-tape: %s: skipping frame, seq != logical\n", tape->name);
-		return 0;
-	}
-	if (logical_blk_num != -1 && ntohl(aux->logical_blk_num) != logical_blk_num) {
-		if (!quiet)
-			printk(KERN_INFO "ide-tape: %s: skipping frame, logical_blk_num %u (expected %d)\n", tape->name, ntohl(aux->logical_blk_num), logical_blk_num);
-		return 0;
-	}
-	if (aux->frame_type == OS_FRAME_TYPE_MARKER) {
-		rq->errors = IDETAPE_ERROR_FILEMARK;
-		rq->current_nr_sectors = rq->nr_sectors;
-	}
-	return 1;
-}
-
-static void idetape_wait_first_stage (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	unsigned long flags;
-
-	if (tape->first_stage == NULL)
-		return;
-	spin_lock_irqsave(&tape->spinlock, flags);
-	if (tape->active_stage == tape->first_stage)
-		idetape_wait_for_request(drive, tape->active_data_request);
-	spin_unlock_irqrestore(&tape->spinlock, flags);
-}
-
-/*
- *	idetape_add_chrdev_write_request tries to add a character device
- *	originated write request to our pipeline. In case we don't succeed,
- *	we revert to non-pipelined operation mode for this request.
- *
- *	1.	Try to allocate a new pipeline stage.
- *	2.	If we can't, wait for more and more requests to be serviced
- *		and try again each time.
- *	3.	If we still can't allocate a stage, fallback to
- *		non-pipelined operation mode for this request.
- */
-static int idetape_add_chrdev_write_request (ide_drive_t *drive, int blocks)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *new_stage;
-	unsigned long flags;
-	struct request *rq;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 3)
-		printk (KERN_INFO "ide-tape: Reached idetape_add_chrdev_write_request\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-     	/*
-     	 *	Attempt to allocate a new stage.
-	 *	Pay special attention to possible race conditions.
-	 */
-	while ((new_stage = idetape_kmalloc_stage (tape)) == NULL) {
-		spin_lock_irqsave(&tape->spinlock, flags);
-		if (idetape_pipeline_active (tape)) {
-			idetape_wait_for_request(drive, tape->active_data_request);
-			spin_unlock_irqrestore(&tape->spinlock, flags);
-		} else {
-			spin_unlock_irqrestore(&tape->spinlock, flags);
-			idetape_insert_pipeline_into_queue (drive);
-			if (idetape_pipeline_active (tape))
-				continue;
-			/*
-			 *	Linux is short on memory. Fallback to
-			 *	non-pipelined operation mode for this request.
-			 */
-			return idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bh);
-		}
-	}
-	rq = &new_stage->rq;
-	ide_init_drive_cmd (rq);
-	rq->cmd = IDETAPE_WRITE_RQ;
-	rq->sector = tape->first_frame_position;	/* Doesn't actually matter - We always assume sequential access */
-	rq->nr_sectors = rq->current_nr_sectors = blocks;
-
-	idetape_switch_buffers (tape, new_stage);
-	idetape_init_stage(drive, new_stage, OS_FRAME_TYPE_DATA, tape->logical_blk_num);
-	tape->logical_blk_num++;
-	idetape_add_stage_tail (drive,new_stage);
-	tape->pipeline_head++;
-#if USE_IOTRACE
-	IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
-	calculate_speeds(drive);
-
-	/*
-	 *	Estimate whether the tape has stopped writing by checking
-	 *	if our write pipeline is currently empty. If we are not
-	 *	writing anymore, wait for the pipeline to be full enough
-	 *	(90%) before starting to service requests, so that we will
-	 *	be able to keep up with the higher speeds of the tape.
-	 *
-	 *	For the OnStream drive, we can query the number of pending
-	 *	frames in the drive's internal buffer. As long as the tape
-	 *	is still writing, it is better to write frames immediately
-	 *	rather than gather them in the pipeline. This will give the
-	 *	tape's firmware the ability to sense the current incoming
-	 *	data rate more accurately, and since the OnStream tape
-	 *	supports variable speeds, it can try to adjust itself to the
-	 *	incoming data rate.
-	 */
-	if (!idetape_pipeline_active(tape)) {
-		if (tape->nr_stages >= tape->max_stages * 9 / 10 ||
-		    tape->nr_stages >= tape->max_stages - tape->uncontrolled_pipeline_head_speed * 3 * 1024 / tape->tape_block_size) {
-			tape->measure_insert_time = 1;
-			tape->insert_time = jiffies;
-			tape->insert_size = 0;
-			tape->insert_speed = 0;
-			idetape_insert_pipeline_into_queue (drive);
-		} else if (tape->onstream) {
-			idetape_update_stats(drive);
-			if (tape->cur_frames > 5)
-				idetape_insert_pipeline_into_queue (drive);
-		}
-	}
-	if (test_and_clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags))		/* Return a deferred error */
-		return -EIO;
-	return blocks;
-}
-
-/*
- *	idetape_wait_for_pipeline will wait until all pending pipeline
- *	requests are serviced. Typically called on device close.
- */
-static void idetape_wait_for_pipeline (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	unsigned long flags;
-
-	while (tape->next_stage || idetape_pipeline_active(tape)) {
-		idetape_insert_pipeline_into_queue (drive);
-		spin_lock_irqsave(&tape->spinlock, flags);
-		if (idetape_pipeline_active(tape))
-			idetape_wait_for_request(drive, tape->active_data_request);
-		spin_unlock_irqrestore(&tape->spinlock, flags);
-	}
-}
-
-static void idetape_empty_write_pipeline (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int blocks, i, min;
-	struct buffer_head *bh;
-	
-#if IDETAPE_DEBUG_BUGS
-	if (tape->chrdev_direction != idetape_direction_write) {
-		printk (KERN_ERR "ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n");
-		return;
-	}
-	if (tape->merge_stage_size > tape->stage_size) {
-		printk (KERN_ERR "ide-tape: bug: merge_buffer too big\n");
-		tape->merge_stage_size = tape->stage_size;
-	}
-#endif /* IDETAPE_DEBUG_BUGS */
-	if (tape->merge_stage_size) {
-		blocks=tape->merge_stage_size/tape->tape_block_size;
-		if (tape->merge_stage_size % tape->tape_block_size) {
-			blocks++;
-			i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size;
-			bh = tape->bh->b_reqnext;
-			while (bh) {
-				atomic_set(&bh->b_count, 0);
-				bh = bh->b_reqnext;
-			}
-			bh = tape->bh;
-			while (i) {
-				if (bh == NULL) {
-					printk(KERN_INFO "ide-tape: bug, bh NULL\n");
-					break;
-				}
-				min = IDE_MIN(i, bh->b_size - atomic_read(&bh->b_count));
-				memset(bh->b_data + atomic_read(&bh->b_count), 0, min);
-				atomic_add(min, &bh->b_count);
-				i -= min;
-				bh = bh->b_reqnext;
-			}
-		}
-		(void) idetape_add_chrdev_write_request (drive, blocks);
-		tape->merge_stage_size = 0;
-	}
-	idetape_wait_for_pipeline (drive);
-	if (tape->merge_stage != NULL) {
-		__idetape_kfree_stage (tape->merge_stage);
-		tape->merge_stage = NULL;
-	}
-	clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
-	tape->chrdev_direction=idetape_direction_none;
-
-	/*
-	 *	On the next backup, perform the feedback loop again.
-	 *	(I don't want to keep sense information between backups,
-	 *	 as some systems are constantly on, and the system load
-	 *	 can be totally different on the next backup).
-	 */
-	tape->max_stages = tape->min_pipeline;
-#if IDETAPE_DEBUG_BUGS
-	if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) {
-		printk (KERN_ERR "ide-tape: ide-tape pipeline bug, "
-		"first_stage %p, next_stage %p, last_stage %p, nr_stages %d\n",
-		tape->first_stage, tape->next_stage, tape->last_stage, tape->nr_stages);
-	}
-#endif /* IDETAPE_DEBUG_BUGS */
-}
-
-static void idetape_restart_speed_control (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	tape->restart_speed_control_req = 0;
-	tape->pipeline_head = 0;
-	tape->buffer_head = tape->tape_head = tape->cur_frames;
-	tape->controlled_last_pipeline_head = tape->uncontrolled_last_pipeline_head = 0;
-	tape->controlled_previous_pipeline_head = tape->uncontrolled_previous_pipeline_head = 0;
-	tape->pipeline_head_speed = tape->controlled_pipeline_head_speed = 5000;
-	tape->uncontrolled_pipeline_head_speed = 0;
-	tape->controlled_pipeline_head_time = tape->uncontrolled_pipeline_head_time = jiffies;
-	tape->controlled_previous_head_time = tape->uncontrolled_previous_head_time = jiffies;
-}
-
-static int idetape_initiate_read (ide_drive_t *drive, int max_stages)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *new_stage;
-	struct request rq;
-	int bytes_read;
-	int blocks = tape->capabilities.ctl;
-
-	if (tape->chrdev_direction != idetape_direction_read) {		/* Initialize read operation */
-		if (tape->chrdev_direction == idetape_direction_write) {
-			idetape_empty_write_pipeline (drive);
-			idetape_flush_tape_buffers (drive);
-		}
-#if IDETAPE_DEBUG_BUGS
-		if (tape->merge_stage || tape->merge_stage_size) {
-			printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n");
-			tape->merge_stage_size = 0;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-		if ((tape->merge_stage = __idetape_kmalloc_stage (tape, 0, 0)) == NULL)
-			return -ENOMEM;
-		tape->chrdev_direction = idetape_direction_read;
-		tape->logical_blk_num = 0;
-
-		/*
-		 *	Issue a read 0 command to ensure that DSC handshake
-		 *	is switched from completion mode to buffer available
-		 *	mode.
-		 */
-		bytes_read = idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 0, tape->merge_stage->bh);
-		if (bytes_read < 0) {
-			kfree (tape->merge_stage);
-			tape->merge_stage = NULL;
-			tape->chrdev_direction = idetape_direction_none;
-			return bytes_read;
-		}
-	}
-	if (tape->restart_speed_control_req)
-		idetape_restart_speed_control(drive);
-	ide_init_drive_cmd (&rq);
-	rq.cmd = IDETAPE_READ_RQ;
-	rq.sector = tape->first_frame_position;
-	rq.nr_sectors = rq.current_nr_sectors = blocks;
-	if (!test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags) && tape->nr_stages <= max_stages) {
-		new_stage=idetape_kmalloc_stage (tape);
-		while (new_stage != NULL) {
-			new_stage->rq=rq;
-			idetape_add_stage_tail (drive,new_stage);
-			if (tape->nr_stages >= max_stages)
-				break;
-			new_stage=idetape_kmalloc_stage (tape);
-		}
-	}
-	if (!idetape_pipeline_active(tape)) {
-		if (tape->nr_pending_stages >= 3 * max_stages / 4) {
-			tape->measure_insert_time = 1;
-			tape->insert_time = jiffies;
-			tape->insert_size = 0;
-			tape->insert_speed = 0;
-			idetape_insert_pipeline_into_queue (drive);
-		} else if (tape->onstream) {
-			idetape_update_stats(drive);
-			if (tape->cur_frames < tape->max_frames - 5)
-				idetape_insert_pipeline_into_queue (drive);
-		}
-	}
-	return 0;
-}
-
-static int idetape_get_logical_blk (ide_drive_t *drive, int logical_blk_num, int max_stages, int quiet)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	unsigned long flags;
-	int cnt = 0, x, position;
-
-	/*
-	 * Search and wait for the next logical tape block
-	 */
-	while (1) {
-		if (cnt++ > 1000) {   /* AJN: was 100 */
-			printk(KERN_INFO "ide-tape: %s: couldn't find logical block %d, aborting\n", tape->name, logical_blk_num);
-			return 0;
-		}
-		idetape_initiate_read(drive, max_stages);
-		if (tape->first_stage == NULL) {
-			if (tape->onstream) {
-#if ONSTREAM_DEBUG
-				if (tape->debug_level >= 1)
-					printk(KERN_INFO "ide-tape: %s: first_stage == NULL, pipeline error %ld\n", tape->name, (long)test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags));
-#endif
-				clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags);
-				position = idetape_read_position(drive);
-				printk(KERN_INFO "ide-tape: %s: blank block detected, positioning tape to block %d\n", tape->name, position + 60);
-				idetape_position_tape(drive, position + 60, 0, 1);
-				cnt += 40;
-				continue;
-			} else
-				return 0;
-		}
-		idetape_wait_first_stage(drive);
-		if (idetape_verify_stage(drive, tape->first_stage, logical_blk_num, quiet))
-			break;
-		if (tape->first_stage->rq.errors == IDETAPE_ERROR_EOD)
-			cnt--;
-		if (idetape_verify_stage(drive, tape->first_stage, -1, quiet)) {
-			x = ntohl(tape->first_stage->aux->logical_blk_num);
-			if (x > logical_blk_num) {
-				printk(KERN_ERR "ide-tape: %s: couldn't find logical block %d, aborting (block %d found)\n", tape->name, logical_blk_num, x);
-				return 0;
-			}
-		}
-		spin_lock_irqsave(&tape->spinlock, flags);
-		idetape_remove_stage_head(drive);
-		spin_unlock_irqrestore(&tape->spinlock, flags);
-	}
-	if (tape->onstream)
-		tape->logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num);
-	return 1;
-}
-
-/*
- *	idetape_add_chrdev_read_request is called from idetape_chrdev_read
- *	to service a character device read request and add read-ahead
- *	requests to our pipeline.
- */
-static int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	unsigned long flags;
-	struct request *rq_ptr;
-	int bytes_read;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_add_chrdev_read_request, %d blocks\n", blocks);
-#endif /* IDETAPE_DEBUG_LOG */
-
-	/*
-	 * Wait for the next logical block to be available at the head
-	 * of the pipeline
-	 */
-	if (!idetape_get_logical_blk(drive, tape->logical_blk_num, tape->max_stages, 0)) {
-		if (tape->onstream) {
-			set_bit(IDETAPE_READ_ERROR, &tape->flags);
-			return 0;
-		}
-		if (test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags))
-		 	return 0;
-		return idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, blocks, tape->merge_stage->bh);
-	}
-	rq_ptr = &tape->first_stage->rq;
-	bytes_read = tape->tape_block_size * (rq_ptr->nr_sectors - rq_ptr->current_nr_sectors);
-	rq_ptr->nr_sectors = rq_ptr->current_nr_sectors = 0;
-
-
-	if (tape->onstream && !tape->raw && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 2)
-			printk(KERN_INFO "ide-tape: %s: EOD reached\n", tape->name);
-#endif
-		return 0;
-	}
-	if (rq_ptr->errors == IDETAPE_ERROR_EOD)
-		return 0;
-	else if (rq_ptr->errors == IDETAPE_ERROR_FILEMARK)
-		set_bit (IDETAPE_FILEMARK, &tape->flags);
-	else {
-		idetape_switch_buffers (tape, tape->first_stage);
-		if (rq_ptr->errors == IDETAPE_ERROR_GENERAL) {
-#if ONSTREAM_DEBUG
-			if (tape->debug_level >= 1)
-				printk(KERN_INFO "ide-tape: error detected, bytes_read %d\n", bytes_read);
-#endif
-		}
-		clear_bit (IDETAPE_FILEMARK, &tape->flags);
-		spin_lock_irqsave(&tape->spinlock, flags);
-		idetape_remove_stage_head (drive);
-		spin_unlock_irqrestore(&tape->spinlock, flags);
-		tape->logical_blk_num++;
-		tape->pipeline_head++;
-#if USE_IOTRACE
-		IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
-#endif
-		calculate_speeds(drive);
-	}
-#if IDETAPE_DEBUG_BUGS
-	if (bytes_read > blocks*tape->tape_block_size) {
-		printk (KERN_ERR "ide-tape: bug: trying to return more bytes than requested\n");
-		bytes_read=blocks*tape->tape_block_size;
-	}
-#endif /* IDETAPE_DEBUG_BUGS */
-	return (bytes_read);
-}
-
-static void idetape_pad_zeros (ide_drive_t *drive, int bcount)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	struct buffer_head *bh;
-	int count, blocks;
-	
-	while (bcount) {
-		bh = tape->merge_stage->bh;
-		count = IDE_MIN (tape->stage_size, bcount);
-		bcount -= count;
-		blocks = count / tape->tape_block_size;
-		while (count) {
-			atomic_set(&bh->b_count, IDE_MIN (count, bh->b_size));
-			memset (bh->b_data, 0, atomic_read(&bh->b_count));
-			count -= atomic_read(&bh->b_count);
-			bh = bh->b_reqnext;
-		}
-		idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bh);
-	}
-}
-
-static int idetape_pipeline_size (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *stage;
-	struct request *rq;
-	int size = 0;
-
-	idetape_wait_for_pipeline (drive);
-	stage = tape->first_stage;
-	while (stage != NULL) {
-		rq = &stage->rq;
-		size += tape->tape_block_size * (rq->nr_sectors-rq->current_nr_sectors);
-		if (rq->errors == IDETAPE_ERROR_FILEMARK)
-			size += tape->tape_block_size;
-		stage = stage->next;
-	}
-	size += tape->merge_stage_size;
-	return size;
-}
-
-/*
- *	Rewinds the tape to the Beginning Of the current Partition (BOP).
- *
- *	We currently support only one partition.
- */ 
-static int idetape_rewind_tape (ide_drive_t *drive)
-{
-	int retval;
-	idetape_pc_t pc;
-	idetape_tape_t *tape = drive->driver_data;
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 2)
-		printk (KERN_INFO "ide-tape: Reached idetape_rewind_tape\n");
-#endif /* IDETAPE_DEBUG_LOG */	
-	
-	idetape_create_rewind_cmd (drive, &pc);
-	retval=idetape_queue_pc_tail (drive,&pc);
-	if (retval) return retval;
-
-	idetape_create_read_position_cmd (&pc);
-	retval = idetape_queue_pc_tail (drive,&pc);
-	if (retval) return retval;
-	tape->logical_blk_num = 0;
-	return 0;
-}
-
-/*
- *	Our special ide-tape ioctl's.
- *
- *	Currently there aren't any ioctl's.
- *	mtio.h compatible commands should be issued to the character device
- *	interface.
- */
-static int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
-				 unsigned int cmd, unsigned long arg)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_config_t config;
-
-#if IDETAPE_DEBUG_LOG	
-	if (tape->debug_level >= 4)
-		printk (KERN_INFO "ide-tape: Reached idetape_blkdev_ioctl\n");
-#endif /* IDETAPE_DEBUG_LOG */
-	switch (cmd) {
-		case 0x0340:
-			if (copy_from_user ((char *) &config, (char *) arg, sizeof (idetape_config_t)))
-				return -EFAULT;
-			tape->best_dsc_rw_frequency = config.dsc_rw_frequency;
-			tape->max_stages = config.nr_stages;
-			break;
-		case 0x0350:
-			config.dsc_rw_frequency = (int) tape->best_dsc_rw_frequency;
-			config.nr_stages = tape->max_stages; 
-			if (copy_to_user ((char *) arg, (char *) &config, sizeof (idetape_config_t)))
-				return -EFAULT;
-			break;
-		default:
-			return -EIO;
-	}
-	return 0;
-}
-
-/*
- *	The block device interface should not be used for data transfers.
- *	However, we still allow opening it so that we can issue general
- *	ide driver configuration ioctl's, such as the interrupt unmask feature.
- */
-static int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
-{
-	MOD_INC_USE_COUNT;
-#if ONSTREAM_DEBUG
-        printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_blkdev_open\n");
-#endif
-	return 0;
-}
-
-static void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
-{
-	MOD_DEC_USE_COUNT;
-#if ONSTREAM_DEBUG
-        printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_blkdev_release\n");
-#endif
-}
-
-/*
- *	idetape_pre_reset is called before an ATAPI/ATA software reset.
- */
-static void idetape_pre_reset (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	if (tape != NULL)
-		set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
-}
-
-/*
- *	Character device interface functions
- */
-static ide_drive_t *get_drive_ptr (kdev_t i_rdev)
-{
-	unsigned int i = MINOR(i_rdev) & ~0xc0;
-
-	if (i >= MAX_HWIFS * MAX_DRIVES)
-		return NULL;
-	return (idetape_chrdevs[i].drive);
-}
-
-static int idetape_onstream_space_over_filemarks_backward (ide_drive_t *drive,short mt_op,int mt_count)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int cnt = 0;
-	int last_mark_addr;
-	unsigned long flags;
-
-	if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
-		printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_bwd\n", tape->name);
-		return -EIO;
-	}
-	while (cnt != mt_count) {
-		last_mark_addr = ntohl(tape->first_stage->aux->last_mark_addr);
-		if (last_mark_addr == -1)
-			return -EIO;
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 2)
-			printk(KERN_INFO "ide-tape: positioning to last mark at %d\n", last_mark_addr);
-#endif
-		idetape_position_tape(drive, last_mark_addr, 0, 0);
-		cnt++;
-		if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
-			printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
-			return -EIO;
-		}
-		if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
-			printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, last_mark_addr);
-			return -EIO;
-		}
-	}
-	if (mt_op == MTBSFM) {
-		spin_lock_irqsave(&tape->spinlock, flags);
-		idetape_remove_stage_head (drive);
-		tape->logical_blk_num++;
-		spin_unlock_irqrestore(&tape->spinlock, flags);
-	}
-	return 0;
-}
-
-/*
- * ADRL 1.1 compatible "slow" space filemarks fwd version
- *
- * Just scans for the filemark sequentially.
- */
-static int idetape_onstream_space_over_filemarks_forward_slow (ide_drive_t *drive,short mt_op,int mt_count)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int cnt = 0;
-	unsigned long flags;
-
-	if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
-		printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name);
-		return -EIO;
-	}
-	while (1) {
-		if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
-			printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
-			return -EIO;
-		}
-		if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER)
-			cnt++;
-		if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
-#if ONSTREAM_DEBUG
-			if (tape->debug_level >= 2)
-				printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name);
-#endif
-			return -EIO;
-		}
-		if (cnt == mt_count)
-			break;
-		spin_lock_irqsave(&tape->spinlock, flags);
-		idetape_remove_stage_head (drive);
-		spin_unlock_irqrestore(&tape->spinlock, flags);
-	}
-	if (mt_op == MTFSF) {
-		spin_lock_irqsave(&tape->spinlock, flags);
-		idetape_remove_stage_head (drive);
-		tape->logical_blk_num++;
-		spin_unlock_irqrestore(&tape->spinlock, flags);
-	}
-	return 0;
-}
-
-
-/*
- * Fast linux specific version of OnStream FSF
- */
-static int idetape_onstream_space_over_filemarks_forward_fast (ide_drive_t *drive,short mt_op,int mt_count)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int cnt = 0, next_mark_addr;
-	unsigned long flags;
-
-	if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
-		printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name);
-		return -EIO;
-	}
-
-	/*
-	 * Find nearest (usually previous) marker
-	 */
-	while (1) {
-		if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER)
-			break;
-		if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
-#if ONSTREAM_DEBUG
-			if (tape->debug_level >= 2)
-				printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name);
-#endif
-			return -EIO;
-		}
-		if (ntohl(tape->first_stage->aux->filemark_cnt) == 0) {
-			if (tape->first_mark_addr == -1) {
-				printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name);
-				return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count);
-			}
-			idetape_position_tape(drive, tape->first_mark_addr, 0, 0);
-			if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
-				printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd_fast\n", tape->name);
-				return -EIO;
-			}
-			if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
-				printk(KERN_INFO "ide-tape: %s: expected to find filemark at %d\n", tape->name, tape->first_mark_addr);
-				return -EIO;
-			}
-		} else {
-			if (idetape_onstream_space_over_filemarks_backward(drive, MTBSF, 1) < 0)
-				return -EIO;
-			mt_count++;
-		}
-	}
-	cnt++;
-	while (cnt != mt_count) {
-		next_mark_addr = ntohl(tape->first_stage->aux->next_mark_addr);
-		if (!next_mark_addr || next_mark_addr > tape->eod_frame_addr) {
-			printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name);
-			return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count - cnt);
-#if ONSTREAM_DEBUG
-		} else if (tape->debug_level >= 2) {
-		     printk(KERN_INFO "ide-tape: positioning to next mark at %d\n", next_mark_addr);
-#endif
-		}
-		idetape_position_tape(drive, next_mark_addr, 0, 0);
-		cnt++;
-		if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
-			printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
-			return -EIO;
-		}
-		if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
-			printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, next_mark_addr);
-			return -EIO;
-		}
-	}
-	if (mt_op == MTFSF) {
-		spin_lock_irqsave(&tape->spinlock, flags);
-		idetape_remove_stage_head (drive);
-		tape->logical_blk_num++;
-		spin_unlock_irqrestore(&tape->spinlock, flags);
-	}
-	return 0;
-}
-
-/*
- *	idetape_space_over_filemarks is now a bit more complicated than just
- *	passing the command to the tape since we may have crossed some
- *	filemarks during our pipelined read-ahead mode.
- *
- *	As a minor side effect, the pipeline enables us to support MTFSFM when
- *	the filemark is in our internal pipeline even if the tape doesn't
- *	support spacing over filemarks in the reverse direction.
- */
-static int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t pc;
-	unsigned long flags;
-	int retval,count=0;
-	int speed_control;
-
-	if (tape->onstream) {
-		if (tape->raw)
-			return -EIO;
-		speed_control = tape->speed_control;
-		tape->speed_control = 0;
-		if (mt_op == MTFSF || mt_op == MTFSFM) {
-			if (tape->linux_media)
-				retval = idetape_onstream_space_over_filemarks_forward_fast(drive, mt_op, mt_count);
-			else
-				retval = idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count);
-		} else
-			retval = idetape_onstream_space_over_filemarks_backward(drive, mt_op, mt_count);
-		tape->speed_control = speed_control;
-		tape->restart_speed_control_req = 1;
-		return retval;
-	}
-
-	if (tape->chrdev_direction == idetape_direction_read) {
-		/*
-		 *	We have a read-ahead buffer. Scan it for crossed
-		 *	filemarks.
-		 */
-		tape->merge_stage_size = 0;
-		clear_bit (IDETAPE_FILEMARK, &tape->flags);
-		while (tape->first_stage != NULL) {
-			idetape_wait_first_stage(drive);
-			if (tape->first_stage->rq.errors == IDETAPE_ERROR_FILEMARK)
-				count++;
-			if (count == mt_count) {
-				switch (mt_op) {
-					case MTFSF:
-						spin_lock_irqsave(&tape->spinlock, flags);
-						idetape_remove_stage_head (drive);
-						spin_unlock_irqrestore(&tape->spinlock, flags);
-					case MTFSFM:
-						return (0);
-					default:
-						break;
-				}
-			}
-			spin_lock_irqsave(&tape->spinlock, flags);
-			idetape_remove_stage_head (drive);
-			spin_unlock_irqrestore(&tape->spinlock, flags);
-		}
-		idetape_discard_read_pipeline (drive, 1);
-	}
-
-	/*
-	 *	The filemark was not found in our internal pipeline.
-	 *	Now we can issue the space command.
-	 */
-	switch (mt_op) {
-		case MTFSF:
-			idetape_create_space_cmd (&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK);
-			return (idetape_queue_pc_tail (drive,&pc));
-		case MTFSFM:
-			if (!tape->capabilities.sprev)
-				return (-EIO);
-			retval = idetape_space_over_filemarks (drive, MTFSF, mt_count-count);
-			if (retval) return (retval);
-			return (idetape_space_over_filemarks (drive, MTBSF, 1));
-		case MTBSF:
-			if (!tape->capabilities.sprev)
-				return (-EIO);
-			idetape_create_space_cmd (&pc,-(mt_count+count),IDETAPE_SPACE_OVER_FILEMARK);
-			return (idetape_queue_pc_tail (drive,&pc));
-		case MTBSFM:
-			if (!tape->capabilities.sprev)
-				return (-EIO);
-			retval = idetape_space_over_filemarks (drive, MTBSF, mt_count+count);
-			if (retval) return (retval);
-			return (idetape_space_over_filemarks (drive, MTFSF, 1));
-		default:
-			printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op);
-			return (-EIO);
-	}
-}
-
-
-/*
- *	Our character device read / write functions.
- *
- *	The tape is optimized to maximize throughput when it is transferring
- *	an integral number of the "continuous transfer limit", which is
- *	a parameter of the specific tape (26 KB on my particular tape).
- *      (32 kB for Onstream)
- *
- *	As of version 1.3 of the driver, the character device provides an
- *	abstract continuous view of the media - any mix of block sizes (even 1
- *	byte) on the same backup/restore procedure is supported. The driver
- *	will internally convert the requests to the recommended transfer unit,
- *	so that an unmatch between the user's block size to the recommended
- *	size will only result in a (slightly) increased driver overhead, but
- *	will no longer hit performance.
- *      This is not applicable to Onstream.
- */
-static ssize_t idetape_chrdev_read (struct file *file, char *buf,
-				    size_t count, loff_t *ppos)
-{
-	struct inode *inode = file->f_dentry->d_inode;
-	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
-	idetape_tape_t *tape = drive->driver_data;
-	ssize_t bytes_read,temp,actually_read=0, rc;
-
-	if (ppos != &file->f_pos) {
-		/* "A request was outside the capabilities of the device." */
-		return -ENXIO;
-	}
-	if (tape->onstream && (count != tape->tape_block_size)) {
-		printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%Zd used)\n", tape->name, tape->tape_block_size, count);
-		return -EINVAL;
-	}
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 3)
-		printk (KERN_INFO "ide-tape: Reached idetape_chrdev_read, count %Zd\n", count);
-#endif /* IDETAPE_DEBUG_LOG */
-
-	if (tape->chrdev_direction != idetape_direction_read) {
-		if (test_bit (IDETAPE_DETECT_BS, &tape->flags))
-			if (count > tape->tape_block_size && (count % tape->tape_block_size) == 0)
-				tape->user_bs_factor = count / tape->tape_block_size;
-	}
-	if ((rc = idetape_initiate_read(drive, tape->max_stages)) < 0)
-		return rc;
-	if (count==0)
-		return (0);
-	if (tape->merge_stage_size) {
-		actually_read=IDE_MIN (tape->merge_stage_size,count);
-		idetape_copy_stage_to_user (tape, buf, tape->merge_stage, actually_read);
-		buf += actually_read; tape->merge_stage_size -= actually_read; count-=actually_read;
-	}
-	while (count >= tape->stage_size) {
-		bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
-		if (bytes_read <= 0)
-			goto finish;
-		idetape_copy_stage_to_user (tape, buf, tape->merge_stage, bytes_read);
-		buf += bytes_read; count -= bytes_read; actually_read += bytes_read;
-	}
-	if (count) {
-		bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
-		if (bytes_read <= 0)
-			goto finish;
-		temp=IDE_MIN (count,bytes_read);
-		idetape_copy_stage_to_user (tape, buf, tape->merge_stage, temp);
-		actually_read+=temp;
-		tape->merge_stage_size=bytes_read-temp;
-	}
-finish:
-	if (!actually_read && test_bit (IDETAPE_FILEMARK, &tape->flags)) {
-#if IDETAPE_DEBUG_LOG
-		if (tape->debug_level >= 2)
-			printk(KERN_INFO "ide-tape: %s: spacing over filemark\n", tape->name);
-#endif
-		idetape_space_over_filemarks (drive, MTFSF, 1);
-		return 0;
-	}
-	if (tape->onstream && !actually_read && test_and_clear_bit(IDETAPE_READ_ERROR, &tape->flags)) {
-		printk(KERN_ERR "ide-tape: %s: unrecovered read error on logical block number %d, skipping\n", tape->name, tape->logical_blk_num);
-		tape->logical_blk_num++;
-		return -EIO;
-	}
-	return actually_read;
-}
-
-static void idetape_update_last_marker (ide_drive_t *drive, int last_mark_addr, int next_mark_addr)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *stage;
-	os_aux_t *aux;
-	int position;
-
-	if (!tape->onstream || tape->raw)
-		return;
-	if (last_mark_addr == -1)
-		return;
-	stage = __idetape_kmalloc_stage(tape, 0, 0);
-	if (stage == NULL)
-		return;
-	idetape_flush_tape_buffers(drive);
-	position = idetape_read_position(drive);
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 2)
-		printk(KERN_INFO "ide-tape: current position (2) %d, lblk %d\n", position, tape->logical_blk_num);
-	if (tape->debug_level >= 2)
-		printk(KERN_INFO "ide-tape: current position (2) tape block %d\n", tape->last_frame_position);
-#endif
-	idetape_position_tape(drive, last_mark_addr, 0, 0);
-	if (!idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 1, stage->bh)) {
-		printk(KERN_INFO "ide-tape: %s: couldn't read last marker\n", tape->name);
-		__idetape_kfree_stage (stage);
-		idetape_position_tape(drive, position, 0, 0);
-		return;
-	}
-	aux = stage->aux;
-	if (aux->frame_type != OS_FRAME_TYPE_MARKER) {
-		printk(KERN_INFO "ide-tape: %s: expected to find marker at addr %d\n", tape->name, last_mark_addr);
-		__idetape_kfree_stage (stage);
-		idetape_position_tape(drive, position, 0, 0);
-		return;
-	}
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 2)
-		printk(KERN_INFO "ide-tape: writing back marker\n");
-#endif
-	aux->next_mark_addr = htonl(next_mark_addr);
-	idetape_position_tape(drive, last_mark_addr, 0, 0);
-	if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) {
-		printk(KERN_INFO "ide-tape: %s: couldn't write back marker frame at %d\n", tape->name, last_mark_addr);
-		__idetape_kfree_stage (stage);
-		idetape_position_tape(drive, position, 0, 0);
-		return;
-	}
-	__idetape_kfree_stage (stage);
-	idetape_flush_tape_buffers (drive);
-	idetape_position_tape(drive, position, 0, 0);
-	return;
-}
-
-static void __idetape_write_header (ide_drive_t *drive, int block, int cnt)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *stage;
-	os_header_t header;
-
-	stage = __idetape_kmalloc_stage(tape, 1, 1);
-	if (stage == NULL)
-		return;
-	idetape_init_stage(drive, stage, OS_FRAME_TYPE_HEADER, tape->logical_blk_num);
-	idetape_wait_ready(drive, 60 * 5 * HZ);
-	idetape_position_tape(drive, block, 0, 0);
-	memset(&header, 0, sizeof(header));
-	strcpy(header.ident_str, "ADR_SEQ");
-	header.major_rev = 1;
-	header.minor_rev = 2;
-	header.par_num = 1;
-	header.partition.partition_num = OS_DATA_PARTITION;
-	header.partition.par_desc_ver = OS_PARTITION_VERSION;
-	header.partition.first_frame_addr = htonl(0x14);
-	header.partition.last_frame_addr = htonl(19239 * 24);
-	header.partition.wrt_pass_cntr = htons(tape->wrt_pass_cntr);
-	header.partition.eod_frame_addr = htonl(tape->eod_frame_addr);
-	memcpy(stage->bh->b_data, &header, sizeof(header));
-	while (cnt--) {
-		if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) {
-			printk(KERN_INFO "ide-tape: %s: couldn't write header frame\n", tape->name);
-			__idetape_kfree_stage (stage);
-			return;
-		}
-	}
-	__idetape_kfree_stage (stage);
-	idetape_flush_tape_buffers (drive);
-}
-
-static void idetape_write_header (ide_drive_t *drive, int locate_eod)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 2)
-		printk(KERN_INFO "ide-tape: %s: writing tape header\n", tape->name);
-#endif
-	if (!tape->onstream || tape->raw)
-		return;
-	tape->update_frame_cntr++;
-	__idetape_write_header(drive, 5, 5);
-	__idetape_write_header(drive, 0xbae, 5);
-	if (locate_eod) {
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 2)
-			printk(KERN_INFO "ide-tape: %s: locating back to eod frame addr %d\n", tape->name, tape->eod_frame_addr);
-#endif
-		idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
-	}
-}
-
-static ssize_t idetape_chrdev_write (struct file *file, const char *buf,
-				     size_t count, loff_t *ppos)
-{
-	struct inode *inode = file->f_dentry->d_inode;
-	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
-	idetape_tape_t *tape = drive->driver_data;
-	ssize_t retval,actually_written=0;
-	int position;
-
-	if (ppos != &file->f_pos) {
-		/* "A request was outside the capabilities of the device." */
-		return -ENXIO;
-	}
-	if (tape->onstream && (count != tape->tape_block_size)) {
-		printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%Zd used)\n", tape->name, tape->tape_block_size, count);
-		return -EINVAL;
-	}
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 3)
-		printk (KERN_INFO "ide-tape: Reached idetape_chrdev_write, count %Zd\n", count);
-#endif /* IDETAPE_DEBUG_LOG */
-
-	if (tape->chrdev_direction != idetape_direction_write) {	/* Initialize write operation */
-		if (tape->chrdev_direction == idetape_direction_read)
-			idetape_discard_read_pipeline (drive, 1);
-#if IDETAPE_DEBUG_BUGS
-		if (tape->merge_stage || tape->merge_stage_size) {
-			printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n");
-			tape->merge_stage_size = 0;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-		if ((tape->merge_stage = __idetape_kmalloc_stage (tape, 0, 0)) == NULL)
-			return -ENOMEM;
-		tape->chrdev_direction = idetape_direction_write;
-		idetape_init_merge_stage (tape);
-
-		if (tape->onstream) {
-			position = idetape_read_position(drive);
-			if (position <= 20) {
-				tape->logical_blk_num = 0;
-				tape->wrt_pass_cntr++;
-#if ONSTREAM_DEBUG
-				if (tape->debug_level >= 2)
-					printk(KERN_INFO "ide-tape: %s: logical block num 0, setting eod to 20\n", tape->name);
-				if (tape->debug_level >= 2)
-					printk(KERN_INFO "ide-tape: %s: allocating new write pass counter %d\n", tape->name, tape->wrt_pass_cntr);
-#endif
-				tape->filemark_cnt = 0;
-				tape->eod_frame_addr = 20;
-				tape->first_mark_addr = tape->last_mark_addr = -1;
-				idetape_write_header(drive, 1);
-			}
-#if ONSTREAM_DEBUG
-			if (tape->debug_level >= 2)
-				printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr);
-#endif
-			position = idetape_read_position(drive);
-			if (position != tape->eod_frame_addr)
-				idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
-#if ONSTREAM_DEBUG
-			if (tape->debug_level >= 2)
-				printk(KERN_INFO "ide-tape: %s: first_frame_position %d\n", tape->name, tape->first_frame_position);
-#endif
-		}
-
-		/*
-		 *	Issue a write 0 command to ensure that DSC handshake
-		 *	is switched from completion mode to buffer available
-		 *	mode.
-		 */
-		retval = idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 0, tape->merge_stage->bh);
-		if (retval < 0) {
-			kfree (tape->merge_stage);
-			tape->merge_stage = NULL;
-			tape->chrdev_direction = idetape_direction_none;
-			return retval;
-		}
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 2)
-			printk("ide-tape: first_frame_position %d\n", tape->first_frame_position);
-#endif
-	}
-	if (count==0)
-		return (0);
-	if (tape->restart_speed_control_req)
-		idetape_restart_speed_control(drive);
-	if (tape->merge_stage_size) {
-#if IDETAPE_DEBUG_BUGS
-		if (tape->merge_stage_size >= tape->stage_size) {
-			printk (KERN_ERR "ide-tape: bug: merge buffer too big\n");
-			tape->merge_stage_size=0;
-		}
-#endif /* IDETAPE_DEBUG_BUGS */
-		actually_written=IDE_MIN (tape->stage_size-tape->merge_stage_size,count);
-		idetape_copy_stage_from_user (tape, tape->merge_stage, buf, actually_written);
-		buf+=actually_written;tape->merge_stage_size+=actually_written;count-=actually_written;
-
-		if (tape->merge_stage_size == tape->stage_size) {
-			tape->merge_stage_size = 0;
-			retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl);
-			if (retval <= 0)
-				return (retval);
-		}
-	}
-	while (count >= tape->stage_size) {
-		idetape_copy_stage_from_user (tape, tape->merge_stage, buf, tape->stage_size);
-		buf+=tape->stage_size;count-=tape->stage_size;
-		retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl);
-		actually_written+=tape->stage_size;
-		if (retval <= 0)
-			return (retval);
-	}
-	if (count) {
-		actually_written+=count;
-		idetape_copy_stage_from_user (tape, tape->merge_stage, buf, count);
-		tape->merge_stage_size+=count;
-	}
-	return (actually_written);
-}
-
-static int idetape_write_filemark (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int last_mark_addr;
-	idetape_pc_t pc;
-
-	if (!tape->onstream) {
-		idetape_create_write_filemark_cmd(drive, &pc,1);	/* Write a filemark */
-		if (idetape_queue_pc_tail (drive,&pc)) {
-			printk (KERN_ERR "ide-tape: Couldn't write a filemark\n");
-			return -EIO;
-		}
-	} else if (!tape->raw) {
-		last_mark_addr = idetape_read_position(drive);
-		tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
-		if (tape->merge_stage != NULL) {
-			idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_MARKER, tape->logical_blk_num);
-			idetape_pad_zeros (drive, tape->stage_size);
-			tape->logical_blk_num++;
-			__idetape_kfree_stage (tape->merge_stage);
-			tape->merge_stage = NULL;
-		}
-		if (tape->filemark_cnt)
-			idetape_update_last_marker(drive, tape->last_mark_addr, last_mark_addr);
-		tape->last_mark_addr = last_mark_addr;
-		if (tape->filemark_cnt++ == 0)
-			tape->first_mark_addr = last_mark_addr;
-	}
-	return 0;
-}
-
-static void idetape_write_eod (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	if (!tape->onstream || tape->raw)
-		return;
-	tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
-	if (tape->merge_stage != NULL) {
-		tape->eod_frame_addr = idetape_read_position(drive);
-		idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_EOD, tape->logical_blk_num);
-		idetape_pad_zeros (drive, tape->stage_size);
-		__idetape_kfree_stage (tape->merge_stage);
-		tape->merge_stage = NULL;
-	}
-	return;
-}
-
-int idetape_seek_logical_blk (ide_drive_t *drive, int logical_blk_num)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int estimated_address = logical_blk_num + 20;
-	int retries = 0;
-	int speed_control;
-
-	speed_control = tape->speed_control;
-	tape->speed_control = 0;
-	if (logical_blk_num < 0)
-		logical_blk_num = 0;
-	if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1))
-		goto ok;
-	while (++retries < 10) {
-		idetape_discard_read_pipeline(drive, 0);
-		idetape_position_tape(drive, estimated_address, 0, 0);
-		if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1))
-			goto ok;
-		if (!idetape_get_logical_blk(drive, -1, 10, 1))
-			goto error;
-		if (tape->logical_blk_num < logical_blk_num)
-			estimated_address += logical_blk_num - tape->logical_blk_num;
-		else
-			break;
-	}
-error:
-	tape->speed_control = speed_control;
-	tape->restart_speed_control_req = 1;
-	printk(KERN_INFO "ide-tape: %s: couldn't seek to logical block %d (at %d), %d retries\n", tape->name, logical_blk_num, tape->logical_blk_num, retries);
-	return -EIO;
-ok:
-	tape->speed_control = speed_control;
-	tape->restart_speed_control_req = 1;
-	return 0;
-}
-
-/*
- *	idetape_mtioctop is called from idetape_chrdev_ioctl when
- *	the general mtio MTIOCTOP ioctl is requested.
- *
- *	We currently support the following mtio.h operations:
- *
- *	MTFSF	-	Space over mt_count filemarks in the positive direction.
- *			The tape is positioned after the last spaced filemark.
- *
- *	MTFSFM	-	Same as MTFSF, but the tape is positioned before the
- *			last filemark.
- *
- *	MTBSF	-	Steps background over mt_count filemarks, tape is
- *			positioned before the last filemark.
- *
- *	MTBSFM	-	Like MTBSF, only tape is positioned after the last filemark.
- *
- *	Note:
- *
- *		MTBSF and MTBSFM are not supported when the tape doesn't
- *		supports spacing over filemarks in the reverse direction.
- *		In this case, MTFSFM is also usually not supported (it is
- *		supported in the rare case in which we crossed the filemark
- *		during our read-ahead pipelined operation mode).
- *		
- *	MTWEOF	-	Writes mt_count filemarks. Tape is positioned after
- *			the last written filemark.
- *
- *	MTREW	-	Rewinds tape.
- *
- *	MTLOAD	-	Loads the tape.
- *
- *	MTOFFL	-	Puts the tape drive "Offline": Rewinds the tape and
- *	MTUNLOAD	prevents further access until the media is replaced.
- *
- *	MTNOP	-	Flushes tape buffers.
- *
- *	MTRETEN	-	Retension media. This typically consists of one end
- *			to end pass on the media.
- *
- *	MTEOM	-	Moves to the end of recorded data.
- *
- *	MTERASE	-	Erases tape.
- *
- *	MTSETBLK - 	Sets the user block size to mt_count bytes. If
- *			mt_count is 0, we will attempt to autodetect
- *			the block size.
- *
- *	MTSEEK	-	Positions the tape in a specific block number, where
- *			each block is assumed to contain which user_block_size
- *			bytes.
- *
- *	MTSETPART - 	Switches to another tape partition.
- *
- *	MTLOCK - 	Locks the tape door.
- *
- *	MTUNLOCK - 	Unlocks the tape door.
- *
- *	The following commands are currently not supported:
- *
- *	MTFSS, MTBSS, MTWSM, MTSETDENSITY,
- *	MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
- */
-static int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t pc;
-	int i,retval;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 1)
-		printk (KERN_INFO "ide-tape: Handling MTIOCTOP ioctl: mt_op=%d, mt_count=%d\n",mt_op,mt_count);
-#endif /* IDETAPE_DEBUG_LOG */
-	/*
-	 *	Commands which need our pipelined read-ahead stages.
-	 */
-	switch (mt_op) {
-		case MTFSF:
-		case MTFSFM:
-		case MTBSF:
-		case MTBSFM:
-			if (!mt_count)
-				return (0);
-			return (idetape_space_over_filemarks (drive,mt_op,mt_count));
-		default:
-			break;
-	}
-	switch (mt_op) {
-		case MTWEOF:
-			idetape_discard_read_pipeline (drive, 1);
-			for (i = 0; i < mt_count; i++) {
-				retval = idetape_write_filemark(drive);
-				if (retval) return retval;
-			}
-			return (0);
-		case MTREW:
-			idetape_discard_read_pipeline (drive, 0);
-			if (idetape_rewind_tape(drive))
-				return -EIO;
-			if (tape->onstream && !tape->raw)
-				return idetape_position_tape(drive, 20, 0, 0);
-			return 0;
-		case MTLOAD:
-			idetape_discard_read_pipeline (drive, 0);
-			idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK);
-			return (idetape_queue_pc_tail (drive,&pc));
-		case MTUNLOAD:
-		case MTOFFL:
-			idetape_discard_read_pipeline (drive, 0);
-			idetape_create_load_unload_cmd (drive, &pc,!IDETAPE_LU_LOAD_MASK);
-			return (idetape_queue_pc_tail (drive,&pc));
-		case MTNOP:
-			idetape_discard_read_pipeline (drive, 0);
-			return (idetape_flush_tape_buffers (drive));
-		case MTRETEN:
-			idetape_discard_read_pipeline (drive, 0);
-			idetape_create_load_unload_cmd (drive, &pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK);
-			return (idetape_queue_pc_tail (drive,&pc));
-		case MTEOM:
-			if (tape->onstream) {
-#if ONSTREAM_DEBUG
-				if (tape->debug_level >= 2)
-					printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr);
-#endif
-				idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
-				if (!idetape_get_logical_blk(drive, -1, 10, 0))
-					return -EIO;
-				if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_EOD)
-					return -EIO;
-				return 0;
-			}
-			idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
-			return (idetape_queue_pc_tail (drive,&pc));
-		case MTERASE:
-			if (tape->onstream) {
-				tape->eod_frame_addr = 20;
-				tape->logical_blk_num = 0;
-				tape->first_mark_addr = tape->last_mark_addr = -1;
-				idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
-				idetape_write_eod(drive);
-				idetape_flush_tape_buffers (drive);
-				idetape_write_header(drive, 0);
-				idetape_flush_tape_buffers (drive);
-				(void) idetape_rewind_tape (drive);
-				return 0;
-			}
-			(void) idetape_rewind_tape (drive);
-			idetape_create_erase_cmd (&pc);
-			return (idetape_queue_pc_tail (drive,&pc));
-		case MTSETBLK:
-			if (tape->onstream) {
-				if (mt_count != tape->tape_block_size) {
-					printk(KERN_INFO "ide-tape: %s: MTSETBLK %d -- only %d bytes block size supported\n", tape->name, mt_count, tape->tape_block_size);
-					return -EINVAL;
-				}
-				return 0;
-			}
-			if (mt_count) {
-				if (mt_count < tape->tape_block_size || mt_count % tape->tape_block_size)
-					return -EIO;
-				tape->user_bs_factor = mt_count / tape->tape_block_size;
-				clear_bit (IDETAPE_DETECT_BS, &tape->flags);
-			} else
-				set_bit (IDETAPE_DETECT_BS, &tape->flags);
-			return 0;
-		case MTSEEK:
-			if (!tape->onstream || tape->raw) {
-				idetape_discard_read_pipeline (drive, 0);
-				return idetape_position_tape (drive, mt_count * tape->user_bs_factor, tape->partition, 0);
-			}
-			return idetape_seek_logical_blk(drive, mt_count);
-		case MTSETPART:
-			idetape_discard_read_pipeline (drive, 0);
-			if (tape->onstream)
-				return -EIO;
-			return (idetape_position_tape (drive, 0, mt_count, 0));
-		case MTFSR:
-		case MTBSR:
-			if (tape->onstream) {
-				if (!idetape_get_logical_blk(drive, -1, 10, 0))
-					return -EIO;
-				if (mt_op == MTFSR)
-					return idetape_seek_logical_blk(drive, tape->logical_blk_num + mt_count);
-				else {
-					idetape_discard_read_pipeline (drive, 0);
-					return idetape_seek_logical_blk(drive, tape->logical_blk_num - mt_count);
-				}
-			}
-		case MTLOCK:
-			idetape_create_prevent_cmd(drive, &pc, 1);
-			retval = idetape_queue_pc_tail (drive,&pc);
-			if (retval) return retval;
-			tape->door_locked = DOOR_EXPLICITLY_LOCKED;
-			return 0;
-		case MTUNLOCK:
-			idetape_create_prevent_cmd(drive, &pc, 0);
-			retval = idetape_queue_pc_tail (drive,&pc);
-			if (retval) return retval;
-			tape->door_locked = DOOR_UNLOCKED;
-			return 0;
-		default:
-			printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op);
-			return (-EIO);
-	}
-}
-
-/*
- *	Our character device ioctls.
- *
- *	General mtio.h magnetic io commands are supported here, and not in
- *	the corresponding block interface.
- *
- *	The following ioctls are supported:
- *
- *	MTIOCTOP -	Refer to idetape_mtioctop for detailed description.
- *
- *	MTIOCGET - 	The mt_dsreg field in the returned mtget structure
- *			will be set to (user block size in bytes <<
- *			MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK.
- *
- *			The mt_blkno is set to the current user block number.
- *			The other mtget fields are not supported.
- *
- *	MTIOCPOS -	The current tape "block position" is returned. We
- *			assume that each block contains user_block_size
- *			bytes.
- *
- *	Our own ide-tape ioctls are supported on both interfaces.
- */
-static int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
-{
-	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
-	idetape_tape_t *tape = drive->driver_data;
-	struct mtop mtop;
-	struct mtget mtget;
-	struct mtpos mtpos;
-	int block_offset = 0, position = tape->first_frame_position;
-
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 3)
-		printk (KERN_INFO "ide-tape: Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
-#endif /* IDETAPE_DEBUG_LOG */
-
-	tape->restart_speed_control_req = 1;
-	if (tape->chrdev_direction == idetape_direction_write) {
-		idetape_empty_write_pipeline (drive);
-		idetape_flush_tape_buffers (drive);
-	}
-	if (cmd == MTIOCGET || cmd == MTIOCPOS) {
-		block_offset = idetape_pipeline_size (drive) / (tape->tape_block_size * tape->user_bs_factor);
-		if ((position = idetape_read_position(drive)) < 0)
-			return -EIO;
-	}
-	switch (cmd) {
-		case MTIOCTOP:
-			if (copy_from_user ((char *) &mtop, (char *) arg, sizeof (struct mtop)))
-				return -EFAULT;
-			return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
-		case MTIOCGET:
-			memset (&mtget, 0, sizeof (struct mtget));
-			mtget.mt_type = MT_ISSCSI2;
-			if (!tape->onstream || tape->raw)
-				mtget.mt_blkno = position / tape->user_bs_factor - block_offset;
-			else {
-				if (!idetape_get_logical_blk(drive, -1, 10, 0))
-					mtget.mt_blkno = -1;
-				else
-					mtget.mt_blkno = tape->logical_blk_num;
-			}
-			mtget.mt_dsreg = ((tape->tape_block_size * tape->user_bs_factor) << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK;
-			if (tape->onstream) {
-				mtget.mt_gstat |= GMT_ONLINE(0xffffffff);
-				if (tape->first_stage && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD)
-					mtget.mt_gstat |= GMT_EOD(0xffffffff);
-				if (position <= 20)
-					mtget.mt_gstat |= GMT_BOT(0xffffffff);
-			}
-			if (copy_to_user ((char *) arg,(char *) &mtget, sizeof (struct mtget)))
-				return -EFAULT;
-			return 0;
-		case MTIOCPOS:
-			if (tape->onstream && !tape->raw) {
-				if (!idetape_get_logical_blk(drive, -1, 10, 0))
-					return -EIO;
-				mtpos.mt_blkno = tape->logical_blk_num;
-			} else
-				mtpos.mt_blkno = position / tape->user_bs_factor - block_offset;
-			if (copy_to_user ((char *) arg,(char *) &mtpos, sizeof (struct mtpos)))
-				return -EFAULT;
-			return 0;
-		default:
-			if (tape->chrdev_direction == idetape_direction_read)
-				idetape_discard_read_pipeline (drive, 1);
-			return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
-	}
-}
-
-static int __idetape_analyze_headers (ide_drive_t *drive, int block)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_stage_t *stage;
-	os_header_t *header;
-	os_aux_t *aux;
-
-	if (!tape->onstream || tape->raw) {
-		tape->header_ok = tape->linux_media = 1;
-		return 1;
-	}
-	tape->header_ok = tape->linux_media = 0;
-	tape->update_frame_cntr = 0;
-	tape->wrt_pass_cntr = 0;
-	tape->eod_frame_addr = 20;
-	tape->first_mark_addr = tape->last_mark_addr = -1;
-	stage = __idetape_kmalloc_stage (tape, 0, 0);
-	if (stage == NULL)
-		return 0;
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 2)
-		printk(KERN_INFO "ide-tape: %s: reading header\n", tape->name);
-#endif
-	idetape_position_tape(drive, block, 0, 0);
-	if (!idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 1, stage->bh)) {
-		printk(KERN_INFO "ide-tape: %s: couldn't read header frame\n", tape->name);
-		__idetape_kfree_stage (stage);
-		return 0;
-	}
-	header = (os_header_t *) stage->bh->b_data;
-	aux = stage->aux;
-	if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0) {
-		printk(KERN_INFO "ide-tape: %s: invalid header identification string\n", tape->name);
-		__idetape_kfree_stage (stage);
-		return 0;
-	}
-	if (header->major_rev != 1 || (header->minor_rev != 1 && header->minor_rev != 2))
-		printk(KERN_INFO "ide-tape: warning: revision %d.%d detected (1.1/1.2 supported)\n", header->major_rev, header->minor_rev);
-	if (header->par_num != 1)
-		printk(KERN_INFO "ide-tape: warning: %d partitions defined, only one supported\n", header->par_num);
-	tape->wrt_pass_cntr = ntohs(header->partition.wrt_pass_cntr);
-	tape->eod_frame_addr = ntohl(header->partition.eod_frame_addr);
-	tape->filemark_cnt = ntohl(aux->filemark_cnt);
-	tape->first_mark_addr = ntohl(aux->next_mark_addr);
-	tape->last_mark_addr = ntohl(aux->last_mark_addr);
-	tape->update_frame_cntr = ntohl(aux->update_frame_cntr);
-	memcpy(tape->application_sig, aux->application_sig, 4); tape->application_sig[4] = 0;
-	if (memcmp(tape->application_sig, "LIN", 3) == 0) {
-		tape->linux_media = 1;
-		tape->linux_media_version = tape->application_sig[3] - '0';
-		if (tape->linux_media_version != 3)
-			printk(KERN_INFO "ide-tape: %s: Linux media version %d detected (current 3)\n", tape->name, tape->linux_media_version);
-	} else {
-		printk(KERN_INFO "ide-tape: %s: non Linux media detected (%s)\n", tape->name, tape->application_sig);
-		tape->linux_media = 0;
-	}
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 2)
-		printk(KERN_INFO "ide-tape: %s: detected write pass counter %d, eod frame addr %d\n", tape->name, tape->wrt_pass_cntr, tape->eod_frame_addr);
-#endif
-	__idetape_kfree_stage (stage);
-	return 1;
-}
-
-static int idetape_analyze_headers (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int position, block;
-
-	if (!tape->onstream || tape->raw) {
-		tape->header_ok = tape->linux_media = 1;
-		return 1;
-	}
-	tape->header_ok = tape->linux_media = 0;
-	position = idetape_read_position(drive);
-	for (block = 5; block < 10; block++)
-		if (__idetape_analyze_headers(drive, block))
-			goto ok;
-#if 0
-	for (block = 0xbae; block < 0xbb8; block++)
-#else
-	for (block = 0xbae; block < 0xbb3; block++)
-#endif
-		if (__idetape_analyze_headers(drive, block))
-			goto ok;
-	printk(KERN_ERR "ide-tape: %s: failed to find valid ADRL header\n", tape->name);
-	return 0;
-ok:
-	if (position < 20)
-		position = 20;
-	idetape_position_tape(drive, position, 0, 0);
-	tape->header_ok = 1;
-	return 1;
-}
-
-/*
- *	Our character device open function.
- */
-static int idetape_chrdev_open (struct inode *inode, struct file *filp)
-{
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
-	idetape_pc_t pc;
-	unsigned int minor=MINOR (inode->i_rdev);
-			
-#if IDETAPE_DEBUG_LOG
-	printk (KERN_INFO "ide-tape: Reached idetape_chrdev_open\n");
-#endif /* IDETAPE_DEBUG_LOG */
-	
-	if ((drive = get_drive_ptr (inode->i_rdev)) == NULL)
-		return -ENXIO;
-	tape = drive->driver_data;
-
-	if (test_and_set_bit (IDETAPE_BUSY, &tape->flags))
-		return -EBUSY;
-	MOD_INC_USE_COUNT;
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 6)
-		printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_chrdev_open-1\n");
-#endif
-	if (!tape->onstream) {
-		idetape_read_position(drive);
-		if (!test_bit (IDETAPE_ADDRESS_VALID, &tape->flags))
-			(void) idetape_rewind_tape (drive);
-	} else {
-		if (minor & 64) {
-			tape->tape_block_size = tape->stage_size = 32768 + 512;
-			tape->raw = 1;
-		} else {
-			tape->tape_block_size = tape->stage_size = 32768;
-			tape->raw = 0;
-		}
-	}
-	if (idetape_wait_ready(drive, 60 * HZ)) {
-		clear_bit(IDETAPE_BUSY, &tape->flags);
-		MOD_DEC_USE_COUNT;
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 6)
-		printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_open-1\n");
-#endif
-		printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name);
-		return -EBUSY;
-	}
-	idetape_read_position(drive);
-	MOD_DEC_USE_COUNT;
-#if ONSTREAM_DEBUG
-	if (tape->debug_level >= 6)
-		printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_open-2\n");
-#endif
-	clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
-
-	if (tape->chrdev_direction == idetape_direction_none) {
-		MOD_INC_USE_COUNT;
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 6)
-			printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_chrdev_open-2\n");
-#endif
-		idetape_create_prevent_cmd(drive, &pc, 1);
-		if (!idetape_queue_pc_tail (drive,&pc)) {
-			if (tape->door_locked != DOOR_EXPLICITLY_LOCKED)
-				tape->door_locked = DOOR_LOCKED;
-		}
-		idetape_analyze_headers(drive);
-	}
-	tape->max_frames = tape->cur_frames = tape->req_buffer_fill = 0;
-	idetape_restart_speed_control(drive);
-	tape->restart_speed_control_req = 0;
-	return 0;
-}
-
-/*
- *	Our character device release function.
- */
-static int idetape_chrdev_release (struct inode *inode, struct file *filp)
-{
-	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t pc;
-	unsigned int minor=MINOR (inode->i_rdev);
-			
-#if IDETAPE_DEBUG_LOG
-	if (tape->debug_level >= 3)
-		printk (KERN_INFO "ide-tape: Reached idetape_chrdev_release\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-	if (tape->chrdev_direction == idetape_direction_write) {
-		idetape_empty_write_pipeline (drive);
-		tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
-		if (tape->merge_stage != NULL) {
-			idetape_pad_zeros (drive, tape->tape_block_size * (tape->user_bs_factor - 1));
-			__idetape_kfree_stage (tape->merge_stage);
-			tape->merge_stage = NULL;
-		}
-		idetape_write_filemark(drive);
-		idetape_write_eod(drive);
-		idetape_flush_tape_buffers (drive);
-		idetape_write_header(drive, minor >= 128);
-		idetape_flush_tape_buffers (drive);
-	}
-	if (tape->chrdev_direction == idetape_direction_read) {
-		if (minor < 128)
-			idetape_discard_read_pipeline (drive, 1);
-		else
-			idetape_wait_for_pipeline (drive);
-	}
-	if (tape->cache_stage != NULL) {
-		__idetape_kfree_stage (tape->cache_stage);
-		tape->cache_stage = NULL;
-	}
-	if (minor < 128)
-		(void) idetape_rewind_tape (drive);
-	if (tape->chrdev_direction == idetape_direction_none) {
-		if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) {
-			idetape_create_prevent_cmd(drive, &pc, 0);
-			if (!idetape_queue_pc_tail (drive,&pc))
-				tape->door_locked = DOOR_UNLOCKED;
-		}
-		MOD_DEC_USE_COUNT;
-#if ONSTREAM_DEBUG
-		if (tape->debug_level >= 6)
-			printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_release\n");
-#endif
-	}
-	clear_bit (IDETAPE_BUSY, &tape->flags);
-	return 0;
-}
-
-/*
- *	idetape_identify_device is called to check the contents of the
- *	ATAPI IDENTIFY command results. We return:
- *
- *	1	If the tape can be supported by us, based on the information
- *		we have so far.
- *
- *	0 	If this tape driver is not currently supported by us.
- */
-static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
-{
-	struct idetape_id_gcw gcw;
-#if IDETAPE_DEBUG_INFO
-	unsigned short mask,i;
-#endif /* IDETAPE_DEBUG_INFO */
-
-	if (!id)
-		return 0;
-
-	*((unsigned short *) &gcw) = id->config;
-
-#if IDETAPE_DEBUG_INFO
-	printk (KERN_INFO "ide-tape: Dumping ATAPI Identify Device tape parameters\n");
-	printk (KERN_INFO "ide-tape: Protocol Type: ");
-	switch (gcw.protocol) {
-		case 0: case 1: printk (KERN_INFO "ATA\n");break;
-		case 2:	printk (KERN_INFO "ATAPI\n");break;
-		case 3: printk (KERN_INFO "Reserved (Unknown to ide-tape)\n");break;
-	}
-	printk (KERN_INFO "ide-tape: Device Type: %x - ",gcw.device_type);	
-	switch (gcw.device_type) {
-		case 0: printk (KERN_INFO "Direct-access Device\n");break;
-		case 1: printk (KERN_INFO "Streaming Tape Device\n");break;
-		case 2: case 3: case 4: printk (KERN_INFO "Reserved\n");break;
-		case 5: printk (KERN_INFO "CD-ROM Device\n");break;
-		case 6: printk (KERN_INFO "Reserved\n");
-		case 7: printk (KERN_INFO "Optical memory Device\n");break;
-		case 0x1f: printk (KERN_INFO "Unknown or no Device type\n");break;
-		default: printk (KERN_INFO "Reserved\n");
-	}
-	printk (KERN_INFO "ide-tape: Removable: %s",gcw.removable ? "Yes\n":"No\n");	
-	printk (KERN_INFO "ide-tape: Command Packet DRQ Type: ");
-	switch (gcw.drq_type) {
-		case 0: printk (KERN_INFO "Microprocessor DRQ\n");break;
-		case 1: printk (KERN_INFO "Interrupt DRQ\n");break;
-		case 2: printk (KERN_INFO "Accelerated DRQ\n");break;
-		case 3: printk (KERN_INFO "Reserved\n");break;
-	}
-	printk (KERN_INFO "ide-tape: Command Packet Size: ");
-	switch (gcw.packet_size) {
-		case 0: printk (KERN_INFO "12 bytes\n");break;
-		case 1: printk (KERN_INFO "16 bytes\n");break;
-		default: printk (KERN_INFO "Reserved\n");break;
-	}
-	printk (KERN_INFO "ide-tape: Model: %.40s\n",id->model);
-	printk (KERN_INFO "ide-tape: Firmware Revision: %.8s\n",id->fw_rev);
-	printk (KERN_INFO "ide-tape: Serial Number: %.20s\n",id->serial_no);
-	printk (KERN_INFO "ide-tape: Write buffer size: %d bytes\n",id->buf_size*512);
-	printk (KERN_INFO "ide-tape: DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
-	printk (KERN_INFO "ide-tape: LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
-	printk (KERN_INFO "ide-tape: IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
-	printk (KERN_INFO "ide-tape: IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
-	printk (KERN_INFO "ide-tape: ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
-	printk (KERN_INFO "ide-tape: PIO Cycle Timing Category: %d\n",id->tPIO);
-	printk (KERN_INFO "ide-tape: DMA Cycle Timing Category: %d\n",id->tDMA);
-	printk (KERN_INFO "ide-tape: Single Word DMA supported modes: ");
-	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
-		if (id->dma_1word & mask)
-			printk (KERN_INFO "%d ",i);
-		if (id->dma_1word & (mask << 8))
-			printk (KERN_INFO "(active) ");
-	}
-	printk (KERN_INFO "\n");
-	printk (KERN_INFO "ide-tape: Multi Word DMA supported modes: ");
-	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
-		if (id->dma_mword & mask)
-			printk (KERN_INFO "%d ",i);
-		if (id->dma_mword & (mask << 8))
-			printk (KERN_INFO "(active) ");
-	}
-	printk (KERN_INFO "\n");
-	if (id->field_valid & 0x0002) {
-		printk (KERN_INFO "ide-tape: Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
-		printk (KERN_INFO "ide-tape: Minimum Multi-word DMA cycle per word: ");
-		if (id->eide_dma_min == 0)
-			printk (KERN_INFO "Not supported\n");
-		else
-			printk (KERN_INFO "%d ns\n",id->eide_dma_min);
-
-		printk (KERN_INFO "ide-tape: Manufacturer\'s Recommended Multi-word cycle: ");
-		if (id->eide_dma_time == 0)
-			printk (KERN_INFO "Not supported\n");
-		else
-			printk (KERN_INFO "%d ns\n",id->eide_dma_time);
-
-		printk (KERN_INFO "ide-tape: Minimum PIO cycle without IORDY: ");
-		if (id->eide_pio == 0)
-			printk (KERN_INFO "Not supported\n");
-		else
-			printk (KERN_INFO "%d ns\n",id->eide_pio);
-
-		printk (KERN_INFO "ide-tape: Minimum PIO cycle with IORDY: ");
-		if (id->eide_pio_iordy == 0)
-			printk (KERN_INFO "Not supported\n");
-		else
-			printk (KERN_INFO "%d ns\n",id->eide_pio_iordy);
-		
-	} else
-		printk (KERN_INFO "ide-tape: According to the device, fields 64-70 are not valid.\n");
-#endif /* IDETAPE_DEBUG_INFO */
-
-	/* Check that we can support this device */
-
-	if (gcw.protocol !=2 )
-		printk (KERN_ERR "ide-tape: Protocol is not ATAPI\n");
-	else if (gcw.device_type != 1)
-		printk (KERN_ERR "ide-tape: Device type is not set to tape\n");
-	else if (!gcw.removable)
-		printk (KERN_ERR "ide-tape: The removable flag is not set\n");
-	else if (gcw.packet_size != 0) {
-		printk (KERN_ERR "ide-tape: Packet size is not 12 bytes long\n");
-		if (gcw.packet_size == 1)
-			printk (KERN_ERR "ide-tape: Sorry, padding to 16 bytes is still not supported\n");
-	} else
-		return 1;
-	return 0;
-}
-
-/*
- * Notify vendor ID to the OnStream tape drive
- */
-static void idetape_onstream_set_vendor (ide_drive_t *drive, char *vendor)
-{
-	idetape_pc_t pc;
-	idetape_mode_parameter_header_t *header;
-
-	idetape_create_mode_select_cmd(&pc, sizeof(*header) + 8);
-	pc.buffer[0] = 3 + 8;	/* Mode Data Length */
-	pc.buffer[1] = 0;	/* Medium Type - ignoring */
-	pc.buffer[2] = 0;	/* Reserved */
-	pc.buffer[3] = 0;	/* Block Descriptor Length */
-	pc.buffer[4 + 0] = 0x36 | (1 << 7);
-	pc.buffer[4 + 1] = 6;
-	pc.buffer[4 + 2] = vendor[0];
-	pc.buffer[4 + 3] = vendor[1];
-	pc.buffer[4 + 4] = vendor[2];
-	pc.buffer[4 + 5] = vendor[3];
-	pc.buffer[4 + 6] = 0;
-	pc.buffer[4 + 7] = 0;
-	if (idetape_queue_pc_tail (drive,&pc))
-		printk (KERN_ERR "ide-tape: Couldn't set vendor name to %s\n", vendor);
-
-}
-
-/*
- * Various unused OnStream commands
- */
-#if ONSTREAM_DEBUG
-static void idetape_onstream_set_retries (ide_drive_t *drive, int retries)
-{
-	idetape_pc_t pc;
-
-	idetape_create_mode_select_cmd(&pc, sizeof(idetape_mode_parameter_header_t) + 4);
-	pc.buffer[0] = 3 + 4;
-	pc.buffer[1] = 0;	/* Medium Type - ignoring */
-	pc.buffer[2] = 0;	/* Reserved */
-	pc.buffer[3] = 0;	/* Block Descriptor Length */
-	pc.buffer[4 + 0] = 0x2f | (1 << 7);
-	pc.buffer[4 + 1] = 2;
-	pc.buffer[4 + 2] = 4;
-	pc.buffer[4 + 3] = retries;
-	if (idetape_queue_pc_tail (drive,&pc))
-		printk (KERN_ERR "ide-tape: Couldn't set retries to %d\n", retries);
-}
-#endif
-
-/*
- * Configure 32.5KB block size.
- */
-static void idetape_onstream_configure_block_size (ide_drive_t *drive)
-{
-	idetape_pc_t pc;
-	idetape_mode_parameter_header_t *header;
-	idetape_block_size_page_t *bs;
-
-	/*
-	 * Get the current block size from the block size mode page
-	 */
-	idetape_create_mode_sense_cmd (&pc,IDETAPE_BLOCK_SIZE_PAGE);
-	if (idetape_queue_pc_tail (drive,&pc))
-		printk (KERN_ERR "ide-tape: can't get tape block size mode page\n");
-	header = (idetape_mode_parameter_header_t *) pc.buffer;
-	bs = (idetape_block_size_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl);
-
-#if IDETAPE_DEBUG_INFO
-	printk(KERN_INFO "ide-tape: 32KB play back: %s\n", bs->play32 ? "Yes" : "No");
-	printk(KERN_INFO "ide-tape: 32.5KB play back: %s\n", bs->play32_5 ? "Yes" : "No");
-	printk(KERN_INFO "ide-tape: 32KB record: %s\n", bs->record32 ? "Yes" : "No");
-	printk(KERN_INFO "ide-tape: 32.5KB record: %s\n", bs->record32_5 ? "Yes" : "No");
-#endif /* IDETAPE_DEBUG_INFO */
-
-	/*
-	 * Configure default auto columns mode, 32.5KB block size
-	 */ 
-	bs->one = 1;
-	bs->play32 = 0;
-	bs->play32_5 = 1;
-	bs->record32 = 0;
-	bs->record32_5 = 1;
-	idetape_create_mode_select_cmd(&pc, sizeof(*header) + sizeof(*bs));
-	if (idetape_queue_pc_tail (drive,&pc))
-		printk (KERN_ERR "ide-tape: Couldn't set tape block size mode page\n");
-
-#if ONSTREAM_DEBUG
-	/*
-	 * In debug mode, we want to see as many errors as possible
-	 * to test the error recovery mechanism.
-	 */
-	idetape_onstream_set_retries(drive, 0);
-#endif
-}
-
-/*
- * Use INQUIRY to get the firmware revision
- */
-static void idetape_get_inquiry_results (ide_drive_t *drive)
-{
-	char *r;
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t pc;
-	idetape_inquiry_result_t *inquiry;
-	
-	idetape_create_inquiry_cmd(&pc);
-	if (idetape_queue_pc_tail (drive,&pc)) {
-		printk (KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", tape->name);
-		return;
-	}
-	inquiry = (idetape_inquiry_result_t *) pc.buffer;
-	memcpy(tape->vendor_id, inquiry->vendor_id, 8);
-	memcpy(tape->product_id, inquiry->product_id, 16);
-	memcpy(tape->firmware_revision, inquiry->revision_level, 4);
-	ide_fixstring(tape->vendor_id, 10, 0);
-	ide_fixstring(tape->product_id, 18, 0);
-	ide_fixstring(tape->firmware_revision, 6, 0);
-	r = tape->firmware_revision;
-	if (*(r + 1) == '.')
-		tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 2) - '0') * 10 + *(r + 3) - '0';
-	else if (tape->onstream)
-		tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 1) - '0') * 10 + *(r + 2) - '0';
-	printk(KERN_INFO "ide-tape: %s <-> %s: %s %s rev %s\n", drive->name, tape->name, tape->vendor_id, tape->product_id, tape->firmware_revision);
-}
-
-/*
- * Configure the OnStream ATAPI tape drive for default operation
- */
-static void idetape_configure_onstream (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-	if (tape->firmware_revision_num < 105) {
-		printk(KERN_INFO "ide-tape: %s: Old OnStream firmware revision detected (%s)\n", tape->name, tape->firmware_revision);
-		printk(KERN_INFO "ide-tape: %s: An upgrade to version 1.05 or above is recommended\n", tape->name);
-	}
-
-	/*
-	 * Configure 32.5KB (data+aux) block size.
-	 */
-	idetape_onstream_configure_block_size(drive);
-
-	/*
-	 * Set vendor name to 'LIN3' for "Linux support version 3".
-	 */
-	idetape_onstream_set_vendor(drive, "LIN3");
-}
-
-/*
- *	idetape_get_mode_sense_results asks the tape about its various
- *	parameters. In particular, we will adjust our data transfer buffer
- *	size to the recommended value as returned by the tape.
- */
-static void idetape_get_mode_sense_results (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	idetape_pc_t pc;
-	idetape_mode_parameter_header_t *header;
-	idetape_capabilities_page_t *capabilities;
-	
-	idetape_create_mode_sense_cmd (&pc,IDETAPE_CAPABILITIES_PAGE);
-	if (idetape_queue_pc_tail (drive,&pc)) {
-		printk (KERN_ERR "ide-tape: Can't get tape parameters - assuming some default values\n");
-		tape->tape_block_size = 512; tape->capabilities.ctl = 52;
-		tape->capabilities.speed = 450; tape->capabilities.buffer_size = 6 * 52;
-		return;
-	}
-	header = (idetape_mode_parameter_header_t *) pc.buffer;
-	capabilities = (idetape_capabilities_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl);
-
-	capabilities->max_speed = ntohs (capabilities->max_speed);
-	capabilities->ctl = ntohs (capabilities->ctl);
-	capabilities->speed = ntohs (capabilities->speed);
-	capabilities->buffer_size = ntohs (capabilities->buffer_size);
-
-	if (!capabilities->speed) {
-		printk(KERN_INFO "ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name);
-		capabilities->speed = 650;
-	}
-	if (!capabilities->max_speed) {
-		printk(KERN_INFO "ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name);
-		capabilities->max_speed = 650;
-	}
-
-	tape->capabilities = *capabilities;		/* Save us a copy */
-	if (capabilities->blk512)
-		tape->tape_block_size = 512;
-	else if (capabilities->blk1024)
-		tape->tape_block_size = 1024;
-	else if (tape->onstream && capabilities->blk32768)
-		tape->tape_block_size = 32768;
-
-#if IDETAPE_DEBUG_INFO
-	printk (KERN_INFO "ide-tape: Dumping the results of the MODE SENSE packet command\n");
-	printk (KERN_INFO "ide-tape: Mode Parameter Header:\n");
-	printk (KERN_INFO "ide-tape: Mode Data Length - %d\n",header->mode_data_length);
-	printk (KERN_INFO "ide-tape: Medium Type - %d\n",header->medium_type);
-	printk (KERN_INFO "ide-tape: Device Specific Parameter - %d\n",header->dsp);
-	printk (KERN_INFO "ide-tape: Block Descriptor Length - %d\n",header->bdl);
-	
-	printk (KERN_INFO "ide-tape: Capabilities and Mechanical Status Page:\n");
-	printk (KERN_INFO "ide-tape: Page code - %d\n",capabilities->page_code);
-	printk (KERN_INFO "ide-tape: Page length - %d\n",capabilities->page_length);
-	printk (KERN_INFO "ide-tape: Read only - %s\n",capabilities->ro ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports reverse space - %s\n",capabilities->sprev ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports erase initiated formatting - %s\n",capabilities->efmt ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports QFA two Partition format - %s\n",capabilities->qfa ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports locking the medium - %s\n",capabilities->lock ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: The volume is currently locked - %s\n",capabilities->locked ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: The device defaults in the prevent state - %s\n",capabilities->prevent ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports ejecting the medium - %s\n",capabilities->eject ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports error correction - %s\n",capabilities->ecc ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Supports 32768 bytes block size / Restricted byte count for PIO transfers - %s\n",capabilities->blk32768 ? "Yes":"No");
-	printk (KERN_INFO "ide-tape: Maximum supported speed in KBps - %d\n",capabilities->max_speed);
-	printk (KERN_INFO "ide-tape: Continuous transfer limits in blocks - %d\n",capabilities->ctl);
-	printk (KERN_INFO "ide-tape: Current speed in KBps - %d\n",capabilities->speed);	
-	printk (KERN_INFO "ide-tape: Buffer size - %d\n",capabilities->buffer_size*512);
-#endif /* IDETAPE_DEBUG_INFO */
-}
-
-static void idetape_add_settings (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-
-/*
- *			drive	setting name	read/write	ioctl	ioctl		data type	min			max			mul_factor			div_factor			data pointer				set function
- */
-	ide_add_setting(drive,	"buffer",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				2,				&tape->capabilities.buffer_size,	NULL);
-	ide_add_setting(drive,	"pipeline_min",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->min_pipeline,			NULL);
-	ide_add_setting(drive,	"pipeline",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->max_stages,			NULL);
-	ide_add_setting(drive,	"pipeline_max",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->max_pipeline,			NULL);
-	ide_add_setting(drive,	"pipeline_used",SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->nr_stages,			NULL);
-	ide_add_setting(drive,	"pipeline_pending",SETTING_READ,-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->nr_pending_stages,		NULL);
-	ide_add_setting(drive,	"speed",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->capabilities.speed,		NULL);
-	ide_add_setting(drive,	"stage",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1024,				&tape->stage_size,			NULL);
-	ide_add_setting(drive,	"tdsc",		SETTING_RW,	-1,	-1,		TYPE_INT,	IDETAPE_DSC_RW_MIN,	IDETAPE_DSC_RW_MAX,	1000,				HZ,				&tape->best_dsc_rw_frequency,		NULL);
-	ide_add_setting(drive,	"dsc_overlap",	SETTING_RW,	-1,	-1,		TYPE_BYTE,	0,			1,			1,				1,				&drive->dsc_overlap,			NULL);
-	ide_add_setting(drive,	"pipeline_head_speed_c",SETTING_READ,	-1,	-1,	TYPE_INT,	0,			0xffff,			1,				1,				&tape->controlled_pipeline_head_speed,	NULL);
-	ide_add_setting(drive,	"pipeline_head_speed_u",SETTING_READ,	-1,	-1,	TYPE_INT,	0,			0xffff,			1,				1,				&tape->uncontrolled_pipeline_head_speed,	NULL);
-	ide_add_setting(drive,	"avg_speed",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->avg_speed,		NULL);
-	if (tape->onstream) {
-		ide_add_setting(drive,	"cur_frames",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->cur_frames,		NULL);
-		ide_add_setting(drive,	"max_frames",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->max_frames,		NULL);
-		ide_add_setting(drive,	"insert_speed",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->insert_speed,		NULL);
-		ide_add_setting(drive,	"speed_control",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->speed_control,		NULL);
-		ide_add_setting(drive,	"debug_level",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->debug_level,		NULL);
-		ide_add_setting(drive,	"tape_still_time",SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->tape_still_time,		NULL);
-		ide_add_setting(drive,	"max_insert_speed",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->max_insert_speed,	NULL);
-		ide_add_setting(drive,	"insert_size",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->insert_size,		NULL);
-	}
-}
-
-/*
- *	ide_setup is called to:
- *
- *		1.	Initialize our various state variables.
- *		2.	Ask the tape for its capabilities.
- *		3.	Allocate a buffer which will be used for data
- *			transfer. The buffer size is chosen based on
- *			the recommendation which we received in step (2).
- *
- *	Note that at this point ide.c already assigned us an irq, so that
- *	we can queue requests here and wait for their completion.
- */
-static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor)
-{
-	unsigned long t1, tmid, tn, t;
-	int speed;
-	struct idetape_id_gcw gcw;
-	int stage_size;
-
-	memset (tape, 0, sizeof (idetape_tape_t));
-	spin_lock_init(&tape->spinlock);
-	drive->driver_data = tape;
-	drive->ready_stat = 0;			/* An ATAPI device ignores DRDY */
-	if (strstr(drive->id->model, "OnStream DI-30"))
-		tape->onstream = 1;
-	drive->dsc_overlap = 1;
-#ifdef CONFIG_BLK_DEV_IDEPCI
-	if (!tape->onstream && HWIF(drive)->pci_dev != NULL) {
-		/*
-		 * These two ide-pci host adapters appear to need DSC overlap disabled.
-		 * This probably needs further analysis.
-		 */
-		if ((HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) ||
-		    (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_TTI_HPT343)) {
-			printk(KERN_INFO "ide-tape: %s: disabling DSC overlap\n", tape->name);
-		    	drive->dsc_overlap = 0;
-		}
-	}
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-	tape->drive = drive;
-	tape->minor = minor;
-	tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor;
-	tape->chrdev_direction = idetape_direction_none;
-	tape->pc = tape->pc_stack;
-	tape->max_insert_speed = 10000;
-	tape->speed_control = 1;
-	*((unsigned short *) &gcw) = drive->id->config;
-	if (gcw.drq_type == 1)
-		set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags);
-
-	tape->min_pipeline = tape->max_pipeline = tape->max_stages = 10;
-	
-	idetape_get_inquiry_results(drive);
-	idetape_get_mode_sense_results(drive);
-	if (tape->onstream)
-		idetape_configure_onstream(drive);
-
-	tape->user_bs_factor = 1;
-	tape->stage_size = tape->capabilities.ctl * tape->tape_block_size;
-	while (tape->stage_size > 0xffff) {
-		printk (KERN_NOTICE "ide-tape: decreasing stage size\n");
-		tape->capabilities.ctl /= 2;
-		tape->stage_size = tape->capabilities.ctl * tape->tape_block_size;
-	}
-	stage_size = tape->stage_size;
-	if (tape->onstream)
-		stage_size = 32768 + 512;
-	tape->pages_per_stage = stage_size / PAGE_SIZE;
-	if (stage_size % PAGE_SIZE) {
-		tape->pages_per_stage++;
-		tape->excess_bh_size = PAGE_SIZE - stage_size % PAGE_SIZE;
-	}
-
-	/*
-	 *	Select the "best" DSC read/write polling frequency
-	 *	and pipeline size.
-	 */
-	speed = IDE_MAX (tape->capabilities.speed, tape->capabilities.max_speed);
-
-	tape->max_stages = speed * 1000 * 10 / tape->stage_size;
-	tape->min_pipeline = tape->max_stages;
-	tape->max_pipeline = tape->max_stages * 2;
-
-	t1 = (tape->stage_size * HZ) / (speed * 1000);
-	tmid = (tape->capabilities.buffer_size * 32 * HZ) / (speed * 125);
-	tn = (IDETAPE_FIFO_THRESHOLD * tape->stage_size * HZ) / (speed * 1000);
-
-	if (tape->max_stages)
-		t = tn;
-	else
-		t = t1;
-
-	/*
-	 *	Ensure that the number we got makes sense; limit
-	 *	it within IDETAPE_DSC_RW_MIN and IDETAPE_DSC_RW_MAX.
-	 */
-	tape->best_dsc_rw_frequency = IDE_MAX (IDE_MIN (t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN);
-	printk (KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, %dkB pipeline, %lums tDSC%s\n",
-		drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size,
-		tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024,
-		tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":"");
-
-	idetape_add_settings(drive);
-}
-
-static int idetape_cleanup (ide_drive_t *drive)
-{
-	idetape_tape_t *tape = drive->driver_data;
-	int minor = tape->minor;
-	unsigned long flags;
-
-	save_flags (flags);	/* all CPUs (overkill?) */
-	cli();			/* all CPUs (overkill?) */
-	if (test_bit (IDETAPE_BUSY, &tape->flags) || tape->first_stage != NULL || tape->merge_stage_size || drive->usage) {
-		restore_flags(flags);	/* all CPUs (overkill?) */
-		return 1;
-	}
-	idetape_chrdevs[minor].drive = NULL;
-	restore_flags (flags);	/* all CPUs (overkill?) */
-	DRIVER(drive)->busy = 0;
-	(void) ide_unregister_subdriver (drive);
-	drive->driver_data = NULL;
-	devfs_unregister (tape->de_r);
-	devfs_unregister (tape->de_n);
-	kfree (tape);
-	for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++)
-		if (idetape_chrdevs[minor].drive != NULL)
-			return 0;
-	devfs_unregister_chrdev (IDETAPE_MAJOR, "ht");
-	idetape_chrdev_present = 0;
-	return 0;
-}
-
-#ifdef CONFIG_PROC_FS
-
-static int proc_idetape_read_name
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	idetape_tape_t	*tape = drive->driver_data;
-	char		*out = page;
-	int		len;
-
-	len = sprintf(out,"%s\n", tape->name);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
-static ide_proc_entry_t idetape_proc[] = {
-	{ "name",	S_IFREG|S_IRUGO,	proc_idetape_read_name,	NULL },
-	{ NULL, 0, NULL, NULL }
-};
-
-#else
-
-#define	idetape_proc	NULL
-
-#endif
-
-/*
- *	IDE subdriver functions, registered with ide.c
- */
-static ide_driver_t idetape_driver = {
-	"ide-tape",		/* name */
-	IDETAPE_VERSION,	/* version */
-	ide_tape,		/* media */
-	1,			/* busy */
-	1,			/* supports_dma */
-	1,			/* supports_dsc_overlap */
-	idetape_cleanup,	/* cleanup */
-	idetape_do_request,	/* do_request */
-	idetape_end_request,	/* end_request */
-	idetape_blkdev_ioctl,	/* ioctl */
-	idetape_blkdev_open,	/* open */
-	idetape_blkdev_release,	/* release */
-	NULL,			/* media_change */
-	idetape_pre_reset,	/* pre_reset */
-	NULL,			/* capacity */
-	NULL,			/* special */
-	idetape_proc		/* proc */
-};
-
-int idetape_init (void);
-static ide_module_t idetape_module = {
-	IDE_DRIVER_MODULE,
-	idetape_init,
-	&idetape_driver,
-	NULL
-};
-
-/*
- *	Our character device supporting functions, passed to register_chrdev.
- */
-static struct file_operations idetape_fops = {
-	read:		idetape_chrdev_read,
-	write:		idetape_chrdev_write,
-	ioctl:		idetape_chrdev_ioctl,
-	open:		idetape_chrdev_open,
-	release:	idetape_chrdev_release,
-};
-
-/*
- *	idetape_init will register the driver for each tape.
- */
-int idetape_init (void)
-{
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
-	int minor, failed = 0, supported = 0;
-
-	MOD_INC_USE_COUNT;
-#if ONSTREAM_DEBUG
-        printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_init\n");
-#endif
-	if (!idetape_chrdev_present)
-		for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ )
-			idetape_chrdevs[minor].drive = NULL;
-
-	if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) {
-		ide_register_module (&idetape_module);
-		MOD_DEC_USE_COUNT;
-#if ONSTREAM_DEBUG
-		printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
-#endif
-		return 0;
-	}
-	if (!idetape_chrdev_present &&
-	    devfs_register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) {
-		printk (KERN_ERR "ide-tape: Failed to register character device interface\n");
-		MOD_DEC_USE_COUNT;
-#if ONSTREAM_DEBUG
-		printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
-#endif
-		return -EBUSY;
-	}
-	do {
-		if (!idetape_identify_device (drive, drive->id)) {
-			printk (KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name);
-			continue;
-		}
-		if (drive->scsi) {
-			if (strstr(drive->id->model, "OnStream DI-30")) {
-				printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model);
-			} else {
-				printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name);
-				continue;
-			}
-		}
-		tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL);
-		if (tape == NULL) {
-			printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name);
-			continue;
-		}
-		if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) {
-			printk (KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name);
-			kfree (tape);
-			continue;
-		}
-		for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++);
-		idetape_setup (drive, tape, minor);
-		idetape_chrdevs[minor].drive = drive;
-		tape->de_r =
-		    devfs_register (drive->de, "mt", 2, DEVFS_FL_DEFAULT,
-				    HWIF(drive)->major, minor,
-				    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
-				    &idetape_fops, NULL);
-		tape->de_n =
-		    devfs_register (drive->de, "mtn", 3, DEVFS_FL_DEFAULT,
-				    HWIF(drive)->major, minor + 128,
-				    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
-				    &idetape_fops, NULL);
-		devfs_register_tape (tape->de_r);
-		supported++; failed--;
-	} while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL);
-	if (!idetape_chrdev_present && !supported) {
-		devfs_unregister_chrdev (IDETAPE_MAJOR, "ht");
-	} else
-		idetape_chrdev_present = 1;
-	ide_register_module (&idetape_module);
-	MOD_DEC_USE_COUNT;
-#if ONSTREAM_DEBUG
-	printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
-#endif
-	return 0;
-}
-
-#ifdef MODULE
-int init_module (void)
-{
-	return idetape_init ();
-}
-
-void cleanup_module (void)
-{
-	ide_drive_t *drive;
-	int minor;
-
-	for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) {
-		drive = idetape_chrdevs[minor].drive;
-		if (drive != NULL && idetape_cleanup (drive))
-			printk (KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name);
-	}
-	ide_unregister_module(&idetape_module);
-}
-#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.3.51/linux/drivers/block/ide.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/ide.c	Wed Dec 31 16:00:00 1969
@@ -1,3637 +0,0 @@
-/*
- *  linux/drivers/block/ide.c		Version 6.30	Dec 28, 1999
- *
- *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
- */
-
-/*
- *  Mostly written by Mark Lord  <mlord@pobox.com>
- *                and Gadi Oxman <gadio@netvision.net.il>
- *
- *  See linux/MAINTAINERS for address of current maintainer.
- *
- * This is the multiple IDE interface driver, as evolved from hd.c.
- * It supports up to MAX_HWIFS IDE interfaces, on one or more IRQs (usually 14 & 15).
- * There can be up to two drives per interface, as per the ATA-2 spec.
- *
- * Primary:    ide0, port 0x1f0; major=3;  hda is minor=0; hdb is minor=64
- * Secondary:  ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
- * Tertiary:   ide2, port 0x???; major=33; hde is minor=0; hdf is minor=64
- * Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64
- * ...
- *
- *  From hd.c:
- *  |
- *  | It traverses the request-list, using interrupts to jump between functions.
- *  | As nearly all functions can be called within interrupts, we may not sleep.
- *  | Special care is recommended.  Have Fun!
- *  |
- *  | modified by Drew Eckhardt to check nr of hd's from the CMOS.
- *  |
- *  | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- *  | in the early extended-partition checks and added DM partitions.
- *  |
- *  | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
- *  |
- *  | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- *  | and general streamlining by Mark Lord (mlord@pobox.com).
- *
- *  October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
- *
- *	Mark Lord	(mlord@pobox.com)		(IDE Perf.Pkg)
- *	Delman Lee	(delman@ieee.org)		("Mr. atdisk2")
- *	Scott Snyder	(snyder@fnald0.fnal.gov)	(ATAPI IDE cd-rom)
- *
- *  This was a rewrite of just about everything from hd.c, though some original
- *  code is still sprinkled about.  Think of it as a major evolution, with
- *  inspiration from lots of linux users, esp.  hamish@zot.apana.org.au
- *
- *  Version 1.0 ALPHA	initial code, primary i/f working okay
- *  Version 1.3 BETA	dual i/f on shared irq tested & working!
- *  Version 1.4 BETA	added auto probing for irq(s)
- *  Version 1.5 BETA	added ALPHA (untested) support for IDE cd-roms,
- *  ...
- * Version 5.50		allow values as small as 20 for idebus=
- * Version 5.51		force non io_32bit in drive_cmd_intr()
- *			change delay_10ms() to delay_50ms() to fix problems
- * Version 5.52		fix incorrect invalidation of removable devices
- *			add "hdx=slow" command line option
- * Version 5.60		start to modularize the driver; the disk and ATAPI
- *			 drivers can be compiled as loadable modules.
- *			move IDE probe code to ide-probe.c
- *			move IDE disk code to ide-disk.c
- *			add support for generic IDE device subdrivers
- *			add m68k code from Geert Uytterhoeven
- *			probe all interfaces by default
- *			add ioctl to (re)probe an interface
- * Version 6.00		use per device request queues
- *			attempt to optimize shared hwgroup performance
- *			add ioctl to manually adjust bandwidth algorithms
- *			add kerneld support for the probe module
- *			fix bug in ide_error()
- *			fix bug in the first ide_get_lock() call for Atari
- *			don't flush leftover data for ATAPI devices
- * Version 6.01		clear hwgroup->active while the hwgroup sleeps
- *			support HDIO_GETGEO for floppies
- * Version 6.02		fix ide_ack_intr() call
- *			check partition table on floppies
- * Version 6.03		handle bad status bit sequencing in ide_wait_stat()
- * Version 6.10		deleted old entries from this list of updates
- *			replaced triton.c with ide-dma.c generic PCI DMA
- *			added support for BIOS-enabled UltraDMA
- *			rename all "promise" things to "pdc4030"
- *			fix EZ-DRIVE handling on small disks
- * Version 6.11		fix probe error in ide_scan_devices()
- *			fix ancient "jiffies" polling bugs
- *			mask all hwgroup interrupts on each irq entry
- * Version 6.12		integrate ioctl and proc interfaces
- *			fix parsing of "idex=" command line parameter
- * Version 6.13		add support for ide4/ide5 courtesy rjones@orchestream.com
- * Version 6.14		fixed IRQ sharing among PCI devices
- * Version 6.15		added SMP awareness to IDE drivers
- * Version 6.16		fixed various bugs; even more SMP friendly
- * Version 6.17		fix for newest EZ-Drive problem
- * Version 6.18		default unpartitioned-disk translation now "BIOS LBA"
- * Version 6.19		Re-design for a UNIFORM driver for all platforms,
- *			  model based on suggestions from Russell King and
- *			  Geert Uytterhoeven
- *			Promise DC4030VL now supported.
- *			add support for ide6/ide7
- *			delay_50ms() changed to ide_delay_50ms() and exported.
- * Version 6.20		Added/Fixed Generic ATA-66 support and hwif detection.
- *			Added hdx=flash to allow for second flash disk
- *			  detection w/o the hang loop.
- *			Added support for ide8/ide9
- *			Added idex=ata66 for the quirky chipsets that are
- *			  ATA-66 compliant, but have yet to determine a method
- *			  of verification of the 80c cable presence.
- *			  Specifically Promise's PDC20262 chipset.
- * Version 6.21		Fixing/Fixed SMP spinlock issue with insight from an old
- *			  hat that clarified original low level driver design.
- * Version 6.30		Added SMP support; fixed multmode issues.  -ml
- *
- *  Some additional driver compile-time options are in ./include/linux/ide.h
- *
- *  To do, in likely order of completion:
- *	- modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
- *
- */
-
-#define	REVISION	"Revision: 6.30"
-#define	VERSION		"Id: ide.c 6.30 1999/12/28"
-
-#undef REALLY_SLOW_IO		/* most systems can safely undef this */
-
-#define _IDE_C			/* Tell ide.h it's really us */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/genhd.h>
-#include <linux/blkpg.h>
-#include <linux/malloc.h>
-#ifndef MODULE
-#include <linux/init.h>
-#endif /* MODULE */
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-#include <linux/devfs_fs_kernel.h>
-
-#include <asm/byteorder.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/bitops.h>
-
-#include "ide_modes.h"
-
-#ifdef CONFIG_KMOD
-#include <linux/kmod.h>
-#endif /* CONFIG_KMOD */
-
-#ifdef CONFIG_BLK_DEV_VIA82CXXX
-extern byte fifoconfig;		/* defined in via82cxxx.c used by ide_setup() */
-#endif /* CONFIG_BLK_DEV_VIA82CXXX */
-
-static const byte ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR };
-
-static int	idebus_parameter = 0; /* holds the "idebus=" parameter */
-static int	system_bus_speed = 0; /* holds what we think is VESA/PCI bus speed */
-static int	initializing;     /* set while initializing built-in drivers */
-
-#ifdef CONFIG_BLK_DEV_IDEPCI
-static int	ide_scan_direction = 0;	/* THIS was formerly 2.2.x pci=reverse */
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-
-#if defined(__mc68000__) || defined(CONFIG_APUS)
-/*
- * ide_lock is used by the Atari code to obtain access to the IDE interrupt,
- * which is shared between several drivers.
- */
-static int	ide_lock = 0;
-#endif /* __mc68000__ || CONFIG_APUS */
-
-/*
- * ide_modules keeps track of the available IDE chipset/probe/driver modules.
- */
-ide_module_t *ide_modules	= NULL;
-ide_module_t *ide_probe		= NULL;
-
-/*
- * This is declared extern in ide.h, for access by other IDE modules:
- */
-ide_hwif_t	ide_hwifs[MAX_HWIFS];	/* master data repository */
-
-#if (DISK_RECOVERY_TIME > 0)
-/*
- * For really screwy hardware (hey, at least it *can* be used with Linux)
- * we can enforce a minimum delay time between successive operations.
- */
-static unsigned long read_timer (void)
-{
-	unsigned long t, flags;
-	int i;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	t = jiffies * 11932;
-    	outb_p(0, 0x43);
-	i = inb_p(0x40);
-	i |= inb(0x40) << 8;
-	__restore_flags(flags);	/* local CPU only */
-	return (t - i);
-}
-#endif /* DISK_RECOVERY_TIME */
-
-static inline void set_recovery_timer (ide_hwif_t *hwif)
-{
-#if (DISK_RECOVERY_TIME > 0)
-	hwif->last_time = read_timer();
-#endif /* DISK_RECOVERY_TIME */
-}
-
-/*
- * Do not even *think* about calling this!
- */
-static void init_hwif_data (unsigned int index)
-{
-	unsigned int unit;
-	hw_regs_t hw;
-	ide_hwif_t *hwif = &ide_hwifs[index];
-
-	/* bulk initialize hwif & drive info with zeros */
-	memset(hwif, 0, sizeof(ide_hwif_t));
-	memset(&hw, 0, sizeof(hw_regs_t));
-
-	/* fill in any non-zero initial values */
-	hwif->index     = index;
-	ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &hwif->irq);
-	memcpy(&hwif->hw, &hw, sizeof(hw));
-	memcpy(hwif->io_ports, hw.io_ports, sizeof(hw.io_ports));
-	hwif->noprobe	= !hwif->io_ports[IDE_DATA_OFFSET];
-#ifdef CONFIG_BLK_DEV_HD
-	if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
-		hwif->noprobe = 1; /* may be overridden by ide_setup() */
-#endif /* CONFIG_BLK_DEV_HD */
-	hwif->major	= ide_hwif_to_major[index];
-	hwif->name[0]	= 'i';
-	hwif->name[1]	= 'd';
-	hwif->name[2]	= 'e';
-	hwif->name[3]	= '0' + index;
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *drive = &hwif->drives[unit];
-
-		drive->media			= ide_disk;
-		drive->select.all		= (unit<<4)|0xa0;
-		drive->hwif			= hwif;
-		drive->ctl			= 0x08;
-		drive->ready_stat		= READY_STAT;
-		drive->bad_wstat		= BAD_W_STAT;
-		drive->special.b.recalibrate	= 1;
-		drive->special.b.set_geometry	= 1;
-		drive->name[0]			= 'h';
-		drive->name[1]			= 'd';
-		drive->name[2]			= 'a' + (index * MAX_DRIVES) + unit;
-		init_waitqueue_head(&drive->wqueue);
-	}
-}
-
-/*
- * init_ide_data() sets reasonable default values into all fields
- * of all instances of the hwifs and drives, but only on the first call.
- * Subsequent calls have no effect (they don't wipe out anything).
- *
- * This routine is normally called at driver initialization time,
- * but may also be called MUCH earlier during kernel "command-line"
- * parameter processing.  As such, we cannot depend on any other parts
- * of the kernel (such as memory allocation) to be functioning yet.
- *
- * This is too bad, as otherwise we could dynamically allocate the
- * ide_drive_t structs as needed, rather than always consuming memory
- * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
- */
-#define MAGIC_COOKIE 0x12345678
-static void __init init_ide_data (void)
-{
-	unsigned int index;
-	static unsigned long magic_cookie = MAGIC_COOKIE;
-
-	if (magic_cookie != MAGIC_COOKIE)
-		return;		/* already initialized */
-	magic_cookie = 0;
-
-	/* Initialise all interface structures */
-	for (index = 0; index < MAX_HWIFS; ++index)
-		init_hwif_data(index);
-
-	/* Add default hw interfaces */
-	ide_init_default_hwifs();
-
-	idebus_parameter = 0;
-	system_bus_speed = 0;
-}
-
-/*
- * CompactFlash cards and their brethern pretend to be removable hard disks, except:
- *	(1) they never have a slave unit, and
- *	(2) they don't have doorlock mechanisms.
- * This test catches them, and is invoked elsewhere when setting appropriate config bits.
- *
- * FIXME: This treatment is probably applicable for *all* PCMCIA (PC CARD) devices,
- * so in linux 2.3.x we should change this to just treat all PCMCIA drives this way,
- * and get rid of the model-name tests below (too big of an interface change for 2.2.x).
- * At that time, we might also consider parameterizing the timeouts and retries,
- * since these are MUCH faster than mechanical drives.	-M.Lord
- */
-int drive_is_flashcard (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-
-	if (drive->removable && id != NULL) {
-		if (id->config == 0x848a) return 1;	/* CompactFlash */
-		if (!strncmp(id->model, "KODAK ATA_FLASH", 15)	/* Kodak */
-		 || !strncmp(id->model, "Hitachi CV", 10)	/* Hitachi */
-		 || !strncmp(id->model, "SunDisk SDCFB", 13)	/* SunDisk */
-		 || !strncmp(id->model, "HAGIWARA HPC", 12)	/* Hagiwara */
-		 || !strncmp(id->model, "ATA_FLASH", 9))	/* Simple Tech */
-		{
-			return 1;	/* yes, it is a flash memory card */
-		}
-	}
-	return 0;	/* no, it is not a flash memory card */
-}
-
-/*
- * ide_system_bus_speed() returns what we think is the system VESA/PCI
- * bus speed (in MHz).  This is used for calculating interface PIO timings.
- * The default is 40 for known PCI systems, 50 otherwise.
- * The "idebus=xx" parameter can be used to override this value.
- * The actual value to be used is computed/displayed the first time through.
- */
-int ide_system_bus_speed (void)
-{
-	if (!system_bus_speed) {
-		if (idebus_parameter)
-			system_bus_speed = idebus_parameter;	/* user supplied value */
-#ifdef CONFIG_PCI
-		else if (pci_present())
-			system_bus_speed = 40;	/* safe default value for PCI */
-#endif /* CONFIG_PCI */
-		else
-			system_bus_speed = 50;	/* safe default value for VESA and PCI */
-		printk("ide: Assuming %dMHz system bus speed for PIO modes%s\n", system_bus_speed,
-			idebus_parameter ? "" : "; override with idebus=xx");
-	}
-	return system_bus_speed;
-}
-
-#if SUPPORT_VLB_SYNC
-/*
- * Some localbus EIDE interfaces require a special access sequence
- * when using 32-bit I/O instructions to transfer data.  We call this
- * the "vlb_sync" sequence, which consists of three successive reads
- * of the sector count register location, with interrupts disabled
- * to ensure that the reads all happen together.
- */
-static inline void do_vlb_sync (ide_ioreg_t port) {
-	(void) inb (port);
-	(void) inb (port);
-	(void) inb (port);
-}
-#endif /* SUPPORT_VLB_SYNC */
-
-/*
- * This is used for most PIO data transfers *from* the IDE interface
- */
-void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
-{
-	byte io_32bit = drive->io_32bit;
-
-	if (io_32bit) {
-#if SUPPORT_VLB_SYNC
-		if (io_32bit & 2) {
-			unsigned long flags;
-			__save_flags(flags);	/* local CPU only */
-			__cli();		/* local CPU only */
-			do_vlb_sync(IDE_NSECTOR_REG);
-			insl(IDE_DATA_REG, buffer, wcount);
-			__restore_flags(flags);	/* local CPU only */
-		} else
-#endif /* SUPPORT_VLB_SYNC */
-			insl(IDE_DATA_REG, buffer, wcount);
-	} else {
-#if SUPPORT_SLOW_DATA_PORTS
-		if (drive->slow) {
-			unsigned short *ptr = (unsigned short *) buffer;
-			while (wcount--) {
-				*ptr++ = inw_p(IDE_DATA_REG);
-				*ptr++ = inw_p(IDE_DATA_REG);
-			}
-		} else
-#endif /* SUPPORT_SLOW_DATA_PORTS */
-			insw(IDE_DATA_REG, buffer, wcount<<1);
-	}
-}
-
-/*
- * This is used for most PIO data transfers *to* the IDE interface
- */
-void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
-{
-	byte io_32bit = drive->io_32bit;
-
-	if (io_32bit) {
-#if SUPPORT_VLB_SYNC
-		if (io_32bit & 2) {
-			unsigned long flags;
-			__save_flags(flags);	/* local CPU only */
-			__cli();		/* local CPU only */
-			do_vlb_sync(IDE_NSECTOR_REG);
-			outsl(IDE_DATA_REG, buffer, wcount);
-			__restore_flags(flags);	/* local CPU only */
-		} else
-#endif /* SUPPORT_VLB_SYNC */
-			outsl(IDE_DATA_REG, buffer, wcount);
-	} else {
-#if SUPPORT_SLOW_DATA_PORTS
-		if (drive->slow) {
-			unsigned short *ptr = (unsigned short *) buffer;
-			while (wcount--) {
-				outw_p(*ptr++, IDE_DATA_REG);
-				outw_p(*ptr++, IDE_DATA_REG);
-			}
-		} else
-#endif /* SUPPORT_SLOW_DATA_PORTS */
-			outsw(IDE_DATA_REG, buffer, wcount<<1);
-	}
-}
-
-/*
- * The following routines are mainly used by the ATAPI drivers.
- *
- * These routines will round up any request for an odd number of bytes,
- * so if an odd bytecount is specified, be sure that there's at least one
- * extra byte allocated for the buffer.
- */
-void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
-{
-	++bytecount;
-#if defined(CONFIG_ATARI) || defined(CONFIG_Q40)
-	if (MACH_IS_ATARI || MACH_IS_Q40) {
-		/* Atari has a byte-swapped IDE interface */
-		insw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
-		return;
-	}
-#endif /* CONFIG_ATARI */
-	ide_input_data (drive, buffer, bytecount / 4);
-	if ((bytecount & 0x03) >= 2)
-		insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
-}
-
-void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
-{
-	++bytecount;
-#if defined(CONFIG_ATARI) || defined(CONFIG_Q40)
-	if (MACH_IS_ATARI || MACH_IS_Q40) {
-		/* Atari has a byte-swapped IDE interface */
-		outsw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
-		return;
-	}
-#endif /* CONFIG_ATARI */
-	ide_output_data (drive, buffer, bytecount / 4);
-	if ((bytecount & 0x03) >= 2)
-		outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
-}
-
-/*
- * Needed for PCI irq sharing
- */
-static inline int drive_is_ready (ide_drive_t *drive)
-{
-	if (drive->waiting_for_dma)
-		return HWIF(drive)->dmaproc(ide_dma_test_irq, drive);
-#if 0
-	udelay(1);	/* need to guarantee 400ns since last command was issued */
-#endif
-	if (GET_STAT() & BUSY_STAT)	/* Note: this may clear a pending IRQ!! */
-		return 0;	/* drive busy:  definitely not interrupting */
-	return 1;		/* drive ready: *might* be interrupting */
-}
-
-/*
- * This is our end_request replacement function.
- */
-void ide_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
-{
-	struct request *rq;
-	unsigned long flags;
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	rq = hwgroup->rq;
-
-	if (!end_that_request_first(rq, uptodate, hwgroup->drive->name)) {
-		add_blkdev_randomness(MAJOR(rq->rq_dev));
-		blkdev_dequeue_request(rq);
-        	hwgroup->rq = NULL;
-		end_that_request_last(rq);
-	}
-	spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
-/*
- * This should get invoked any time we exit the driver to
- * wait for an interrupt response from a drive.  handler() points
- * at the appropriate code to handle the next interrupt, and a
- * timer is started to prevent us from waiting forever in case
- * something goes wrong (see the ide_timer_expiry() handler later on).
- */
-void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
-		      unsigned int timeout, ide_expiry_t *expiry)
-{
-	unsigned long flags;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	if (hwgroup->handler != NULL) {
-		printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
-			drive->name, hwgroup->handler, handler);
-	}
-	hwgroup->handler	= handler;
-	hwgroup->expiry		= expiry;
-	hwgroup->timer.expires	= jiffies + timeout;
-	add_timer(&hwgroup->timer);
-	spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
-/*
- * current_capacity() returns the capacity (in sectors) of a drive
- * according to its current geometry/LBA settings.
- */
-unsigned long current_capacity (ide_drive_t *drive)
-{
-	if (!drive->present)
-		return 0;
-	if (drive->driver != NULL)
-		return DRIVER(drive)->capacity(drive);
-	return 0;
-}
-
-extern struct block_device_operations ide_fops[];
-/*
- * ide_geninit() is called exactly *once* for each interface.
- */
-void ide_geninit (ide_hwif_t *hwif)
-{
-	unsigned int unit;
-	struct gendisk *gd = hwif->gd;
-
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *drive = &hwif->drives[unit];
-
-		if (!drive->present)
-			continue;
-		if (drive->media!=ide_disk && drive->media!=ide_floppy)
-			continue;
-		register_disk(gd,MKDEV(hwif->major,unit<<PARTN_BITS),
-#ifdef CONFIG_BLK_DEV_ISAPNP
-			(drive->forced_geom && drive->noprobe) ? 1 :
-#endif /* CONFIG_BLK_DEV_ISAPNP */
-			1<<PARTN_BITS, ide_fops,
-			current_capacity(drive));
-	}
-}
-
-static ide_startstop_t do_reset1 (ide_drive_t *, int);		/* needed below */
-
-/*
- * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
- * during an atapi drive reset operation. If the drive has not yet responded,
- * and we have not yet hit our maximum waiting time, then the timer is restarted
- * for another 50ms.
- */
-static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive)
-{
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	byte stat;
-
-	SELECT_DRIVE(HWIF(drive),drive);
-	udelay (10);
-
-	if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
-		printk("%s: ATAPI reset complete\n", drive->name);
-	} else {
-		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
-			ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL);
-			return ide_started;	/* continue polling */
-		}
-		hwgroup->poll_timeout = 0;	/* end of polling */
-		printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
-		return do_reset1 (drive, 1);	/* do it the old fashioned way */
-	}
-	hwgroup->poll_timeout = 0;	/* done polling */
-	return ide_stopped;
-}
-
-/*
- * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
- * during an ide reset operation. If the drives have not yet responded,
- * and we have not yet hit our maximum waiting time, then the timer is restarted
- * for another 50ms.
- */
-static ide_startstop_t reset_pollfunc (ide_drive_t *drive)
-{
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	ide_hwif_t *hwif = HWIF(drive);
-	byte tmp;
-
-	if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
-		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
-			ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL);
-			return ide_started;	/* continue polling */
-		}
-		printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
-	} else  {
-		printk("%s: reset: ", hwif->name);
-		if ((tmp = GET_ERR()) == 1)
-			printk("success\n");
-		else {
-#if FANCY_STATUS_DUMPS
-			printk("master: ");
-			switch (tmp & 0x7f) {
-				case 1: printk("passed");
-					break;
-				case 2: printk("formatter device error");
-					break;
-				case 3: printk("sector buffer error");
-					break;
-				case 4: printk("ECC circuitry error");
-					break;
-				case 5: printk("controlling MPU error");
-					break;
-				default:printk("error (0x%02x?)", tmp);
-			}
-			if (tmp & 0x80)
-				printk("; slave: failed");
-			printk("\n");
-#else
-			printk("failed\n");
-#endif /* FANCY_STATUS_DUMPS */
-		}
-	}
-	hwgroup->poll_timeout = 0;	/* done polling */
-	return ide_stopped;
-}
-
-static void pre_reset (ide_drive_t *drive)
-{
-	if (drive->driver != NULL)
-		DRIVER(drive)->pre_reset(drive);
-
-	if (!drive->keep_settings) {
-		if (drive->using_dma) {
-			(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
-		} else {
-			drive->unmask = 0;
-			drive->io_32bit = 0;
-		}
-	}
-}
-
-/*
- * do_reset1() attempts to recover a confused drive by resetting it.
- * Unfortunately, resetting a disk drive actually resets all devices on
- * the same interface, so it can really be thought of as resetting the
- * interface rather than resetting the drive.
- *
- * ATAPI devices have their own reset mechanism which allows them to be
- * individually reset without clobbering other devices on the same interface.
- *
- * Unfortunately, the IDE interface does not generate an interrupt to let
- * us know when the reset operation has finished, so we must poll for this.
- * Equally poor, though, is the fact that this may a very long time to complete,
- * (up to 30 seconds worstcase).  So, instead of busy-waiting here for it,
- * we set a timer to poll at 50ms intervals.
- */
-static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
-{
-	unsigned int unit;
-	unsigned long flags;
-	ide_hwif_t *hwif = HWIF(drive);
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-
-	/* For an ATAPI device, first try an ATAPI SRST. */
-	if (drive->media != ide_disk && !do_not_try_atapi) {
-		pre_reset(drive);
-		SELECT_DRIVE(hwif,drive);
-		udelay (20);
-		OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
-		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
-		ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL);
-		__restore_flags (flags);	/* local CPU only */
-		return ide_started;
-	}
-
-	/*
-	 * First, reset any device state data we were maintaining
-	 * for any of the drives on this interface.
-	 */
-	for (unit = 0; unit < MAX_DRIVES; ++unit)
-		pre_reset(&hwif->drives[unit]);
-
-#if OK_TO_RESET_CONTROLLER
-	if (!IDE_CONTROL_REG) {
-		__restore_flags(flags);
-		return ide_stopped;
-	}
-	/*
-	 * Note that we also set nIEN while resetting the device,
-	 * to mask unwanted interrupts from the interface during the reset.
-	 * However, due to the design of PC hardware, this will cause an
-	 * immediate interrupt due to the edge transition it produces.
-	 * This single interrupt gives us a "fast poll" for drives that
-	 * recover from reset very quickly, saving us the first 50ms wait time.
-	 */
-	OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG);	/* set SRST and nIEN */
-	udelay(10);			/* more than enough time */
-	OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG);	/* clear SRST, leave nIEN */
-	udelay(10);			/* more than enough time */
-	hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
-	ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL);
-
-	/*
-	 * Some weird controller like resetting themselves to a strange
-	 * state when the disks are reset this way. At least, the Winbond
-	 * 553 documentation says that
-	 */
-	if (hwif->resetproc != NULL)
-		hwif->resetproc(drive);
-
-#endif	/* OK_TO_RESET_CONTROLLER */
-
-	__restore_flags (flags);	/* local CPU only */
-	return ide_started;
-}
-
-/*
- * ide_do_reset() is the entry point to the drive/interface reset code.
- */
-ide_startstop_t ide_do_reset (ide_drive_t *drive)
-{
-	return do_reset1 (drive, 0);
-}
-
-/*
- * Clean up after success/failure of an explicit drive cmd
- */
-void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
-{
-	unsigned long flags;
-	struct request *rq = HWGROUP(drive)->rq;
-
-	if (rq->cmd == IDE_DRIVE_CMD) {
-		byte *args = (byte *) rq->buffer;
-		rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
-		if (args) {
-			args[0] = stat;
-			args[1] = err;
-			args[2] = IN_BYTE(IDE_NSECTOR_REG);
-		}
-	}
-	spin_lock_irqsave(&io_request_lock, flags);
-	blkdev_dequeue_request(rq);
-	HWGROUP(drive)->rq = NULL;
-	rq->rq_status = RQ_INACTIVE;
-	spin_unlock_irqrestore(&io_request_lock, flags);
-	if (rq->sem != NULL)
-		up(rq->sem);	/* inform originator that rq has been serviced */
-}
-
-/*
- * Error reporting, in human readable form (luxurious, but a memory hog).
- */
-byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
-{
-	unsigned long flags;
-	byte err = 0;
-
-	__save_flags (flags);	/* local CPU only */
-	ide__sti();		/* local CPU only */
-	printk("%s: %s: status=0x%02x", drive->name, msg, stat);
-#if FANCY_STATUS_DUMPS
-	printk(" { ");
-	if (stat & BUSY_STAT)
-		printk("Busy ");
-	else {
-		if (stat & READY_STAT)	printk("DriveReady ");
-		if (stat & WRERR_STAT)	printk("DeviceFault ");
-		if (stat & SEEK_STAT)	printk("SeekComplete ");
-		if (stat & DRQ_STAT)	printk("DataRequest ");
-		if (stat & ECC_STAT)	printk("CorrectedError ");
-		if (stat & INDEX_STAT)	printk("Index ");
-		if (stat & ERR_STAT)	printk("Error ");
-	}
-	printk("}");
-#endif	/* FANCY_STATUS_DUMPS */
-	printk("\n");
-	if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
-		err = GET_ERR();
-		printk("%s: %s: error=0x%02x", drive->name, msg, err);
-#if FANCY_STATUS_DUMPS
-		if (drive->media == ide_disk) {
-			printk(" { ");
-			if (err & ABRT_ERR)	printk("DriveStatusError ");
-			if (err & ICRC_ERR)	printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
-			if (err & ECC_ERR)	printk("UncorrectableError ");
-			if (err & ID_ERR)	printk("SectorIdNotFound ");
-			if (err & TRK0_ERR)	printk("TrackZeroNotFound ");
-			if (err & MARK_ERR)	printk("AddrMarkNotFound ");
-			printk("}");
-			if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) {
-				byte cur = IN_BYTE(IDE_SELECT_REG);
-				if (cur & 0x40) {	/* using LBA? */
-					printk(", LBAsect=%ld", (unsigned long)
-					 ((cur&0xf)<<24)
-					 |(IN_BYTE(IDE_HCYL_REG)<<16)
-					 |(IN_BYTE(IDE_LCYL_REG)<<8)
-					 | IN_BYTE(IDE_SECTOR_REG));
-				} else {
-					printk(", CHS=%d/%d/%d",
-					 (IN_BYTE(IDE_HCYL_REG)<<8) +
-					  IN_BYTE(IDE_LCYL_REG),
-					  cur & 0xf,
-					  IN_BYTE(IDE_SECTOR_REG));
-				}
-				if (HWGROUP(drive)->rq)
-					printk(", sector=%ld", HWGROUP(drive)->rq->sector);
-			}
-		}
-#endif	/* FANCY_STATUS_DUMPS */
-		printk("\n");
-	}
-	__restore_flags (flags);	/* local CPU only */
-	return err;
-}
-
-/*
- * try_to_flush_leftover_data() is invoked in response to a drive
- * unexpectedly having its DRQ_STAT bit set.  As an alternative to
- * resetting the drive, this routine tries to clear the condition
- * by read a sector's worth of data from the drive.  Of course,
- * this may not help if the drive is *waiting* for data from *us*.
- */
-static void try_to_flush_leftover_data (ide_drive_t *drive)
-{
-	int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
-
-	if (drive->media != ide_disk)
-		return;
-	while (i > 0) {
-		u32 buffer[16];
-		unsigned int wcount = (i > 16) ? 16 : i;
-		i -= wcount;
-		ide_input_data (drive, buffer, wcount);
-	}
-}
-
-/*
- * ide_error() takes action based on the error returned by the drive.
- */
-ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat)
-{
-	struct request *rq;
-	byte err;
-
-	err = ide_dump_status(drive, msg, stat);
-	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
-		return ide_stopped;
-	/* retry only "normal" I/O: */
-	if (rq->cmd == IDE_DRIVE_CMD) {
-		rq->errors = 1;
-		ide_end_drive_cmd(drive, stat, err);
-		return ide_stopped;
-	}
-	if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */
-		rq->errors |= ERROR_RESET;
-	} else {
-		if (drive->media == ide_disk && (stat & ERR_STAT)) {
-			/* err has different meaning on cdrom and tape */
-			if (err == ABRT_ERR) {
-				if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY)
-					return ide_stopped; /* some newer drives don't support WIN_SPECIFY */
-			} else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR))
-				; /* UDMA crc error -- just retry the operation */
-			else if (err & (BBD_ERR | ECC_ERR))	/* retries won't help these */
-				rq->errors = ERROR_MAX;
-			else if (err & TRK0_ERR)	/* help it find track zero */
-				rq->errors |= ERROR_RECAL;
-		}
-		if ((stat & DRQ_STAT) && rq->cmd != WRITE)
-			try_to_flush_leftover_data(drive);
-	}
-	if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
-		OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG);	/* force an abort */
-
-	if (rq->errors >= ERROR_MAX) {
-		if (drive->driver != NULL)
-			DRIVER(drive)->end_request(0, HWGROUP(drive));
-		else
-	 		ide_end_request(0, HWGROUP(drive));
-	} else {
-		if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
-			++rq->errors;
-			return ide_do_reset(drive);
-		}
-		if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
-			drive->special.b.recalibrate = 1;
-		++rq->errors;
-	}
-	return ide_stopped;
-}
-
-/*
- * Issue a simple drive command
- * The drive must be selected beforehand.
- */
-void ide_cmd (ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
-{
-	ide_set_handler (drive, handler, WAIT_CMD, NULL);
-	if (IDE_CONTROL_REG)
-		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* clear nIEN */
-	OUT_BYTE(nsect,IDE_NSECTOR_REG);
-	OUT_BYTE(cmd,IDE_COMMAND_REG);
-}
-
-/*
- * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
- */
-static ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
-{
-	struct request *rq = HWGROUP(drive)->rq;
-	byte *args = (byte *) rq->buffer;
-	byte stat = GET_STAT();
-	int retries = 10;
-
-	ide__sti();	/* local CPU only */
-	if ((stat & DRQ_STAT) && args && args[3]) {
-		byte io_32bit = drive->io_32bit;
-		drive->io_32bit = 0;
-		ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
-		drive->io_32bit = io_32bit;
-		while (((stat = GET_STAT()) & BUSY_STAT) && retries--)
-			udelay(100);
-	}
-
-	if (!OK_STAT(stat, READY_STAT, BAD_STAT))
-		return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
-	ide_end_drive_cmd (drive, stat, GET_ERR());
-	return ide_stopped;
-}
-
-/*
- * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
- * commands to a drive.  It used to do much more, but has been scaled back.
- */
-static ide_startstop_t do_special (ide_drive_t *drive)
-{
-	special_t *s = &drive->special;
-
-#ifdef DEBUG
-	printk("%s: do_special: 0x%02x\n", drive->name, s->all);
-#endif
-	if (s->b.set_tune) {
-		ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
-		s->b.set_tune = 0;
-		if (tuneproc != NULL)
-			tuneproc(drive, drive->tune_req);
-	} else if (drive->driver != NULL) {
-		return DRIVER(drive)->special(drive);
-	} else if (s->all) {
-		printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
-		s->all = 0;
-	}
-	return ide_stopped;
-}
-
-/*
- * This routine busy-waits for the drive status to be not "busy".
- * It then checks the status for all of the "good" bits and none
- * of the "bad" bits, and if all is okay it returns 0.  All other
- * cases return 1 after invoking ide_error() -- caller should just return.
- *
- * This routine should get fixed to not hog the cpu during extra long waits..
- * That could be done by busy-waiting for the first jiffy or two, and then
- * setting a timer to wake up at half second intervals thereafter,
- * until timeout is achieved, before timing out.
- */
-int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) {
-	byte stat;
-	int i;
-	unsigned long flags;
- 
-	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
-	if ((stat = GET_STAT()) & BUSY_STAT) {
-		__save_flags(flags);	/* local CPU only */
-		ide__sti();		/* local CPU only */
-		timeout += jiffies;
-		while ((stat = GET_STAT()) & BUSY_STAT) {
-			if (0 < (signed long)(jiffies - timeout)) {
-				__restore_flags(flags);	/* local CPU only */
-				*startstop = ide_error(drive, "status timeout", stat);
-				return 1;
-			}
-		}
-		__restore_flags(flags);	/* local CPU only */
-	}
-	/*
-	 * Allow status to settle, then read it again.
-	 * A few rare drives vastly violate the 400ns spec here,
-	 * so we'll wait up to 10usec for a "good" status
-	 * rather than expensively fail things immediately.
-	 * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
-	 */
-	for (i = 0; i < 10; i++) {
-		udelay(1);
-		if (OK_STAT((stat = GET_STAT()), good, bad))
-			return 0;
-	}
-	*startstop = ide_error(drive, "status error", stat);
-	return 1;
-}
-
-/*
- * execute_drive_cmd() issues a special drive command,
- * usually initiated by ioctl() from the external hdparm program.
- */
-static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq)
-{
-	byte *args = rq->buffer;
-	if (args) {
-#ifdef DEBUG
-		printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
-		 drive->name, args[0], args[1], args[2], args[3]);
-#endif
-		if (args[0] == WIN_SMART) {
-			OUT_BYTE(0x4f, IDE_LCYL_REG);
-			OUT_BYTE(0xc2, IDE_HCYL_REG);
-			OUT_BYTE(args[2],IDE_FEATURE_REG);
-			OUT_BYTE(args[1],IDE_SECTOR_REG);
-			ide_cmd(drive, args[0], args[3], &drive_cmd_intr);
-			return ide_started;
-		}
-		OUT_BYTE(args[2],IDE_FEATURE_REG);
-		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
-		return ide_started;
-	} else {
-		/*
-		 * NULL is actually a valid way of waiting for
-		 * all current requests to be flushed from the queue.
-		 */
-#ifdef DEBUG
-		printk("%s: DRIVE_CMD (null)\n", drive->name);
-#endif
-		ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
-		return ide_stopped;
-	}
-}
-
-/*
- * start_request() initiates handling of a new I/O request
- */
-static ide_startstop_t start_request (ide_drive_t *drive)
-{
-	ide_startstop_t startstop;
-	unsigned long block, blockend;
-	struct request *rq = blkdev_entry_next_request(&drive->queue.queue_head);
-	unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
-	ide_hwif_t *hwif = HWIF(drive);
-
-#ifdef DEBUG
-	printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
-#endif
-	if (unit >= MAX_DRIVES) {
-		printk("%s: bad device number: %s\n", hwif->name, kdevname(rq->rq_dev));
-		goto kill_rq;
-	}
-#ifdef DEBUG
-	if (rq->bh && !buffer_locked(rq->bh)) {
-		printk("%s: block not locked\n", drive->name);
-		goto kill_rq;
-	}
-#endif
-	block    = rq->sector;
-	blockend = block + rq->nr_sectors;
-#if 0
-	if ((rq->cmd == READ || rq->cmd == WRITE) &&
-	    (drive->media == ide_disk || drive->media == ide_floppy))
-#endif
-	{
-		if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {
-			printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,
-			 (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);
-			goto kill_rq;
-		}
-		block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
-	}
-	/* Yecch - this will shift the entire interval,
-	   possibly killing some innocent following sector */
-	if (block == 0 && drive->remap_0_to_1 == 1)
-		block = 1;  /* redirect MBR access to EZ-Drive partn table */
-
-#if (DISK_RECOVERY_TIME > 0)
-	while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
-#endif
-
-	SELECT_DRIVE(hwif, drive);
-	if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
-		printk("%s: drive not ready for command\n", drive->name);
-		return startstop;
-	}
-	if (!drive->special.all) {
-		if (rq->cmd == IDE_DRIVE_CMD) {
-			return execute_drive_cmd(drive, rq);
-		}
-		if (drive->driver != NULL) {
-			return (DRIVER(drive)->do_request(drive, rq, block));
-		}
-		printk("%s: media type %d not supported\n", drive->name, drive->media);
-		goto kill_rq;
-	}
-	return do_special(drive);
-kill_rq:
-	if (drive->driver != NULL)
-		DRIVER(drive)->end_request(0, HWGROUP(drive));
-	else
-		ide_end_request(0, HWGROUP(drive));
-	return ide_stopped;
-}
-
-/*
- * ide_stall_queue() can be used by a drive to give excess bandwidth back
- * to the hwgroup by sleeping for timeout jiffies.
- */
-void ide_stall_queue (ide_drive_t *drive, unsigned long timeout)
-{
-	if (timeout > WAIT_WORSTCASE)
-		timeout = WAIT_WORSTCASE;
-	drive->sleep = timeout + jiffies;
-}
-
-#define WAKEUP(drive)	((drive)->service_start + 2 * (drive)->service_time)
-
-/*
- * choose_drive() selects the next drive which will be serviced.
- */
-static inline ide_drive_t *choose_drive (ide_hwgroup_t *hwgroup)
-{
-	ide_drive_t *drive, *best;
-
-repeat:	
-	best = NULL;
-	drive = hwgroup->drive;
-	do {
-		if (!list_empty(&drive->queue.queue_head) && (!drive->sleep || 0 <= (signed long)(jiffies - drive->sleep))) {
-			if (!best
-			 || (drive->sleep && (!best->sleep || 0 < (signed long)(best->sleep - drive->sleep)))
-			 || (!best->sleep && 0 < (signed long)(WAKEUP(best) - WAKEUP(drive))))
-			{
-				if( !drive->queue.plugged )
-					best = drive;
-			}
-		}
-	} while ((drive = drive->next) != hwgroup->drive);
-	if (best && best->nice1 && !best->sleep && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) {
-		long t = (signed long)(WAKEUP(best) - jiffies);
-		if (t >= WAIT_MIN_SLEEP) {
-			/*
-			 * We *may* have some time to spare, but first let's see if
-			 * someone can potentially benefit from our nice mood today..
-			 */
-			drive = best->next;
-			do {
-				if (!drive->sleep
-				 && 0 < (signed long)(WAKEUP(drive) - (jiffies - best->service_time))
-				 && 0 < (signed long)((jiffies + t) - WAKEUP(drive)))
-				{
-					ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP));
-					goto repeat;
-				}
-			} while ((drive = drive->next) != best);
-		}
-	}
-	return best;
-}
-
-/*
- * Issue a new request to a drive from hwgroup
- * Caller must have already done spin_lock_irqsave(&io_request_lock, ..);
- *
- * A hwgroup is a serialized group of IDE interfaces.  Usually there is
- * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
- * may have both interfaces in a single hwgroup to "serialize" access.
- * Or possibly multiple ISA interfaces can share a common IRQ by being grouped
- * together into one hwgroup for serialized access.
- *
- * Note also that several hwgroups can end up sharing a single IRQ,
- * possibly along with many other devices.  This is especially common in
- * PCI-based systems with off-board IDE controller cards.
- *
- * The IDE driver uses the single global io_request_lock spinlock to protect
- * access to the request queues, and to protect the hwgroup->busy flag.
- *
- * The first thread into the driver for a particular hwgroup sets the
- * hwgroup->busy flag to indicate that this hwgroup is now active,
- * and then initiates processing of the top request from the request queue.
- *
- * Other threads attempting entry notice the busy setting, and will simply
- * queue their new requests and exit immediately.  Note that hwgroup->busy
- * remains set even when the driver is merely awaiting the next interrupt.
- * Thus, the meaning is "this hwgroup is busy processing a request".
- *
- * When processing of a request completes, the completing thread or IRQ-handler
- * will start the next request from the queue.  If no more work remains,
- * the driver will clear the hwgroup->busy flag and exit.
- *
- * The io_request_lock (spinlock) is used to protect all access to the
- * hwgroup->busy flag, but is otherwise not needed for most processing in
- * the driver.  This makes the driver much more friendlier to shared IRQs
- * than previous designs, while remaining 100% (?) SMP safe and capable.
- */
-static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
-{
-	ide_drive_t	*drive;
-	ide_hwif_t	*hwif;
-	ide_startstop_t	startstop;
-
-	ide_get_lock(&ide_lock, ide_intr, hwgroup);	/* for atari only: POSSIBLY BROKEN HERE(?) */
-
-	__cli();	/* necessary paranoia: ensure IRQs are masked on local CPU */
-
-	while (!hwgroup->busy) {
-		hwgroup->busy = 1;
-		drive = choose_drive(hwgroup);
-		if (drive == NULL) {
-			unsigned long sleep = 0;
-			hwgroup->rq = NULL;
-			drive = hwgroup->drive;
-			do {
-				if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep)))
-					sleep = drive->sleep;
-			} while ((drive = drive->next) != hwgroup->drive);
-			if (sleep) {
-				/*
-				 * Take a short snooze, and then wake up this hwgroup again.
-				 * This gives other hwgroups on the same a chance to
-				 * play fairly with us, just in case there are big differences
-				 * in relative throughputs.. don't want to hog the cpu too much.
-				 */
-				if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) 
-					sleep = jiffies + WAIT_MIN_SLEEP;
-#if 1
-				if (hwgroup->timer.next || hwgroup->timer.prev)
-					printk("ide_set_handler: timer already active\n");
-#endif
-				hwgroup->sleeping = 1;	/* so that ide_timer_expiry knows what to do */
-				mod_timer(&hwgroup->timer, sleep);
-				/* we purposely leave hwgroup->busy==1 while sleeping */
-			} else {
-				/* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
-				ide_release_lock(&ide_lock);	/* for atari only */
-				hwgroup->busy = 0;
-			}
-			return;		/* no more work for this hwgroup (for now) */
-		}
-		hwif = HWIF(drive);
-		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && hwif->io_ports[IDE_CONTROL_OFFSET]) {
-			/* set nIEN for previous hwif */
-			OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
-		}
-		hwgroup->hwif = hwif;
-		hwgroup->drive = drive;
-		drive->sleep = 0;
-		drive->service_start = jiffies;
-
-		if ( drive->queue.plugged )	/* paranoia */
-			printk("%s: Huh? nuking plugged queue\n", drive->name);
-		hwgroup->rq = blkdev_entry_next_request(&drive->queue.queue_head);
-		/*
-		 * Some systems have trouble with IDE IRQs arriving while
-		 * the driver is still setting things up.  So, here we disable
-		 * the IRQ used by this interface while the request is being started.
-		 * This may look bad at first, but pretty much the same thing
-		 * happens anyway when any interrupt comes in, IDE or otherwise
-		 *  -- the kernel masks the IRQ while it is being handled.
-		 */
-		if (hwif->irq != masked_irq)
-			disable_irq_nosync(hwif->irq);
-		spin_unlock(&io_request_lock);
-		ide__sti();	/* allow other IRQs while we start this request */
-		startstop = start_request(drive);
-		spin_lock_irq(&io_request_lock);
-		if (hwif->irq != masked_irq)
-			enable_irq(hwif->irq);
-		if (startstop == ide_stopped)
-			hwgroup->busy = 0;
-	}
-}
-
-/*
- * ide_get_queue() returns the queue which corresponds to a given device.
- */
-request_queue_t *ide_get_queue (kdev_t dev)
-{
-	ide_hwif_t *hwif = (ide_hwif_t *)blk_dev[MAJOR(dev)].data;
-
-	return &hwif->drives[DEVICE_NR(dev) & 1].queue;
-}
-
-void do_ide0_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[0].hwgroup, 0);
-}
-
-#if MAX_HWIFS > 1
-void do_ide1_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[1].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 1 */
-
-#if MAX_HWIFS > 2
-void do_ide2_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[2].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 2 */
-
-#if MAX_HWIFS > 3
-void do_ide3_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[3].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 3 */
-
-#if MAX_HWIFS > 4
-void do_ide4_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[4].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 4 */
-
-#if MAX_HWIFS > 5
-void do_ide5_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[5].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 5 */
-
-#if MAX_HWIFS > 6
-void do_ide6_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[6].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 6 */
-
-#if MAX_HWIFS > 7
-void do_ide7_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[7].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 7 */
-
-#if MAX_HWIFS > 8
-void do_ide8_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[8].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 8 */
-
-#if MAX_HWIFS > 9
-void do_ide9_request (request_queue_t *q)
-{
-	ide_do_request (ide_hwifs[9].hwgroup, 0);
-}
-#endif /* MAX_HWIFS > 9 */
-
-/*
- * ide_timer_expiry() is our timeout function for all drive operations.
- * But note that it can also be invoked as a result of a "sleep" operation
- * triggered by the mod_timer() call in ide_do_request.
- */
-void ide_timer_expiry (unsigned long data)
-{
-	ide_hwgroup_t	*hwgroup = (ide_hwgroup_t *) data;
-	ide_handler_t	*handler;
-	ide_expiry_t	*expiry;
- 	unsigned long	flags;
-	unsigned long	wait;
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	del_timer(&hwgroup->timer);
-
-	if ((handler = hwgroup->handler) == NULL) {
-		/*
-		 * Either a marginal timeout occured
-		 * (got the interrupt just as timer expired),
-		 * or we were "sleeping" to give other devices a chance.
-		 * Either way, we don't really want to complain about anything.
-		 */
-		if (hwgroup->sleeping) {
-			hwgroup->sleeping = 0;
-			hwgroup->busy = 0;
-		}
-	} else {
-		ide_drive_t *drive = hwgroup->drive;
-		if (!drive) {
-			printk("ide_timer_expiry: hwgroup->drive was NULL\n");
-			hwgroup->handler = NULL;
-		} else {
-			ide_hwif_t *hwif;
-			ide_startstop_t startstop;
-			if (!hwgroup->busy) {
-				hwgroup->busy = 1;	/* paranoia */
-				printk("%s: ide_timer_expiry: hwgroup->busy was 0 ??\n", drive->name);
-			}
-			if ((expiry = hwgroup->expiry) != NULL) {
-				/* continue */
-				if ((wait = expiry(drive)) != 0) {
-					/* reset timer */
-					hwgroup->timer.expires  = jiffies + wait;
-					add_timer(&hwgroup->timer);
-					spin_unlock_irqrestore(&io_request_lock, flags);
-					return;
-				}
-			}
-			hwgroup->handler = NULL;
-			/*
-			 * We need to simulate a real interrupt when invoking
-			 * the handler() function, which means we need to globally
-			 * mask the specific IRQ:
-			 */
-			spin_unlock(&io_request_lock);
-			hwif  = HWIF(drive);
-			disable_irq(hwif->irq);	/* disable_irq_nosync ?? */
-			__cli();	/* local CPU only, as if we were handling an interrupt */
-			if (hwgroup->poll_timeout != 0) {
-				startstop = handler(drive);
-			} else if (drive_is_ready(drive)) {
-				if (drive->waiting_for_dma)
-					(void) hwgroup->hwif->dmaproc(ide_dma_lostirq, drive);
-				(void)ide_ack_intr(hwif);
-				printk("%s: lost interrupt\n", drive->name);
-				startstop = handler(drive);
-			} else {
-				if (drive->waiting_for_dma) {
-					(void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
-					printk("%s: timeout waiting for DMA\n", drive->name);
-					(void) hwgroup->hwif->dmaproc(ide_dma_timeout, drive);
-				}
-				startstop = ide_error(drive, "irq timeout", GET_STAT());
-			}
-			set_recovery_timer(hwif);
-			drive->service_time = jiffies - drive->service_start;
-			enable_irq(hwif->irq);
-			spin_lock_irq(&io_request_lock);
-			if (startstop == ide_stopped)
-				hwgroup->busy = 0;
-		}
-	}
-	ide_do_request(hwgroup, 0);
-	spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
-/*
- * There's nothing really useful we can do with an unexpected interrupt,
- * other than reading the status register (to clear it), and logging it.
- * There should be no way that an irq can happen before we're ready for it,
- * so we needn't worry much about losing an "important" interrupt here.
- *
- * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
- * drive enters "idle", "standby", or "sleep" mode, so if the status looks
- * "good", we just ignore the interrupt completely.
- *
- * This routine assumes __cli() is in effect when called.
- *
- * If an unexpected interrupt happens on irq15 while we are handling irq14
- * and if the two interfaces are "serialized" (CMD640), then it looks like
- * we could screw up by interfering with a new request being set up for irq15.
- *
- * In reality, this is a non-issue.  The new command is not sent unless the
- * drive is ready to accept one, in which case we know the drive is not
- * trying to interrupt us.  And ide_set_handler() is always invoked before
- * completing the issuance of any new drive command, so we will not be
- * accidently invoked as a result of any valid command completion interrupt.
- *
- */
-static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
-{
-	byte stat;
-	ide_hwif_t *hwif = hwgroup->hwif;
-
-	/*
-	 * handle the unexpected interrupt
-	 */
-	do {
-		if (hwif->irq == irq) {
-			stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
-			if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
-				/* Try to not flood the console with msgs */
-				static unsigned long last_msgtime = 0, count = 0;
-				++count;
-				if (0 < (signed long)(jiffies - (last_msgtime + HZ))) {
-					last_msgtime = jiffies;
-					printk("%s%s: unexpected interrupt, status=0x%02x, count=%ld\n",
-					 hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count);
-				}
-			}
-		}
-	} while ((hwif = hwif->next) != hwgroup->hwif);
-}
-
-/*
- * entry point for all interrupts, caller does __cli() for us
- */
-void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
-{
-	unsigned long flags;
-	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
-	ide_hwif_t *hwif;
-	ide_drive_t *drive;
-	ide_handler_t *handler;
-	ide_startstop_t startstop;
-
-	spin_lock_irqsave(&io_request_lock, flags);
-	hwif = hwgroup->hwif;
-
-	if (!ide_ack_intr(hwif)) {
-		spin_unlock_irqrestore(&io_request_lock, flags);
-		return;
-	}
-
-	if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) {
-		/*
-		 * Not expecting an interrupt from this drive.
-		 * That means this could be:
-		 *	(1) an interrupt from another PCI device
-		 *	sharing the same PCI INT# as us.
-		 * or	(2) a drive just entered sleep or standby mode,
-		 *	and is interrupting to let us know.
-		 * or	(3) a spurious interrupt of unknown origin.
-		 *
-		 * For PCI, we cannot tell the difference,
-		 * so in that case we just ignore it and hope it goes away.
-		 */
-#ifdef CONFIG_BLK_DEV_IDEPCI
-		if (IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
-#endif	/* CONFIG_BLK_DEV_IDEPCI */
-		{
-			/*
-			 * Probably not a shared PCI interrupt,
-			 * so we can safely try to do something about it:
-			 */
-			unexpected_intr(irq, hwgroup);
-#ifdef CONFIG_BLK_DEV_IDEPCI
-		} else {
-			/*
-			 * Whack the status register, just in case we have a leftover pending IRQ.
-			 */
-			(void) IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-		}
-		spin_unlock_irqrestore(&io_request_lock, flags);
-		return;
-	}
-	drive = hwgroup->drive;
-	if (!drive) {
-		/*
-		 * This should NEVER happen, and there isn't much we could do about it here.
-		 */
-		spin_unlock_irqrestore(&io_request_lock, flags);
-		return;
-	}
-	if (!drive_is_ready(drive)) {
-		/*
-		 * This happens regularly when we share a PCI IRQ with another device.
-		 * Unfortunately, it can also happen with some buggy drives that trigger
-		 * the IRQ before their status register is up to date.  Hopefully we have
-		 * enough advance overhead that the latter isn't a problem.
-		 */
-		spin_unlock_irqrestore(&io_request_lock, flags);
-		return;
-	}
-	if (!hwgroup->busy) {
-		hwgroup->busy = 1;	/* paranoia */
-		printk("%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
-	}
-	hwgroup->handler = NULL;
-	del_timer(&hwgroup->timer);
-	spin_unlock(&io_request_lock);
-
-	if (drive->unmask)
-		ide__sti();	/* local CPU only */
-	startstop = handler(drive);		/* service this interrupt, may set handler for next interrupt */
-	spin_lock_irq(&io_request_lock);
-
-	/*
-	 * Note that handler() may have set things up for another
-	 * interrupt to occur soon, but it cannot happen until
-	 * we exit from this routine, because it will be the
-	 * same irq as is currently being serviced here, and Linux
-	 * won't allow another of the same (on any CPU) until we return.
-	 */
-	set_recovery_timer(HWIF(drive));
-	drive->service_time = jiffies - drive->service_start;
-	if (startstop == ide_stopped) {
-		if (hwgroup->handler == NULL) {	/* paranoia */
-			hwgroup->busy = 0;
-			ide_do_request(hwgroup, hwif->irq);
-		} else {
-			printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
-		}
-	}
-	spin_unlock_irqrestore(&io_request_lock, flags);
-}
-
-/*
- * get_info_ptr() returns the (ide_drive_t *) for a given device number.
- * It returns NULL if the given device number does not match any present drives.
- */
-ide_drive_t *get_info_ptr (kdev_t i_rdev)
-{
-	int		major = MAJOR(i_rdev);
-#if 0
-	int		minor = MINOR(i_rdev) & PARTN_MASK;
-#endif
-	unsigned int	h;
-
-	for (h = 0; h < MAX_HWIFS; ++h) {
-		ide_hwif_t  *hwif = &ide_hwifs[h];
-		if (hwif->present && major == hwif->major) {
-			unsigned unit = DEVICE_NR(i_rdev);
-			if (unit < MAX_DRIVES) {
-				ide_drive_t *drive = &hwif->drives[unit];
-#if 0
-				if ((drive->present) && (drive->part[minor].nr_sects))
-#else
-				if (drive->present)
-#endif
-					return drive;
-			}
-			break;
-		}
-	}
-	return NULL;
-}
-
-/*
- * This function is intended to be used prior to invoking ide_do_drive_cmd().
- */
-void ide_init_drive_cmd (struct request *rq)
-{
-	rq->buffer = NULL;
-	rq->cmd = IDE_DRIVE_CMD;
-	rq->sector = 0;
-	rq->nr_sectors = 0;
-	rq->current_nr_sectors = 0;
-	rq->sem = NULL;
-	rq->bh = NULL;
-	rq->bhtail = NULL;
-	rq->q = NULL;
-}
-
-/*
- * This function issues a special IDE device request
- * onto the request queue.
- *
- * If action is ide_wait, then the rq is queued at the end of the
- * request queue, and the function sleeps until it has been processed.
- * This is for use when invoked from an ioctl handler.
- *
- * If action is ide_preempt, then the rq is queued at the head of
- * the request queue, displacing the currently-being-processed
- * request and this function returns immediately without waiting
- * for the new rq to be completed.  This is VERY DANGEROUS, and is
- * intended for careful use by the ATAPI tape/cdrom driver code.
- *
- * If action is ide_next, then the rq is queued immediately after
- * the currently-being-processed-request (if any), and the function
- * returns without waiting for the new rq to be completed.  As above,
- * This is VERY DANGEROUS, and is intended for careful use by the
- * ATAPI tape/cdrom driver code.
- *
- * If action is ide_end, then the rq is queued at the end of the
- * request queue, and the function returns immediately without waiting
- * for the new rq to be completed. This is again intended for careful
- * use by the ATAPI tape/cdrom driver code.
- */
-int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
-{
-	unsigned long flags;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	unsigned int major = HWIF(drive)->major;
-	struct list_head * queue_head;
-	DECLARE_MUTEX_LOCKED(sem);
-
-#ifdef CONFIG_BLK_DEV_PDC4030
-	if (HWIF(drive)->chipset == ide_pdc4030 && rq->buffer != NULL)
-		return -ENOSYS;  /* special drive cmds not supported */
-#endif
-	rq->errors = 0;
-	rq->rq_status = RQ_ACTIVE;
-	rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
-	if (action == ide_wait)
-		rq->sem = &sem;
-	spin_lock_irqsave(&io_request_lock, flags);
-	queue_head = &drive->queue.queue_head;
-	if (list_empty(queue_head) || action == ide_preempt) {
-		if (action == ide_preempt)
-			hwgroup->rq = NULL;
-	} else {
-		if (action == ide_wait || action == ide_end) {
-			queue_head = queue_head->prev;
-		} else
-			queue_head = queue_head->next;
-	}
-	list_add(&rq->queue, queue_head);
-	ide_do_request(hwgroup, 0);
-	spin_unlock_irqrestore(&io_request_lock, flags);
-	if (action == ide_wait) {
-		down(&sem);			/* wait for it to be serviced */
-		return rq->errors ? -EIO : 0;	/* return -EIO if errors */
-	}
-	return 0;
-
-}
-
-/*
- * This routine is called to flush all partitions and partition tables
- * for a changed disk, and then re-read the new partition table.
- * If we are revalidating a disk because of a media change, then we
- * enter with usage == 0.  If we are using an ioctl, we automatically have
- * usage == 1 (we need an open channel to use an ioctl :-), so this
- * is our limit.
- */
-int ide_revalidate_disk (kdev_t i_rdev)
-{
-	ide_drive_t *drive;
-	ide_hwgroup_t *hwgroup;
-	unsigned int p, major, minor;
-	long flags;
-
-	if ((drive = get_info_ptr(i_rdev)) == NULL)
-		return -ENODEV;
-	major = MAJOR(i_rdev);
-	minor = drive->select.b.unit << PARTN_BITS;
-	hwgroup = HWGROUP(drive);
-	spin_lock_irqsave(&io_request_lock, flags);
-	if (drive->busy || (drive->usage > 1)) {
-		spin_unlock_irqrestore(&io_request_lock, flags);
-		return -EBUSY;
-	};
-	drive->busy = 1;
-	MOD_INC_USE_COUNT;
-	spin_unlock_irqrestore(&io_request_lock, flags);
-
-	for (p = 0; p < (1<<PARTN_BITS); ++p) {
-		if (drive->part[p].nr_sects > 0) {
-			kdev_t devp = MKDEV(major, minor+p);
-			struct super_block * sb = get_super(devp);
-			fsync_dev          (devp);
-			if (sb)
-				invalidate_inodes(sb);
-			invalidate_buffers (devp);
-			set_blocksize(devp, 1024);
-		}
-		drive->part[p].start_sect = 0;
-		drive->part[p].nr_sects   = 0;
-	};
-
-	grok_partitions(HWIF(drive)->gd, drive->select.b.unit,
-			(drive->media != ide_disk &&
-			 drive->media != ide_floppy) ? 1 : 1<<PARTN_BITS,
-				current_capacity(drive));
-
-	drive->busy = 0;
-	wake_up(&drive->wqueue);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-static void revalidate_drives (void)
-{
-	ide_hwif_t *hwif;
-	ide_drive_t *drive;
-	int index, unit;
-
-	for (index = 0; index < MAX_HWIFS; ++index) {
-		hwif = &ide_hwifs[index];
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			drive = &ide_hwifs[index].drives[unit];
-			if (drive->revalidate) {
-				drive->revalidate = 0;
-				if (!initializing)
-					(void) ide_revalidate_disk(MKDEV(hwif->major, unit<<PARTN_BITS));
-			}
-		}
-	}
-}
-
-static void ide_probe_module (void)
-{
-	if (!ide_probe) {
-#ifdef CONFIG_KMOD
-		(void) request_module("ide-probe-mod");
-#endif /* CONFIG_KMOD */
-	} else {
-		(void) ide_probe->init();
-	}
-	revalidate_drives();
-}
-
-static void ide_driver_module (void)
-{
-	int index;
-	ide_module_t *module = ide_modules;
-
-	for (index = 0; index < MAX_HWIFS; ++index)
-		if (ide_hwifs[index].present)
-			goto search;
-	ide_probe_module();
-search:
-	while (module) {
-		(void) module->init();
-		module = module->next;
-	}
-	revalidate_drives();
-}
-
-static int ide_open (struct inode * inode, struct file * filp)
-{
-	ide_drive_t *drive;
-	int rc;
-
-	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
-		return -ENXIO;
-	MOD_INC_USE_COUNT;
-	if (drive->driver == NULL)
-		ide_driver_module();
-#ifdef CONFIG_KMOD
-	if (drive->driver == NULL) {
-		if (drive->media == ide_disk)
-			(void) request_module("ide-disk");
-		if (drive->media == ide_cdrom)
-			(void) request_module("ide-cd");
-		if (drive->media == ide_tape)
-			(void) request_module("ide-tape");
-		if (drive->media == ide_floppy)
-			(void) request_module("ide-floppy");
-	}
-#endif /* CONFIG_KMOD */
-	while (drive->busy)
-		sleep_on(&drive->wqueue);
-	drive->usage++;
-	if (drive->driver != NULL) {
-		if ((rc = DRIVER(drive)->open(inode, filp, drive)))
-			MOD_DEC_USE_COUNT;
-		return rc;
-	}
-	printk ("%s: driver not present\n", drive->name);
-	drive->usage--;
-	MOD_DEC_USE_COUNT;
-	return -ENXIO;
-}
-
-/*
- * Releasing a block device means we sync() it, so that it can safely
- * be forgotten about...
- */
-static int ide_release (struct inode * inode, struct file * file)
-{
-	ide_drive_t *drive;
-
-	if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
-		drive->usage--;
-		if (drive->driver != NULL)
-			DRIVER(drive)->release(inode, file, drive);
-		MOD_DEC_USE_COUNT;
-	}
-	return 0;
-}
-
-int ide_replace_subdriver (ide_drive_t *drive, const char *driver)
-{
-	if (!drive->present || drive->busy || drive->usage)
-		goto abort;
-	if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
-		goto abort;
-	strncpy(drive->driver_req, driver, 9);
-	ide_driver_module();
-	drive->driver_req[0] = 0;
-	ide_driver_module();
-	if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver))
-		return 0;
-abort:
-	return 1;
-}
-
-#ifdef CONFIG_PROC_FS
-ide_proc_entry_t generic_subdriver_entries[] = {
-	{ "capacity",	S_IFREG|S_IRUGO,	proc_ide_read_capacity,	NULL },
-	{ NULL, 0, NULL, NULL }
-};
-#endif
-
-/*
- * Note that we only release the standard ports,
- * and do not even try to handle any extra ports
- * allocated for weird IDE interface chipsets.
- */
-void hwif_unregister (ide_hwif_t *hwif)
-{
-	if (hwif->straight8) {
-		ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8);
-		goto jump_eight;
-	}
-	if (hwif->io_ports[IDE_DATA_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
-	if (hwif->io_ports[IDE_ERROR_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
-	if (hwif->io_ports[IDE_NSECTOR_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
-	if (hwif->io_ports[IDE_SECTOR_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1);
-	if (hwif->io_ports[IDE_LCYL_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_LCYL_OFFSET], 1);
-	if (hwif->io_ports[IDE_HCYL_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
-	if (hwif->io_ports[IDE_SELECT_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
-	if (hwif->io_ports[IDE_STATUS_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
-jump_eight:
-	if (hwif->io_ports[IDE_CONTROL_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
-	if (hwif->io_ports[IDE_IRQ_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
-}
-
-void ide_unregister (unsigned int index)
-{
-	struct gendisk *gd, **gdp;
-	ide_drive_t *drive, *d;
-	ide_hwif_t *hwif, *g;
-	ide_hwgroup_t *hwgroup;
-	int irq_count = 0, unit, i;
-	unsigned long flags;
-	unsigned int p, minor;
-	ide_hwif_t old_hwif;
-
-	if (index >= MAX_HWIFS)
-		return;
-	save_flags(flags);	/* all CPUs */
-	cli();			/* all CPUs */
-	hwif = &ide_hwifs[index];
-	if (!hwif->present)
-		goto abort;
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		drive = &hwif->drives[unit];
-		if (!drive->present)
-			continue;
-		if (drive->busy || drive->usage)
-			goto abort;
-		if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
-			goto abort;
-	}
-	hwif->present = 0;
-	
-	/*
-	 * All clear?  Then blow away the buffer cache
-	 */
-	sti();
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		drive = &hwif->drives[unit];
-		if (!drive->present)
-			continue;
-		minor = drive->select.b.unit << PARTN_BITS;
-		for (p = 0; p < (1<<PARTN_BITS); ++p) {
-			if (drive->part[p].nr_sects > 0) {
-				kdev_t devp = MKDEV(hwif->major, minor+p);
-				struct super_block * sb = get_super(devp);
-				if (sb) invalidate_inodes(sb);
-				invalidate_buffers (devp);
-			}
-		}
-#ifdef CONFIG_PROC_FS
-		destroy_proc_ide_drives(hwif);
-#endif
-	}
-	cli();
-	hwgroup = hwif->hwgroup;
-
-	/*
-	 * free the irq if we were the only hwif using it
-	 */
-	g = hwgroup->hwif;
-	do {
-		if (g->irq == hwif->irq)
-			++irq_count;
-		g = g->next;
-	} while (g != hwgroup->hwif);
-	if (irq_count == 1)
-		free_irq(hwif->irq, hwgroup);
-
-	/*
-	 * Note that we only release the standard ports,
-	 * and do not even try to handle any extra ports
-	 * allocated for weird IDE interface chipsets.
-	 */
-	hwif_unregister(hwif);
-
-	/*
-	 * Remove us from the hwgroup, and free
-	 * the hwgroup if we were the only member
-	 */
-	d = hwgroup->drive;
-	for (i = 0; i < MAX_DRIVES; ++i) {
-		drive = &hwif->drives[i];
-		if (drive->de) {
-			devfs_unregister (drive->de);
-			drive->de = NULL;
-		}
-		if (!drive->present)
-			continue;
-		while (hwgroup->drive->next != drive)
-			hwgroup->drive = hwgroup->drive->next;
-		hwgroup->drive->next = drive->next;
-		if (hwgroup->drive == drive)
-			hwgroup->drive = NULL;
-		if (drive->id != NULL) {
-			kfree(drive->id);
-			drive->id = NULL;
-		}
-		drive->present = 0;
-	}
-	if (d->present)
-		hwgroup->drive = d;
-	while (hwgroup->hwif->next != hwif)
-		hwgroup->hwif = hwgroup->hwif->next;
-	hwgroup->hwif->next = hwif->next;
-	if (hwgroup->hwif == hwif)
-		kfree(hwgroup);
-	else
-		hwgroup->hwif = HWIF(hwgroup->drive);
-
-#ifdef CONFIG_BLK_DEV_IDEDMA
-	if (hwif->dma_base)
-		(void) ide_release_dma(hwif);
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-	/*
-	 * Remove us from the kernel's knowledge
-	 */
-	unregister_blkdev(hwif->major, hwif->name);
-	kfree(blksize_size[hwif->major]);
-	kfree(max_sectors[hwif->major]);
-	kfree(max_readahead[hwif->major]);
-	blk_cleanup_queue(BLK_DEFAULT_QUEUE(hwif->major));
-	blk_dev[hwif->major].data = NULL;
-	blk_dev[hwif->major].queue = NULL;
-	blksize_size[hwif->major] = NULL;
-	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
-		if (*gdp == hwif->gd)
-			break;
-	if (*gdp == NULL)
-		printk("gd not in disk chain!\n");
-	else {
-		gd = *gdp; *gdp = gd->next;
-		kfree(gd->sizes);
-		kfree(gd->part);
-		if (gd->de_arr)
-			kfree (gd->de_arr);
-		if (gd->flags)
-			kfree (gd->flags);
-		kfree(gd);
-	}
-	old_hwif		= *hwif;
-	init_hwif_data (index);	/* restore hwif data to pristine status */
-	hwif->hwgroup		= old_hwif.hwgroup;
-	hwif->tuneproc		= old_hwif.tuneproc;
-	hwif->selectproc	= old_hwif.selectproc;
-	hwif->resetproc		= old_hwif.resetproc;
-	hwif->dmaproc		= old_hwif.dmaproc;
-	hwif->dma_base		= old_hwif.dma_base;
-	hwif->dma_extra		= old_hwif.dma_extra;
-	hwif->config_data	= old_hwif.config_data;
-	hwif->select_data	= old_hwif.select_data;
-	hwif->proc		= old_hwif.proc;
-	hwif->irq		= old_hwif.irq;
-	hwif->major		= old_hwif.major;
-	hwif->chipset		= old_hwif.chipset;
-	hwif->autodma		= old_hwif.autodma;
-	hwif->udma_four		= old_hwif.udma_four;
-#ifdef CONFIG_BLK_DEV_IDEPCI
-	hwif->pci_dev		= old_hwif.pci_dev;
-	hwif->pci_devid		= old_hwif.pci_devid;
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-	hwif->straight8		= old_hwif.straight8;
-
-abort:
-	restore_flags(flags);	/* all CPUs */
-}
-
-/*
- * Setup hw_regs_t structure described by parameters.  You
- * may set up the hw structure yourself OR use this routine to
- * do it for you.
- */
-void ide_setup_ports (	hw_regs_t *hw,
-			ide_ioreg_t base, int *offsets,
-			ide_ioreg_t ctrl, ide_ioreg_t intr,
-			ide_ack_intr_t *ack_intr, int irq)
-{
-	int i;
-
-	for (i = 0; i < IDE_NR_PORTS; i++) {
-		if (offsets[i] == -1) {
-			switch(i) {
-				case IDE_CONTROL_OFFSET:
-					hw->io_ports[i] = ctrl;
-					break;
-				case IDE_IRQ_OFFSET:
-					hw->io_ports[i] = intr;
-					break;
-				default:
-					hw->io_ports[i] = 0;
-					break;
-			}
-		} else {
-			hw->io_ports[i] = base + offsets[i];
-		}
-	}
-	hw->irq = irq;
-	hw->dma = NO_DMA;
-	hw->ack_intr = ack_intr;
-}
-
-/*
- * Register an IDE interface, specifing exactly the registers etc
- * Set init=1 iff calling before probes have taken place.
- */
-int ide_register_hw (hw_regs_t *hw, ide_hwif_t **hwifp)
-{
-	int index, retry = 1;
-	ide_hwif_t *hwif;
-
-	do {
-		for (index = 0; index < MAX_HWIFS; ++index) {
-			hwif = &ide_hwifs[index];
-			if (hwif->hw.io_ports[IDE_DATA_OFFSET] == hw->io_ports[IDE_DATA_OFFSET])
-				goto found;
-		}
-		for (index = 0; index < MAX_HWIFS; ++index) {
-			hwif = &ide_hwifs[index];
-			if ((!hwif->present && !hwif->mate && !initializing) ||
-			    (!hwif->hw.io_ports[IDE_DATA_OFFSET] && initializing))
-				goto found;
-		}
-		for (index = 0; index < MAX_HWIFS; index++)
-			ide_unregister(index);
-	} while (retry--);
-	return -1;
-found:
-	if (hwif->present)
-		ide_unregister(index);
-	if (hwif->present)
-		return -1;
-	memcpy(&hwif->hw, hw, sizeof(*hw));
-	memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports));
-	hwif->irq = hw->irq;
-	hwif->noprobe = 0;
-
-	if (!initializing) {
-		ide_probe_module();
-#ifdef CONFIG_PROC_FS
-		create_proc_ide_interfaces();
-#endif
-		ide_driver_module();
-	}
-
-	if (hwifp)
-		*hwifp = hwif;
-
-	return (initializing || hwif->present) ? index : -1;
-}
-
-/*
- * Compatability function with existing drivers.  If you want
- * something different, use the function above.
- */
-int ide_register (int arg1, int arg2, int irq)
-{
-	hw_regs_t hw;
-	ide_init_hwif_ports(&hw, (ide_ioreg_t) arg1, (ide_ioreg_t) arg2, NULL);
-	hw.irq = irq;
-	return ide_register_hw(&hw, NULL);
-}
-
-void ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
-{
-	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
-
-	while ((*p) && strcmp((*p)->name, name) < 0)
-		p = &((*p)->next);
-	if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
-		goto abort;
-	memset(setting, 0, sizeof(*setting));
-	if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
-		goto abort;
-	strcpy(setting->name, name);		setting->rw = rw;
-	setting->read_ioctl = read_ioctl;	setting->write_ioctl = write_ioctl;
-	setting->data_type = data_type;		setting->min = min;
-	setting->max = max;			setting->mul_factor = mul_factor;
-	setting->div_factor = div_factor;	setting->data = data;
-	setting->set = set;			setting->next = *p;
-	if (drive->driver)
-		setting->auto_remove = 1;
-	*p = setting;
-	return;
-abort:
-	if (setting)
-		kfree(setting);
-}
-
-void ide_remove_setting (ide_drive_t *drive, char *name)
-{
-	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting;
-
-	while ((*p) && strcmp((*p)->name, name))
-		p = &((*p)->next);
-	if ((setting = (*p)) == NULL)
-		return;
-	(*p) = setting->next;
-	kfree(setting->name);
-	kfree(setting);
-}
-
-static ide_settings_t *ide_find_setting_by_ioctl (ide_drive_t *drive, int cmd)
-{
-	ide_settings_t *setting = drive->settings;
-
-	while (setting) {
-		if (setting->read_ioctl == cmd || setting->write_ioctl == cmd)
-			break;
-		setting = setting->next;
-	}
-	return setting;
-}
-
-ide_settings_t *ide_find_setting_by_name (ide_drive_t *drive, char *name)
-{
-	ide_settings_t *setting = drive->settings;
-
-	while (setting) {
-		if (strcmp(setting->name, name) == 0)
-			break;
-		setting = setting->next;
-	}
-	return setting;
-}
-
-static void auto_remove_settings (ide_drive_t *drive)
-{
-	ide_settings_t *setting;
-repeat:
-	setting = drive->settings;
-	while (setting) {
-		if (setting->auto_remove) {
-			ide_remove_setting(drive, setting->name);
-			goto repeat;
-		}
-		setting = setting->next;
-	}
-}
-
-int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting)
-{
-	int		val = -EINVAL;
-	unsigned long	flags;
-
-	if ((setting->rw & SETTING_READ)) {
-		spin_lock_irqsave(&io_request_lock, flags);
-		switch(setting->data_type) {
-			case TYPE_BYTE:
-				val = *((u8 *) setting->data);
-				break;
-			case TYPE_SHORT:
-				val = *((u16 *) setting->data);
-				break;
-			case TYPE_INT:
-			case TYPE_INTA:
-				val = *((u32 *) setting->data);
-				break;
-		}
-		spin_unlock_irqrestore(&io_request_lock, flags);
-	}
-	return val;
-}
-
-int ide_spin_wait_hwgroup (ide_drive_t *drive, unsigned long *flags)
-{
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	unsigned long timeout = jiffies + (3 * HZ);
-
-	spin_lock_irqsave(&io_request_lock, *flags);
-	while (hwgroup->busy) {
-		unsigned long lflags;
-		spin_unlock_irqrestore(&io_request_lock, *flags);
-		__save_flags(lflags);	/* local CPU only */
-		__sti();		/* local CPU only; needed for jiffies */
-		if (0 < (signed long)(jiffies - timeout)) {
-			__restore_flags(lflags);	/* local CPU only */
-			printk("%s: channel busy\n", drive->name);
-			return -EBUSY;
-		}
-		__restore_flags(lflags);	/* local CPU only */
-		spin_lock_irqsave(&io_request_lock, *flags);
-	}
-	return 0;
-}
-
-/*
- * FIXME:  This should be changed to enqueue a special request
- * to the driver to change settings, and then wait on a sema for completion.
- * The current scheme of polling is kludgey, though safe enough.
- */
-int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val)
-{
-	unsigned long flags;
-	int i;
-	u32 *p;
-
-	if (!capable(CAP_SYS_ADMIN))
-		return -EACCES;
-	if (!(setting->rw & SETTING_WRITE))
-		return -EPERM;
-	if (val < setting->min || val > setting->max)
-		return -EINVAL;
-	if (setting->set)
-		return setting->set(drive, val);
-	if (ide_spin_wait_hwgroup(drive, &flags))
-		return -EBUSY;
-	switch (setting->data_type) {
-		case TYPE_BYTE:
-			*((u8 *) setting->data) = val;
-			break;
-		case TYPE_SHORT:
-			*((u16 *) setting->data) = val;
-			break;
-		case TYPE_INT:
-			*((u32 *) setting->data) = val;
-			break;
-		case TYPE_INTA:
-			p = (u32 *) setting->data;
-			for (i = 0; i < 1 << PARTN_BITS; i++, p++)
-				*p = val;
-			break;
-	}
-	spin_unlock_irqrestore(&io_request_lock, flags);
-	return 0;
-}
-
-static int set_io_32bit(ide_drive_t *drive, int arg)
-{
-	drive->io_32bit = arg;
-#ifdef CONFIG_BLK_DEV_DTC2278
-	if (HWIF(drive)->chipset == ide_dtc2278)
-		HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
-#endif /* CONFIG_BLK_DEV_DTC2278 */
-	return 0;
-}
-
-static int set_using_dma (ide_drive_t *drive, int arg)
-{
-	if (!drive->driver || !DRIVER(drive)->supports_dma)
-		return -EPERM;
-	if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
-		return -EPERM;
-	if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive))
-		return -EIO;
-	return 0;
-}
-
-static int set_pio_mode (ide_drive_t *drive, int arg)
-{
-	struct request rq;
-
-	if (!HWIF(drive)->tuneproc)
-		return -ENOSYS;
-	if (drive->special.b.set_tune)
-		return -EBUSY;
-	ide_init_drive_cmd(&rq);
-	drive->tune_req = (byte) arg;
-	drive->special.b.set_tune = 1;
-	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
-	return 0;
-}
-
-void ide_add_generic_settings (ide_drive_t *drive)
-{
-/*
- *			drive	setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function
- */
-	ide_add_setting(drive,	"io_32bit",		drive->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->io_32bit,		set_io_32bit);
-	ide_add_setting(drive,	"keepsettings",		SETTING_RW,					HDIO_GET_KEEPSETTINGS,	HDIO_SET_KEEPSETTINGS,	TYPE_BYTE,	0,	1,				1,		1,		&drive->keep_settings,		NULL);
-	ide_add_setting(drive,	"nice1",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->nice1,			NULL);
-	ide_add_setting(drive,	"pio_mode",		SETTING_WRITE,					-1,			HDIO_SET_PIO_MODE,	TYPE_BYTE,	0,	255,				1,		1,		NULL,				set_pio_mode);
-	ide_add_setting(drive,	"slow",			SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->slow,			NULL);
-	ide_add_setting(drive,	"unmaskirq",		drive->no_unmask ? SETTING_READ : SETTING_RW,	HDIO_GET_UNMASKINTR,	HDIO_SET_UNMASKINTR,	TYPE_BYTE,	0,	1,				1,		1,		&drive->unmask,			NULL);
-	ide_add_setting(drive,	"using_dma",		SETTING_RW,					HDIO_GET_DMA,		HDIO_SET_DMA,		TYPE_BYTE,	0,	1,				1,		1,		&drive->using_dma,		set_using_dma);
-	ide_add_setting(drive,	"ide_scsi",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->scsi,			NULL);
-}
-
-int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf)
-{
-	struct request rq;
-	byte buffer[4];
-
-	if (!buf)
-		buf = buffer;
-	memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors);
-	ide_init_drive_cmd(&rq);
-	rq.buffer = buf;
-	*buf++ = cmd;
-	*buf++ = nsect;
-	*buf++ = feature;
-	*buf++ = sectors;
-	return ide_do_drive_cmd(drive, &rq, ide_wait);
-}
-
-/*
- * Delay for *at least* 50ms.  As we don't know how much time is left
- * until the next tick occurs, we wait an extra tick to be safe.
- * This is used only during the probing/polling for drives at boot time.
- *
- * However, its usefullness may be needed in other places, thus we export it now.
- * The future may change this to a millisecond setable delay.
- */
-void ide_delay_50ms (void)
-{
-	unsigned long timeout = jiffies + ((HZ + 19)/20) + 1;
-	while (0 < (signed long)(timeout - jiffies));
-}
-
-static int ide_ioctl (struct inode *inode, struct file *file,
-			unsigned int cmd, unsigned long arg)
-{
-	int err = 0, major, minor;
-	ide_drive_t *drive;
-	struct request rq;
-	kdev_t dev;
-	ide_settings_t *setting;
-
-	if (!inode || !(dev = inode->i_rdev))
-		return -EINVAL;
-	major = MAJOR(dev); minor = MINOR(dev);
-	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
-		return -ENODEV;
-
-	if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) {
-		if (cmd == setting->read_ioctl) {
-			err = ide_read_setting(drive, setting);
-			return err >= 0 ? put_user(err, (long *) arg) : err;
-		} else {
-			if ((MINOR(inode->i_rdev) & PARTN_MASK))
-				return -EINVAL;
-			return ide_write_setting(drive, setting, arg);
-		}
-	}
-
-	ide_init_drive_cmd (&rq);
-	switch (cmd) {
-		case HDIO_GETGEO:
-		{
-			struct hd_geometry *loc = (struct hd_geometry *) arg;
-			unsigned short bios_cyl = drive->bios_cyl; /* truncate */
-			if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
-			if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
-			if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
-			if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
-			if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
-				(unsigned long *) &loc->start)) return -EFAULT;
-			return 0;
-		}
-
-	 	case BLKGETSIZE:   /* Return device size */
-			return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
-
-		case BLKRRPART: /* Re-read partition tables */
-			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-			return ide_revalidate_disk(inode->i_rdev);
-
-		case HDIO_OBSOLETE_IDENTITY:
-		case HDIO_GET_IDENTITY:
-			if (MINOR(inode->i_rdev) & PARTN_MASK)
-				return -EINVAL;
-			if (drive->id == NULL)
-				return -ENOMSG;
-			if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
-				return -EFAULT;
-			return 0;
-
-		case HDIO_GET_NICE:
-			return put_user(drive->dsc_overlap	<<	IDE_NICE_DSC_OVERLAP	|
-					drive->atapi_overlap	<<	IDE_NICE_ATAPI_OVERLAP	|
-					drive->nice0		<< 	IDE_NICE_0		|
-					drive->nice1		<<	IDE_NICE_1		|
-					drive->nice2		<<	IDE_NICE_2,
-					(long *) arg);
-		case HDIO_DRIVE_CMD:
-		{
-			byte args[4], *argbuf = args;
-			int argsize = 4;
-			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-			if (NULL == (void *) arg)
-				return ide_do_drive_cmd(drive, &rq, ide_wait);
-			if (copy_from_user(args, (void *)arg, 4))
-				return -EFAULT;
-			if (args[3]) {
-				argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
-				argbuf = kmalloc(argsize, GFP_KERNEL);
-				if (argbuf == NULL)
-					return -ENOMEM;
-				memcpy(argbuf, args, 4);
-			}
-			if (ide_ata66_check(drive, args[0], args[1], args[2]))
-				goto abort;
-
-			err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
-
-			if (!err && set_transfer(drive, args[0], args[1], args[2])) {
-#if 0
-				/* active-retuning-calls future */
-				if (HWIF(drive)->tune2proc)
-					HWIF(drive)->tune2proc(drive, args[1]);
-#endif
-				ide_driveid_update(drive);
-			}
-		abort:
-			if (copy_to_user((void *)arg, argbuf, argsize))
-				err = -EFAULT;
-			if (argsize > 4)
-				kfree(argbuf);
-			return err;
-		}
-
-		case HDIO_SCAN_HWIF:
-		{
-			int args[3];
-			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-			if (copy_from_user(args, (void *)arg, 3 * sizeof(int)))
-				return -EFAULT;
-			if (ide_register(args[0], args[1], args[2]) == -1)
-				return -EIO;
-			return 0;
-		}
-	        case HDIO_UNREGISTER_HWIF:
-			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-			/* should I check here for arg > MAX_HWIFS, or
-			   just let ide_unregister fail silently? -- shaver */
-			ide_unregister(arg);
-			return 0;
-		case HDIO_SET_NICE:
-			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-			if (drive->driver == NULL)
-				return -EPERM;
-			if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
-				return -EPERM;
-			drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1;
-			if (drive->dsc_overlap && !DRIVER(drive)->supports_dsc_overlap) {
-				drive->dsc_overlap = 0;
-				return -EPERM;
-			}
-			drive->nice1 = (arg >> IDE_NICE_1) & 1;
-			return 0;
-
-		case BLKROSET:
-		case BLKROGET:
-		case BLKFLSBUF:
-		case BLKSSZGET:
-		case BLKPG:
-			return blk_ioctl(inode->i_rdev, cmd, arg);
-
-		default:
-			if (drive->driver != NULL)
-				return DRIVER(drive)->ioctl(drive, inode, file, cmd, arg);
-			return -EPERM;
-	}
-}
-
-static int ide_check_media_change (kdev_t i_rdev)
-{
-	ide_drive_t *drive;
-
-	if ((drive = get_info_ptr(i_rdev)) == NULL)
-		return -ENODEV;
-	if (drive->driver != NULL)
-		return DRIVER(drive)->media_change(drive);
-	return 0;
-}
-
-void ide_fixstring (byte *s, const int bytecount, const int byteswap)
-{
-	byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
-
-	if (byteswap) {
-		/* convert from big-endian to host byte order */
-		for (p = end ; p != s;) {
-			unsigned short *pp = (unsigned short *) (p -= 2);
-			*pp = ntohs(*pp);
-		}
-	}
-
-	/* strip leading blanks */
-	while (s != end && *s == ' ')
-		++s;
-
-	/* compress internal blanks and strip trailing blanks */
-	while (s != end && *s) {
-		if (*s++ != ' ' || (s != end && *s && *s != ' '))
-			*p++ = *(s-1);
-	}
-
-	/* wipe out trailing garbage */
-	while (p != end)
-		*p++ = '\0';
-}
-
-/*
- * stridx() returns the offset of c within s,
- * or -1 if c is '\0' or not found within s.
- */
-static int __init stridx (const char *s, char c)
-{
-	char *i = strchr(s, c);
-	return (i && c) ? i - s : -1;
-}
-
-/*
- * match_parm() does parsing for ide_setup():
- *
- * 1. the first char of s must be '='.
- * 2. if the remainder matches one of the supplied keywords,
- *     the index (1 based) of the keyword is negated and returned.
- * 3. if the remainder is a series of no more than max_vals numbers
- *     separated by commas, the numbers are saved in vals[] and a
- *     count of how many were saved is returned.  Base10 is assumed,
- *     and base16 is allowed when prefixed with "0x".
- * 4. otherwise, zero is returned.
- */
-static int __init match_parm (char *s, const char *keywords[], int vals[], int max_vals)
-{
-	static const char *decimal = "0123456789";
-	static const char *hex = "0123456789abcdef";
-	int i, n;
-
-	if (*s++ == '=') {
-		/*
-		 * Try matching against the supplied keywords,
-		 * and return -(index+1) if we match one
-		 */
-		if (keywords != NULL) {
-			for (i = 0; *keywords != NULL; ++i) {
-				if (!strcmp(s, *keywords++))
-					return -(i+1);
-			}
-		}
-		/*
-		 * Look for a series of no more than "max_vals"
-		 * numeric values separated by commas, in base10,
-		 * or base16 when prefixed with "0x".
-		 * Return a count of how many were found.
-		 */
-		for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
-			vals[n] = i;
-			while ((i = stridx(decimal, *++s)) >= 0)
-				vals[n] = (vals[n] * 10) + i;
-			if (*s == 'x' && !vals[n]) {
-				while ((i = stridx(hex, *++s)) >= 0)
-					vals[n] = (vals[n] * 0x10) + i;
-			}
-			if (++n == max_vals)
-				break;
-			if (*s == ',' || *s == ';')
-				++s;
-		}
-		if (!*s)
-			return n;
-	}
-	return 0;	/* zero = nothing matched */
-}
-
-/*
- * ide_setup() gets called VERY EARLY during initialization,
- * to handle kernel "command line" strings beginning with "hdx="
- * or "ide".  Here is the complete set currently supported:
- *
- * "hdx="  is recognized for all "x" from "a" to "h", such as "hdc".
- * "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
- *
- * "hdx=noprobe"	: drive may be present, but do not probe for it
- * "hdx=none"		: drive is NOT present, ignore cmos and do not probe
- * "hdx=nowerr"		: ignore the WRERR_STAT bit on this drive
- * "hdx=cdrom"		: drive is present, and is a cdrom drive
- * "hdx=cyl,head,sect"	: disk drive is present, with specified geometry
- * "hdx=noremap"	: do not remap 0->1 even though EZD was detected
- * "hdx=autotune"	: driver will attempt to tune interface speed
- *				to the fastest PIO mode supported,
- *				if possible for this drive only.
- *				Not fully supported by all chipset types,
- *				and quite likely to cause trouble with
- *				older/odd IDE drives.
- *
- * "hdx=slow"		: insert a huge pause after each access to the data
- *				port. Should be used only as a last resort.
- *
- * "hdx=swapdata"	: when the drive is a disk, byte swap all data
- * "hdx=bswap"		: same as above..........
- * "hdxlun=xx"          : set the drive last logical unit.
- * "hdx=flash"		: allows for more than one ata_flash disk to be
- *				registered. In most cases, only one device
- *				will be present.
- * "hdx=scsi"		: the return of the ide-scsi flag, this is useful for
- *				allowwing ide-floppy, ide-tape, and ide-cdrom|writers
- *				to use ide-scsi emulation on a device specific option.
- * "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in MHz,
- *				where "xx" is between 20 and 66 inclusive,
- *				used when tuning chipset PIO modes.
- *				For PCI bus, 25 is correct for a P75 system,
- *				30 is correct for P90,P120,P180 systems,
- *				and 33 is used for P100,P133,P166 systems.
- *				If in doubt, use idebus=33 for PCI.
- *				As for VLB, it is safest to not specify it.
- *
- * "idex=noprobe"	: do not attempt to access/use this interface
- * "idex=base"		: probe for an interface at the addr specified,
- *				where "base" is usually 0x1f0 or 0x170
- *				and "ctl" is assumed to be "base"+0x206
- * "idex=base,ctl"	: specify both base and ctl
- * "idex=base,ctl,irq"	: specify base, ctl, and irq number
- * "idex=autotune"	: driver will attempt to tune interface speed
- *				to the fastest PIO mode supported,
- *				for all drives on this interface.
- *				Not fully supported by all chipset types,
- *				and quite likely to cause trouble with
- *				older/odd IDE drives.
- * "idex=noautotune"	: driver will NOT attempt to tune interface speed
- *				This is the default for most chipsets,
- *				except the cmd640.
- * "idex=serialize"	: do not overlap operations on idex and ide(x^1)
- * "idex=four"		: four drives on idex and ide(x^1) share same ports
- * "idex=reset"		: reset interface before first use
- * "idex=dma"		: enable DMA by default on both drives if possible
- * "idex=ata66"		: informs the interface that it has an 80c cable
- *				for chipsets that are ATA-66 capable, but
- *				the ablity to bit test for detection is
- *				currently unknown.
- * "ide=reverse"	: Formerly called to pci sub-system, but now local.
- *
- * "splitfifo=betweenChan"
- *			: FIFO Configuration of VIA 82c586(<nothing>,"A"or"B").
- *                                 --see what follows...
- * "splitfifo=betweenChan,thresholdprim,thresholdsec"
- *			: FIFO Configuration of VIA 82c586(<nothing>,"A" or "B").
- *				betweenChan = 1(all FIFO's to primary channel)
- *                                          , 2(all FIFO's to secondary channel)
- *                                          , 3 or 4(evenly shared between them).
- *				note: without FIFO, a channel is (u)dma disabled!
- *				thresholdprim = 4, 3, 2 or 1
- *						(standing for 1, 3/4, 1/2, 1/4).
- *                                    Sets the threshold of FIFO to begin dma
- *                                    transfer on the primary channel.
- *				thresholdsec = cf upper, but for secondary channel.
- *
- * The following are valid ONLY on ide0, (except dc4030)
- * and the defaults for the base,ctl ports must not be altered.
- *
- * "ide0=dtc2278"	: probe/support DTC2278 interface
- * "ide0=ht6560b"	: probe/support HT6560B interface
- * "ide0=cmd640_vlb"	: *REQUIRED* for VLB cards with the CMD640 chip
- *			  (not for PCI -- automatically detected)
- * "ide0=qd6580"	: probe/support qd6580 interface
- * "ide0=ali14xx"	: probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
- * "ide0=umc8672"	: probe/support umc8672 chipsets
- * "idex=dc4030"	: probe/support Promise DC4030VL interface
- * "ide=doubler"	: probe/support IDE doublers on Amiga
- */
-int __init ide_setup (char *s)
-{
-	int i, vals[3];
-	ide_hwif_t *hwif;
-	ide_drive_t *drive;
-	unsigned int hw, unit;
-	const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
-	const char max_hwif  = '0' + (MAX_HWIFS - 1);
-
-	printk("ide_setup: %s", s);
-	init_ide_data ();
-
-#ifdef CONFIG_BLK_DEV_IDEDOUBLER
-	if (!strcmp(s, "ide=doubler")) {
-		extern int ide_doubler;
-
-		printk(" : Enabled support for IDE doublers\n");
-		ide_doubler = 1;
-		return 0;
-	}
-#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
-
-#ifdef CONFIG_BLK_DEV_IDEPCI
-	if (!strcmp(s, "ide=reverse")) {
-		ide_scan_direction = 1;
-		printk(" : Enabled support for IDE inverse scan order.\n");
-		return 0;
-	}
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-
-	/*
-	 * Look for drive options:  "hdx="
-	 */
-	if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
-		const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
-				"serialize", "autotune", "noautotune",
-				"slow", "swapdata", "bswap", "flash",
-				"remap", "noremap", "scsi", NULL};
-		unit = s[2] - 'a';
-		hw   = unit / MAX_DRIVES;
-		unit = unit % MAX_DRIVES;
-		hwif = &ide_hwifs[hw];
-		drive = &hwif->drives[unit];
-		if (strncmp(s + 4, "ide-", 4) == 0) {
-			strncpy(drive->driver_req, s + 4, 9);
-			goto done;
-		}
-		/*
-		 * Look for last lun option:  "hdxlun="
-		 */
-		if (s[3] == 'l' && s[4] == 'u' && s[5] == 'n') {
-			if (match_parm(&s[6], NULL, vals, 1) != 1)
-				goto bad_option;
-			if (vals[0] >= 0 && vals[0] <= 7) {
-				drive->last_lun = vals[0];
-				drive->forced_lun = 1;
-			} else
-				printk(" -- BAD LAST LUN! Expected value from 0 to 7");
-			goto done;
-		}
-		switch (match_parm(&s[3], hd_words, vals, 3)) {
-			case -1: /* "none" */
-				drive->nobios = 1;  /* drop into "noprobe" */
-			case -2: /* "noprobe" */
-				drive->noprobe = 1;
-				goto done;
-			case -3: /* "nowerr" */
-				drive->bad_wstat = BAD_R_STAT;
-				hwif->noprobe = 0;
-				goto done;
-			case -4: /* "cdrom" */
-				drive->present = 1;
-				drive->media = ide_cdrom;
-				hwif->noprobe = 0;
-				goto done;
-			case -5: /* "serialize" */
-				printk(" -- USE \"ide%d=serialize\" INSTEAD", hw);
-				goto do_serialize;
-			case -6: /* "autotune" */
-				drive->autotune = 1;
-				goto done;
-			case -7: /* "noautotune" */
-				drive->autotune = 2;
-				goto done;
-			case -8: /* "slow" */
-				drive->slow = 1;
-				goto done;
-			case -9: /* "swapdata" or "bswap" */
-			case -10:
-				drive->bswap = 1;
-				goto done;
-			case -11: /* "flash" */
-				drive->ata_flash = 1;
-				goto done;
-			case -12: /* "remap" */
-				drive->remap_0_to_1 = 1;
-				goto done;
-			case -13: /* "noremap" */
-				drive->remap_0_to_1 = 2;
-				goto done;
-			case -14: /* "scsi" */
-#if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI)
-				drive->scsi = 1;
-				goto done;
-#else
-				drive->scsi = 0;
-				goto bad_option;
-#endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */
-			case 3: /* cyl,head,sect */
-				drive->media	= ide_disk;
-				drive->cyl	= drive->bios_cyl  = vals[0];
-				drive->head	= drive->bios_head = vals[1];
-				drive->sect	= drive->bios_sect = vals[2];
-				drive->present	= 1;
-				drive->forced_geom = 1;
-				hwif->noprobe = 0;
-				goto done;
-			default:
-				goto bad_option;
-		}
-	}
-
-#if defined(CONFIG_BLK_DEV_VIA82CXXX)
-	/*
-	 *  Look for drive option "splitfifo=..."
-	 */
-
-	if (s[0] == 's' && s[1] == 'p' && s[2] == 'l' &&
-	    s[3] == 'i' && s[4] == 't' && s[5] == 'f' &&
-	    s[6] == 'i' && s[7] == 'f' && s[8] == 'o') {
-		byte tmp = 0x3a;		/* default config byte */
-
-		i = match_parm(&s[9], NULL, vals, 3);
-		switch(i) {
-		case 3:	
-			tmp &= 0xf0;
-			if ((vals[1] > 0) && (vals[1] < 5)) {
-				/* sets threshold for primary Channel: */
-				byte x = 4 - vals[1];
-				tmp |= (x << 2);
-			}
-			else
-				goto bad_option;
-			if ((vals[2] > 0) && (vals[2] < 5)) {
-				/* sets threshold for secondary Channel: */
-				byte x = 4 - vals[2];
-				tmp |= x;
-			}
-			else
-				goto bad_option;
-		case 1:
-			/* set the FIFO config between channels to 0: */
-			tmp &= 0x9f;
-			/* set the needed FIFO config between channels: */
-			if (vals[0] == 1)	/* primary fifo only */
-				tmp |= 0x10;
-			else if (vals[0] == 2)	/* secondary fifo only */
-				tmp |= 0x70;
-			else if (vals[0] == 4)	/* other shared fifo config */
-				tmp |= 0x50;
-			else if (vals[0] == 3)	/* default config */
-				tmp |= 0x30;
-			else
-				goto bad_option;
-			break;
-		default:
-			goto bad_option;
-		}
-		/* set the found option in fifoconfig */
-		fifoconfig = tmp;		
-		goto done;
-	}
-#endif  /* defined(CONFIG_BLK_DEV_VIA82CXXX) */
-
-	if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e')
-		goto bad_option;
-	/*
-	 * Look for bus speed option:  "idebus="
-	 */
-	if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
-		if (match_parm(&s[6], NULL, vals, 1) != 1)
-			goto bad_option;
-		if (vals[0] >= 20 && vals[0] <= 66) {
-			idebus_parameter = vals[0];
-		} else
-			printk(" -- BAD BUS SPEED! Expected value from 20 to 66");
-		goto done;
-	}
-	/*
-	 * Look for interface options:  "idex="
-	 */
-	if (s[3] >= '0' && s[3] <= max_hwif) {
-		/*
-		 * Be VERY CAREFUL changing this: note hardcoded indexes below
-		 * -8,-9,-10 : are reserved for future idex calls to ease the hardcoding.
-		 */
-		const char *ide_words[] = {
-			"noprobe", "serialize", "autotune", "noautotune", "reset", "dma", "ata66",
-			"minus8", "minus9", "minus10",
-			"four", "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL };
-		hw = s[3] - '0';
-		hwif = &ide_hwifs[hw];
-		i = match_parm(&s[4], ide_words, vals, 3);
-
-		/*
-		 * Cryptic check to ensure chipset not already set for hwif:
-		 */
-		if (i > 0 || i <= -11) {			/* is parameter a chipset name? */
-			if (hwif->chipset != ide_unknown)
-				goto bad_option;	/* chipset already specified */
-			if (i <= -11 && i != -18 && hw != 0)
-				goto bad_hwif;		/* chipset drivers are for "ide0=" only */
-			if (i <= -11 && i != -18 && ide_hwifs[hw+1].chipset != ide_unknown)
-				goto bad_option;	/* chipset for 2nd port already specified */
-			printk("\n");
-		}
-
-		switch (i) {
-#ifdef CONFIG_BLK_DEV_PDC4030
-			case -18: /* "dc4030" */
-			{
-				extern void init_pdc4030(void);
-				init_pdc4030();
-				goto done;
-			}
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-#ifdef CONFIG_BLK_DEV_ALI14XX
-			case -17: /* "ali14xx" */
-			{
-				extern void init_ali14xx (void);
-				init_ali14xx();
-				goto done;
-			}
-#endif /* CONFIG_BLK_DEV_ALI14XX */
-#ifdef CONFIG_BLK_DEV_UMC8672
-			case -16: /* "umc8672" */
-			{
-				extern void init_umc8672 (void);
-				init_umc8672();
-				goto done;
-			}
-#endif /* CONFIG_BLK_DEV_UMC8672 */
-#ifdef CONFIG_BLK_DEV_DTC2278
-			case -15: /* "dtc2278" */
-			{
-				extern void init_dtc2278 (void);
-				init_dtc2278();
-				goto done;
-			}
-#endif /* CONFIG_BLK_DEV_DTC2278 */
-#ifdef CONFIG_BLK_DEV_CMD640
-			case -14: /* "cmd640_vlb" */
-			{
-				extern int cmd640_vlb; /* flag for cmd640.c */
-				cmd640_vlb = 1;
-				goto done;
-			}
-#endif /* CONFIG_BLK_DEV_CMD640 */
-#ifdef CONFIG_BLK_DEV_HT6560B
-			case -13: /* "ht6560b" */
-			{
-				extern void init_ht6560b (void);
-				init_ht6560b();
-				goto done;
-			}
-#endif /* CONFIG_BLK_DEV_HT6560B */
-#if CONFIG_BLK_DEV_QD6580
-			case -12: /* "qd6580" */
-			{
-				extern void init_qd6580 (void);
-				init_qd6580();
-				goto done;
-			}
-#endif /* CONFIG_BLK_DEV_QD6580 */
-#ifdef CONFIG_BLK_DEV_4DRIVES
-			case -11: /* "four" drives on one set of ports */
-			{
-				ide_hwif_t *mate = &ide_hwifs[hw^1];
-				mate->drives[0].select.all ^= 0x20;
-				mate->drives[1].select.all ^= 0x20;
-				hwif->chipset = mate->chipset = ide_4drives;
-				mate->irq = hwif->irq;
-				memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports));
-				goto do_serialize;
-			}
-#endif /* CONFIG_BLK_DEV_4DRIVES */
-			case -10: /* minus10 */
-			case -9: /* minus9 */
-			case -8: /* minus8 */
-				goto bad_option;
-			case -7: /* ata66 */
-#ifdef CONFIG_BLK_DEV_IDEPCI
-				hwif->udma_four = 1;
-				goto done;
-#else /* !CONFIG_BLK_DEV_IDEPCI */
-				hwif->udma_four = 0;
-				goto bad_hwif;
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-			case -6: /* dma */
-				hwif->autodma = 1;
-				goto done;
-			case -5: /* "reset" */
-				hwif->reset = 1;
-				goto done;
-			case -4: /* "noautotune" */
-				hwif->drives[0].autotune = 2;
-				hwif->drives[1].autotune = 2;
-				goto done;
-			case -3: /* "autotune" */
-				hwif->drives[0].autotune = 1;
-				hwif->drives[1].autotune = 1;
-				goto done;
-			case -2: /* "serialize" */
-			do_serialize:
-				hwif->mate = &ide_hwifs[hw^1];
-				hwif->mate->mate = hwif;
-				hwif->serialized = hwif->mate->serialized = 1;
-				goto done;
-
-			case -1: /* "noprobe" */
-				hwif->noprobe = 1;
-				goto done;
-
-			case 1:	/* base */
-				vals[1] = vals[0] + 0x206; /* default ctl */
-			case 2: /* base,ctl */
-				vals[2] = 0;	/* default irq = probe for it */
-			case 3: /* base,ctl,irq */
-				hwif->hw.irq = vals[2];
-				ide_init_hwif_ports(&hwif->hw, (ide_ioreg_t) vals[0], (ide_ioreg_t) vals[1], &hwif->irq);
-				memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
-				hwif->irq      = vals[2];
-				hwif->noprobe  = 0;
-				hwif->chipset  = ide_generic;
-				goto done;
-
-			case 0: goto bad_option;
-			default:
-				printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
-				return 0;
-		}
-	}
-bad_option:
-	printk(" -- BAD OPTION\n");
-	return 0;
-bad_hwif:
-	printk("-- NOT SUPPORTED ON ide%d", hw);
-done:
-	printk("\n");
-	return 0;
-}
-
-/*
- * probe_for_hwifs() finds/initializes "known" IDE interfaces
- */
-static void __init probe_for_hwifs (void)
-{
-#ifdef CONFIG_PCI
-	if (pci_present())
-	{
-#ifdef CONFIG_BLK_DEV_IDEPCI
-		ide_scan_pcibus(ide_scan_direction);
-#else
-#ifdef CONFIG_BLK_DEV_RZ1000
-		{
-			extern void ide_probe_for_rz100x(void);
-			ide_probe_for_rz100x();
-		}
-#endif /* CONFIG_BLK_DEV_RZ1000 */
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-	}
-#endif /* CONFIG_PCI */
-
-#ifdef CONFIG_BLK_DEV_CMD640
-	{
-		extern void ide_probe_for_cmd640x(void);
-		ide_probe_for_cmd640x();
-	}
-#endif /* CONFIG_BLK_DEV_CMD640 */
-#ifdef CONFIG_BLK_DEV_PDC4030
-	{
-		extern int ide_probe_for_pdc4030(void);
-		(void) ide_probe_for_pdc4030();
-	}
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-#ifdef CONFIG_BLK_DEV_IDE_PMAC
-	{
-		extern void pmac_ide_probe(void);
-		pmac_ide_probe();
-	}
-#endif /* CONFIG_BLK_DEV_IDE_PMAC */
-#ifdef CONFIG_BLK_DEV_IDE_ICSIDE
-	{
-		extern void icside_init(void);
-		icside_init();
-	}
-#endif /* CONFIG_BLK_DEV_IDE_ICSIDE */
-#ifdef CONFIG_BLK_DEV_IDE_RAPIDE
-	{
-		extern void rapide_init(void);
-		rapide_init();
-	}
-#endif /* CONFIG_BLK_DEV_IDE_RAPIDE */
-#ifdef CONFIG_BLK_DEV_GAYLE
-	{
-		extern void gayle_init(void);
-		gayle_init();
-	}
-#endif /* CONFIG_BLK_DEV_GAYLE */
-#ifdef CONFIG_BLK_DEV_FALCON_IDE
-	{
-		extern void falconide_init(void);
-		falconide_init();
-	}
-#endif /* CONFIG_BLK_DEV_FALCON_IDE */
-#ifdef CONFIG_BLK_DEV_MAC_IDE
-	{
-		extern void macide_init(void);
-		macide_init();
-	}
-#endif /* CONFIG_BLK_DEV_MAC_IDE */
-#ifdef CONFIG_BLK_DEV_BUDDHA
-	{
-		extern void buddha_init(void);
-		buddha_init();
-	}
-#endif /* CONFIG_BLK_DEV_BUDDHA */
-#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP)
-	{
-		extern void pnpide_init(int enable);
-		pnpide_init(1);
-	}
-#endif /* CONFIG_BLK_DEV_ISAPNP */
-}
-
-void __init ide_init_builtin_drivers (void)
-{
-	/*
-	 * Probe for special PCI and other "known" interface chipsets
-	 */
-	probe_for_hwifs ();
-
-#ifdef CONFIG_BLK_DEV_IDE
-#if defined(__mc68000__) || defined(CONFIG_APUS)
-	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
-		ide_get_lock(&ide_lock, NULL, NULL);	/* for atari only */
-		disable_irq(ide_hwifs[0].irq);	/* disable_irq_nosync ?? */
-	}
-#endif /* __mc68000__ || CONFIG_APUS */
-
-	(void) ideprobe_init();
-
-#if defined(__mc68000__) || defined(CONFIG_APUS)
-	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
-		enable_irq(ide_hwifs[0].irq);
-		ide_release_lock(&ide_lock);	/* for atari only */
-	}
-#endif /* __mc68000__ || CONFIG_APUS */
-#endif /* CONFIG_BLK_DEV_IDE */
-
-#ifdef CONFIG_PROC_FS
-	proc_ide_create();
-#endif
-
-	/*
-	 * Attempt to match drivers for the available drives
-	 */
-#ifdef CONFIG_BLK_DEV_IDEDISK
-	(void) idedisk_init();
-#endif /* CONFIG_BLK_DEV_IDEDISK */
-#ifdef CONFIG_BLK_DEV_IDECD
-	(void) ide_cdrom_init();
-#endif /* CONFIG_BLK_DEV_IDECD */
-#ifdef CONFIG_BLK_DEV_IDETAPE
-	(void) idetape_init();
-#endif /* CONFIG_BLK_DEV_IDETAPE */
-#ifdef CONFIG_BLK_DEV_IDEFLOPPY
-	(void) idefloppy_init();
-#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
-#ifdef CONFIG_BLK_DEV_IDESCSI
- #ifdef CONFIG_SCSI
-	(void) idescsi_init();
- #else
-    #warning ide scsi-emulation selected but no SCSI-subsystem in kernel
- #endif
-#endif /* CONFIG_BLK_DEV_IDESCSI */
-}
-
-static int default_cleanup (ide_drive_t *drive)
-{
-	return ide_unregister_subdriver(drive);
-}
-
-static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
-{
-	ide_end_request(0, HWGROUP(drive));
-	return ide_stopped;
-}
- 
-static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
-{
-	ide_end_request(uptodate, hwgroup);
-}
-  
-static int default_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
-			  unsigned int cmd, unsigned long arg)
-{
-	return -EIO;
-}
-
-static int default_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
-{
-	drive->usage--;
-	return -EIO;
-}
-
-static void default_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
-{
-}
-
-static int default_check_media_change (ide_drive_t *drive)
-{
-	return 1;
-}
-
-static void default_pre_reset (ide_drive_t *drive)
-{
-}
-
-static unsigned long default_capacity (ide_drive_t *drive)
-{
-	return 0x7fffffff;	/* cdrom or tape */
-}
-
-static ide_startstop_t default_special (ide_drive_t *drive)
-{
-	special_t *s = &drive->special;
-
-	s->all = 0;
-	drive->mult_req = 0;
-	return ide_stopped;
-}
-
-static void setup_driver_defaults (ide_drive_t *drive)
-{
-	ide_driver_t *d = drive->driver;
-
-	if (d->cleanup == NULL)		d->cleanup = default_cleanup;
-	if (d->do_request == NULL)	d->do_request = default_do_request;
-	if (d->end_request == NULL)	d->end_request = default_end_request;
-	if (d->ioctl == NULL)		d->ioctl = default_ioctl;
-	if (d->open == NULL)		d->open = default_open;
-	if (d->release == NULL)		d->release = default_release;
-	if (d->media_change == NULL)	d->media_change = default_check_media_change;
-	if (d->pre_reset == NULL)	d->pre_reset = default_pre_reset;
-	if (d->capacity == NULL)	d->capacity = default_capacity;
-	if (d->special == NULL)		d->special = default_special;
-}
-
-ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n)
-{
-	unsigned int unit, index, i;
-
-	for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
-		ide_hwif_t *hwif = &ide_hwifs[index];
-		if (!hwif->present)
-			continue;
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			ide_drive_t *drive = &hwif->drives[unit];
-			char *req = drive->driver_req;
-			if (*req && !strstr(name, req))
-				continue;
-			if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
-				return drive;
-		}
-	}
-	return NULL;
-}
-
-int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version)
-{
-	unsigned long flags;
-	
-	save_flags(flags);		/* all CPUs */
-	cli();				/* all CPUs */
-	if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL || drive->busy || drive->usage) {
-		restore_flags(flags);	/* all CPUs */
-		return 1;
-	}
-	drive->driver = driver;
-	setup_driver_defaults(drive);
-	restore_flags(flags);		/* all CPUs */
-	if (drive->autotune != 2) {
-		if (driver->supports_dma && HWIF(drive)->dmaproc != NULL) {
-			/*
-			 * Force DMAing for the beginning of the check.
-			 * Some chipsets appear to do interesting things,
-			 * if not checked and cleared.
-			 *   PARANOIA!!!
-			 */
-			(void) (HWIF(drive)->dmaproc(ide_dma_off_quietly, drive));
-			(void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
-		}
-		drive->dsc_overlap = (drive->next != drive && driver->supports_dsc_overlap);
-		drive->nice1 = 1;
-	}
-	drive->revalidate = 1;
-#ifdef CONFIG_PROC_FS
-	ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
-	ide_add_proc_entries(drive->proc, driver->proc, drive);
-#endif
-	return 0;
-}
-
-int ide_unregister_subdriver (ide_drive_t *drive)
-{
-	unsigned long flags;
-	
-	save_flags(flags);		/* all CPUs */
-	cli();				/* all CPUs */
-	if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) {
-		restore_flags(flags);	/* all CPUs */
-		return 1;
-	}
-#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE)
-	pnpide_init(0);
-#endif /* CONFIG_BLK_DEV_ISAPNP */
-#ifdef CONFIG_PROC_FS
-	ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc);
-	ide_remove_proc_entries(drive->proc, generic_subdriver_entries);
-#endif
-	auto_remove_settings(drive);
-	drive->driver = NULL;
-	restore_flags(flags);		/* all CPUs */
-	return 0;
-}
-
-int ide_register_module (ide_module_t *module)
-{
-	ide_module_t *p = ide_modules;
-
-	while (p) {
-		if (p == module)
-			return 1;
-		p = p->next;
-	}
-	module->next = ide_modules;
-	ide_modules = module;
-	revalidate_drives();
-	return 0;
-}
-
-void ide_unregister_module (ide_module_t *module)
-{
-	ide_module_t **p;
-
-	for (p = &ide_modules; (*p) && (*p) != module; p = &((*p)->next));
-	if (*p)
-		*p = (*p)->next;
-}
-
-struct block_device_operations ide_fops[] = {{
-	open:			ide_open,
-	release:		ide_release,
-	ioctl:			ide_ioctl,
-	check_media_change:	ide_check_media_change,
-	revalidate:		ide_revalidate_disk
-}};
-
-EXPORT_SYMBOL(ide_hwifs);
-EXPORT_SYMBOL(ide_register_module);
-EXPORT_SYMBOL(ide_unregister_module);
-EXPORT_SYMBOL(ide_spin_wait_hwgroup);
-
-/*
- * Probe module
- */
-devfs_handle_t ide_devfs_handle = NULL;
-
-EXPORT_SYMBOL(ide_probe);
-EXPORT_SYMBOL(drive_is_flashcard);
-EXPORT_SYMBOL(ide_timer_expiry);
-EXPORT_SYMBOL(ide_intr);
-EXPORT_SYMBOL(ide_fops);
-EXPORT_SYMBOL(ide_get_queue);
-EXPORT_SYMBOL(do_ide0_request);
-EXPORT_SYMBOL(ide_add_generic_settings);
-EXPORT_SYMBOL(ide_devfs_handle);
-#if MAX_HWIFS > 1
-EXPORT_SYMBOL(do_ide1_request);
-#endif /* MAX_HWIFS > 1 */
-#if MAX_HWIFS > 2
-EXPORT_SYMBOL(do_ide2_request);
-#endif /* MAX_HWIFS > 2 */
-#if MAX_HWIFS > 3
-EXPORT_SYMBOL(do_ide3_request);
-#endif /* MAX_HWIFS > 3 */
-#if MAX_HWIFS > 4
-EXPORT_SYMBOL(do_ide4_request);
-#endif /* MAX_HWIFS > 4 */
-#if MAX_HWIFS > 5
-EXPORT_SYMBOL(do_ide5_request);
-#endif /* MAX_HWIFS > 5 */
-#if MAX_HWIFS > 6
-EXPORT_SYMBOL(do_ide6_request);
-#endif /* MAX_HWIFS > 6 */
-#if MAX_HWIFS > 7
-EXPORT_SYMBOL(do_ide7_request);
-#endif /* MAX_HWIFS > 7 */
-#if MAX_HWIFS > 8
-EXPORT_SYMBOL(do_ide8_request);
-#endif /* MAX_HWIFS > 8 */
-#if MAX_HWIFS > 9
-EXPORT_SYMBOL(do_ide9_request);
-#endif /* MAX_HWIFS > 9 */
-
-/*
- * Driver module
- */
-EXPORT_SYMBOL(ide_scan_devices);
-EXPORT_SYMBOL(ide_register_subdriver);
-EXPORT_SYMBOL(ide_unregister_subdriver);
-EXPORT_SYMBOL(ide_replace_subdriver);
-EXPORT_SYMBOL(ide_input_data);
-EXPORT_SYMBOL(ide_output_data);
-EXPORT_SYMBOL(atapi_input_bytes);
-EXPORT_SYMBOL(atapi_output_bytes);
-EXPORT_SYMBOL(ide_set_handler);
-EXPORT_SYMBOL(ide_dump_status);
-EXPORT_SYMBOL(ide_error);
-EXPORT_SYMBOL(ide_fixstring);
-EXPORT_SYMBOL(ide_wait_stat);
-EXPORT_SYMBOL(ide_do_reset);
-EXPORT_SYMBOL(ide_init_drive_cmd);
-EXPORT_SYMBOL(ide_do_drive_cmd);
-EXPORT_SYMBOL(ide_end_drive_cmd);
-EXPORT_SYMBOL(ide_end_request);
-EXPORT_SYMBOL(ide_revalidate_disk);
-EXPORT_SYMBOL(ide_cmd);
-EXPORT_SYMBOL(ide_wait_cmd);
-EXPORT_SYMBOL(ide_delay_50ms);
-EXPORT_SYMBOL(ide_stall_queue);
-#ifdef CONFIG_PROC_FS
-EXPORT_SYMBOL(ide_add_proc_entries);
-EXPORT_SYMBOL(ide_remove_proc_entries);
-EXPORT_SYMBOL(proc_ide_read_geometry);
-EXPORT_SYMBOL(create_proc_ide_interfaces);
-#endif
-EXPORT_SYMBOL(ide_add_setting);
-EXPORT_SYMBOL(ide_remove_setting);
-
-EXPORT_SYMBOL(ide_register_hw);
-EXPORT_SYMBOL(ide_register);
-EXPORT_SYMBOL(ide_unregister);
-EXPORT_SYMBOL(ide_setup_ports);
-EXPORT_SYMBOL(hwif_unregister);
-EXPORT_SYMBOL(get_info_ptr);
-EXPORT_SYMBOL(current_capacity);
-
-/*
- * This is gets invoked once during initialization, to set *everything* up
- */
-int __init ide_init (void)
-{
-	static char banner_printed = 0;
-	int i;
-
-	if (!banner_printed) {
-		printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n");
-		ide_devfs_handle = devfs_mk_dir (NULL, "ide", 3, NULL);
-		(void) ide_system_bus_speed();
-		banner_printed = 1;
-	}
-
-	init_ide_data ();
-
-	initializing = 1;
-	ide_init_builtin_drivers();
-	initializing = 0;
-
-	for (i = 0; i < MAX_HWIFS; ++i) {
-		ide_hwif_t  *hwif = &ide_hwifs[i];
-		if (hwif->present)
-			ide_geninit(hwif);
-	}
-
-	return 0;
-}
-
-static void __init parse_options (char *line)
-{
-	char *next = line;
-
-	if (line == NULL || !*line)
-		return;
-	while ((line = next) != NULL) {
- 		if ((next = strchr(line,' ')) != NULL)
-			*next++ = 0;
-		if (!strncmp(line,"ide",3) ||
-		    !strncmp(line,"idebus",6) ||
-#ifdef CONFIG_BLK_DEV_VIA82CXXX
-		    !strncmp(line,"splitfifo",9) ||
-#endif /* CONFIG_BLK_DEV_VIA82CXXX */
-		    !strncmp(line,"hdxlun",6) ||
-		    (!strncmp(line,"hd",2) && line[2] != '='))
-			(void) ide_setup(line);
-	}
-}
-
-#ifdef MODULE
-char *options = NULL;
-MODULE_PARM(options,"s");
-
-int init_module (void)
-{
-	parse_options(options);
-	return ide_init();
-}
-
-void cleanup_module (void)
-{
-	int index;
-
-	for (index = 0; index < MAX_HWIFS; ++index)
-		ide_unregister(index);
-
-#ifdef CONFIG_PROC_FS
-	proc_ide_destroy();
-#endif
-	devfs_unregister (ide_devfs_handle);
-}
-
-#else /* !MODULE */
-
-static int parse_ide_setup (char *line)
-{
-	parse_options(line);
-	return 0;
-}
-__setup("", parse_ide_setup);
-
-#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ide_modes.h linux/drivers/block/ide_modes.h
--- v2.3.51/linux/drivers/block/ide_modes.h	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/ide_modes.h	Wed Dec 31 16:00:00 1969
@@ -1,233 +0,0 @@
-/*
- *  linux/drivers/block/ide_modes.h
- *
- *  Copyright (C) 1996  Linus Torvalds, Igor Abramov, and Mark Lord
- */
-
-#ifndef _IDE_MODES_H
-#define _IDE_MODES_H
-
-#include <linux/config.h>
-
-/*
- * Shared data/functions for determining best PIO mode for an IDE drive.
- * Most of this stuff originally lived in cmd640.c, and changes to the
- * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
- * breaking the fragile cmd640.c support.
- */
-
-#ifdef CONFIG_BLK_DEV_IDE_MODES
-
-/*
- * Standard (generic) timings for PIO modes, from ATA2 specification.
- * These timings are for access to the IDE data port register *only*.
- * Some drives may specify a mode, while also specifying a different
- * value for cycle_time (from drive identification data).
- */
-typedef struct ide_pio_timings_s {
-	int	setup_time;	/* Address setup (ns) minimum */
-	int	active_time;	/* Active pulse (ns) minimum */
-	int	cycle_time;	/* Cycle time (ns) minimum = (setup + active + recovery) */
-} ide_pio_timings_t;
-
-typedef struct ide_pio_data_s {
-	byte pio_mode;
-	byte use_iordy;
-	byte overridden;
-	byte blacklisted;
-	unsigned int cycle_time;
-} ide_pio_data_t;
-	
-#ifndef _IDE_C
-
-int ide_scan_pio_blacklist (char *model);
-byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d);
-extern const ide_pio_timings_t ide_pio_timings[6];
-
-#else /* _IDE_C */
-
-const ide_pio_timings_t ide_pio_timings[6] = {
-	{ 70,	165,	600 },	/* PIO Mode 0 */
-	{ 50,	125,	383 },	/* PIO Mode 1 */
-	{ 30,	100,	240 },	/* PIO Mode 2 */
-	{ 30,	80,	180 },	/* PIO Mode 3 with IORDY */
-	{ 25,	70,	120 },	/* PIO Mode 4 with IORDY */
-	{ 20,	50,	100 }	/* PIO Mode 5 with IORDY (nonstandard) */
-};
-
-/*
- * Black list. Some drives incorrectly report their maximal PIO mode,
- * at least in respect to CMD640. Here we keep info on some known drives.
- */
-static struct ide_pio_info {
-	const char	*name;
-	int		pio;
-} ide_pio_blacklist [] = {
-/*	{ "Conner Peripherals 1275MB - CFS1275A", 4 }, */
-	{ "Conner Peripherals 540MB - CFS540A", 3 },
-
-	{ "WDC AC2700",  3 },
-	{ "WDC AC2540",  3 },
-	{ "WDC AC2420",  3 },
-	{ "WDC AC2340",  3 },
-	{ "WDC AC2250",  0 },
-	{ "WDC AC2200",  0 },
-	{ "WDC AC21200", 4 },
-	{ "WDC AC2120",  0 },
-	{ "WDC AC2850",  3 },
-	{ "WDC AC1270",  3 },
-	{ "WDC AC1170",  1 },
-	{ "WDC AC1210",  1 },
-	{ "WDC AC280",   0 },
-/*	{ "WDC AC21000", 4 }, */
-	{ "WDC AC31000", 3 },
-	{ "WDC AC31200", 3 },
-/*	{ "WDC AC31600", 4 }, */
-
-	{ "Maxtor 7131 AT", 1 },
-	{ "Maxtor 7171 AT", 1 },
-	{ "Maxtor 7213 AT", 1 },
-	{ "Maxtor 7245 AT", 1 },
-	{ "Maxtor 7345 AT", 1 },
-	{ "Maxtor 7546 AT", 3 },
-	{ "Maxtor 7540 AV", 3 },
-
-	{ "SAMSUNG SHD-3121A", 1 },
-	{ "SAMSUNG SHD-3122A", 1 },
-	{ "SAMSUNG SHD-3172A", 1 },
-
-/*	{ "ST51080A", 4 },
- *	{ "ST51270A", 4 },
- *	{ "ST31220A", 4 },
- *	{ "ST31640A", 4 },
- *	{ "ST32140A", 4 },
- *	{ "ST3780A",  4 },
- */
-	{ "ST5660A",  3 },
-	{ "ST3660A",  3 },
-	{ "ST3630A",  3 },
-	{ "ST3655A",  3 },
-	{ "ST3391A",  3 },
-	{ "ST3390A",  1 },
-	{ "ST3600A",  1 },
-	{ "ST3290A",  0 },
-	{ "ST3144A",  0 },
-	{ "ST3491A",  1 },	/* reports 3, should be 1 or 2 (depending on */	
-				/* drive) according to Seagates FIND-ATA program */
-
-	{ "QUANTUM ELS127A", 0 },
-	{ "QUANTUM ELS170A", 0 },
-	{ "QUANTUM LPS240A", 0 },
-	{ "QUANTUM LPS210A", 3 },
-	{ "QUANTUM LPS270A", 3 },
-	{ "QUANTUM LPS365A", 3 },
-	{ "QUANTUM LPS540A", 3 },
-	{ "QUANTUM LIGHTNING 540A", 3 },
-	{ "QUANTUM LIGHTNING 730A", 3 },
-	{ "QUANTUM FIREBALL", 3 }, /* For models 540/640/1080/1280 */
-				   /* 1080A works fine in mode4 with triton */
-	{ NULL,	0 }
-};
-
-/*
- * This routine searches the ide_pio_blacklist for an entry
- * matching the start/whole of the supplied model name.
- *
- * Returns -1 if no match found.
- * Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
- */
-int ide_scan_pio_blacklist (char *model)
-{
-	struct ide_pio_info *p;
-
-	for (p = ide_pio_blacklist; p->name != NULL; p++) {
-		if (strncmp(p->name, model, strlen(p->name)) == 0)
-			return p->pio;
-	}
-	return -1;
-}
-
-/*
- * This routine returns the recommended PIO settings for a given drive,
- * based on the drive->id information and the ide_pio_blacklist[].
- * This is used by most chipset support modules when "auto-tuning".
- */
-
-/*
- * Drive PIO mode auto selection
- */
-byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d)
-{
-	int pio_mode;
-	int cycle_time = 0;
-	int use_iordy = 0;
-	struct hd_driveid* id = drive->id;
-	int overridden  = 0;
-	int blacklisted = 0;
-
-	if (mode_wanted != 255) {
-		pio_mode = mode_wanted;
-	} else if (!drive->id) {
-		pio_mode = 0;
-	} else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
-		overridden = 1;
-		blacklisted = 1;
-		use_iordy = (pio_mode > 2);
-	} else {
-		pio_mode = id->tPIO;
-		if (pio_mode > 2) {	/* 2 is maximum allowed tPIO value */
-			pio_mode = 2;
-			overridden = 1;
-		}
-		if (id->field_valid & 2) {	  /* drive implements ATA2? */
-			if (id->capability & 8) { /* drive supports use_iordy? */
-				use_iordy = 1;
-				cycle_time = id->eide_pio_iordy;
-				if (id->eide_pio_modes & 7) {
-					overridden = 0;
-					if (id->eide_pio_modes & 4)
-						pio_mode = 5;
-					else if (id->eide_pio_modes & 2)
-						pio_mode = 4;
-					else
-						pio_mode = 3;
-				}
-			} else {
-				cycle_time = id->eide_pio;
-			}
-		}
-
-#if 0
-		if (drive->id->major_rev_num & 0x0004) printf("ATA-2 ");
-#endif
-
-		/*
-		 * Conservative "downgrade" for all pre-ATA2 drives
-		 */
-		if (pio_mode && pio_mode < 4) {
-			pio_mode--;
-			overridden = 1;
-#if 0
-			use_iordy = (pio_mode > 2);
-#endif
-			if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time)
-				cycle_time = 0; /* use standard timing */
-		}
-	}
-	if (pio_mode > max_mode) {
-		pio_mode = max_mode;
-		cycle_time = 0;
-	}
-	if (d) {
-		d->pio_mode = pio_mode;
-		d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time;
-		d->use_iordy = use_iordy;
-		d->overridden = overridden;
-		d->blacklisted = blacklisted;
-	}
-	return pio_mode;
-}
-
-#endif /* _IDE_C */
-#endif /* CONFIG_BLK_DEV_IDE_MODES */
-#endif /* _IDE_MODES_H */
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.3.51/linux/drivers/block/ll_rw_blk.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/ll_rw_blk.c	Sun Mar 12 19:49:24 2000
@@ -28,8 +28,6 @@
 
 #include <linux/module.h>
 
-#define DEBUG_ELEVATOR
-
 /*
  * MAC Floppy IWM hooks
  */
@@ -150,18 +148,6 @@
 	return ret;
 }
 
-static inline int get_request_latency(elevator_t * elevator, int rw)
-{
-	int latency;
-
-	if (rw != READ)
-		latency = elevator->write_latency;
-	else
-		latency = elevator->read_latency;
-
-	return latency;
-}
-
 void blk_cleanup_queue(request_queue_t * q)
 {
 	memset(q, 0, sizeof(*q));
@@ -186,7 +172,7 @@
 {
 	if (req->nr_segments < max_segments) {
 		req->nr_segments++;
-		q->nr_segments++;
+		q->elevator.nr_segments++;
 		return 1;
 	}
 	return 0;
@@ -212,18 +198,18 @@
 				struct request *next, int max_segments)
 {
 	int total_segments = req->nr_segments + next->nr_segments;
-       int same_segment;
+	int same_segment;
 
-       same_segment = 0;
+	same_segment = 0;
 	if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) {
 		total_segments--;
-               same_segment = 1;
+		same_segment = 1;
 	}
     
 	if (total_segments > max_segments)
 		return 0;
 
-       q->nr_segments -= same_segment;
+	q->elevator.nr_segments -= same_segment;
 	req->nr_segments = total_segments;
 	return 1;
 }
@@ -254,7 +240,7 @@
 void blk_init_queue(request_queue_t * q, request_fn_proc * rfn)
 {
 	INIT_LIST_HEAD(&q->queue_head);
-	q->elevator = ELEVATOR_DEFAULTS;
+	elevator_init(&q->elevator);
 	q->request_fn     	= rfn;
 	q->back_merge_fn       	= ll_back_merge_fn;
 	q->front_merge_fn      	= ll_front_merge_fn;
@@ -425,113 +411,6 @@
 		printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
 }
 
-/* elevator */
-
-#define elevator_sequence_after(a,b) ((int)((b)-(a)) < 0)
-#define elevator_sequence_before(a,b) elevator_sequence_after(b,a)
-#define elevator_sequence_after_eq(a,b) ((int)((b)-(a)) <= 0)
-#define elevator_sequence_before_eq(a,b) elevator_sequence_after_eq(b,a)
-
-static inline struct list_head * seek_to_not_starving_chunk(request_queue_t * q,
-							    int * lat, int * starving)
-{
-	int sequence = q->elevator.sequence;
-	struct list_head * entry = q->queue_head.prev;
-	int pos = 0;
-
-	do {
-		struct request * req = blkdev_entry_to_request(entry);
-		if (elevator_sequence_before(req->elevator_sequence, sequence)) {
-			*lat -= q->nr_segments - pos;
-			*starving = 1;
-			return entry;
-		}
-		pos += req->nr_segments;
-	} while ((entry = entry->prev) != &q->queue_head);
-
-	*starving = 0;
-
-	return entry->next;
-}
-
-static inline void elevator_merge_requests(elevator_t * e, struct request * req, struct request * next)
-{
-	if (elevator_sequence_before(next->elevator_sequence, req->elevator_sequence))
-		req->elevator_sequence = next->elevator_sequence;
-	if (req->cmd == READ)
-		e->read_pendings--;
-
-}
-
-static inline int elevator_sequence(elevator_t * e, int latency)
-{
-	return latency + e->sequence;
-}
-
-#define elevator_merge_before(q, req, lat)	__elevator_merge((q), (req), (lat), 0)
-#define elevator_merge_after(q, req, lat)	__elevator_merge((q), (req), (lat), 1)
-static inline void __elevator_merge(request_queue_t * q, struct request * req, int latency, int after)
-{
-	int sequence = elevator_sequence(&q->elevator, latency);
-	if (after)
-		sequence -= req->nr_segments;
-	if (elevator_sequence_before(sequence, req->elevator_sequence))
-		req->elevator_sequence = sequence;
-}
-
-static inline void elevator_queue(request_queue_t * q,
-				  struct request * req,
-				  struct list_head * entry,
-				  int latency, int starving)
-{
-	struct request * tmp, * __tmp;
-	int __latency = latency;
-
-	__tmp = tmp = blkdev_entry_to_request(entry);
-
-	for (;; tmp = blkdev_next_request(tmp))
-	{
-		if ((latency -= tmp->nr_segments) <= 0)
-		{
-			tmp = __tmp;
-			latency = __latency;
-
-			if (starving)
-				break;
-
-			if (q->head_active && !q->plugged)
-			{
-				latency -= tmp->nr_segments;
-				break;
-			}
-
-			list_add(&req->queue, &q->queue_head);
-			goto after_link;
-		}
-
-		if (tmp->queue.next == &q->queue_head)
-			break;
-
-		{
-			const int after_current = IN_ORDER(tmp,req);
-			const int before_next = IN_ORDER(req,blkdev_next_request(tmp));
-
-			if (!IN_ORDER(tmp,blkdev_next_request(tmp))) {
-				if (after_current || before_next)
-					break;
-			} else {
-				if (after_current && before_next)
-					break;
-			}
-		}
-	}
-
-	list_add(&req->queue, &tmp->queue);
-
- after_link:
-	req->elevator_sequence = elevator_sequence(&q->elevator, latency);
-}
-
 /*
  * add-request adds a request to the linked list.
  * It disables interrupts (aquires the request spinlock) so that it can muck
@@ -542,20 +421,19 @@
  * which is important for drive_stat_acct() above.
  */
 
-static inline void __add_request(request_queue_t * q, struct request * req,
-				 int empty, struct list_head * entry,
-				 int latency, int starving)
+static inline void add_request(request_queue_t * q, struct request * req,
+			       struct list_head * head, int latency)
 {
 	int major;
 
 	drive_stat_acct(req, req->nr_sectors, 1);
 
-	if (empty) {
+	if (list_empty(head)) {
 		req->elevator_sequence = elevator_sequence(&q->elevator, latency);
 		list_add(&req->queue, &q->queue_head);
 		return;
 	}
-	elevator_queue(q, req, entry, latency, starving);
+	q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, latency);
 
 	/*
 	 * FIXME(eric) I don't understand why there is a need for this
@@ -579,19 +457,17 @@
 /*
  * Has to be called with the request spinlock aquired
  */
-static inline void attempt_merge (request_queue_t * q,
-				  struct request *req,
-				  int max_sectors,
-				  int max_segments)
+static void attempt_merge(request_queue_t * q,
+			  struct request *req,
+			  int max_sectors,
+			  int max_segments)
 {
 	struct request *next;
   
-	if (req->queue.next == &q->queue_head)
-		return;
 	next = blkdev_next_request(req);
 	if (req->sector + req->nr_sectors != next->sector)
 		return;
-	if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors)
+	if (req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors || next->sem)
 		return;
 	/*
 	 * If we are not allowed to merge these requests, then
@@ -611,54 +487,28 @@
 	wake_up (&wait_for_request);
 }
 
-static inline void elevator_debug(request_queue_t * q, kdev_t dev)
+static inline void attempt_back_merge(request_queue_t * q,
+				      struct request *req,
+				      int max_sectors,
+				      int max_segments)
 {
-#ifdef DEBUG_ELEVATOR
-	int read_pendings = 0, nr_segments = 0;
-	elevator_t * elevator = &q->elevator;
-	struct list_head * entry = &q->queue_head;
-	static int counter;
-
-	if (counter++ % 100)
+	if (&req->queue == q->queue_head.prev)
 		return;
-
-	while ((entry = entry->next) != &q->queue_head)
-	{
-		struct request * req;
-
-		req = blkdev_entry_to_request(entry);
-		if (!req->q)
-			continue;
-		if (req->cmd == READ)
-			read_pendings++;
-		nr_segments += req->nr_segments;
-	}
-
-	if (read_pendings != elevator->read_pendings)
-	{
-		printk(KERN_WARNING
-		       "%s: elevator read_pendings %d should be %d\n",
-		       kdevname(dev), elevator->read_pendings,
-		       read_pendings);
-		elevator->read_pendings = read_pendings;
-	}
-	if (nr_segments != q->nr_segments)
-	{
-		printk(KERN_WARNING
-		       "%s: elevator nr_segments %d should be %d\n",
-		       kdevname(dev), q->nr_segments,
-		       nr_segments);
-		q->nr_segments = nr_segments;
-	}
-#endif
+	attempt_merge(q, req, max_sectors, max_segments);
 }
 
-static inline void elevator_account_request(request_queue_t * q, struct request * req)
+static inline void attempt_front_merge(request_queue_t * q,
+				       struct list_head * head,
+				       struct request *req,
+				       int max_sectors,
+				       int max_segments)
 {
-	q->elevator.sequence++;
-	if (req->cmd == READ)
-		q->elevator.read_pendings++;
-	q->nr_segments++;
+	struct list_head * prev;
+
+	prev = req->queue.prev;
+	if (head == prev)
+		return;
+	attempt_merge(q, blkdev_entry_to_request(prev), max_sectors, max_segments);
 }
 
 static inline void __make_request(request_queue_t * q, int rw,
@@ -667,11 +517,13 @@
 	int major = MAJOR(bh->b_rdev);
 	unsigned int sector, count;
 	int max_segments = MAX_SEGMENTS;
-	struct request * req, * prev;
+	struct request * req;
 	int rw_ahead, max_req, max_sectors;
 	unsigned long flags;
-	int orig_latency, latency, __latency, starving, __starving, empty;
-	struct list_head * entry, * __entry = NULL;
+
+	int orig_latency, latency, starving, sequence;
+	struct list_head * entry, * head = &q->queue_head;
+	elevator_t * elevator;
 
 	count = bh->b_size >> 9;
 	sector = bh->b_rsector;
@@ -758,7 +610,8 @@
 	 */
 	max_sectors = get_max_sectors(bh->b_rdev);
 
-	__latency = orig_latency = get_request_latency(&q->elevator, rw);
+	elevator = &q->elevator;
+	orig_latency = elevator_request_latency(elevator, rw);
 
 	/*
 	 * Now we acquire the request spinlock, we have to be mega careful
@@ -767,46 +620,42 @@
 	spin_lock_irqsave(&io_request_lock,flags);
 	elevator_debug(q, bh->b_rdev);
 
-	empty = 0;
-	if (list_empty(&q->queue_head)) {
-		empty = 1;
+	if (list_empty(head)) {
 		q->plug_device_fn(q, bh->b_rdev); /* is atomic */
 		goto get_rq;
 	}
 
 	/* avoid write-bombs to not hurt iteractiveness of reads */
-	if (rw != READ && q->elevator.read_pendings)
-		max_segments = q->elevator.max_bomb_segments;
+	if (rw != READ && elevator->read_pendings)
+		max_segments = elevator->max_bomb_segments;
 
-	entry = seek_to_not_starving_chunk(q, &__latency, &starving);
-
-	__entry = entry;
-	__starving = starving;
-
-	latency = __latency;
-
-	if (q->head_active && !q->plugged) {
-		/*
-		 * The scsi disk and cdrom drivers completely remove the request
-		 * from the queue when they start processing an entry.  For this
-		 * reason it is safe to continue to add links to the top entry
-		 * for those devices.
-		 *
-		 * All other drivers need to jump over the first entry, as that
-		 * entry may be busy being processed and we thus can't change
-		 * it.
-		 */
-		if (entry == q->queue_head.next) {
-			latency -= blkdev_entry_to_request(entry)->nr_segments;
-			if ((entry = entry->next) == &q->queue_head)
-				goto get_rq;
-			starving = 0;
-		}
-	}
+	sequence = elevator->sequence;
+	latency = orig_latency - elevator->nr_segments;
+	starving = 0;
+	entry = head;
+
+	/*
+	 * The scsi disk and cdrom drivers completely remove the request
+	 * from the queue when they start processing an entry.  For this
+	 * reason it is safe to continue to add links to the top entry
+	 * for those devices.
+	 *
+	 * All other drivers need to jump over the first entry, as that
+	 * entry may be busy being processed and we thus can't change
+	 * it.
+	 */
+	if (q->head_active && !q->plugged)
+		head = head->next;
 
-	prev = NULL;
-	do {
+	while ((entry = entry->prev) != head && !starving) {
 		req = blkdev_entry_to_request(entry);
+		if (!req->q)
+			break;
+		latency += req->nr_segments;
+		if (elevator_sequence_before(req->elevator_sequence, sequence))
+			starving = 1;
+		if (latency < 0)
+			continue;
 
 		if (req->sem)
 			continue;
@@ -833,20 +682,20 @@
 			 * this 
 			 */
 			if(!(q->back_merge_fn)(q, req, bh, max_segments))
-				continue;
+				break;
 			req->bhtail->b_reqnext = bh;
 			req->bhtail = bh;
 		    	req->nr_sectors += count;
 			drive_stat_acct(req, count, 0);
 
-			elevator_merge_after(q, req, latency);
+			elevator_merge_after(elevator, req, latency);
 
 			/* Can we now merge this req with the next? */
-			attempt_merge(q, req, max_sectors, max_segments);
+			attempt_back_merge(q, req, max_sectors, max_segments);
 		/* or to the beginning? */
 		} else if (req->sector - count == sector) {
-			if (!prev && starving)
-				continue;
+			if (starving)
+				break;
 			/*
 			 * The merge_fn is a more advanced way
 			 * of accomplishing the same task.  Instead
@@ -860,7 +709,7 @@
 			 * this 
 			 */
 			if(!(q->front_merge_fn)(q, req, bh, max_segments))
-				continue;
+				break;
 		    	bh->b_reqnext = req->bh;
 		    	req->bh = bh;
 		    	req->buffer = bh->b_data;
@@ -869,10 +718,9 @@
 		    	req->nr_sectors += count;
 			drive_stat_acct(req, count, 0);
 
-			elevator_merge_before(q, req, latency);
+			elevator_merge_before(elevator, req, latency);
 
-			if (prev)
-				attempt_merge(q, prev, max_sectors, max_segments);
+			attempt_front_merge(q, head, req, max_sectors, max_segments);
 		} else
 			continue;
 
@@ -880,9 +728,7 @@
 		spin_unlock_irqrestore(&io_request_lock,flags);
 	    	return;
 
-	} while (prev = req,
-		 (latency -= req->nr_segments) >= 0 &&
-		 (entry = entry->next) != &q->queue_head);
+	}
 
 /* find an unused request. */
 get_rq:
@@ -899,31 +745,10 @@
 		req = __get_request_wait(max_req, bh->b_rdev);
 		spin_lock_irqsave(&io_request_lock,flags);
 
-		/* lock got dropped so revalidate elevator */
-		empty = 1;
-		if (!list_empty(&q->queue_head)) {
-			empty = 0;
-			__latency = orig_latency;
-			__entry = seek_to_not_starving_chunk(q, &__latency, &__starving);
-		}
-	}
-	/*
-	 * Dont start the IO if the buffer has been
-	 * invalidated meanwhile. (we have to do this
-	 * within the io request lock and atomically
-	 * before adding the request, see buffer.c's
-	 * insert_into_queues_exclusive() function.
-	 */
-	if (!test_bit(BH_Req, &bh->b_state)) {
-		req->rq_status = RQ_INACTIVE;
-		spin_unlock_irqrestore(&io_request_lock,flags);
-		/*
-		 * A fake 'everything went ok' completion event.
-		 * The bh doesnt matter anymore, but we should not
-		 * signal errors to RAID levels.
-	 	 */
-		bh->b_end_io(bh, 1);
-		return;
+		/* revalidate elevator */
+		head = &q->queue_head;
+		if (q->head_active && !q->plugged)
+			head = head->next;
 	}
 
 /* fill up the request-info, and add it to the queue */
@@ -939,8 +764,8 @@
 	req->bh = bh;
 	req->bhtail = bh;
 	req->q = q;
-	__add_request(q, req, empty, __entry, __latency, __starving);
-	elevator_account_request(q, req);
+	add_request(q, req, head, orig_latency);
+	elevator_account_request(elevator, req);
 
 	spin_unlock_irqrestore(&io_request_lock, flags);
 	return;
@@ -1166,10 +991,10 @@
 #ifdef CONFIG_ISP16_CDI
 	isp16_init();
 #endif CONFIG_ISP16_CDI
-#ifdef CONFIG_BLK_DEV_IDE
+#if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_IDE)
 	ide_init();		/* this MUST precede hd_init */
 #endif
-#ifdef CONFIG_BLK_DEV_HD
+#if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_HD)
 	hd_init();
 #endif
 #ifdef CONFIG_BLK_DEV_PS2
diff -u --recursive --new-file v2.3.51/linux/drivers/block/macide.c linux/drivers/block/macide.c
--- v2.3.51/linux/drivers/block/macide.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/macide.c	Wed Dec 31 16:00:00 1969
@@ -1,116 +0,0 @@
-/*
- *  linux/drivers/ide/macide.c -- Macintosh IDE Driver
- *
- *     Copyright (C) 1998 by Michael Schmitz
- *
- *  This driver was written based on information obtained from the MacOS IDE
- *  driver binary by Mikael Forselius
- *
- *  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 <linux/types.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/machw.h>
-#include <asm/macintosh.h>
-#include <asm/macints.h>
-
-    /*
-     *  Base of the IDE interface (see ATAManager ROM code)
-     */
-
-#define MAC_HD_BASE	0x50f1a000
-
-    /*
-     *  Offsets from the above base (scaling 4)
-     */
-
-#define MAC_HD_DATA	0x00
-#define MAC_HD_ERROR	0x04		/* see err-bits */
-#define MAC_HD_NSECTOR	0x08		/* nr of sectors to read/write */
-#define MAC_HD_SECTOR	0x0c		/* starting sector */
-#define MAC_HD_LCYL	0x10		/* starting cylinder */
-#define MAC_HD_HCYL	0x14		/* high byte of starting cyl */
-#define MAC_HD_SELECT	0x18		/* 101dhhhh , d=drive, hhhh=head */
-#define MAC_HD_STATUS	0x1c		/* see status-bits */
-#define MAC_HD_CONTROL	0x38		/* control/altstatus */
-
-static int __init macide_offsets[IDE_NR_PORTS] = {
-    MAC_HD_DATA, MAC_HD_ERROR, MAC_HD_NSECTOR, MAC_HD_SECTOR, MAC_HD_LCYL,
-    MAC_HD_HCYL, MAC_HD_SELECT, MAC_HD_STATUS, MAC_HD_CONTROL
-};
-
-	/*
-	 * Other registers
-	 */
-
-	/* 
-	 * IDE interrupt status register for both (?) hwifs on Quadra
-	 * Initial setting: 0xc
-	 * Guessing again:
-	 * Bit 0+1: some interrupt flags
-	 * Bit 2+3: some interrupt enable
-	 * Bit 4:   ??
-	 * Bit 5:   IDE interrupt flag (any hwif)
-	 * Bit 6:   maybe IDE interrupt enable (any hwif) ??
-	 * Bit 7:   Any interrupt condition
-	 *
-	 * Only relevant item: bit 5, to be checked by mac_ack_intr
-	 */
-
-#define MAC_HD_ISR	0x101
-
-static int mac_ack_intr(ide_hwif_t* hwif)
-{
-	unsigned char isr;
-	isr = readb(MAC_HD_BASE + MAC_HD_ISR);
-	if (isr & (1<<5)) {
-		writeb(isr & ~(1<<5), MAC_HD_BASE + MAC_HD_ISR);
-		return 1;
-	}
-
-	return 0;
-}
-
-    /*
-     *  Probe for a Macintosh IDE interface
-     */
-
-void __init macide_init(void)
-{
-	hw_regs_t hw;
-	int index = -1;
-
-	if (!MACH_IS_MAC || macintosh_config->ide_type == 0)
-		return;
-
-	switch (macintosh_config->ide_type) {
-	case MAC_IDE_QUADRA:
-		ide_setup_ports(&hw, (ide_ioreg_t)MAC_HD_BASE, macide_offsets,
-				0, (ide_ioreg_t)(MAC_HD_BASE+MAC_HD_ISR),
-				mac_ack_intr, IRQ_NUBUS_F);
-		index = ide_register_hw(&hw, NULL);
-		break;
-
-	default:
-	    ide_setup_ports(&hw, (ide_ioreg_t)MAC_HD_BASE, macide_offsets,
-	    		    0, 0, NULL, IRQ_NUBUS_C);
-	    index = ide_register_hw(&hw, NULL);
-	    break;
-	}
-
-        if (index != -1) {
-		if (macintosh_config->ide_type == MAC_IDE_QUADRA)
-			printk("ide%d: Macintosh Quadra IDE interface\n", index);
-		else
-			printk("ide%d: Macintosh Powerbook IDE interface\n", index);
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/ns87415.c linux/drivers/block/ns87415.c
--- v2.3.51/linux/drivers/block/ns87415.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/ns87415.c	Wed Dec 31 16:00:00 1969
@@ -1,185 +0,0 @@
-/*
- * linux/drivers/block/ns87415.c		Version 1.00  December 7, 1997
- *
- * Copyright (C) 1997-1998  Mark Lord
- * Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be)
- *
- * Inspired by an earlier effort from David S. Miller (davem@caipfs.rutgers.edu)
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 };
-
-/*
- * This routine either enables/disables (according to drive->present)
- * the IRQ associated with the port (HWIF(drive)),
- * and selects either PIO or DMA handshaking for the next I/O operation.
- */
-static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data;
-	struct pci_dev *dev = hwif->pci_dev;
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	new = *old;
-
-	/* Adjust IRQ enable bit */
-	bit = 1 << (8 + hwif->channel);
-	new = drive->present ? (new & ~bit) : (new | bit);
-
-	/* Select PIO or DMA, DMA may only be selected for one drive/channel. */
-	bit   = 1 << (20 + drive->select.b.unit       + (hwif->channel << 1));
-	other = 1 << (20 + (1 - drive->select.b.unit) + (hwif->channel << 1));
-	new = use_dma ? ((new & ~other) | bit) : (new & ~bit);
-
-	if (new != *old) {
-		unsigned char stat;
-
-		/*
-		 * Don't change DMA engine settings while Write Buffers
-		 * are busy.
-		 */
-		(void) pci_read_config_byte(dev, 0x43, &stat);
-		while (stat & 0x03) {
-			udelay(1);
-			(void) pci_read_config_byte(dev, 0x43, &stat);
-		}
-
-		*old = new;
-		(void) pci_write_config_dword(dev, 0x40, new);
-
-		/*
-		 * And let things settle...
-		 */
-		udelay(10);
-	}
-
-	__restore_flags(flags);	/* local CPU only */
-}
-
-static void ns87415_selectproc (ide_drive_t *drive)
-{
-	ns87415_prepare_drive (drive, drive->using_dma);
-}
-
-static int ns87415_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
-{
-	ide_hwif_t	*hwif = HWIF(drive);
-	byte		dma_stat;
-
-	switch (func) {
-		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-			drive->waiting_for_dma = 0;
-			dma_stat = inb(hwif->dma_base+2);
-			outb(inb(hwif->dma_base)&~1, hwif->dma_base);	/* stop DMA */
-			outb(inb(hwif->dma_base)|6, hwif->dma_base);	/* from ERRATA: clear the INTR & ERROR bits */
-			ide_destroy_dmatable(drive);			/* and free any DMA resources */
-			return (dma_stat & 7) != 4;		/* verify good DMA status */
-		case ide_dma_write:
-		case ide_dma_read:
-			ns87415_prepare_drive(drive, 1);	/* select DMA xfer */
-			if (!ide_dmaproc(func, drive))		/* use standard DMA stuff */
-				return 0;
-			ns87415_prepare_drive(drive, 0);	/* DMA failed: select PIO xfer */
-			return 1;
-		case ide_dma_check:
-			if (drive->media != ide_disk)
-				return ide_dmaproc(ide_dma_off_quietly, drive);
-			/* Fallthrough... */
-		default:
-			return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-	}
-}
-
-void __init ide_init_ns87415 (ide_hwif_t *hwif)
-{
-	struct pci_dev *dev = hwif->pci_dev;
-	unsigned int ctrl, using_inta;
-	byte progif;
-#ifdef __sparc_v9__
-	int timeout;
-	byte stat;
-#endif
-
-	/* Set a good latency timer and cache line size value. */
-	(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
-#ifdef __sparc_v9__
-	(void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
-#endif
-
-	/*
-	 * We cannot probe for IRQ: both ports share common IRQ on INTA.
-	 * Also, leave IRQ masked during drive probing, to prevent infinite
-	 * interrupts from a potentially floating INTA..
-	 *
-	 * IRQs get unmasked in selectproc when drive is first used.
-	 */
-	(void) pci_read_config_dword(dev, 0x40, &ctrl);
-	(void) pci_read_config_byte(dev, 0x09, &progif);
-	/* is irq in "native" mode? */
-	using_inta = progif & (1 << (hwif->channel << 1));
-	if (!using_inta)
-		using_inta = ctrl & (1 << (4 + hwif->channel));
-	if (hwif->mate) {
-		hwif->select_data = hwif->mate->select_data;
-	} else {
-		hwif->select_data = (unsigned long)
-					&ns87415_control[ns87415_count++];
-		ctrl |= (1 << 8) | (1 << 9);	/* mask both IRQs */
-		if (using_inta)
-			ctrl &= ~(1 << 6);	/* unmask INTA */
-		*((unsigned int *)hwif->select_data) = ctrl;
-		(void) pci_write_config_dword(dev, 0x40, ctrl);
-
-		/*
-		 * Set prefetch size to 512 bytes for both ports,
-		 * but don't turn on/off prefetching here.
-		 */
-		pci_write_config_byte(dev, 0x55, 0xee);
-
-#ifdef __sparc_v9__
-		/*
-		 * XXX: Reset the device, if we don't it will not respond
-		 *      to SELECT_DRIVE() properly during first probe_hwif().
-		 */
-		timeout = 10000;
-		outb(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
-		udelay(10);
-		outb(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
-		do {
-			udelay(50);
-			stat = inb(hwif->io_ports[IDE_STATUS_OFFSET]);
-                	if (stat == 0xff)
-                        	break;
-        	} while ((stat & BUSY_STAT) && --timeout);
-#endif
-	}
-
-	if (hwif->dma_base)
-		outb(0x60, hwif->dma_base + 2);
-
-	if (!using_inta)
-		hwif->irq = hwif->channel ? 15 : 14;	/* legacy mode */
-	else if (!hwif->irq && hwif->mate && hwif->mate->irq)
-		hwif->irq = hwif->mate->irq;	/* share IRQ with mate */
-
-	if (hwif->dma_base)
-		hwif->dmaproc = &ns87415_dmaproc;
-	hwif->selectproc = &ns87415_selectproc;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/opti621.c linux/drivers/block/opti621.c
--- v2.3.51/linux/drivers/block/opti621.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/opti621.c	Wed Dec 31 16:00:00 1969
@@ -1,315 +0,0 @@
-/*
- *  linux/drivers/block/opti621.c		Version 0.6	Jan 02, 1999
- *
- *  Copyright (C) 1996-1998  Linus Torvalds & authors (see below)
- */
-
-/*
- * Authors:
- * Jaromir Koutek <miri@punknet.cz>,
- * Jan Harkes <jaharkes@cwi.nl>,
- * Mark Lord <mlord@pobox.com>
- * Some parts of code are from ali14xx.c and from rz1000.c.
- *
- * OPTi is trademark of OPTi, Octek is trademark of Octek.
- *
- * I used docs from OPTi databook, from ftp.opti.com, file 9123-0002.ps
- * and disassembled/traced setupvic.exe (DOS program).
- * It increases kernel code about 2 kB.
- * I don't have this card no more, but I hope I can get some in case
- * of needed development.
- * My card is Octek PIDE 1.01 (on card) or OPTiViC (program).
- * It has a place for a secondary connector in circuit, but nothing
- * is there. Also BIOS says no address for
- * secondary controller (see bellow in ide_init_opti621).
- * I've only tested this on my system, which only has one disk.
- * It's Western Digital WDAC2850, with PIO mode 3. The PCI bus
- * is at 20 MHz (I have DX2/80, I tried PCI at 40, but I got random
- * lockups). I tried the OCTEK double speed CD-ROM and
- * it does not work! But I can't boot DOS also, so it's probably
- * hardware fault. I have connected Conner 80MB, the Seagate 850MB (no
- * problems) and Seagate 1GB (as slave, WD as master). My experiences
- * with the third, 1GB drive: I got 3MB/s (hdparm), but sometimes
- * it slows to about 100kB/s! I don't know why and I have
- * not this drive now, so I can't try it again.
- * I write this driver because I lost the paper ("manual") with
- * settings of jumpers on the card and I have to boot Linux with
- * Loadlin except LILO, cause I have to run the setupvic.exe program
- * already or I get disk errors (my test: rpm -Vf
- * /usr/X11R6/bin/XF86_SVGA - or any big file).
- * Some numbers from hdparm -t /dev/hda:
- * Timing buffer-cache reads:   32 MB in  3.02 seconds =10.60 MB/sec
- * Timing buffered disk reads:  16 MB in  5.52 seconds = 2.90 MB/sec
- * I have 4 Megs/s before, but I don't know why (maybe changes
- * in hdparm test).
- * After release of 0.1, I got some successful reports, so it might work.
- *
- * The main problem with OPTi is that some timings for master
- * and slave must be the same. For example, if you have master
- * PIO 3 and slave PIO 0, driver have to set some timings of
- * master for PIO 0. Second problem is that opti621_tune_drive
- * got only one drive to set, but have to set both drives.
- * This is solved in compute_pios. If you don't set
- * the second drive, compute_pios use ide_get_best_pio_mode
- * for autoselect mode (you can change it to PIO 0, if you want).
- * If you then set the second drive to another PIO, the old value
- * (automatically selected) will be overrided by yours.
- * There is a 25/33MHz switch in configuration
- * register, but driver is written for use at any frequency which get
- * (use idebus=xx to select PCI bus speed).
- * Use ide0=autotune for automatical tune of the PIO modes.
- * If you get strange results, do not use this and set PIO manually
- * by hdparm.
- *
- * Version 0.1, Nov 8, 1996
- * by Jaromir Koutek, for 2.1.8. 
- * Initial version of driver.
- * 
- * Version 0.2
- * Number 0.2 skipped.
- *
- * Version 0.3, Nov 29, 1997
- * by Mark Lord (probably), for 2.1.68
- * Updates for use with new IDE block driver.
- *
- * Version 0.4, Dec 14, 1997
- * by Jan Harkes
- * Fixed some errors and cleaned the code.
- *
- * Version 0.5, Jan 2, 1998
- * by Jaromir Koutek
- * Updates for use with (again) new IDE block driver.
- * Update of documentation.
- * 
- * Version 0.6, Jan 2, 1999
- * by Jaromir Koutek
- * Reversed to version 0.3 of the driver, because
- * 0.5 doesn't work.
- */
-
-#undef REALLY_SLOW_IO	/* most systems can safely undef this */
-#define OPTI621_DEBUG		/* define for debug messages */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-#define OPTI621_MAX_PIO 3
-/* In fact, I do not have any PIO 4 drive
- * (address: 25 ns, data: 70 ns, recovery: 35 ns),
- * but OPTi 82C621 is programmable and it can do (minimal values):
- * on 40MHz PCI bus (pulse 25 ns):
- *  address: 25 ns, data: 25 ns, recovery: 50 ns;
- * on 20MHz PCI bus (pulse 50 ns):
- *  address: 50 ns, data: 50 ns, recovery: 100 ns.
- */
-
-/* #define READ_PREFETCH 0 */
-/* Uncommnent for disable read prefetch.
- * There is some readprefetch capatibility in hdparm,
- * but when I type hdparm -P 1 /dev/hda, I got errors
- * and till reset drive is inacessible.
- * This (hw) read prefetch is safe on my drive.
- */
-
-#ifndef READ_PREFETCH
-#define READ_PREFETCH 0x40 /* read prefetch is enabled */
-#endif /* else read prefetch is disabled */
-
-#define READ_REG 0	/* index of Read cycle timing register */
-#define WRITE_REG 1	/* index of Write cycle timing register */
-#define CNTRL_REG 3	/* index of Control register */
-#define STRAP_REG 5	/* index of Strap register */
-#define MISC_REG 6	/* index of Miscellaneous register */
-
-int reg_base;
-
-#define PIO_NOT_EXIST 254
-#define PIO_DONT_KNOW 255
-
-/* there are stored pio numbers from other calls of opti621_tune_drive */
-
-static void compute_pios(ide_drive_t *drive, byte pio)
-/* Store values into drive->drive_data
- *	second_contr - 0 for primary controller, 1 for secondary
- *	slave_drive - 0 -> pio is for master, 1 -> pio is for slave
- *	pio - PIO mode for selected drive (for other we don't know)
- */
-{
-	int d;
-	ide_hwif_t *hwif = HWIF(drive);
-
-	drive->drive_data = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL);
-	for (d = 0; d < 2; ++d) {
-		drive = &hwif->drives[d];
-		if (drive->present) {
-			if (drive->drive_data == PIO_DONT_KNOW)
-				drive->drive_data = ide_get_best_pio_mode(drive, 255, OPTI621_MAX_PIO, NULL);
-#ifdef OPTI621_DEBUG
-			printk("%s: Selected PIO mode %d\n", drive->name, drive->drive_data);
-#endif
-		} else {
-			drive->drive_data = PIO_NOT_EXIST;
-		}
-	}
-}
-
-int cmpt_clk(int time, int bus_speed)
-/* Returns (rounded up) time in clocks for time in ns,
- * with bus_speed in MHz.
- * Example: bus_speed = 40 MHz, time = 80 ns
- * 1000/40 = 25 ns (clk value),
- * 80/25 = 3.2, rounded up to 4 (I hope ;-)).
- * Use idebus=xx to select right frequency.
- */
-{
-	return ((time*bus_speed+999)/1000);
-}
-
-static void write_reg(byte value, int reg)
-/* Write value to register reg, base of register
- * is at reg_base (0x1f0 primary, 0x170 secondary,
- * if not changed by PCI configuration).
- * This is from setupvic.exe program.
- */
-{
-	inw(reg_base+1);
-	inw(reg_base+1);
-	outb(3, reg_base+2);
-	outb(value, reg_base+reg);
-	outb(0x83, reg_base+2);
-}
-
-static byte read_reg(int reg)
-/* Read value from register reg, base of register
- * is at reg_base (0x1f0 primary, 0x170 secondary,
- * if not changed by PCI configuration).
- * This is from setupvic.exe program.
- */
-{
-	byte ret;
-	inw(reg_base+1);
-	inw(reg_base+1);
-	outb(3, reg_base+2);
-	ret=inb(reg_base+reg);
-	outb(0x83, reg_base+2);
-	return ret;
-}
-
-typedef struct pio_clocks_s {
-	int	address_time;	/* Address setup (clocks) */
-	int	data_time;	/* Active/data pulse (clocks) */
-	int	recovery_time;	/* Recovery time (clocks) */
-} pio_clocks_t;
-
-static void compute_clocks(int pio, pio_clocks_t *clks)
-{
-        if (pio != PIO_NOT_EXIST) {
-        	int adr_setup, data_pls, bus_speed;
-        	bus_speed = ide_system_bus_speed();
- 	       	adr_setup = ide_pio_timings[pio].setup_time;
-  	      	data_pls = ide_pio_timings[pio].active_time;
-	  	clks->address_time = cmpt_clk(adr_setup, bus_speed);
-	     	clks->data_time = cmpt_clk(data_pls, bus_speed);
-     		clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time
-     			- adr_setup-data_pls, bus_speed);
-     		if (clks->address_time<1) clks->address_time = 1;
-     		if (clks->address_time>4) clks->address_time = 4;
-     		if (clks->data_time<1) clks->data_time = 1;
-     		if (clks->data_time>16) clks->data_time = 16;
-     		if (clks->recovery_time<2) clks->recovery_time = 2;
-     		if (clks->recovery_time>17) clks->recovery_time = 17;
-	} else {
-		clks->address_time = 1;
-		clks->data_time = 1;
-		clks->recovery_time = 2;
-		/* minimal values */
-	}
- 
-}
-
-/* Main tune procedure, called from tuneproc. */
-static void opti621_tune_drive (ide_drive_t *drive, byte pio)
-{
-	/* primary and secondary drives share some registers,
-	 * so we have to program both drives
-	 */
-	unsigned long flags;
-	byte pio1, pio2;
-	pio_clocks_t first, second;
-	int ax, drdy;
-	byte cycle1, cycle2, misc;
-	ide_hwif_t *hwif = HWIF(drive);
-
-	/* sets drive->drive_data for both drives */
-	compute_pios(drive, pio);
- 	pio1 = hwif->drives[0].drive_data;
- 	pio2 = hwif->drives[1].drive_data;
-
-	compute_clocks(pio1, &first);
-	compute_clocks(pio2, &second);
-
-	/* ax = max(a1,a2) */
-	ax = (first.address_time < second.address_time) ? second.address_time : first.address_time;
-
-	drdy = 2; /* DRDY is default 2 (by OPTi Databook) */
-
-	cycle1 = ((first.data_time-1)<<4)  | (first.recovery_time-2);
-	cycle2 = ((second.data_time-1)<<4) | (second.recovery_time-2);
-	misc = READ_PREFETCH | ((ax-1)<<4) | ((drdy-2)<<1);
-
-#ifdef OPTI621_DEBUG
-	printk("%s: master: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n",
-		hwif->name, ax, first.data_time, first.recovery_time, drdy);
-	printk("%s: slave:  address: %d, data: %d, recovery: %d, drdy: %d [clk]\n",
-		hwif->name, ax, second.data_time, second.recovery_time, drdy);
-#endif
-
-	save_flags(flags);	/* all CPUs */
-	cli();			/* all CPUs */
-
-     	reg_base = hwif->io_ports[IDE_DATA_OFFSET];
-	outb(0xc0, reg_base+CNTRL_REG);	/* allow Register-B */
-	outb(0xff, reg_base+5);		/* hmm, setupvic.exe does this ;-) */
-	inb(reg_base+CNTRL_REG); 	/* if reads 0xff, adapter not exist? */
-	read_reg(CNTRL_REG);		/* if reads 0xc0, no interface exist? */
-	read_reg(STRAP_REG);		/* read version, probably 0 */
-
-	/* program primary drive */
-	write_reg(0,      MISC_REG);	/* select Index-0 for Register-A */
-	write_reg(cycle1, READ_REG);	/* set read cycle timings */
-	write_reg(cycle1, WRITE_REG);	/* set write cycle timings */
-
-	/* program secondary drive */
-	write_reg(1,      MISC_REG);	/* select Index-1 for Register-B */
-	write_reg(cycle2, READ_REG);	/* set read cycle timings */
-	write_reg(cycle2, WRITE_REG);	/* set write cycle timings */
-
-	write_reg(0x85, CNTRL_REG);	/* use Register-A for drive 0 */
-					/* use Register-B for drive 1 */
-
- 	write_reg(misc, MISC_REG);	/* set address setup, DRDY timings,   */
- 					/*  and read prefetch for both drives */
-
-	restore_flags(flags);	/* all CPUs */
-}
-
-/*
- * ide_init_opti621() is called once for each hwif found at boot.
- */
-void ide_init_opti621 (ide_hwif_t *hwif)
-{
-	hwif->drives[0].drive_data = PIO_DONT_KNOW;
-	hwif->drives[1].drive_data = PIO_DONT_KNOW;
-	hwif->tuneproc = &opti621_tune_drive;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/pdc202xx.c linux/drivers/block/pdc202xx.c
--- v2.3.51/linux/drivers/block/pdc202xx.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/pdc202xx.c	Wed Dec 31 16:00:00 1969
@@ -1,731 +0,0 @@
-/*
- *  linux/drivers/block/pdc202xx.c	Version 0.29	Feb. 10, 2000
- *
- *  Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
- *  May be copied or modified under the terms of the GNU General Public License
- *
- *  Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this
- *  compiled into the kernel if you have more than one card installed.
- *  Note that BIOS v1.29 is reported to fix the problem.  Since this is
- *  safe chipset tuning, including this support is harmless
- *
- *  The latest chipset code will support the following ::
- *  Three Ultra33 controllers and 12 drives.
- *  8 are UDMA supported and 4 are limited to DMA mode 2 multi-word.
- *  The 8/4 ratio is a BIOS code limit by promise.
- *
- *  UNLESS you enable "CONFIG_PDC202XX_BURST"
- *
- *  There is only one BIOS in the three contollers.
- *
- *  May  8 20:56:17 Orion kernel:
- *  Uniform Multi-Platform E-IDE driver Revision: 6.19
- *  PDC20246: IDE controller on PCI bus 00 dev a0
- *  PDC20246: not 100% native mode: will probe irqs later
- *  PDC20246: ROM enabled at 0xfebd0000
- *  PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
- *      ide0: BM-DMA at 0xef80-0xef87, BIOS settings: hda:DMA, hdb:DMA
- *      ide1: BM-DMA at 0xef88-0xef8f, BIOS settings: hdc:pio, hdd:pio
- *  PDC20246: IDE controller on PCI bus 00 dev 98
- *  PDC20246: not 100% native mode: will probe irqs later
- *  PDC20246: ROM enabled at 0xfebc0000
- *  PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
- *      ide2: BM-DMA at 0xef40-0xef47, BIOS settings: hde:DMA, hdf:DMA
- *      ide3: BM-DMA at 0xef48-0xef4f, BIOS settings: hdg:DMA, hdh:DMA
- *  PDC20246: IDE controller on PCI bus 00 dev 90
- *  PDC20246: not 100% native mode: will probe irqs later
- *  PDC20246: ROM enabled at 0xfebb0000
- *  PDC20246: (U)DMA Burst Bit DISABLED Primary PCI Mode Secondary PCI Mode.
- *  PDC20246: FORCING BURST BIT 0x00 -> 0x01 ACTIVE
- *      ide4: BM-DMA at 0xef00-0xef07, BIOS settings: hdi:DMA, hdj:pio
- *      ide5: BM-DMA at 0xef08-0xef0f, BIOS settings: hdk:pio, hdl:pio
- *  PIIX3: IDE controller on PCI bus 00 dev 39
- *  PIIX3: device not capable of full native PCI mode
- *
- *  ide0 at 0xeff0-0xeff7,0xefe6 on irq 19
- *  ide1 at 0xefa8-0xefaf,0xebe6 on irq 19
- *  ide2 at 0xefa0-0xefa7,0xef7e on irq 18
- *  ide3 at 0xef68-0xef6f,0xef66 on irq 18
- *  ide4 at 0xef38-0xef3f,0xef62 on irq 17
- *  hda: QUANTUM FIREBALL ST6.4A, 6149MB w/81kB Cache, CHS=13328/15/63, UDMA(33)
- *  hdb: QUANTUM FIREBALL ST3.2A, 3079MB w/81kB Cache, CHS=6256/16/63, UDMA(33)
- *  hde: Maxtor 72004 AP, 1916MB w/128kB Cache, CHS=3893/16/63, DMA
- *  hdf: Maxtor 71626 A, 1554MB w/64kB Cache, CHS=3158/16/63, DMA
- *  hdi: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
- *  hdj: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
- *
- *  Promise Ultra66 cards with BIOS v1.11 this
- *  compiled into the kernel if you have more than one card installed.
- *
- *  PDC20262: IDE controller on PCI bus 00 dev a0
- *  PDC20262: not 100% native mode: will probe irqs later
- *  PDC20262: ROM enabled at 0xfebb0000
- *  PDC20262: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
- *      ide0: BM-DMA at 0xef00-0xef07, BIOS settings: hda:pio, hdb:pio
- *      ide1: BM-DMA at 0xef08-0xef0f, BIOS settings: hdc:pio, hdd:pio
- *
- *  UDMA 4/2 and UDMA 3/1 only differ by the testing bit 13 in word93.
- *  Chipset timing speeds must be identical
- *
- *  drive_number
- *      = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
- *      = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
- */
-
-/*
- *  Portions Copyright (C) 1999 Promise Technology, Inc.
- *  Author: Frank Tiernan (frankt@promise.com)
- *  Released under terms of General Public License
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "ide_modes.h"
-
-#define PDC202XX_DEBUG_DRIVE_INFO		0
-#define PDC202XX_DECODE_REGISTER_INFO		0
-
-#undef DISPLAY_PDC202XX_TIMINGS
-
-#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int pdc202xx_get_info(char *, char **, off_t, int);
-extern int (*pdc202xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-extern char *ide_media_verbose(ide_drive_t *);
-static struct pci_dev *bmide_dev;
-
-static int pdc202xx_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-
-	u32 bibma = bmide_dev->resource[4].start;
-	u8 c0 = 0, c1 = 0;
-
-        /*
-         * at that point bibma+0x2 et bibma+0xa are byte registers
-         * to investigate:
-         */
-	c0 = inb_p((unsigned short)bibma + 0x02);
-	c1 = inb_p((unsigned short)bibma + 0x0a);
-
-	switch(bmide_dev->device) {	
-		case PCI_DEVICE_ID_PROMISE_20262:
-			p += sprintf(p, "\n                                PDC20262 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_PROMISE_20246:
-			p += sprintf(p, "\n                                PDC20246 Chipset.\n");
-			break;
-		default:
-			p += sprintf(p, "\n                                PDC202XX Chipset.\n");
-			break;
-	}
-
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %sabled                         %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
-	return p-buffer;	/* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-byte pdc202xx_proc = 0;
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-/* A Register */
-#define	SYNC_ERRDY_EN	0xC0
-
-#define	SYNC_IN		0x80	/* control bit, different for master vs. slave drives */
-#define	ERRDY_EN	0x40	/* control bit, different for master vs. slave drives */
-#define	IORDY_EN	0x20	/* PIO: IOREADY */
-#define	PREFETCH_EN	0x10	/* PIO: PREFETCH */
-
-#define	PA3		0x08	/* PIO"A" timing */
-#define	PA2		0x04	/* PIO"A" timing */
-#define	PA1		0x02	/* PIO"A" timing */
-#define	PA0		0x01	/* PIO"A" timing */
-
-/* B Register */
-
-#define	MB2		0x80	/* DMA"B" timing */
-#define	MB1		0x40	/* DMA"B" timing */
-#define	MB0		0x20	/* DMA"B" timing */
-
-#define	PB4		0x10	/* PIO_FORCE 1:0 */
-
-#define	PB3		0x08	/* PIO"B" timing */	/* PIO flow Control mode */
-#define	PB2		0x04	/* PIO"B" timing */	/* PIO 4 */
-#define	PB1		0x02	/* PIO"B" timing */	/* PIO 3 half */
-#define	PB0		0x01	/* PIO"B" timing */	/* PIO 3 other half */
-
-/* C Register */
-#define	IORDYp_NO_SPEED	0x4F
-#define	SPEED_DIS	0x0F
-
-#define	DMARQp		0x80
-#define	IORDYp		0x40
-#define	DMAR_EN		0x20
-#define	DMAW_EN		0x10
-
-#define	MC3		0x08	/* DMA"C" timing */
-#define	MC2		0x04	/* DMA"C" timing */
-#define	MC1		0x02	/* DMA"C" timing */
-#define	MC0		0x01	/* DMA"C" timing */
-
-#if PDC202XX_DECODE_REGISTER_INFO
-
-#define REG_A		0x01
-#define REG_B		0x02
-#define REG_C		0x04
-#define REG_D		0x08
-
-static void decode_registers (byte registers, byte value)
-{
-	byte	bit = 0, bit1 = 0, bit2 = 0;
-
-	switch(registers) {
-		case REG_A:
-			bit2 = 0;
-			printk("A Register ");
-			if (value & 0x80) printk("SYNC_IN ");
-			if (value & 0x40) printk("ERRDY_EN ");
-			if (value & 0x20) printk("IORDY_EN ");
-			if (value & 0x10) printk("PREFETCH_EN ");
-			if (value & 0x08) { printk("PA3 ");bit2 |= 0x08; }
-			if (value & 0x04) { printk("PA2 ");bit2 |= 0x04; }
-			if (value & 0x02) { printk("PA1 ");bit2 |= 0x02; }
-			if (value & 0x01) { printk("PA0 ");bit2 |= 0x01; }
-			printk("PIO(A) = %d ", bit2);
-			break;
-		case REG_B:
-			bit1 = 0;bit2 = 0;
-			printk("B Register ");
-			if (value & 0x80) { printk("MB2 ");bit1 |= 0x80; }
-			if (value & 0x40) { printk("MB1 ");bit1 |= 0x40; }
-			if (value & 0x20) { printk("MB0 ");bit1 |= 0x20; }
-			printk("DMA(B) = %d ", bit1 >> 5);
-			if (value & 0x10) printk("PIO_FORCED/PB4 ");
-			if (value & 0x08) { printk("PB3 ");bit2 |= 0x08; }
-			if (value & 0x04) { printk("PB2 ");bit2 |= 0x04; }
-			if (value & 0x02) { printk("PB1 ");bit2 |= 0x02; }
-			if (value & 0x01) { printk("PB0 ");bit2 |= 0x01; }
-			printk("PIO(B) = %d ", bit2);
-			break;
-		case REG_C:
-			bit2 = 0;
-			printk("C Register ");
-			if (value & 0x80) printk("DMARQp ");
-			if (value & 0x40) printk("IORDYp ");
-			if (value & 0x20) printk("DMAR_EN ");
-			if (value & 0x10) printk("DMAW_EN ");
-
-			if (value & 0x08) { printk("MC3 ");bit2 |= 0x08; }
-			if (value & 0x04) { printk("MC2 ");bit2 |= 0x04; }
-			if (value & 0x02) { printk("MC1 ");bit2 |= 0x02; }
-			if (value & 0x01) { printk("MC0 ");bit2 |= 0x01; }
-			printk("DMA(C) = %d ", bit2);
-			break;
-		case REG_D:
-			printk("D Register ");
-			break;
-		default:
-			return;
-	}
-	printk("\n        %s ", (registers & REG_D) ? "DP" :
-				(registers & REG_C) ? "CP" :
-				(registers & REG_B) ? "BP" :
-				(registers & REG_A) ? "AP" : "ERROR");
-	for (bit=128;bit>0;bit/=2)
-		printk("%s", (value & bit) ? "1" : "0");
-	printk("\n");
-}
-
-#endif /* PDC202XX_DECODE_REGISTER_INFO */
-
-static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
-{
-	struct hd_driveid *id	= drive->id;
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned long high_16	= dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
-
-	int			err;
-	unsigned int		drive_conf;
-	byte			drive_pci;
-	byte			test1, test2, speed = -1;
-	byte			AP, BP, CP, DP, TB, TC;
-	unsigned short		EP;
-	byte CLKSPD		= IN_BYTE(high_16 + 0x11);
-	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
-	byte udma_33		= ultra ? (inb(high_16 + 0x001f) & 1) : 0;
-
-	/*
-	 * Set the control register to use the 66Mhz system
-	 * clock for UDMA 3/4 mode operation. If one drive on
-	 * a channel is U66 capable but the other isn't we
-	 * fall back to U33 mode. The BIOS INT 13 hooks turn
-	 * the clock on then off for each read/write issued. I don't
-	 * do that here because it would require modifying the
-	 * kernel, seperating the fop routines from the kernel or
-	 * somehow hooking the fops calls. It may also be possible to
-	 * leave the 66Mhz clock on and readjust the timing
-	 * parameters.
-	 */
-
-	byte mask		= hwif->channel ? 0x08 : 0x02;
-	unsigned short c_mask	= hwif->channel ? (1<<11) : (1<<10);
-	byte ultra_66		= ((id->dma_ultra & 0x0010) || (id->dma_ultra & 0x0008)) ? 1 : 0;
-
-	pci_read_config_word(dev, 0x50, &EP);
-
-	if ((ultra_66) && (EP & c_mask)) {
-#ifdef DEBUG
-		printk("ULTRA66: %s channel of Ultra 66 requires an 80-pin cable for Ultra66 operation.\n", hwif->channel ? "Secondary", "Primary");
-		printk("         Switching to Ultra33 mode.\n");
-#endif /* DEBUG */
-		/* Primary   : zero out second bit */
-		/* Secondary : zero out fourth bit */
-		OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11));
-	} else {
-		if (ultra_66) {
-			/*
-			 * check to make sure drive on same channel
-			 * is u66 capable
-			 */
-			if (hwif->drives[!(drive_number%2)].id) {
-				if ((hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0010) ||
-				    (hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0008)) {
-					OUT_BYTE(CLKSPD | mask, (high_16 + 0x11));
-				} else {
-					OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11));
-				}
-			} else { /* udma4 drive by itself */
-				OUT_BYTE(CLKSPD | mask, (high_16 + 0x11));
-			}
-		}
-	}
-
-	switch(drive_number) {
-		case 0:	drive_pci = 0x60;
-			pci_read_config_dword(dev, drive_pci, &drive_conf);
-			if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
-				goto chipset_is_set;
-			pci_read_config_byte(dev, (drive_pci), &test1);
-			if (!(test1 & SYNC_ERRDY_EN))
-				pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
-			break;
-		case 1:	drive_pci = 0x64;
-			pci_read_config_dword(dev, drive_pci, &drive_conf);
-			if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
-				goto chipset_is_set;
-			pci_read_config_byte(dev, 0x60, &test1);
-			pci_read_config_byte(dev, (drive_pci), &test2);
-			if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
-				pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
-			break;
-		case 2:	drive_pci = 0x68;
-			pci_read_config_dword(dev, drive_pci, &drive_conf);
-			if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
-				goto chipset_is_set;
-			pci_read_config_byte(dev, (drive_pci), &test1);
-			if (!(test1 & SYNC_ERRDY_EN))
-				pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
-			break;
-		case 3:	drive_pci = 0x6c;
-			pci_read_config_dword(dev, drive_pci, &drive_conf);
-			if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
-				goto chipset_is_set;
-			pci_read_config_byte(dev, 0x68, &test1);
-			pci_read_config_byte(dev, (drive_pci), &test2);
-			if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
-				pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
-			break;
-		default:
-			return ide_dma_off;
-	}
-
-chipset_is_set:
-
-	if (drive->media != ide_disk)
-		return ide_dma_off_quietly;
-
-	pci_read_config_byte(dev, (drive_pci), &AP);
-	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
-	pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
-
-	if (id->capability & 4) {		/* IORDY_EN */
-		pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN);
-		pci_read_config_byte(dev, (drive_pci), &AP);
-	}
-
-	if (drive->media == ide_disk) {		/* PREFETCH_EN */
-		pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN);
-		pci_read_config_byte(dev, (drive_pci), &AP);
-	}
-
-	if ((BP & 0xF0) && (CP & 0x0F)) {
-		/* clear DMA modes of upper 842 bits of B Register */
-		/* clear PIO forced mode upper 1 bit of B Register */
-		pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0xF0);
-		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-
-		/* clear DMA modes of lower 8421 bits of C Register */
-		pci_write_config_byte(dev, (drive_pci)|0x02, CP & ~0x0F);
-		pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
-	}
-
-	pci_read_config_byte(dev, (drive_pci), &AP);
-	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
-
-	if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
-		/* speed 8 == UDMA mode 4 == speed 6 plus cable */
-		speed = XFER_UDMA_4; TB = 0x20; TC = 0x01;
-	} else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
-		/* speed 7 == UDMA mode 3 == speed 5 plus cable */
-		speed = XFER_UDMA_3; TB = 0x40; TC = 0x02;
-	} else if ((id->dma_ultra & 0x0004) && (udma_33)) {
-		/* speed 6 == UDMA mode 2 */
-		speed = XFER_UDMA_2; TB = 0x20; TC = 0x01;
-	} else if ((id->dma_ultra & 0x0002) && (udma_33)) {
-		/* speed 5 == UDMA mode 1 */
-		speed = XFER_UDMA_1; TB = 0x40; TC = 0x02;
-	} else if ((id->dma_ultra & 0x0001) && (udma_33)) {
-		/* speed 4 == UDMA mode 0 */
-		speed = XFER_UDMA_0; TB = 0x60; TC = 0x03;
-	} else if (id->dma_mword & 0x0004) {
-		/* speed 4 == DMA mode 2 multi-word */
-		speed = XFER_MW_DMA_2; TB = 0x60; TC = 0x03;
-	} else if (id->dma_mword & 0x0002) {
-		/* speed 3 == DMA mode 1 multi-word */
-		speed = XFER_MW_DMA_1; TB = 0x60; TC = 0x04;
-	} else if (id->dma_mword & 0x0001) {
-		/* speed 2 == DMA mode 0 multi-word */
-		speed = XFER_MW_DMA_0; TB = 0x60; TC = 0x05;
-	} else if (id->dma_1word & 0x0004) {
-		/* speed 2 == DMA mode 2 single-word */
-		speed = XFER_SW_DMA_2; TB = 0x60; TC = 0x05;
-	} else if (id->dma_1word & 0x0002) {
-		/* speed 1 == DMA mode 1 single-word */
-		speed = XFER_SW_DMA_1; TB = 0x80; TC = 0x06;
-	} else if (id->dma_1word & 0x0001) {
-		/* speed 0 == DMA mode 0 single-word */
-		speed = XFER_SW_DMA_0; TB = 0xC0; TC = 0x0B;
-	} else {
-		/* restore original pci-config space */
-		pci_write_config_dword(dev, drive_pci, drive_conf);
-		return ide_dma_off_quietly;
-	}
-
-	pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
-	pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC);
-
-#if PDC202XX_DECODE_REGISTER_INFO
-	pci_read_config_byte(dev, (drive_pci), &AP);
-	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
-
-	decode_registers(REG_A, AP);
-	decode_registers(REG_B, BP);
-	decode_registers(REG_C, CP);
-	decode_registers(REG_D, DP);
-#endif /* PDC202XX_DECODE_REGISTER_INFO */
-
-	err = ide_config_drive_speed(drive, speed);
-
-#if PDC202XX_DEBUG_DRIVE_INFO
-	printk("%s: %s drive%d 0x%08x ",
-		drive->name, ide_xfer_verbose(speed),
-		drive_number, drive_conf);
-	pci_read_config_dword(dev, drive_pci, &drive_conf);
-	printk("0x%08x\n", drive_conf);
-#endif /* PDC202XX_DEBUG_DRIVE_INFO */
-
-	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on : 
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-}
-
-/*   0    1    2    3    4    5    6   7   8
- * 960, 480, 390, 300, 240, 180, 120, 90, 60
- *           180, 150, 120,  90,  60
- * DMA_Speed
- * 180, 120,  90,  90,  90,  60,  30
- *  11,   5,   4,   3,   2,   1,   0
- */
-
-static int config_chipset_for_pio (ide_drive_t *drive, byte pio)
-{
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	byte			drive_pci, speed;
-	byte			AP, BP, TA, TB;
-
-	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-	int err;
-
-	switch (drive_number) {
-		case 0: drive_pci = 0x60;	break;
-		case 1: drive_pci = 0x64;	break;
-		case 2: drive_pci = 0x68;	break;
-		case 3: drive_pci = 0x6c;	break;
-		default:			return 1;
-	}
-
-        pci_read_config_byte(dev, (drive_pci), &AP);
-        pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-
-
-        if ((AP & 0x0F) || (BP & 0x07)) {
-		/* clear PIO modes of lower 8421 bits of A Register */
-		pci_write_config_byte(dev, (drive_pci), AP & ~0x0F);
-		pci_read_config_byte(dev, (drive_pci), &AP);
-
-		/* clear PIO modes of lower 421 bits of B Register */
-		pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07);
-		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-
-		pci_read_config_byte(dev, (drive_pci), &AP);
-		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-	}
-
-	pio = (pio == 5) ? 4 : pio;
-	switch (ide_get_best_pio_mode(drive, 255, pio, NULL)) {
-		case 4:  speed = XFER_PIO_4; TA=0x01; TB=0x04; break;
-		case 3:  speed = XFER_PIO_3; TA=0x02; TB=0x06; break;
-		case 2:  speed = XFER_PIO_2; TA=0x03; TB=0x08; break;
-		case 1:  speed = XFER_PIO_1; TA=0x05; TB=0x0C; break;
-		case 0:
-		default: speed = XFER_PIO_0; TA=0x09; TB=0x13; break;
-	}
-	pci_write_config_byte(dev, (drive_pci), AP|TA);
-	pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
-
-#if PDC202XX_DECODE_REGISTER_INFO
-	pci_read_config_byte(dev, (drive_pci), &AP);
-	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
-	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
-	pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
-
-	decode_registers(REG_A, AP);
-	decode_registers(REG_B, BP);
-	decode_registers(REG_C, CP);
-	decode_registers(REG_D, DP);
-#endif /* PDC202XX_DECODE_REGISTER_INFO */
-
-	err = ide_config_drive_speed(drive, speed);
-
-#if PDC202XX_DEBUG_DRIVE_INFO
-	printk("%s: %s drive%d 0x%08x ",
-		drive->name, ide_xfer_verbose(speed),
-		drive_number, drive_conf);
-	pci_read_config_dword(dev, drive_pci, &drive_conf);
-	printk("0x%08x\n", drive_conf);
-#endif /* PDC202XX_DEBUG_DRIVE_INFO */
-
-	return err;
-}
-
-static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio)
-{
-	(void) config_chipset_for_pio(drive, pio);
-}
-
-static int config_drive_xfer_rate (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	ide_hwif_t *hwif = HWIF(drive);
-	ide_dma_action_t dma_func = ide_dma_off_quietly;
-
-	if (id && (id->capability & 1) && hwif->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
-			goto fast_ata_pio;
-		}
-		dma_func = ide_dma_off_quietly;
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & 0x001F) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, 1);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & 0x0007) ||
-			    (id->dma_1word & 0x0007)) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, 0);
-				if (dma_func != ide_dma_on)
-					goto no_dma_set;
-			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			if (id->eide_dma_time > 150) {
-				goto no_dma_set;
-			}
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, 0);
-			if (dma_func != ide_dma_on)
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
-no_dma_set:
-		(void) config_chipset_for_pio(drive, 5);
-	}
-
-	return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-/*
- * pdc202xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- */
-int pdc202xx_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-}
-
-unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name)
-{
-	unsigned long high_16	= dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
-	byte udma_speed_flag	= inb(high_16 + 0x001f);
-	byte primary_mode	= inb(high_16 + 0x001a);
-	byte secondary_mode	= inb(high_16 + 0x001b);
-
-	if (dev->device == PCI_DEVICE_ID_PROMISE_20262) {
-		int i = 0;
-		/*
-		 * software reset -  this is required because the bios
-		 * will set UDMA timing on if the hdd supports it. The
-		 * user may want to turn udma off. A bug in the pdc20262
-		 * is that it cannot handle a downgrade in timing from UDMA
-		 * to DMA. Disk accesses after issuing a set feature command
-		 * will result in errors. A software reset leaves the timing
-		 * registers intact, but resets the drives.
-		 */
-
-		OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f);
-		ide_delay_50ms();
-		ide_delay_50ms();
-		OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f);
-		for (i=0; i<40; i++)
-			ide_delay_50ms();
-	}
-
-	if (dev->resource[PCI_ROM_RESOURCE].start) {
-		pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
-		printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
-	}
-
-	if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) {
-		byte irq = 0, irq2 = 0;
-		pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
-		pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2);	/* 0xbc */
-		if (irq != irq2) {
-			pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq);	/* 0xbc */
-			printk("%s: pci-config space interrupt mirror fixed.\n", name);
-		}
-	}
-
-	printk("%s: (U)DMA Burst Bit %sABLED " \
-		"Primary %s Mode " \
-		"Secondary %s Mode.\n",
-		name,
-		(udma_speed_flag & 1) ? "EN" : "DIS",
-		(primary_mode & 1) ? "MASTER" : "PCI",
-		(secondary_mode & 1) ? "MASTER" : "PCI" );
-
-#ifdef CONFIG_PDC202XX_BURST
-	if (!(udma_speed_flag & 1)) {
-		printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1));
-		outb(udma_speed_flag|1, high_16 + 0x001f);
-		printk("%sCTIVE\n", (inb(high_16 + 0x001f) & 1) ? "A" : "INA");
-	}
-#endif /* CONFIG_PDC202XX_BURST */
-
-#ifdef CONFIG_PDC202XX_MASTER
-	if (!(primary_mode & 1)) {
-		printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ",
-			name, primary_mode, (primary_mode|1));
-		outb(primary_mode|1, high_16 + 0x001a);
-		printk("%s\n", (inb(high_16 + 0x001a) & 1) ? "MASTER" : "PCI");
-	}
-
-	if (!(secondary_mode & 1)) {
-		printk("%s: FORCING SECONDARY MODE BIT 0x%02x -> 0x%02x ",
-			name, secondary_mode, (secondary_mode|1));
-		outb(secondary_mode|1, high_16 + 0x001b);
-		printk("%s\n", (inb(high_16 + 0x001b) & 1) ? "MASTER" : "PCI");
-	}
-#endif /* CONFIG_PDC202XX_MASTER */
-
-#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
-        pdc202xx_proc = 1;
-        bmide_dev = dev;
-        pdc202xx_display_info = &pdc202xx_get_info;
-#endif /* DISPLAY_PDC202XX_TIMINGS && CONFIG_PROC_FS */
-
-	return dev->irq;
-}
-
-unsigned int __init ata66_pdc202xx (ide_hwif_t *hwif)
-{
-	unsigned short mask = (hwif->channel) ? (1<<11) : (1<<10);
-	unsigned short CIS;
-
-	pci_read_config_word(hwif->pci_dev, 0x50, &CIS);
-        return ((CIS & mask) ? 0 : 1);
-}
-
-void __init ide_init_pdc202xx (ide_hwif_t *hwif)
-{
-	hwif->tuneproc = &pdc202xx_tune_drive;
-
-	if (hwif->dma_base) {
-		hwif->dmaproc = &pdc202xx_dmaproc;
-	} else {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/pdc4030.c linux/drivers/block/pdc4030.c
--- v2.3.51/linux/drivers/block/pdc4030.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/pdc4030.c	Wed Dec 31 16:00:00 1969
@@ -1,552 +0,0 @@
-/*  -*- linux-c -*-
- *  linux/drivers/ide/pdc4030.c		Version 0.90  May 27, 1999
- *
- *  Copyright (C) 1995-1999  Linus Torvalds & authors (see below)
- */
-
-/*
- *  Principal Author/Maintainer:  peterd@pnd-pc.demon.co.uk
- *
- *  This file provides support for the second port and cache of Promise
- *  IDE interfaces, e.g. DC4030VL, DC4030VL-1 and DC4030VL-2.
- *
- *  Thanks are due to Mark Lord for advice and patiently answering stupid
- *  questions, and all those mugs^H^H^H^Hbrave souls who've tested this,
- *  especially Andre Hedrick.
- *
- *  Version 0.01	Initial version, #include'd in ide.c rather than
- *                      compiled separately.
- *                      Reads use Promise commands, writes as before. Drives
- *                      on second channel are read-only.
- *  Version 0.02        Writes working on second channel, reads on both
- *                      channels. Writes fail under high load. Suspect
- *			transfers of >127 sectors don't work.
- *  Version 0.03        Brought into line with ide.c version 5.27.
- *                      Other minor changes.
- *  Version 0.04        Updated for ide.c version 5.30
- *                      Changed initialization strategy
- *  Version 0.05	Kernel integration.  -ml
- *  Version 0.06	Ooops. Add hwgroup to direct call of ide_intr() -ml
- *  Version 0.07	Added support for DC4030 variants
- *			Secondary interface autodetection
- *  Version 0.08	Renamed to pdc4030.c
- *  Version 0.09	Obsolete - never released - did manual write request
- *			splitting before max_sectors[major][minor] available.
- *  Version 0.10	Updated for 2.1 series of kernels
- *  Version 0.11	Updated for 2.3 series of kernels
- *			Autodetection code added.
- *
- *  Version 0.90	Transition to BETA code. No lost/unexpected interrupts
- */
-
-/*
- * Once you've compiled it in, you'll have to also enable the interface
- * setup routine from the kernel command line, as in 
- *
- *	'linux ide0=dc4030' or 'linux ide1=dc4030'
- *
- * It should now work as a second controller also ('ide1=dc4030') but only
- * if you DON'T have BIOS V4.44, which has a bug. If you have this version
- * and EPROM programming facilities, you need to fix 4 bytes:
- * 	2496:	81	81
- *	2497:	3E	3E
- *	2498:	22	98	*
- *	2499:	06	05	*
- *	249A:	F0	F0
- *	249B:	01	01
- *	...
- *	24A7:	81	81
- *	24A8:	3E	3E
- *	24A9:	22	98	*
- *	24AA:	06	05	*
- *	24AB:	70	70
- *	24AC:	01	01
- *
- * As of January 1999, Promise Technology Inc. have finally supplied me with
- * some technical information which has shed a glimmer of light on some of the
- * problems I was having, especially with writes. 
- *
- * There are still problems with the robustness and efficiency of this driver
- * because I still don't understand what the card is doing with interrupts.
- */
-
-#define DEBUG_READ
-#define DEBUG_WRITE
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "pdc4030.h"
-
-/*
- * promise_selectproc() is invoked by ide.c
- * in preparation for access to the specified drive.
- */
-static void promise_selectproc (ide_drive_t *drive)
-{
-	unsigned int number;
-
-	number = (HWIF(drive)->channel << 1) + drive->select.b.unit;
-	OUT_BYTE(number,IDE_FEATURE_REG);
-}
-
-/*
- * pdc4030_cmd handles the set of vendor specific commands that are initiated
- * by command F0. They all have the same success/failure notification -
- * 'P' (=0x50) on success, 'p' (=0x70) on failure.
- */
-int pdc4030_cmd(ide_drive_t *drive, byte cmd)
-{
-	unsigned long timeout, timer;
-	byte status_val;
-
-	promise_selectproc(drive);	/* redundant? */
-	OUT_BYTE(0xF3,IDE_SECTOR_REG);
-	OUT_BYTE(cmd,IDE_SELECT_REG);
-	OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG);
-	timeout = HZ * 10;
-	timeout += jiffies;
-	do {
-		if(time_after(jiffies, timeout)) {
-			return 2; /* device timed out */
-		}
-		/* This is out of delay_10ms() */
-		/* Delays at least 10ms to give interface a chance */
-		timer = jiffies + (HZ + 99)/100 + 1;
-		while (time_after(timer, jiffies));
-		status_val = IN_BYTE(IDE_SECTOR_REG);
-	} while (status_val != 0x50 && status_val != 0x70);
-
-	if(status_val == 0x50)
-		return 0; /* device returned success */
-	else
-		return 1; /* device returned failure */
-}
-
-/*
- * pdc4030_identify sends a vendor-specific IDENTIFY command to the drive
- */
-int pdc4030_identify(ide_drive_t *drive)
-{
-	return pdc4030_cmd(drive, PROMISE_IDENTIFY);
-}
-
-int enable_promise_support = 0;
-
-void __init init_pdc4030 (void)
-{
-	enable_promise_support = 1;
-}
-
-/*
- * setup_pdc4030()
- * Completes the setup of a Promise DC4030 controller card, once found.
- */
-int __init setup_pdc4030 (ide_hwif_t *hwif)
-{
-        ide_drive_t *drive;
-	ide_hwif_t *hwif2;
-	struct dc_ident ident;
-	int i;
-	ide_startstop_t startstop;
-	
-	if (!hwif) return 0;
-
-	drive = &hwif->drives[0];
-	hwif2 = &ide_hwifs[hwif->index+1];
-	if (hwif->chipset == ide_pdc4030) /* we've already been found ! */
-		return 1;
-
-	if (IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) {
-		return 0;
-	}
-	if (IDE_CONTROL_REG)
-		OUT_BYTE(0x08,IDE_CONTROL_REG);
-	if (pdc4030_cmd(drive,PROMISE_GET_CONFIG)) {
-		return 0;
-	}
-	if (ide_wait_stat(&startstop, drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
-		printk(KERN_INFO
-			"%s: Failed Promise read config!\n",hwif->name);
-		return 0;
-	}
-	ide_input_data(drive,&ident,SECTOR_WORDS);
-	if (ident.id[1] != 'P' || ident.id[0] != 'T') {
-		return 0;
-	}
-	printk(KERN_INFO "%s: Promise caching controller, ",hwif->name);
-	switch(ident.type) {
-		case 0x43:	printk("DC4030VL-2, "); break;
-		case 0x41:	printk("DC4030VL-1, "); break;
-		case 0x40:	printk("DC4030VL, "); break;
-		default:
-			printk("unknown - type 0x%02x - please report!\n"
-			       ,ident.type);
-			printk("Please e-mail the following data to "
-			       "promise@pnd-pc.demon.co.uk along with\n"
-			       "a description of your card and drives:\n");
-			for (i=0; i < 0x90; i++) {
-				printk("%02x ", ((unsigned char *)&ident)[i]);
-				if ((i & 0x0f) == 0x0f) printk("\n");
-			}
-			return 0;
-	}
-	printk("%dKB cache, ",(int)ident.cache_mem);
-	switch(ident.irq) {
-            case 0x00: hwif->irq = 14; break;
-            case 0x01: hwif->irq = 12; break;
-            default:   hwif->irq = 15; break;
-	}
-	printk("on IRQ %d\n",hwif->irq);
-
-	/*
-	 * Once found and identified, we set up the next hwif in the array
-	 * (hwif2 = ide_hwifs[hwif->index+1]) with the same io ports, irq
-	 * and other settings as the main hwif. This gives us two "mated"
-	 * hwifs pointing to the Promise card.
-	 *
-	 * We also have to shift the default values for the remaining
-	 * interfaces "up by one" to make room for the second interface on the
-	 * same set of values.
-	 */
-
-	hwif->chipset	= hwif2->chipset = ide_pdc4030;
-	hwif->mate	= hwif2;
-	hwif2->mate	= hwif;
-	hwif2->channel	= 1;
-	hwif->selectproc = hwif2->selectproc = &promise_selectproc;
-	hwif->serialized = hwif2->serialized = 1;
-
-/* Shift the remaining interfaces down by one */
-	for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
-		ide_hwif_t *h = &ide_hwifs[i];
-
-#ifdef DEBUG
-		printk(KERN_DEBUG "Shifting i/f %d values to i/f %d\n",i-1,i);
-#endif
-		ide_init_hwif_ports(&h->hw, (h-1)->io_ports[IDE_DATA_OFFSET], 0, NULL);
-		memcpy(h->io_ports, h->hw.io_ports, sizeof(h->io_ports));
-		h->noprobe = (h-1)->noprobe;
-	}
-	ide_init_hwif_ports(&hwif2->hw, hwif->io_ports[IDE_DATA_OFFSET], 0, NULL);
-	memcpy(hwif2->io_ports, hwif->hw.io_ports, sizeof(hwif2->io_ports));
-	hwif2->irq = hwif->irq;
-	hwif2->hw.irq = hwif->hw.irq = hwif->irq;
-	for (i=0; i<2 ; i++) {
-		hwif->drives[i].io_32bit = 3;
-		hwif2->drives[i].io_32bit = 3;
-		hwif->drives[i].keep_settings = 1;
-		hwif2->drives[i].keep_settings = 1;
-		if (!ident.current_tm[i].cyl)
-			hwif->drives[i].noprobe = 1;
-		if (!ident.current_tm[i+2].cyl)
-			hwif2->drives[i].noprobe = 1;
-	}
-        return 1;
-}
-
-/*
- * detect_pdc4030()
- * Tests for the presence of a DC4030 Promise card on this interface
- * Returns: 1 if found, 0 if not found
- */
-int __init detect_pdc4030(ide_hwif_t *hwif)
-{
-	ide_drive_t *drive = &hwif->drives[0];
-
-	if (IDE_DATA_REG == 0) { /* Skip test for non-existent interface */
-		return 0;
-	}
-	OUT_BYTE(0xF3, IDE_SECTOR_REG);
-	OUT_BYTE(0x14, IDE_SELECT_REG);
-	OUT_BYTE(PROMISE_EXTENDED_COMMAND, IDE_COMMAND_REG);
-	
-	ide_delay_50ms();
-
-	if (IN_BYTE(IDE_ERROR_REG) == 'P' &&
-	    IN_BYTE(IDE_NSECTOR_REG) == 'T' &&
-	    IN_BYTE(IDE_SECTOR_REG) == 'I') {
-		return 1;
-	} else {
-		return 0;
-	}
-}
-
-void __init ide_probe_for_pdc4030(void)
-{
-	unsigned int	index;
-	ide_hwif_t	*hwif;
-
-	if (enable_promise_support == 0)
-		return;
-	for (index = 0; index < MAX_HWIFS; index++) {
-		hwif = &ide_hwifs[index];
-		if (hwif->chipset == ide_unknown && detect_pdc4030(hwif)) {
-			setup_pdc4030(hwif);
-		}
-	}
-}
-
-
-
-/*
- * promise_read_intr() is the handler for disk read/multread interrupts
- */
-static ide_startstop_t promise_read_intr (ide_drive_t *drive)
-{
-	byte stat;
-	int total_remaining;
-	unsigned int sectors_left, sectors_avail, nsect;
-	struct request *rq;
-
-	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-		return ide_error(drive, "promise_read_intr", stat);
-	}
-
-read_again:
-	do {
-		sectors_left = IN_BYTE(IDE_NSECTOR_REG);
-		IN_BYTE(IDE_SECTOR_REG);
-	} while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left);
-	rq = HWGROUP(drive)->rq;
-	sectors_avail = rq->nr_sectors - sectors_left;
-	if (!sectors_avail)
-		goto read_again;
-
-read_next:
-	rq = HWGROUP(drive)->rq;
-	nsect = rq->current_nr_sectors;
-	if (nsect > sectors_avail)
-		nsect = sectors_avail;
-	sectors_avail -= nsect;
-	ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
-#ifdef DEBUG_READ
-	printk(KERN_DEBUG "%s:  promise_read: sectors(%ld-%ld), "
-	       "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector,
-	       rq->sector+nsect-1, (unsigned long) rq->buffer,
-	       rq->nr_sectors-nsect);
-#endif
-	rq->sector += nsect;
-	rq->buffer += nsect<<9;
-	rq->errors = 0;
-	rq->nr_sectors -= nsect;
-	total_remaining = rq->nr_sectors;
-	if ((rq->current_nr_sectors -= nsect) <= 0) {
-		ide_end_request(1, HWGROUP(drive));
-	}
-/*
- * Now the data has been read in, do the following:
- * 
- * if there are still sectors left in the request, 
- *   if we know there are still sectors available from the interface,
- *     go back and read the next bit of the request.
- *   else if DRQ is asserted, there are more sectors available, so
- *     go back and find out how many, then read them in.
- *   else if BUSY is asserted, we are going to get an interrupt, so
- *     set the handler for the interrupt and just return
- */
-	if (total_remaining > 0) {
-		if (sectors_avail)
-			goto read_next;
-		stat = GET_STAT();
-		if (stat & DRQ_STAT)
-			goto read_again;
-		if (stat & BUSY_STAT) {
-			ide_set_handler (drive, &promise_read_intr, WAIT_CMD, NULL);
-#ifdef DEBUG_READ
-			printk(KERN_DEBUG "%s: promise_read: waiting for"
-			       "interrupt\n", drive->name);
-#endif 
-			return ide_started;
-		}
-		printk(KERN_ERR "%s: Eeek! promise_read_intr: sectors left "
-		       "!DRQ !BUSY\n", drive->name);
-		return ide_error(drive, "promise read intr", stat);
-	}
-	return ide_stopped;
-}
-
-/*
- * promise_complete_pollfunc()
- * This is the polling function for waiting (nicely!) until drive stops
- * being busy. It is invoked at the end of a write, after the previous poll
- * has finished.
- *
- * Once not busy, the end request is called.
- */
-static ide_startstop_t promise_complete_pollfunc(ide_drive_t *drive)
-{
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	struct request *rq = hwgroup->rq;
-	int i;
-
-	if (GET_STAT() & BUSY_STAT) {
-		if (time_before(jiffies, hwgroup->poll_timeout)) {
-			ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
-			return ide_started; /* continue polling... */
-		}
-		hwgroup->poll_timeout = 0;
-		printk(KERN_ERR "%s: completion timeout - still busy!\n",
-		       drive->name);
-		return ide_error(drive, "busy timeout", GET_STAT());
-	}
-
-	hwgroup->poll_timeout = 0;
-#ifdef DEBUG_WRITE
-	printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name);
-#endif
-	for (i = rq->nr_sectors; i > 0; ) {
-		i -= rq->current_nr_sectors;
-		ide_end_request(1, hwgroup);
-	}
-	return ide_stopped;
-}
-
-/*
- * promise_write_pollfunc() is the handler for disk write completion polling.
- */
-static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive)
-{
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-
-	if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
-		if (time_before(jiffies, hwgroup->poll_timeout)) {
-			ide_set_handler (drive, &promise_write_pollfunc, HZ/100, NULL);
-			return ide_started; /* continue polling... */
-		}
-		hwgroup->poll_timeout = 0;
-		printk(KERN_ERR "%s: write timed-out!\n",drive->name);
-		return ide_error (drive, "write timeout", GET_STAT());
-	}
-
-	/*
-	 * Now write out last 4 sectors and poll for not BUSY
-	 */
-	ide_multwrite(drive, 4);
-	hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
-	ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
-#ifdef DEBUG_WRITE
-	printk(KERN_DEBUG "%s: Done last 4 sectors - status = %02x\n",
-		drive->name, GET_STAT());
-#endif
-	return ide_started;
-}
-
-/*
- * promise_write() transfers a block of one or more sectors of data to a
- * drive as part of a disk write operation. All but 4 sectors are transfered
- * in the first attempt, then the interface is polled (nicely!) for completion
- * before the final 4 sectors are transfered. There is no interrupt generated
- * on writes (at least on the DC4030VL-2), we just have to poll for NOT BUSY.
- */
-static ide_startstop_t promise_write (ide_drive_t *drive)
-{
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	struct request *rq = &hwgroup->wrq;
-
-#ifdef DEBUG_WRITE
-	printk(KERN_DEBUG "%s: promise_write: sectors(%ld-%ld), "
-	       "buffer=0x%08x\n", drive->name, rq->sector,
-	       rq->sector + rq->nr_sectors - 1, (unsigned int)rq->buffer);
-#endif
-
-	/*
-	 * If there are more than 4 sectors to transfer, do n-4 then go into
-	 * the polling strategy as defined above.
-	 */
-	if (rq->nr_sectors > 4) {
-		if (ide_multwrite(drive, rq->nr_sectors - 4))
-			return ide_stopped;
-		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
-		ide_set_handler (drive, &promise_write_pollfunc, HZ/100, NULL);
-		return ide_started;
-	} else {
-	/*
-	 * There are 4 or fewer sectors to transfer, do them all in one go
-	 * and wait for NOT BUSY.
-	 */
-		if (ide_multwrite(drive, rq->nr_sectors))
-			return ide_stopped;
-		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
-		ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
-#ifdef DEBUG_WRITE
-		printk(KERN_DEBUG "%s: promise_write: <= 4 sectors, "
-			"status = %02x\n", drive->name, GET_STAT());
-#endif
-		return ide_started;
-	}
-}
-
-/*
- * do_pdc4030_io() is called from do_rw_disk, having had the block number
- * already set up. It issues a READ or WRITE command to the Promise
- * controller, assuming LBA has been used to set up the block number.
- */
-ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq)
-{
-	unsigned long timeout;
-	byte stat;
-
-	if (rq->cmd == READ) {
-		OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG);
-/*
- * The card's behaviour is odd at this point. If the data is
- * available, DRQ will be true, and no interrupt will be
- * generated by the card. If this is the case, we need to call the 
- * "interrupt" handler (promise_read_intr) directly. Otherwise, if
- * an interrupt is going to occur, bit0 of the SELECT register will
- * be high, so we can set the handler the just return and be interrupted.
- * If neither of these is the case, we wait for up to 50ms (badly I'm
- * afraid!) until one of them is.
- */
-		timeout = jiffies + HZ/20; /* 50ms wait */
-		do {
-			stat=GET_STAT();
-			if (stat & DRQ_STAT) {
-				udelay(1);
-				return promise_read_intr(drive);
-			}
-			if (IN_BYTE(IDE_SELECT_REG) & 0x01) {
-#ifdef DEBUG_READ
-				printk(KERN_DEBUG "%s: read: waiting for "
-				                  "interrupt\n", drive->name);
-#endif
-				ide_set_handler(drive, &promise_read_intr, WAIT_CMD, NULL);
-				return ide_started;
-			}
-			udelay(1);
-		} while (time_before(jiffies, timeout));
-
-		printk(KERN_ERR "%s: reading: No DRQ and not waiting - Odd!\n",
-			drive->name);
-		return ide_stopped;
-	} else if (rq->cmd == WRITE) {
-		ide_startstop_t startstop;
-		OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
-		if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
-			printk(KERN_ERR "%s: no DRQ after issuing "
-			       "PROMISE_WRITE\n", drive->name);
-			return startstop;
-	    	}
-		if (!drive->unmask)
-			__cli();	/* local CPU only */
-		HWGROUP(drive)->wrq = *rq; /* scratchpad */
-		return promise_write(drive);
-
-	} else {
-		printk("KERN_WARNING %s: bad command: %d\n",
-		       drive->name, rq->cmd);
-		ide_end_request(0, HWGROUP(drive));
-		return ide_stopped;
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/pdc4030.h linux/drivers/block/pdc4030.h
--- v2.3.51/linux/drivers/block/pdc4030.h	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/pdc4030.h	Wed Dec 31 16:00:00 1969
@@ -1,44 +0,0 @@
-/*
- *  linux/drivers/ide/pdc4030.h
- *
- *  Copyright (C) 1995-1998  Linus Torvalds & authors
- */
-
-/*
- * Principal author: Peter Denison <peterd@pnd-pc.demon.co.uk>
- */
-
-#ifndef IDE_PROMISE_H
-#define IDE_PROMISE_H
-
-#define	PROMISE_EXTENDED_COMMAND	0xF0
-#define	PROMISE_READ			0xF2
-#define	PROMISE_WRITE			0xF3
-/* Extended commands - main command code = 0xf0 */
-#define	PROMISE_GET_CONFIG		0x10
-#define	PROMISE_IDENTIFY		0x20
-
-struct translation_mode {
-	u16	cyl;
-	u8	head;
-	u8	sect;
-};
-
-struct dc_ident {
-	u8	type;
-	u8	unknown1;
-	u8	hw_revision;
-	u8	firmware_major;
-	u8	firmware_minor;
-	u8	bios_address;
-	u8	irq;
-	u8	unknown2;
-	u16	cache_mem;
-	u16	unknown3;
-	u8	id[2];
-	u16	info;
-	struct translation_mode current_tm[4];
-	u8	pad[SECTOR_WORDS*4 - 32];
-};
-
-#endif IDE_PROMISE_H
diff -u --recursive --new-file v2.3.51/linux/drivers/block/piix.c linux/drivers/block/piix.c
--- v2.3.51/linux/drivers/block/piix.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/block/piix.c	Wed Dec 31 16:00:00 1969
@@ -1,434 +0,0 @@
-/*
- * linux/drivers/block/piix.c		Version 0.30	Feb. 26, 2000
- *
- *  Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
- *  Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
- *  May be copied or modified under the terms of the GNU General Public License
- *
- *  PIO mode setting function for Intel chipsets.  
- *  For use instead of BIOS settings.
- *
- * 40-41
- * 42-43
- * 
- *                 41
- *                 43
- *
- * | PIO 0       | c0 | 80 | 0 | 	piix_tune_drive(drive, 0);
- * | PIO 2 | SW2 | d0 | 90 | 4 | 	piix_tune_drive(drive, 2);
- * | PIO 3 | MW1 | e1 | a1 | 9 | 	piix_tune_drive(drive, 3);
- * | PIO 4 | MW2 | e3 | a3 | b | 	piix_tune_drive(drive, 4);
- * 
- * sitre = word40 & 0x4000; primary
- * sitre = word42 & 0x4000; secondary
- *
- * 44 8421|8421    hdd|hdb
- * 
- * 48 8421         hdd|hdc|hdb|hda udma enabled
- *
- *    0001         hda
- *    0010         hdb
- *    0100         hdc
- *    1000         hdd
- *
- * 4a 84|21        hdb|hda
- * 4b 84|21        hdd|hdc
- *
- *    ata-33/82371AB
- *    ata-33/82371EB
- *    ata-33/82801AB            ata-66/82801AA
- *    00|00 udma 0              00|00 reserved
- *    01|01 udma 1              01|01 udma 3
- *    10|10 udma 2              10|10 udma 4
- *    11|11 reserved            11|11 reserved
- *
- * 54 8421|8421    ata66 drive|ata66 enable
- *
- * pci_read_config_word(HWIF(drive)->pci_dev, 0x40, &reg40);
- * pci_read_config_word(HWIF(drive)->pci_dev, 0x42, &reg42);
- * pci_read_config_word(HWIF(drive)->pci_dev, 0x44, &reg44);
- * pci_read_config_word(HWIF(drive)->pci_dev, 0x48, &reg48);
- * pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, &reg4a);
- * pci_read_config_word(HWIF(drive)->pci_dev, 0x54, &reg54);
- *
- * 00:1f.1 IDE interface: Intel Corporation:
- *                          Unknown device 2411 (rev 01) (prog-if 80 [Master])
- *         Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop-
- *                          ParErr- Stepping- SERR- FastB2B-
- *         Status: Cap- 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
- *                          <TAbort- <MAbort- >SERR- <PERR-
- *         Latency: 0 set
- *         Region 4: I/O ports at ffa0
- * 00: 86 80 11 24 05 00 80 02 01 80 01 01 00 00 00 00
- * 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 20: a1 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 40: 07 a3 03 a3 00 00 00 00 05 00 02 02 00 00 00 00
- * 50: 00 00 00 00 11 04 00 00 00 00 00 00 00 00 00 00
- * 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * f0: 00 00 00 00 00 00 00 00 3a 0f 00 00 00 00 00 00
- *
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-#include <linux/delay.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-#define PIIX_DEBUG_DRIVE_INFO		0
-
-#define DISPLAY_PIIX_TIMINGS
-
-#if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int piix_get_info(char *, char **, off_t, int);
-extern int (*piix_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-extern char *ide_media_verbose(ide_drive_t *);
-static struct pci_dev *bmide_dev;
-
-static int piix_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-	u32 bibma = bmide_dev->resource[4].start;
-        u16 reg40 = 0, psitre = 0, reg42 = 0, ssitre = 0;
-	u8  c0 = 0, c1 = 0;
-	u8  reg44 = 0, reg48 = 0, reg4a = 0, reg4b = 0, reg54 = 0;
-
-	pci_read_config_word(bmide_dev, 0x40, &reg40);
-	pci_read_config_word(bmide_dev, 0x42, &reg42);
-	pci_read_config_byte(bmide_dev, 0x44, &reg44);
-	pci_read_config_byte(bmide_dev, 0x48, &reg48);
-	pci_read_config_byte(bmide_dev, 0x4a, &reg4a);
-	pci_read_config_byte(bmide_dev, 0x4b, &reg4b);
-	pci_read_config_byte(bmide_dev, 0x54, &reg54);
-
-	psitre = (reg40 & 0x4000) ? 1 : 0;
-	ssitre = (reg42 & 0x4000) ? 1 : 0;
-
-        /*
-         * at that point bibma+0x2 et bibma+0xa are byte registers
-         * to investigate:
-         */
-	c0 = inb_p((unsigned short)bibma + 0x02);
-	c1 = inb_p((unsigned short)bibma + 0x0a);
-
-	switch(bmide_dev->device) {
-		case PCI_DEVICE_ID_INTEL_82372FB_1:
-		case PCI_DEVICE_ID_INTEL_82801AA_1:
-			p += sprintf(p, "\n                                Intel PIIX4 Ultra 66 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_INTEL_82801AB_1:
-		case PCI_DEVICE_ID_INTEL_82371AB:
-			p += sprintf(p, "\n                                Intel PIIX4 Ultra 33 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_INTEL_82371SB_1:
-			p += sprintf(p, "\n                                Intel PIIX3 Chipset.\n");
-			break;
-		case PCI_DEVICE_ID_INTEL_82371FB_1:
-		case PCI_DEVICE_ID_INTEL_82371FB_0:
-		default:
-			p += sprintf(p, "\n                                Intel PIIX Chipset.\n");
-			break;
-	}
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	p += sprintf(p, "                %sabled                         %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
-			(c0&0x20) ? "yes" : "no ",
-			(c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ",
-			(c1&0x40) ? "yes" : "no " );
-	p += sprintf(p, "UDMA enabled:   %s              %s             %s               %s\n",
-			(reg48&0x01) ? "yes" : "no ",
-			(reg48&0x02) ? "yes" : "no ",
-			(reg48&0x04) ? "yes" : "no ",
-			(reg48&0x08) ? "yes" : "no " );
-	p += sprintf(p, "UDMA enabled:   %s                %s               %s                 %s\n",
-			((reg54&0x11) && (reg4a&0x02)) ? "4" :
-			((reg54&0x11) && (reg4a&0x01)) ? "3" :
-			(reg4a&0x02) ? "2" :
-			(reg4a&0x01) ? "1" :
-			(reg4a&0x00) ? "0" : "X",
-			((reg54&0x22) && (reg4a&0x20)) ? "4" :
-			((reg54&0x22) && (reg4a&0x10)) ? "3" :
-			(reg4a&0x20) ? "2" :
-			(reg4a&0x10) ? "1" :
-			(reg4a&0x00) ? "0" : "X",
-			((reg54&0x44) && (reg4b&0x02)) ? "4" :
-			((reg54&0x44) && (reg4b&0x01)) ? "3" :
-			(reg4b&0x02) ? "2" :
-			(reg4b&0x01) ? "1" :
-			(reg4b&0x00) ? "0" : "X",
-			((reg54&0x88) && (reg4b&0x20)) ? "4" :
-			((reg54&0x88) && (reg4b&0x10)) ? "3" :
-			(reg4b&0x20) ? "2" :
-			(reg4b&0x10) ? "1" :
-			(reg4b&0x00) ? "0" : "X");
-
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
-/*
- *	FIXME.... Add configuration junk data....blah blah......
- */
-
-	return p-buffer;	 /* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-/*
- *  Used to set Fifo configuration via kernel command line:
- */
-
-byte piix_proc = 0;
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-/*
- *
- */
-static byte piix_dma_2_pio (byte xfer_rate) {
-	switch(xfer_rate) {
-		case XFER_UDMA_4:
-		case XFER_UDMA_3:
-		case XFER_UDMA_2:
-		case XFER_UDMA_1:
-		case XFER_UDMA_0:
-		case XFER_MW_DMA_2:
-		case XFER_PIO_4:
-			return 4;
-		case XFER_MW_DMA_1:
-		case XFER_PIO_3:
-			return 3;
-		case XFER_SW_DMA_2:
-		case XFER_PIO_2:
-			return 2;
-		case XFER_MW_DMA_0:
-		case XFER_SW_DMA_1:
-		case XFER_SW_DMA_0:
-		case XFER_PIO_1:
-		case XFER_PIO_0:
-		case XFER_PIO_SLOW:
-		default:
-			return 0;
-	}
-}
-
-/*
- *  Based on settings done by AMI BIOS
- *  (might be usefull if drive is not registered in CMOS for any reason).
- */
-static void piix_tune_drive (ide_drive_t *drive, byte pio)
-{
-	unsigned long flags;
-	u16 master_data;
-	byte slave_data;
-	int is_slave		= (&HWIF(drive)->drives[1] == drive);
-	int master_port		= HWIF(drive)->index ? 0x42 : 0x40;
-	int slave_port		= 0x44;
-				 /* ISP  RTC */
-	byte timings[][2]	= { { 0, 0 },
-				    { 0, 0 },
-				    { 1, 0 },
-				    { 2, 1 },
-				    { 2, 3 }, };
-
-	pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
-	pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data);
-	if (is_slave) {
-		master_data = master_data | 0x4000;
-		if (pio > 1)
-			/* enable PPE, IE and TIME */
-			master_data = master_data | 0x0070;
-		pci_read_config_byte(HWIF(drive)->pci_dev, slave_port, &slave_data);
-		slave_data = slave_data & (HWIF(drive)->index ? 0x0f : 0xf0);
-		slave_data = slave_data | ((timings[pio][0] << 2) | (timings[pio][1]
-					   << (HWIF(drive)->index ? 4 : 0)));
-	} else {
-		master_data = master_data & 0xccf8;
-		if (pio > 1)
-			/* enable PPE, IE and TIME */
-			master_data = master_data | 0x0007;
-		master_data = master_data | (timings[pio][0] << 12) |
-			      (timings[pio][1] << 8);
-	}
-	save_flags(flags);
-	cli();
-	pci_write_config_word(HWIF(drive)->pci_dev, master_port, master_data);
-	if (is_slave)
-		pci_write_config_byte(HWIF(drive)->pci_dev, slave_port, slave_data);
-	restore_flags(flags);
-}
-
-static int piix_config_drive_for_dma (ide_drive_t *drive)
-{
-	struct hd_driveid *id	= drive->id;
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-
-	int			sitre;
-	short			reg4042, reg44, reg48, reg4a, reg54;
-	byte			speed;
-
-	byte maslave		= hwif->channel ? 0x42 : 0x40;
-	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
-	int ultra66		= ((dev->device == PCI_DEVICE_ID_INTEL_82801AA_1) ||
-				   (dev->device == PCI_DEVICE_ID_INTEL_82372FB_1)) ? 1 : 0;
-	int ultra		= ((ultra66) ||
-				   (dev->device == PCI_DEVICE_ID_INTEL_82371AB) ||
-				   (dev->device == PCI_DEVICE_ID_INTEL_82801AB_1)) ? 1 : 0;
-	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-	int a_speed		= 2 << (drive_number * 4);
-	int u_flag		= 1 << drive_number;
-	int v_flag		= 0x10 << drive_number;
-	int u_speed		= 0;
-
-	pci_read_config_word(dev, maslave, &reg4042);
-	sitre = (reg4042 & 0x4000) ? 1 : 0;
-	pci_read_config_word(dev, 0x44, &reg44);
-	pci_read_config_word(dev, 0x48, &reg48);
-	pci_read_config_word(dev, 0x4a, &reg4a);
-	pci_read_config_word(dev, 0x54, &reg54);
-
-	if ((id->dma_ultra & 0x0010) && (ultra)) {
-		u_speed = 2 << (drive_number * 4);
-		speed = ((udma_66) && (ultra66)) ? XFER_UDMA_4 : XFER_UDMA_2;
-	} else if ((id->dma_ultra & 0x0008) && (ultra)) {
-		u_speed = 1 << (drive_number * 4);
-		speed = ((udma_66) && (ultra66)) ? XFER_UDMA_3 : XFER_UDMA_1;
-	} else if ((id->dma_ultra & 0x0004) && (ultra)) {
-		u_speed = 2 << (drive_number * 4);
-		speed = XFER_UDMA_2;
-	} else if ((id->dma_ultra & 0x0002) && (ultra)) {
-		u_speed = 1 << (drive_number * 4);
-		speed = XFER_UDMA_1;
-	} else if ((id->dma_ultra & 0x0001) && (ultra)) {
-		u_speed = 0 << (drive_number * 4);
-		speed = XFER_UDMA_0;
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_1word & 0x0004) {
-		speed = XFER_SW_DMA_2;
-        } else {
-		speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
-	}
-
-	if (speed >= XFER_UDMA_0) {
-		if (!(reg48 & u_flag))
-			pci_write_config_word(dev, 0x48, reg48|u_flag);
-		if (!(reg4a & u_speed)) {
-			pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
-			pci_write_config_word(dev, 0x4a, reg4a|u_speed);
-		}
-		if (speed > XFER_UDMA_2) {
-			if (!(reg54 & v_flag)) {
-				pci_write_config_word(dev, 0x54, reg54|v_flag);
-			}
-		} else {
-			pci_write_config_word(dev, 0x54, reg54 & ~v_flag);
-		}
-	}
-
-	if (speed < XFER_UDMA_0) {
-		if (reg48 & u_flag)
-			pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
-		if (reg4a & a_speed)
-			pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
-		if (reg54 & v_flag)
-			pci_write_config_word(dev, 0x54, reg54 & ~v_flag);
-	}
-
-	piix_tune_drive(drive, piix_dma_2_pio(speed));
-
-	(void) ide_config_drive_speed(drive, speed);
-
-#if PIIX_DEBUG_DRIVE_INFO
-	printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number);
-#endif /* PIIX_DEBUG_DRIVE_INFO */
-
-	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-}
-
-static int piix_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch (func) {
-		case ide_dma_check:
-			 return ide_dmaproc((ide_dma_action_t) piix_config_drive_for_dma(drive), drive);
-		default :
-			break;
-	}
-	/* Other cases are done by generic IDE-DMA code. */
-	return ide_dmaproc(func, drive);
-}
-
-unsigned int __init pci_init_piix (struct pci_dev *dev, const char *name)
-{
-#if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS)
-	piix_proc = 1;
-	bmide_dev = dev;
-	piix_display_info = &piix_get_info;
-#endif /* DISPLAY_PIIX_TIMINGS && CONFIG_PROC_FS */
-	return 0;
-}
-
-/*
- * Sheesh, someone at Intel needs to go read the ATA-4/5 T13 standards.
- * It does not specify device detection, but channel!!!
- * You determine later if bit 13 of word93 is set...
- */
-unsigned int __init ata66_piix (ide_hwif_t *hwif)
-{
-	byte reg54h = 0, reg55h = 0, ata66 = 0;
-	byte mask = hwif->channel ? 0x0c : 0x03;
-
-	pci_read_config_byte(hwif->pci_dev, 0x54, &reg54h);
-	pci_read_config_byte(hwif->pci_dev, 0x55, &reg55h);
-	ata66 = (reg54h & mask) ? 1 : 0;
-
-	return ata66;
-}
-
-void __init ide_init_piix (ide_hwif_t *hwif)
-{
-	hwif->tuneproc = &piix_tune_drive;
-
-	if (hwif->dma_base) {
-#ifdef CONFIG_PIIX_TUNING
-		hwif->dmaproc = &piix_dmaproc;
-#endif /* CONFIG_PIIX_TUNING */
-		hwif->drives[0].autotune = 0;
-		hwif->drives[1].autotune = 0;
-	} else {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-	if (!hwif->irq)
-		hwif->irq = hwif->channel ? 15 : 14;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/q40ide.c linux/drivers/block/q40ide.c
--- v2.3.51/linux/drivers/block/q40ide.c	Mon Dec 20 18:48:21 1999
+++ linux/drivers/block/q40ide.c	Wed Dec 31 16:00:00 1969
@@ -1,109 +0,0 @@
-/*
- *  linux/drivers/block/q40ide.c -- Q40 I/O port IDE Driver
- *
- *     original file created 12 Jul 1997 by Geert Uytterhoeven
- *
- *  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.
- *
- * RZ:
- *  almost identical with pcide.c, maybe we can merge it later. 
- *  Differences:
- *       max 2 HWIFS for now
- *       translate portaddresses to q40 native addresses (not yet...) instead rely on in/out[bw]
- *         address translation
- *
- */
-
-#include <linux/types.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/ide.h>
-
-    /*
-     *  Bases of the IDE interfaces
-     */
-
-#define PCIDE_NUM_HWIFS	2
-
-#define PCIDE_BASE1	0x1f0
-#define PCIDE_BASE2	0x170
-#define PCIDE_BASE3	0x1e8
-#define PCIDE_BASE4	0x168
-#define PCIDE_BASE5	0x1e0
-#define PCIDE_BASE6	0x160
-
-static const q40ide_ioreg_t pcide_bases[PCIDE_NUM_HWIFS] = {
-    PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4  , PCIDE_BASE5,
-    PCIDE_BASE6 */
-};
-
-
-    /*
-     *  Offsets from one of the above bases
-     */
-
-#undef HD_DATA
-#define HD_DATA  0x1f0
-
-#define PCIDE_REG(x)	((q40ide_ioreg_t)(HD_##x-PCIDE_BASE1))
-
-static const int pcide_offsets[IDE_NR_PORTS] = {
-    PCIDE_REG(DATA), PCIDE_REG(ERROR), PCIDE_REG(NSECTOR), PCIDE_REG(SECTOR),
-    PCIDE_REG(LCYL), PCIDE_REG(HCYL), PCIDE_REG(CURRENT), PCIDE_REG(STATUS),
-    PCIDE_REG(CMD)
-};
-
-int q40ide_default_irq(q40ide_ioreg_t base)
-{
-           switch (base) { 
-	            case 0x1f0: return 14;
-		    case 0x170: return 15;
-		    case 0x1e8: return 11;
-		    default:
-			return 0;
-	   }
-}
-
-void q40_ide_init_hwif_ports (q40ide_ioreg_t *p, q40ide_ioreg_t base, int *irq)
-{
-	q40ide_ioreg_t port = base;
-	int i = 8;
-
-	while (i--)
-		*p++ = port++;
-	*p++ = base + 0x206;
-	if (irq != NULL)
-		*irq = 0;
-}
-
-
-    /*
-     *  Probe for PC IDE interfaces
-     */
-
-int q40ide_probe_hwif(int index, ide_hwif_t *hwif)
-{
-    static int pcide_index[PCIDE_NUM_HWIFS] = { 0, };
-    int i;
-
-    if (!MACH_IS_Q40)
-      return 0;
-
-    for (i = 0; i < PCIDE_NUM_HWIFS; i++) {
-	if (!pcide_index[i]) {
-	  /*printk("ide%d: Q40 IDE interface\n", index);*/
-	    pcide_index[i] = index+1;
-	}
-	if (pcide_index[i] == index+1) {
-	    ide_setup_ports(hwif,(ide_ioreg_t) pcide_bases[i], pcide_offsets, 0, /*q40_ack_intr???*/ NULL);
-	    hwif->irq = ide_default_irq((ide_ioreg_t)pcide_bases[i]); /*q40_ide_irq[i];  */ /* 14 */
-	    return 1;
-	}
-    }
-    return 0;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/qd6580.c linux/drivers/block/qd6580.c
--- v2.3.51/linux/drivers/block/qd6580.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/qd6580.c	Wed Dec 31 16:00:00 1969
@@ -1,71 +0,0 @@
-/*
- *  linux/drivers/block/qd6580.c		Version 0.02	Feb 09, 1996
- *
- *  Copyright (C) 1996  Linus Torvalds & author (see below)
- */
-
-/*
- * QDI QD6580 EIDE controller fast support by Colten Edwards.
- * No net access, but (maybe) can be reached at pje120@cs.usask.ca
- */
-
-#undef REALLY_SLOW_IO           /* most systems can safely undef this */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-/*
- * Register 0xb3 looks like:
- *	0x4f is fast		mode3 ?
- *	0x3f is medium		mode2 ?
- *	0x2f is slower		mode1 ?
- *	0x1f is slower yet	mode0 ?
- *	0x0f ???		???
- *
- * Don't know whether this sets BOTH drives, or just the first drive.
- * Don't know if there is a separate setting for the second drive.
- *
- * Feel free to patch this if you have one of these beasts
- * and can work out the answers!
- *
- * I/O ports are 0xb0 0xb2 and 0xb3
- *
- * More research on qd6580 being done by willmore@cig.mot.com (David)
- *	-- this is apparently a *dual* IDE interface
- */
-
-static void tune_qd6580 (ide_drive_t *drive, byte pio)
-{
-	unsigned long flags;
-
-	pio = ide_get_best_pio_mode(drive, pio, 3, NULL);
-
-	save_flags(flags);	/* all CPUs */
-	cli();			/* all CPUs */
-	outb_p(0x8d,0xb0);
-	outb_p(0x0 ,0xb2);
-	outb_p(((pio+1)<<4)|0x0f,0xb3);
-	inb(0x3f6);
-	restore_flags(flags);	/* all CPUs */
-}
-
-void __init init_qd6580 (void)
-{
-	ide_hwifs[0].chipset = ide_qd6580;
-	ide_hwifs[1].chipset = ide_qd6580;
-	ide_hwifs[0].tuneproc = &tune_qd6580;
-	ide_hwifs[0].mate = &ide_hwifs[1];
-	ide_hwifs[1].mate = &ide_hwifs[0];
-	ide_hwifs[1].channel = 1;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/rapide.c linux/drivers/block/rapide.c
--- v2.3.51/linux/drivers/block/rapide.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/rapide.c	Wed Dec 31 16:00:00 1969
@@ -1,92 +0,0 @@
-/*
- * linux/drivers/block/rapide.c
- *
- * Copyright (c) 1996-1998 Russell King.
- *
- * Changelog:
- *  08-06-1996	RMK	Created
- *  13-04-1998	RMK	Added manufacturer and product IDs
- */
-
-#include <linux/module.h>
-#include <linux/malloc.h>
-#include <linux/blkdev.h>
-#include <linux/errno.h>
-#include <linux/ide.h>
-
-#include <asm/ecard.h>
-
-static const card_ids __init rapide_cids[] = {
-	{ MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
-	{ 0xffff, 0xffff }
-};
-
-static struct expansion_card *ec[MAX_ECARDS];
-static int result[MAX_ECARDS];
-
-static inline int rapide_register(struct expansion_card *ec)
-{
-	unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
-	hw_regs_t hw;
-
-	int i;
-
-	memset(&hw, 0, sizeof(hw));
-
-	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
-		hw.io_ports[i] = (ide_ioreg_t)port;
-		port += 1 << 4;
-	}
-	hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206;
-	hw.irq = ec->irq;
-
-	return ide_register_hw(&hw, NULL);
-}
-
-int __init rapide_init(void)
-{
-	int i;
-
-	for (i = 0; i < MAX_ECARDS; i++)
-		ec[i] = NULL;
-
-	ecard_startfind();
-
-	for (i = 0; ; i++) {
-		if ((ec[i] = ecard_find(0, rapide_cids)) == NULL)
-			break;
-
-		ecard_claim(ec[i]);
-		result[i] = rapide_register(ec[i]);
-	}
-	for (i = 0; i < MAX_ECARDS; i++)
-		if (ec[i] && result[i] < 0) {
-			ecard_release(ec[i]);
-			ec[i] = NULL;
-	}
-	return 0;
-}
-
-#ifdef MODULE
-
-int init_module (void)
-{
-	return rapide_init();
-}
-
-void cleanup_module (void)
-{
-	int i;
-
-	for (i = 0; i < MAX_ECARDS; i++)
-		if (ec[i]) {
-			unsigned long port;
-			port = ecard_address(ec[i], ECARD_MEMC, 0);
-
-			ide_unregister_port(port, ec[i]->irq, 16);
-			ecard_release(ec[i]);
-			ec[i] = NULL;
-		}
-}
-#endif
-
diff -u --recursive --new-file v2.3.51/linux/drivers/block/rz1000.c linux/drivers/block/rz1000.c
--- v2.3.51/linux/drivers/block/rz1000.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/rz1000.c	Wed Dec 31 16:00:00 1969
@@ -1,96 +0,0 @@
-/*
- *  linux/drivers/block/rz1000.c		Version 0.05	December 8, 1997
- *
- *  Copyright (C) 1995-1998  Linus Torvalds & author (see below)
- */
-
-/*
- *  Principal Author:  mlord@pobox.com (Mark Lord)
- *
- *  See linux/MAINTAINERS for address of current maintainer.
- *
- *  This file provides support for disabling the buggy read-ahead
- *  mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
- *
- *  Dunno if this fixes both ports, or only the primary port (?).
- */
-
-#undef REALLY_SLOW_IO		/* most systems can safely undef this */
-
-#include <linux/config.h> /* for CONFIG_BLK_DEV_IDEPCI */
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/pci.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#ifdef CONFIG_BLK_DEV_IDEPCI
-
-void __init ide_init_rz1000 (ide_hwif_t *hwif)	/* called from ide-pci.c */
-{
-	unsigned short reg;
-	struct pci_dev *dev = hwif->pci_dev;
-
-	hwif->chipset = ide_rz1000;
-	if (!pci_read_config_word (dev, 0x40, &reg)
-	 && !pci_write_config_word(dev, 0x40, reg & 0xdfff))
-	{
-		printk("%s: disabled chipset read-ahead (buggy RZ1000/RZ1001)\n", hwif->name);
-	} else {
-		hwif->serialized = 1;
-		hwif->drives[0].no_unmask = 1;
-		hwif->drives[1].no_unmask = 1;
-		printk("%s: serialized, disabled unmasking (buggy RZ1000/RZ1001)\n", hwif->name);
-	}
-}
-
-#else
-
-static void __init init_rz1000 (struct pci_dev *dev, const char *name)
-{
-	unsigned short reg, h;
-
-	if (!pci_read_config_word (dev, PCI_COMMAND, &reg) && !(reg & PCI_COMMAND_IO)) {
-		printk("%s: buggy IDE controller disabled (BIOS)\n", name);
-		return;
-	}
-	if (!pci_read_config_word (dev, 0x40, &reg)
-	 && !pci_write_config_word(dev, 0x40, reg & 0xdfff))
-	{
-		printk("IDE: disabled chipset read-ahead (buggy %s)\n", name);
-	} else {
-		for (h = 0; h < MAX_HWIFS; ++h) {
-			ide_hwif_t *hwif = &ide_hwifs[h];
-			if ((hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0 || hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
-			 && (hwif->chipset == ide_unknown || hwif->chipset == ide_generic))
-			{
-				hwif->chipset = ide_rz1000;
-				hwif->serialized = 1;
-				hwif->drives[0].no_unmask = 1;
-				hwif->drives[1].no_unmask = 1;
-				if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
-					hwif->channel = 1;
-				printk("%s: serialized, disabled unmasking (buggy %s)\n", hwif->name, name);
-			}
-		}
-	}
-}
-
-void __init ide_probe_for_rz100x (void)	/* called from ide.c */
-{
-	struct pci_dev *dev = NULL;
-
-	while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, dev))!=NULL)
-		init_rz1000 (dev, "RZ1000");
-	while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, dev))!=NULL)
-		init_rz1000 (dev, "RZ1001");
-}
-
-#endif CONFIG_BLK_DEV_IDEPCI
diff -u --recursive --new-file v2.3.51/linux/drivers/block/sis5513.c linux/drivers/block/sis5513.c
--- v2.3.51/linux/drivers/block/sis5513.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/sis5513.c	Wed Dec 31 16:00:00 1969
@@ -1,549 +0,0 @@
-/*
- * linux/drivers/block/sis5513.c		Version 0.09	Feb. 10, 2000
- *
- * Copyright (C) 1999-2000	Andre Hedrick (andre@suse.com)
- * May be copied or modified under the terms of the GNU General Public License
- *
- * Thanks to SIS Taiwan for direct support and hardware.
- * Tested and designed on the SiS620/5513 chipset.
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "ide_modes.h"
-
-#define DISPLAY_SIS_TIMINGS
-#define SIS5513_DEBUG_DRIVE_INFO	0
-
-static struct pci_dev *host_dev = NULL;
-
-#define arraysize(x)			(sizeof(x)/sizeof(*(x)))
-
-#define SIS5513_FLAG_ATA_00		0x00000000
-#define SIS5513_FLAG_ATA_16		0x00000001
-#define SIS5513_FLAG_ATA_33		0x00000002
-#define SIS5513_FLAG_ATA_66		0x00000004
-#define SIS5513_FLAG_LATENCY		0x00000010
-
-static const struct {
-	const char *name;
-	unsigned short host_id;
-	unsigned int flags;
-} SiSHostChipInfo[] = {
-	{ "SiS530",	PCI_DEVICE_ID_SI_530,	SIS5513_FLAG_ATA_66, },
-	{ "SiS540",	PCI_DEVICE_ID_SI_540,	SIS5513_FLAG_ATA_66, },
-	{ "SiS620",	PCI_DEVICE_ID_SI_620,	SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, },
-	{ "SiS630",	PCI_DEVICE_ID_SI_630,	SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, },
-	{ "SiS5591",	PCI_DEVICE_ID_SI_5591,	SIS5513_FLAG_ATA_33, },
-	{ "SiS5597",	PCI_DEVICE_ID_SI_5597,	SIS5513_FLAG_ATA_33, },
-	{ "SiS5600",	PCI_DEVICE_ID_SI_5600,	SIS5513_FLAG_ATA_33, },
-	{ "SiS5511",	PCI_DEVICE_ID_SI_5511,	SIS5513_FLAG_ATA_16, },
-};
-
-#if 0
-
-static struct _pio_mode_mapping {
-	byte data_active;
-	byte recovery;
-	byte pio_mode;
-} pio_mode_mapping[] = {
-	{ 8, 12, 0 },
-	{ 6,  7, 1 },
-	{ 4,  4, 2 },
-	{ 3,  3, 3 },
-	{ 3,  1, 4 }
-};
-
-static struct _dma_mode_mapping {
-	byte data_active;
-	byte recovery;
-	byte dma_mode;
-} dma_mode_mapping[] = {
-	{ 8, 8, 0 },
-	{ 3, 2, 1 },
-	{ 3, 1, 2 }
-};
-
-static struct _udma_mode_mapping {
-	byte cycle_time;
-	char * udma_mode;
-} udma_mode_mapping[] = {
-	{ 8, "Mode 0" },
-	{ 6, "Mode 1" },
-	{ 4, "Mode 2" }, 
-	{ 3, "Mode 3" },
-	{ 2, "Mode 4" },
-	{ 0, "Undefined" }
-};
-
-static __inline__ char * find_udma_mode (byte cycle_time)
-{
-	int n;
-	
-	for (n = 0; n <= 4; n++)
-		if (udma_mode_mapping[n].cycle_time <= cycle_time)
-			return udma_mode_mapping[n].udma_mode;
-	return udma_mode_mapping[4].udma_mode;
-}
-#endif
-
-#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static int sis_get_info(char *, char **, off_t, int);
-extern int (*sis_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-struct pci_dev *bmide_dev;
-
-static char *cable_type[] = {
-	"80 pins",
-	"40 pins"
-};
-
-static char *recovery_time [] ={
-	"12 PCICLK", "1 PCICLK",
-	"2 PCICLK", "3 PCICLK",
-	"4 PCICLK", "5 PCICLCK",
-	"6 PCICLK", "7 PCICLCK",
-	"8 PCICLK", "9 PCICLCK",
-	"10 PCICLK", "11 PCICLK",
-	"13 PCICLK", "14 PCICLK",
-	"15 PCICLK", "15 PCICLK"
-};
-
-static char *cycle_time [] = {
-	"Undefined", "2 CLCK",
-	"3 CLK", "4 CLK",
-	"5 CLK", "6 CLK",
-	"7 CLK", "8 CLK"
-};
-
-static char *active_time [] = {
-	"8 PCICLK", "1 PCICLCK",
-	"2 PCICLK", "2 PCICLK",
-	"4 PCICLK", "5 PCICLK",
-	"6 PCICLK", "12 PCICLK"
-};
-
-static int sis_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	int rc;
-	char *p = buffer;
-	byte reg,reg1;
-	u16 reg2, reg3;
-
-	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
-	rc = pci_read_config_byte(bmide_dev, 0x4a, &reg);
-	p += sprintf(p, "Channel Status: %s \t \t \t \t %s \n",
-		     (reg & 0x02) ? "On" : "Off",
-		     (reg & 0x04) ? "On" : "Off");
-		     
-	rc = pci_read_config_byte(bmide_dev, 0x09, &reg);
-	p += sprintf(p, "Operation Mode: %s \t \t \t %s \n",
-		     (reg & 0x01) ? "Native" : "Compatible",
-		     (reg & 0x04) ? "Native" : "Compatible");
-		     	     
-	rc = pci_read_config_byte(bmide_dev, 0x48, &reg);
-	p += sprintf(p, "Cable Type:     %s \t \t \t %s\n",
-		     (reg & 0x10) ? cable_type[1] : cable_type[0],
-		     (reg & 0x20) ? cable_type[1] : cable_type[0]);
-		     
-	rc = pci_read_config_word(bmide_dev, 0x4c, &reg2);
-	rc = pci_read_config_word(bmide_dev, 0x4e, &reg3);
-	p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n",
-		     reg2, reg3);
-
-	rc = pci_read_config_byte(bmide_dev, 0x4b, &reg);	     
-	p += sprintf(p, "Drvie 0:        Postwrite %s \t \t Postwrite %s\n",
-		     (reg & 0x10) ? "Enabled" : "Disabled",
-		     (reg & 0x40) ? "Enabled" : "Disabled");
-	p += sprintf(p, "                Prefetch  %s \t \t Prefetch  %s\n",
-		     (reg & 0x01) ? "Enabled" : "Disabled",
-		     (reg & 0x04) ? "Enabled" : "Disabled");
-		          
-	rc = pci_read_config_byte(bmide_dev, 0x41, &reg);
-	rc = pci_read_config_byte(bmide_dev, 0x45, &reg1);
-	p += sprintf(p, "                UDMA %s \t \t \t UDMA %s\n",
-		     (reg & 0x80)  ? "Enabled" : "Disabled",
-		     (reg1 & 0x80) ? "Enabled" : "Disabled");
-	p += sprintf(p, "                UDMA Cycle Time    %s \t UDMA Cycle Time    %s\n",
-		     cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]);
-	p += sprintf(p, "                Data Active Time   %s \t Data Active Time   %s\n",
-		     active_time[(reg & 0x07)], active_time[(reg &0x07)] ); 
-
-	rc = pci_read_config_byte(bmide_dev, 0x40, &reg);
-	rc = pci_read_config_byte(bmide_dev, 0x44, &reg1);
-	p += sprintf(p, "                Data Recovery Time %s \t Data Recovery Time %s\n",
-		     recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]);
-
-
-	rc = pci_read_config_byte(bmide_dev, 0x4b, &reg);	     
-	p += sprintf(p, "Drvie 1:        Postwrite %s \t \t Postwrite %s\n",
-		     (reg & 0x20) ? "Enabled" : "Disabled",
-		     (reg & 0x80) ? "Enabled" : "Disabled");
-	p += sprintf(p, "                Prefetch  %s \t \t Prefetch  %s\n",
-		     (reg & 0x02) ? "Enabled" : "Disabled",
-		     (reg & 0x08) ? "Enabled" : "Disabled");
-
-	rc = pci_read_config_byte(bmide_dev, 0x43, &reg);
-	rc = pci_read_config_byte(bmide_dev, 0x47, &reg1);
-	p += sprintf(p, "                UDMA %s \t \t \t UDMA %s\n",
-		     (reg & 0x80)  ? "Enabled" : "Disabled",
-		     (reg1 & 0x80) ? "Enabled" : "Disabled");
-	p += sprintf(p, "                UDMA Cycle Time    %s \t UDMA Cycle Time    %s\n",
-		     cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]);
-	p += sprintf(p, "                Data Active Time   %s \t Data Active Time   %s\n",
-		     active_time[(reg & 0x07)], active_time[(reg &0x07)] ); 
-
-	rc = pci_read_config_byte(bmide_dev, 0x42, &reg);
-	rc = pci_read_config_byte(bmide_dev, 0x46, &reg1);
-	p += sprintf(p, "                Data Recovery Time %s \t Data Recovery Time %s\n",
-		     recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]);
-	return p-buffer;
-}
-#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-byte sis_proc = 0;
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-/*
- * ((id->hw_config & 0x2000) && (HWIF(drive)->udma_four))
- */
-static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
-{
-	struct hd_driveid *id	= drive->id;
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-
-	byte			drive_pci, test1, test2, mask;
-	int			err;
-
-	unsigned long dma_base	= hwif->dma_base;
-	byte unit		= (drive->select.b.unit & 0x01);
-	byte speed		= 0x00, unmask = 0xE0, four_two = 0x00;
-	int drive_number	= ((hwif->channel ? 2 : 0) + unit);
-	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
-
-	if (host_dev) {
-		switch(host_dev->device) {
-			case PCI_DEVICE_ID_SI_530:
-			case PCI_DEVICE_ID_SI_540:
-			case PCI_DEVICE_ID_SI_620:
-			case PCI_DEVICE_ID_SI_630:
-				unmask   = 0xF0;
-				four_two = 0x01;
-			default:
-				break;
-		}
-	}
-
-	switch(drive_number) {
-		case 0:		drive_pci = 0x40;break;
-		case 1:		drive_pci = 0x42;break;
-		case 2:		drive_pci = 0x44;break;
-		case 3:		drive_pci = 0x46;break;
-		default:	return ide_dma_off;
-	}
-
-	pci_read_config_byte(dev, drive_pci, &test1);
-	pci_read_config_byte(dev, drive_pci|0x01, &test2);
-
-	if ((!ultra) && (test2 & 0x80)) {
-		pci_write_config_byte(dev, drive_pci|0x01, test2 & ~0x80);
-		pci_read_config_byte(dev, drive_pci|0x01, &test2);
-	}
-
-	if ((id->dma_ultra & 0x0010) && (ultra) && (udma_66) && (four_two)) {
-		if (!(test2 & 0x90)) {
-			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
-			pci_write_config_byte(dev, drive_pci|0x01, test2|0x90);
-		}
-		speed = XFER_UDMA_4;
-	} else if ((id->dma_ultra & 0x0008) && (ultra) && (udma_66) && (four_two)) {
-		if (!(test2 & 0xA0)) {
-			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
-			pci_write_config_byte(dev, drive_pci|0x01, test2|0xA0);
-		}
-		speed = XFER_UDMA_3;
-	} else if ((id->dma_ultra & 0x0004) && (ultra)) {
-		mask = (four_two) ? 0xB0 : 0xA0;
-		if (!(test2 & mask)) {
-			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
-			pci_write_config_byte(dev, drive_pci|0x01, test2|mask);
-		}
-		speed = XFER_UDMA_2;
-	} else if ((id->dma_ultra & 0x0002) && (ultra)) {
-		mask = (four_two) ? 0xD0 : 0xC0;
-		if (!(test2 & mask)) {
-			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
-			pci_write_config_byte(dev, drive_pci|0x01, test2|mask);
-		}
-		speed = XFER_UDMA_1;
-	} else if ((id->dma_ultra & 0x0001) && (ultra)) {
-		if (!(test2 & unmask)) {
-			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
-			pci_write_config_byte(dev, drive_pci|0x01, test2|unmask);
-		}
-		speed = XFER_UDMA_0;
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-	} else if (id->dma_1word & 0x0004) {
-		speed = XFER_SW_DMA_2;
-	} else if (id->dma_1word & 0x0002) {
-		speed = XFER_SW_DMA_1;
-	} else if (id->dma_1word & 0x0001) {
-		speed = XFER_SW_DMA_0;
-        } else {
-		return ((int) ide_dma_off_quietly);
-	}
-
-	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-	err = ide_config_drive_speed(drive, speed);
-
-#if SIS5513_DEBUG_DRIVE_INFO
-	printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number);
-#endif /* SIS5513_DEBUG_DRIVE_INFO */
-
-	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-}
-
-static void config_drive_art_rwp (ide_drive_t *drive)
-{
-	ide_hwif_t *hwif		= HWIF(drive);
-	struct pci_dev *dev		= hwif->pci_dev;
-
-	byte				timing, pio, drive_pci, test1, test2;
-
-	unsigned short eide_pio_timing[6] = {600, 390, 240, 180, 120, 90};
-	unsigned short xfer_pio		= drive->id->eide_pio_modes;
-	int drive_number		= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
-
-	if (drive->media == ide_disk) {
-		struct pci_dev *dev	= hwif->pci_dev;
-		byte reg4bh		= 0;
-                byte rw_prefetch	= (0x11 << drive_number);
-
-		pci_read_config_byte(dev, 0x4b, &reg4bh);
-		if ((reg4bh & rw_prefetch) != rw_prefetch)
-			pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch);
-	}
-
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-/*
- *               Mode 0       Mode 1     Mode 2     Mode 3     Mode 4
- * Active time    8T (240ns)  6T (180ns) 4T (120ns) 3T  (90ns) 3T  (90ns)
- * 0x41 2:0 bits  000          110        100        011        011
- * Recovery time 12T (360ns)  7T (210ns) 4T (120ns) 3T  (90ns) 1T  (30ns)
- * 0x40 3:0 bits 0000         0111       0100       0011       0001
- * Cycle time    20T (600ns) 13T (390ns) 8T (240ns) 6T (180ns) 4T (120ns)
- */
-
-	switch(drive_number) {
-		case 0:		drive_pci = 0x40;break;
-		case 1:		drive_pci = 0x42;break;
-		case 2:		drive_pci = 0x44;break;
-		case 3:		drive_pci = 0x46;break;
-		default:	return;
-	}
-
-	pci_read_config_byte(dev, drive_pci, &test1);
-	pci_read_config_byte(dev, drive_pci|0x01, &test2);
-
-	/*
-	 * Do a blanket clear of active and recovery timings.
-	 */
-
-	test1 &= ~0x07;
-	test2 &= ~0x0F;
-
-	switch(timing) {
-		case 4:		test1 |= 0x01;test2 |= 0x03;break;
-		case 3:		test1 |= 0x03;test2 |= 0x03;break;
-		case 2:		test1 |= 0x04;test2 |= 0x04;break;
-		case 1:		test1 |= 0x07;test2 |= 0x06;break;
-		default:	break;
-	}
-
-	pci_write_config_byte(dev, drive_pci, test1);
-	pci_write_config_byte(dev, drive_pci|0x01, test2);
-}
-
-static int config_drive_xfer_rate (ide_drive_t *drive)
-{
-	struct hd_driveid *id		= drive->id;
-	ide_dma_action_t dma_func	= ide_dma_off_quietly;
-
-	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			return HWIF(drive)->dmaproc(ide_dma_off, drive);
-		}
-
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & 0x001F) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, 1);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & 0x0007) ||
-			    (id->dma_1word & 0x0007)) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, 0);
-			}
-		} else if ((ide_dmaproc(ide_dma_good_drive, drive)) &&
-			   (id->eide_dma_time > 150)) {
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, 0);
-		}
-	}
-	return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-/*
- * sis5513_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- */
-int sis5513_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch (func) {
-		case ide_dma_check:
-			config_drive_art_rwp(drive);
-			return config_drive_xfer_rate(drive);
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-}
-
-unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name)
-{
-	struct pci_dev *host;
-	int i = 0;
-	byte latency = 0;
-
-	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
-
-	for (i = 0; i < arraysize (SiSHostChipInfo) && !host_dev; i++) {
-		host = pci_find_device (PCI_VENDOR_ID_SI,
-					SiSHostChipInfo[i].host_id,
-					NULL);
-		if (!host)
-			continue;
-
-		host_dev = host;
-		printk(SiSHostChipInfo[i].name);
-		printk("\n");
-		if (SiSHostChipInfo[i].flags & SIS5513_FLAG_LATENCY) {
-			if (latency != 0x10)
-				pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x10);
-		}
-	}
-
-	if (host_dev) {
-		byte reg52h = 0;
-
-		pci_read_config_byte(dev, 0x52, &reg52h);
-		if (!(reg52h & 0x04)) {
-			/* set IDE controller to operate in Compabitility mode obly */
-			pci_write_config_byte(dev, 0x52, reg52h|0x04);
-		}
-#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
-		sis_proc = 1;
-		bmide_dev = dev;
-		sis_display_info = &sis_get_info;
-#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */
-	}
-	return 0;
-}
-
-unsigned int __init ata66_sis5513 (ide_hwif_t *hwif)
-{
-	byte reg48h = 0, ata66 = 0;
-	byte mask = hwif->channel ? 0x20 : 0x10;
-	pci_read_config_byte(hwif->pci_dev, 0x48, &reg48h);
-
-	if (host_dev) {
-		switch(host_dev->device) {
-			case PCI_DEVICE_ID_SI_530:
-			case PCI_DEVICE_ID_SI_540:
-			case PCI_DEVICE_ID_SI_620:
-			case PCI_DEVICE_ID_SI_630:
-				ata66 = (reg48h & mask) ? 0 : 1;
-			default:
-				break;
-		}
-	}
-        return (ata66);
-}
-
-void __init ide_init_sis5513 (ide_hwif_t *hwif)
-{
-
-	hwif->irq = hwif->channel ? 15 : 14;
-
-	if (!(hwif->dma_base))
-		return;
-
-	if (host_dev) {
-		switch(host_dev->device) {
-			case PCI_DEVICE_ID_SI_530:
-			case PCI_DEVICE_ID_SI_540:
-			case PCI_DEVICE_ID_SI_620:
-			case PCI_DEVICE_ID_SI_630:
-			case PCI_DEVICE_ID_SI_5600:
-			case PCI_DEVICE_ID_SI_5597:
-			case PCI_DEVICE_ID_SI_5591:
-				hwif->autodma = 1;
-				hwif->dmaproc = &sis5513_dmaproc;
-				break;
-			default:
-				hwif->autodma = 0;
-				break;
-		}
-	}
-	return;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/sl82c105.c linux/drivers/block/sl82c105.c
--- v2.3.51/linux/drivers/block/sl82c105.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/block/sl82c105.c	Wed Dec 31 16:00:00 1969
@@ -1,138 +0,0 @@
-/*
- * linux/drivers/block/sl82c105.c
- *
- * SL82C105/Winbond 553 IDE driver
- *
- * Maintainer unknown.
- *
- * Drive tuning added from Rebel.com's kernel sources
- *  -- Russell King (15/11/98) linux@arm.linux.org.uk
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/pci.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include "ide_modes.h"
-
-extern char *ide_xfer_verbose (byte xfer_rate);
-
-#ifdef CONFIG_ARCH_NETWINDER
-/*
- * Convert a PIO mode and cycle time to the required on/off
- * times for the interface.  This has protection against run-away
- * timings.
- */
-static unsigned int get_timing_sl82c105(ide_pio_data_t *p)
-{
-	unsigned int cmd_on;
-	unsigned int cmd_off;
-
-	cmd_on = (ide_pio_timings[p->pio_mode].active_time + 29) / 30;
-	cmd_off = (p->cycle_time - 30 * cmd_on + 29) / 30;
-
-	if (cmd_on > 32)
-		cmd_on = 32;
-	if (cmd_on == 0)
-		cmd_on = 1;
-
-	if (cmd_off > 32)
-		cmd_off = 32;
-	if (cmd_off == 0)
-		cmd_off = 1;
-
-	return (cmd_on - 1) << 8 | (cmd_off - 1) | (p->use_iordy ? 0x40 : 0x00);
-}
-
-/*
- * We only deal with PIO mode here - DMA mode 'using_dma' is not
- * initialised at the point that this function is called.
- */
-static void tune_sl82c105(ide_drive_t *drive, byte pio)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	struct pci_dev *dev = hwif->pci_dev;
-	ide_pio_data_t p;
-	unsigned short drv_ctrl = 0x909;
-	unsigned int xfer_mode, reg;
-
-	reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0);
-
-	pio = ide_get_best_pio_mode(drive, pio, 5, &p);
-
-	switch (pio) {
-	default:
-	case 0:		xfer_mode = XFER_PIO_0;		break;
-	case 1:		xfer_mode = XFER_PIO_1;		break;
-	case 2:		xfer_mode = XFER_PIO_2;		break;
-	case 3:		xfer_mode = XFER_PIO_3;		break;
-	case 4:		xfer_mode = XFER_PIO_4;		break;
-	}
-
-	if (ide_config_drive_speed(drive, xfer_mode) == 0)
-		drv_ctrl = get_timing_sl82c105(&p);
-
-	pci_write_config_word(dev, reg, drv_ctrl);
-	pci_read_config_word(dev, reg, &drv_ctrl);
-
-	printk("%s: selected %s (%dns) (%04X)\n", drive->name,
-	       ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl);
-}
-#endif
-
-void ide_dmacapable_sl82c105(ide_hwif_t *hwif, unsigned long dmabase)
-{
-	unsigned char rev;
-
-	pci_read_config_byte(hwif->pci_dev, PCI_REVISION_ID, &rev);
-
-	if (rev <= 5) {
-		hwif->autodma = 0;
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-		printk("    %s: revision %d, Bus-Master DMA disabled\n",
-		       hwif->name, rev);
-	}
-	ide_setup_dma(hwif, dmabase, 8);
-}
-
-void ide_init_sl82c105(ide_hwif_t *hwif)
-{
-	struct pci_dev *dev = hwif->pci_dev;
-
-#ifdef CONFIG_ARCH_NETWINDER
-	unsigned char ctrl_stat;
-
-	pci_read_config_byte(dev, 0x40, &ctrl_stat);
-	pci_write_config_byte(dev, 0x40, ctrl_stat | 0x33);
-
-	hwif->tuneproc = tune_sl82c105;
-#else
-	unsigned short t16;
-	unsigned int t32;
-	pci_read_config_word(dev, PCI_COMMAND, &t16);
-	printk("SL82C105 command word: %x\n",t16);
-        t16 |= PCI_COMMAND_IO;
-        pci_write_config_word(dev, PCI_COMMAND, t16);
-	/* IDE timing */
-	pci_read_config_dword(dev, 0x44, &t32);
-	printk("IDE timing: %08x, resetting to PIO0 timing\n",t32);
-	pci_write_config_dword(dev, 0x44, 0x03e4);
-#ifndef CONFIG_MBX
-	pci_read_config_dword(dev, 0x40, &t32);
-	printk("IDE control/status register: %08x\n",t32);
-	pci_write_config_dword(dev, 0x40, 0x10ff08a1);
-#endif /* CONFIG_MBX */
-#endif
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/trm290.c linux/drivers/block/trm290.c
--- v2.3.51/linux/drivers/block/trm290.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/trm290.c	Wed Dec 31 16:00:00 1969
@@ -1,282 +0,0 @@
-/*
- *  linux/drivers/block/trm290.c		Version 1.01	December 5, 1997
- *
- *  Copyright (c) 1997-1998  Mark Lord
- *  May be copied or modified under the terms of the GNU General Public License
- */
-
-/*
- * This module provides support for the bus-master IDE DMA function
- * of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards,
- * including a "Precision Instruments" board.  The TRM290 pre-dates
- * the sff-8038 standard (ide-dma.c) by a few months, and differs
- * significantly enough to warrant separate routines for some functions,
- * while re-using others from ide-dma.c.
- *
- * EXPERIMENTAL!  It works for me (a sample of one).
- *
- * Works reliably for me in DMA mode (READs only),
- * DMA WRITEs are disabled by default (see #define below);
- *
- * DMA is not enabled automatically for this chipset,
- * but can be turned on manually (with "hdparm -d1") at run time.
- *
- * I need volunteers with "spare" drives for further testing
- * and development, and maybe to help figure out the peculiarities.
- * Even knowing the registers (below), some things behave strangely.
- */
-
-#define TRM290_NO_DMA_WRITES	/* DMA writes seem unreliable sometimes */
-
-/*
- * TRM-290 PCI-IDE2 Bus Master Chip
- * ================================
- * The configuration registers are addressed in normal I/O port space
- * and are used as follows:
- *
- * trm290_base depends on jumper settings, and is probed for by ide-dma.c
- *
- * trm290_base+2 when WRITTEN: chiptest register (byte, write-only)
- *	bit7 must always be written as "1"
- *	bits6-2 undefined
- *	bit1 1=legacy_compatible_mode, 0=native_pci_mode
- *	bit0 1=test_mode, 0=normal(default)
- *
- * trm290_base+2 when READ: status register (byte, read-only)
- *	bits7-2 undefined
- *	bit1 channel0 busmaster interrupt status 0=none, 1=asserted
- *	bit0 channel0 interrupt status 0=none, 1=asserted
- *
- * trm290_base+3 Interrupt mask register
- *	bits7-5 undefined
- *	bit4 legacy_header: 1=present, 0=absent
- *	bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only)
- *	bit2 channel1 interrupt status 0=none, 1=asserted (read only)
- *	bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default)
- *	bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default)
- *
- * trm290_base+1 "CPR" Config Pointer Register (byte)
- *	bit7 1=autoincrement CPR bits 2-0 after each access of CDR
- *	bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state
- *	bit5 0=enabled master burst access (default), 1=disable  (write only)
- *	bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast
- *	bit3 0=primary IDE channel, 1=secondary IDE channel
- *	bits2-0 register index for accesses through CDR port
- *
- * trm290_base+0 "CDR" Config Data Register (word)
- *	two sets of seven config registers,
- *	selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6),
- *	each index defined below:
- *
- * Index-0 Base address register for command block (word)
- *	defaults: 0x1f0 for primary, 0x170 for secondary
- *
- * Index-1 general config register (byte)
- *	bit7 1=DMA enable, 0=DMA disable
- *	bit6 1=activate IDE_RESET, 0=no action (default)
- *	bit5 1=enable IORDY, 0=disable IORDY (default)
- *	bit4 0=16-bit data port(default), 1=8-bit (XT) data port
- *	bit3 interrupt polarity: 1=active_low, 0=active_high(default)
- *	bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only)
- *	bit1 bus_master_mode(?): 1=enable, 0=disable(default)
- *	bit0 enable_io_ports: 1=enable(default), 0=disable
- *
- * Index-2 read-ahead counter preload bits 0-7 (byte, write only)
- *	bits7-0 bits7-0 of readahead count
- *
- * Index-3 read-ahead config register (byte, write only)
- *	bit7 1=enable_readahead, 0=disable_readahead(default)
- *	bit6 1=clear_FIFO, 0=no_action
- *	bit5 undefined
- *	bit4 mode4 timing control: 1=enable, 0=disable(default)
- *	bit3 undefined
- *	bit2 undefined
- *	bits1-0 bits9-8 of read-ahead count
- *
- * Index-4 base address register for control block (word)
- *	defaults: 0x3f6 for primary, 0x376 for secondary
- *
- * Index-5 data port timings (shared by both drives) (byte)
- *	standard PCI "clk" (clock) counts, default value = 0xf5
- *
- *	bits7-6 setup time:  00=1clk, 01=2clk, 10=3clk, 11=4clk
- *	bits5-3 hold time:	000=1clk, 001=2clk, 010=3clk,
- *				011=4clk, 100=5clk, 101=6clk,
- *				110=8clk, 111=12clk
- *	bits2-0 active time:	000=2clk, 001=3clk, 010=4clk,
- *				011=5clk, 100=6clk, 101=8clk,
- *				110=12clk, 111=16clk
- *
- * Index-6 command/control port timings (shared by both drives) (byte)
- *	same layout as Index-5, default value = 0xde
- *
- * Suggested CDR programming for PIO mode0 (600ns):
- *	0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde	; primary
- *	0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde	; secondary
- *
- * Suggested CDR programming for PIO mode3 (180ns):
- *	0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde	; primary
- *	0x0170,0x21,0xff,0x80,0x0376,0x09,0xde	; secondary
- *
- * Suggested CDR programming for PIO mode4 (120ns):
- *	0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde	; primary
- *	0x0170,0x21,0xff,0x80,0x0376,0x00,0xde	; secondary
- *
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/init.h>
-#include <linux/hdreg.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	unsigned int reg;
-	unsigned long flags;
-
-	/* select PIO or DMA */
-	reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82);
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-
-	if (reg != hwif->select_data) {
-		hwif->select_data = reg;
-		outb(0x51|(hwif->channel<<3), hwif->config_data+1);	/* set PIO/DMA */
-		outw(reg & 0xff, hwif->config_data);
-	}
-
-	/* enable IRQ if not probing */
-	if (drive->present) {
-		reg = inw(hwif->config_data+3) & 0x13;
-		reg &= ~(1 << hwif->channel);
-		outw(reg, hwif->config_data+3);
-	}
-
-	__restore_flags(flags);	/* local CPU only */
-}
-
-static void trm290_selectproc (ide_drive_t *drive)
-{
-	trm290_prepare_drive(drive, drive->using_dma);
-}
-
-static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	unsigned int count, reading = 2, writing = 0;
-
-	switch (func) {
-		case ide_dma_write:
-			reading = 0;
-			writing = 1;
-#ifdef TRM290_NO_DMA_WRITES
-			break;	/* always use PIO for writes */
-#endif
-		case ide_dma_read:
-			if (!(count = ide_build_dmatable(drive, func)))
-				break;		/* try PIO instead of DMA */
-			trm290_prepare_drive(drive, 1);	/* select DMA xfer */
-			outl(hwif->dmatable_dma|reading|writing, hwif->dma_base);
-			drive->waiting_for_dma = 1;
-			outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */
-			if (drive->media != ide_disk)
-				return 0;
-			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
-			OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
-			return 0;
-		case ide_dma_begin:
-			return 0;
-		case ide_dma_end:
-			drive->waiting_for_dma = 0;
-			ide_destroy_dmatable(drive);		/* purge DMA mappings */
-			return (inw(hwif->dma_base+2) != 0x00ff);
-		case ide_dma_test_irq:
-			return (inw(hwif->dma_base+2) == 0x00ff);
-		default:
-			return ide_dmaproc(func, drive);
-	}
-	trm290_prepare_drive(drive, 0);	/* select PIO xfer */
-	return 1;
-}
-
-/*
- * Invoked from ide-dma.c at boot time.
- */
-void __init ide_init_trm290 (ide_hwif_t *hwif)
-{
-	unsigned int cfgbase = 0;
-	unsigned long flags;
-	byte reg;
-	struct pci_dev *dev = hwif->pci_dev;
-
-	hwif->chipset = ide_trm290;
-	cfgbase = dev->resource[4].start;
-	if ((dev->class & 5) && cfgbase)
-	{
-		hwif->config_data = cfgbase & PCI_BASE_ADDRESS_IO_MASK;
-		printk("TRM290: chip config base at 0x%04lx\n", hwif->config_data);
-	} else {
-		hwif->config_data = 0x3df0;
-		printk("TRM290: using default config base at 0x%04lx\n", hwif->config_data);
-	}
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	/* put config reg into first byte of hwif->select_data */
-	outb(0x51|(hwif->channel<<3), hwif->config_data+1);
-	hwif->select_data = 0x21;			/* select PIO as default */
-	outb(hwif->select_data, hwif->config_data);
-	reg = inb(hwif->config_data+3);			/* get IRQ info */
-	reg = (reg & 0x10) | 0x03;			/* mask IRQs for both ports */
-	outb(reg, hwif->config_data+3);
-	__restore_flags(flags);	/* local CPU only */
-
-	if ((reg & 0x10))
-		hwif->irq = hwif->channel ? 15 : 14;	/* legacy mode */
-	else if (!hwif->irq && hwif->mate && hwif->mate->irq)
-		hwif->irq = hwif->mate->irq;		/* sharing IRQ with mate */
-	ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3);
-	hwif->dmaproc = &trm290_dmaproc;
-	hwif->selectproc = &trm290_selectproc;
-	hwif->autodma = 0;				/* play it safe for now */
-#if 1
-	{
-		/*
-		 * My trm290-based card doesn't seem to work with all possible values
-		 * for the control basereg, so this kludge ensures that we use only
-		 * values that are known to work.  Ugh.		-ml
-		 */
-		unsigned short old, compat = hwif->channel ? 0x374 : 0x3f4;
-		static unsigned short next_offset = 0;
-
-		outb(0x54|(hwif->channel<<3), hwif->config_data+1);
-		old = inw(hwif->config_data) & ~1;
-		if (old != compat && inb(old+2) == 0xff) {
-			compat += (next_offset += 0x400);	/* leave lower 10 bits untouched */
-#if 1
-			if (ide_check_region(compat + 2, 1))
-				printk("Aieee %s: ide_check_region failure at 0x%04x\n", hwif->name, (compat + 2));
-			/*
-			 * The region check is not needed; however.........
-			 * Since this is the checked in ide-probe.c,
-			 * this is only an assignment.
-			 */
-#endif
-			hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2;
-			outw(compat|1, hwif->config_data);
-			printk("%s: control basereg workaround: old=0x%04x, new=0x%04x\n", hwif->name, old, inw(hwif->config_data) & ~1);
-		}
-	}
-#endif
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/umc8672.c linux/drivers/block/umc8672.c
--- v2.3.51/linux/drivers/block/umc8672.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/block/umc8672.c	Wed Dec 31 16:00:00 1969
@@ -1,159 +0,0 @@
-/*
- *  linux/drivers/block/umc8672.c		Version 0.05	Jul 31, 1996
- *
- *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
- */
-
-/*
- *  Principal Author/Maintainer:  PODIEN@hml2.atlas.de (Wolfram Podien)
- *
- *  This file provides support for the advanced features
- *  of the UMC 8672 IDE interface.
- *
- *  Version 0.01	Initial version, hacked out of ide.c,
- *			and #include'd rather than compiled separately.
- *			This will get cleaned up in a subsequent release.
- *
- *  Version 0.02	now configs/compiles separate from ide.c  -ml
- *  Version 0.03	enhanced auto-tune, fix display bug
- *  Version 0.05	replace sti() with restore_flags()  -ml
- *			add detection of possible race condition  -ml
- */
-
-/*
- * VLB Controller Support from 
- * Wolfram Podien
- * Rohoefe 3
- * D28832 Achim
- * Germany
- *
- * To enable UMC8672 support there must a lilo line like
- * append="ide0=umc8672"...
- * To set the speed according to the abilities of the hardware there must be a
- * line like
- * #define UMC_DRIVE0 11
- * in the beginning of the driver, which sets the speed of drive 0 to 11 (there
- * are some lines present). 0 - 11 are allowed speed values. These values are
- * the results from the DOS speed test program supplied from UMC. 11 is the 
- * highest speed (about PIO mode 3)
- */
-#define REALLY_SLOW_IO		/* some systems can safely undef this */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-#include "ide_modes.h"
-
-/*
- * Default speeds.  These can be changed with "auto-tune" and/or hdparm.
- */
-#define UMC_DRIVE0      1              /* DOS measured drive speeds */
-#define UMC_DRIVE1      1              /* 0 to 11 allowed */
-#define UMC_DRIVE2      1              /* 11 = Fastest Speed */
-#define UMC_DRIVE3      1              /* In case of crash reduce speed */
-
-static byte current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
-static const byte pio_to_umc [5] = {0,3,7,10,11};	/* rough guesses */
-
-/*       0    1    2    3    4    5    6    7    8    9    10   11      */
-static const byte speedtab [3][12] = {
-	{0xf, 0xb, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
-	{0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
-	{0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}};
-
-static void out_umc (char port,char wert)
-{
-	outb_p (port,0x108);
-	outb_p (wert,0x109);
-}
-
-static inline byte in_umc (char port)
-{
-	outb_p (port,0x108);
-	return inb_p (0x109);
-}
-
-static void umc_set_speeds (byte speeds[])
-{
-	int i, tmp;
-
-	outb_p (0x5A,0x108); /* enable umc */
-
-	out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
-	out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
-	tmp = 0;
-	for (i = 3; i >= 0; i--)
-	{
-		tmp = (tmp << 2) | speedtab[1][speeds[i]];
-	}
-	out_umc (0xdc,tmp);
-	for (i = 0;i < 4; i++)
-	{
-		out_umc (0xd0+i,speedtab[2][speeds[i]]);
-		out_umc (0xd8+i,speedtab[2][speeds[i]]);
-	}
-	outb_p (0xa5,0x108); /* disable umc */
-
-	printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
-		speeds[0], speeds[1], speeds[2], speeds[3]);
-}
-
-static void tune_umc (ide_drive_t *drive, byte pio)
-{
-	unsigned long flags;
-	ide_hwgroup_t *hwgroup = ide_hwifs[HWIF(drive)->index^1].hwgroup;
-
-	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
-	printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", drive->name, pio, pio_to_umc[pio]);
-	save_flags(flags);	/* all CPUs */
-	cli();			/* all CPUs */
-	if (hwgroup && hwgroup->handler != NULL) {
-		printk("umc8672: other interface is busy: exiting tune_umc()\n");
-	} else {
-		current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
-		umc_set_speeds (current_speeds);
-	}
-	restore_flags(flags);	/* all CPUs */
-}
-
-void __init init_umc8672 (void)	/* called from ide.c */
-{
-	unsigned long flags;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-	if (check_region(0x108, 2)) {
-		__restore_flags(flags);
-		printk("\numc8672: PORTS 0x108-0x109 ALREADY IN USE\n");
-		return;
-	}
-	outb_p (0x5A,0x108); /* enable umc */
-	if (in_umc (0xd5) != 0xa0)
-	{
-		__restore_flags(flags);	/* local CPU only */
-		printk ("umc8672: not found\n");
-		return;  
-	}
-	outb_p (0xa5,0x108); /* disable umc */
-
-	umc_set_speeds (current_speeds);
-	__restore_flags(flags);	/* local CPU only */
-
-	request_region(0x108, 2, "umc8672");
-	ide_hwifs[0].chipset = ide_umc8672;
-	ide_hwifs[1].chipset = ide_umc8672;
-	ide_hwifs[0].tuneproc = &tune_umc;
-	ide_hwifs[1].tuneproc = &tune_umc;
-	ide_hwifs[0].mate = &ide_hwifs[1];
-	ide_hwifs[1].mate = &ide_hwifs[0];
-	ide_hwifs[1].channel = 1;
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/block/via82cxxx.c linux/drivers/block/via82cxxx.c
--- v2.3.51/linux/drivers/block/via82cxxx.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/block/via82cxxx.c	Wed Dec 31 16:00:00 1969
@@ -1,834 +0,0 @@
-/*
- * linux/drivers/block/via82cxxx.c	Version 0.07	Feb. 10, 2000
- *
- *  Copyright (C) 1998-99	Michel Aubry, Maintainer
- *  Copyright (C) 1999		Jeff Garzik, MVP4 Support
- *					(jgarzik@mandrakesoft.com)
- *  Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
- *  May be copied or modified under the terms of the GNU General Public License
- *
- *  The VIA MVP-4 is reported OK with UDMA.
- *  The VIA MVP-3 is reported OK with UDMA.
- *  The TX Pro III is also reported OK with UDMA.
- *
- *  VIA chips also have a single FIFO, with the same 64 bytes deep
- *  buffer (16 levels of 4 bytes each).
- *
- *  However, VIA chips can have the buffer split either 8:8 levels,
- *  16:0 levels or 0:16 levels between both channels. One could think
- *  of using this feature, as even if no level of FIFO is given to a
- *  given channel, one can for instance always reach ATAPI drives through
- *  it, or, if one channel is unused, configuration defaults to
- *  an even split FIFO levels.
- *  
- *  This feature is available only through a kernel command line :
- *		"splitfifo=Chan,Thr0,Thr1" or "splitfifo=Chan".
- *		where:  Chan =1,2,3 or 4 and Thrx = 1,2,3,or 4.
- *
- *  If Chan == 1:
- *	gives all the fifo to channel 0,
- *	sets its threshold to Thr0/4,
- *	and disables any dma access to channel 1.
- *
- *  If chan == 2:
- *	gives all the fifo to channel 1,
- *	sets its threshold to Thr1/4,
- *	and disables any dma access to channel 0.
- *
- *  If chan == 3 or 4:
- *	shares evenly fifo between channels,
- *	gives channel 0 a threshold of Thr0/4,
- *	and channel 1 a threshold of Thr1/4.
- *
- *  Note that by default (if no command line is provided) and if a channel
- *  has been disabled in Bios, all the fifo is given to the active channel,
- *  and its threshold is set to 3/4.
- *
- *  VT82c586B
- *
- *    Offset 4B-48 - Drive Timing Control
- *             | pio0 | pio1 | pio2 | pio3 | pio4
- *    25.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20 
- *    33.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
- *    37.5 MHz | 0xA9 | 0x76 | 0x76 | 0x32 | 0x21
- *
- *    Offset 53-50 - UltraDMA Extended Timing Control
- *      UDMA   |  NO  |   0  |   1  |   2
- *             | 0x03 | 0x62 | 0x61 | 0x60
- *
- *  VT82c596B & VT82c686A
- *
- *    Offset 4B-48 - Drive Timing Control
- *             | pio0 | pio1 | pio2 | pio3 | pio4
- *    25.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
- *    33.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
- *    37.5 MHz | 0xDB | 0x87 | 0x87 | 0x42 | 0x31
- *    41.5 MHz | 0xFE | 0xA8 | 0xA8 | 0x53 | 0x32
- *
- *    Offset 53-50 - UltraDMA Extended Timing Control
- *      UDMA   |  NO  |   0  |   1  |   2
- *    33.0 MHz | 0x03 | 0xE2 | 0xE1 | 0xE0
- *    37.5 MHz | 0x03 | 0xE2 | 0xE2 | 0xE1   (1)
- *
- *    Offset 53-50 - UltraDMA Extended Timing Control
- *      UDMA   |  NO  |   0  |   1  |   2  |   3  |   4
- *    33.0 MHz |  (2) | 0xE6 | 0xE4 | 0xE2 | 0xE1 | 0xE0
- *    37.5 MHz |  (2) | 0xE6 | 0xE6 | 0xE4 | 0xE2 | 0xE1   (1)
- *
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-
-static struct pci_dev *host_dev = NULL;
-static struct pci_dev *isa_dev = NULL;
-
-static const struct {
-	const char *name;
-	unsigned short host_id;
-} ApolloHostChipInfo[] = {
-	{ "VT 82C585 Apollo VP1/VPX",	PCI_DEVICE_ID_VIA_82C585, },
-	{ "VT 82C595 Apollo VP2",	PCI_DEVICE_ID_VIA_82C595, },
-	{ "VT 82C597 Apollo VP3",	PCI_DEVICE_ID_VIA_82C597_0, },
-	{ "VT 82C598 Apollo MVP3",	PCI_DEVICE_ID_VIA_82C598_0, },
-	{ "VT 82C680 Apollo P6",	PCI_DEVICE_ID_VIA_82C680, },
-	{ "VT 82C691 Apollo Pro",	PCI_DEVICE_ID_VIA_82C691, },
-	{ "VT 82C693 Apollo Pro Plus",	PCI_DEVICE_ID_VIA_82C693, },
-	{ "Apollo MVP4",		PCI_DEVICE_ID_VIA_8501_0, },
-	{ "VT 8371",			PCI_DEVICE_ID_VIA_8371_0, },
-	{ "VT 8601",			PCI_DEVICE_ID_VIA_8601_0, },
-};
-
-#define NUM_APOLLO_ISA_CHIP_DEVICES	2
-#define VIA_FLAG_CHECK_REV		0x00000001
-#define VIA_FLAG_ATA_66			0x00000002
-
-static const struct {
-	unsigned short host_id;
-	unsigned short isa_id;
-	unsigned int flags;
-} ApolloISAChipInfo[] = {
-	{ PCI_DEVICE_ID_VIA_82C585,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
-	{ PCI_DEVICE_ID_VIA_82C595,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
-	{ PCI_DEVICE_ID_VIA_82C597_0,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
-	{ PCI_DEVICE_ID_VIA_82C598_0,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
-	{ PCI_DEVICE_ID_VIA_82C598_0,	PCI_DEVICE_ID_VIA_82C596,	0 },
-	{ PCI_DEVICE_ID_VIA_82C680,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
-	{ PCI_DEVICE_ID_VIA_82C691,	PCI_DEVICE_ID_VIA_82C596,	0 },
-	{ PCI_DEVICE_ID_VIA_82C693,	PCI_DEVICE_ID_VIA_82C596,	0 },
-	{ PCI_DEVICE_ID_VIA_8501_0,	PCI_DEVICE_ID_VIA_82C686,	VIA_FLAG_ATA_66 },
-	{ PCI_DEVICE_ID_VIA_8371_0,	PCI_DEVICE_ID_VIA_82C686,	VIA_FLAG_ATA_66 },
-	{ PCI_DEVICE_ID_VIA_8601_0,	PCI_DEVICE_ID_VIA_8231,		VIA_FLAG_ATA_66 },
-};
-
-#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
-
-#define DISPLAY_VIA_TIMINGS
-
-#if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static char *FIFO_str[] = {
-	" 1 ",
-	"3/4",
-	"1/2",
-	"1/4"
-};
-
-static char *control3_str[] = {
-	"No limitation",
-	"64",
-	"128",
-	"192"
-};
-
-static int via_get_info(char *, char **, off_t, int);
-extern int (*via_display_info)(char *, char **, off_t, int); /* ide-proc.c */
-static struct pci_dev *bmide_dev;
-
-static char * print_apollo_drive_config (char *buf, struct pci_dev *dev)
-{
-	int rc;
-	unsigned int time;
-	byte tm;
-	char *p = buf;  
- 
-	/* Drive Timing Control */
-	rc = pci_read_config_dword(dev, 0x48, &time);
-	p += sprintf(p, "Act Pls Width:  %02d          %02d           %02d          %02d\n",
-			((time & 0xf0000000)>>28) + 1,
-			((time & 0xf00000)>>20) + 1,
-			((time & 0xf000)>>12) + 1,
-			((time & 0xf0)>>4) + 1 );
-	p += sprintf(p, "Recovery Time:  %02d          %02d           %02d          %02d\n",
-			((time & 0x0f000000)>>24) + 1,
-			((time & 0x0f0000)>>16) + 1,
-			((time & 0x0f00)>>8) + 1,
-			(time & 0x0f) + 1 );
- 
-	/* Address Setup Time */
-	rc = pci_read_config_byte(dev, 0x4C, &tm);
-	p += sprintf(p, "Add. Setup T.:  %01dT          %01dT           %01dT          %01dT\n",
-			((tm & 0xc0)>>6) + 1,
-			((tm & 0x30)>>4) + 1,
-			((tm & 0x0c)>>2) + 1,
-			(tm & 0x03) + 1 );
- 
-	/* UltraDMA33 Extended Timing Control */
- 	rc = pci_read_config_dword(dev, 0x50, &time);
- 	p += sprintf(p, "------------------UDMA-Timing-Control------------------------\n");
- 	p += sprintf(p, "Enable Meth.:    %01d           %01d            %01d           %01d\n",
-			(time & 0x80000000)	? 1 : 0,
-			(time & 0x800000)	? 1 : 0,
-			(time & 0x8000)		? 1 : 0,
-			(time & 0x80)		? 1 : 0 );
- 	p += sprintf(p, "Enable:         %s         %s          %s         %s\n",
-			(time & 0x40000000)	? "yes" : "no ",
-			(time & 0x400000)	? "yes" : "no ",
-			(time & 0x4000)		? "yes" : "no ",
-			(time & 0x40)		? "yes" : "no " );
- 	p += sprintf(p, "Transfer Mode: %s         %s          %s         %s\n",
-			(time & 0x20000000)	? "PIO" : "DMA",
-			(time & 0x200000)	? "PIO" : "DMA",
-			(time & 0x2000)		? "PIO" : "DMA",
-			(time & 0x20)		? "PIO" : "DMA" );
- 	p += sprintf(p, "Cycle Time:     %01dT          %01dT           %01dT          %01dT\n",
-			((time & 0x03000000)>>24) + 2,
-			((time & 0x030000)>>16) + 2,
-			((time & 0x0300)>>8) + 2,
-			(time & 0x03) + 2 );
- 
- 	return (char *)p;
-}
-
-static char * print_apollo_ide_config (char *buf, struct pci_dev *dev)
-{
-	byte time, tmp; 
-	unsigned short size0, size1;
-	int rc;
-	char *p = buf;  
- 
-	rc = pci_read_config_byte(dev, 0x41, &time);
-	p += sprintf(p, "Prefetch Buffer :      %s                     %s\n",
-		(time & 128)	? "on " : "off",
-		(time & 32)	? "on " : "off" );
-	p += sprintf(p, "Post Write Buffer:     %s                     %s\n",
-		(time & 64)	? "on " : "off",
-		(time & 16)	? "on " : "off" );
- 
-	/* FIFO configuration */
-	rc = pci_read_config_byte(dev, 0x43, &time);
-	tmp = ((time & 0x20)>>2) + ((time & 0x40)>>3);
-	p += sprintf(p, "FIFO Conf/Chan. :      %02d                      %02d\n",
-			16 - tmp, tmp);
-	tmp = (time & 0x0F)>>2;
-	p += sprintf(p, "Threshold Prim. :      %s                     %s\n",
-			FIFO_str[tmp],
-			FIFO_str[time & 0x03] );
- 
-	/* chipset Control3 */
-	rc = pci_read_config_byte(dev, 0x46, &time);
-	p += sprintf(p, "Read DMA FIFO flush:   %s                     %s\n",
-			(time & 0x80)	? "on " : "off",
-			(time & 0x40)	? "on " : "off" );
-	p += sprintf(p, "End Sect. FIFO flush:  %s                     %s\n",
-			(time & 0x20)	? "on " : "off",
-			(time & 0x10)	? "on " : "off" );
-	p += sprintf(p, "Max DRDY Pulse Width:  %s %s\n",
-			control3_str[(time & 0x03)],
-			(time & 0x03) ? "PCI clocks" : "" );
- 
-	/* Primary and Secondary sector sizes */
-	rc = pci_read_config_word(dev, 0x60, &size0);
-	rc = pci_read_config_word(dev, 0x68, &size1);
-	p += sprintf(p, "Bytes Per Sector:      %03d                     %03d\n",
-			size0 & 0xfff,
-			size1 & 0xfff );
-
-	return (char *)p;
-}
-
-static char * print_apollo_chipset_control1 (char *buf, struct pci_dev *dev)
-{
-	byte t;
-	int rc;
-	char *p = buf;  
-	unsigned short c;
-	byte l, l_max;   
- 
-	rc = pci_read_config_word(dev, 0x04, &c);
-	rc = pci_read_config_byte(dev, 0x44, &t);
-	rc = pci_read_config_byte(dev, 0x0d, &l);
-	rc = pci_read_config_byte(dev, 0x3f, &l_max);
-
-	p += sprintf(p, "Command register = 0x%x\n", c);
-	p += sprintf(p, "Master Read  Cycle IRDY %d Wait State\n",
-			(t & 64) >>6 );
-	p += sprintf(p, "Master Write Cycle IRDY %d Wait State\n",
-			(t & 32) >> 5 );
-	p += sprintf(p, "FIFO Output Data 1/2 Clock Advance: %s\n",
-			(t & 16) ? "on " : "off" );
-	p += sprintf(p, "Bus Master IDE Status Register Read Retry: %s\n",
-			(t & 8) ? "on " : "off" );
-	p += sprintf(p, "Latency timer = %d (max. = %d)\n",
-			l, l_max);
-
-	return (char *)p;
-}
- 
-static char * print_apollo_chipset_control2 (char *buf, struct pci_dev *dev)
-{
-	byte t;
-	int rc;
-	char *p = buf;  
-	rc = pci_read_config_byte(dev, 0x45, &t);
-	p += sprintf(p, "Interrupt Steering Swap: %s\n",
-			(t & 64) ? "on ":"off" );
-
-	return (char *)p;
-}
- 
-static char * print_apollo_chipset_control3 (char *buf, struct pci_dev *dev,
-						unsigned short n)
-{
-	/*
-	 * at that point we can be sure that register 0x20 of the
-	 * chipset contains the right address...
-	 */
-	unsigned int bibma;
-	int rc;
-	byte c0, c1;    
-	char *p = buf; 
- 
-	rc = pci_read_config_dword(dev, 0x20, &bibma);
-	bibma = (bibma & 0xfff0) ;
- 
-	/*
-	 * at that point bibma+0x2 et bibma+0xa are byte registers
-	 * to investigate:
-	 */
-	c0 = inb((unsigned short)bibma + 0x02);
-	c1 = inb((unsigned short)bibma + 0x0a);
- 
-	if (n == 0) {
-        /*p = sprintf(p,"--------------------Primary IDE------------Secondary IDE-----");*/
-		p += sprintf(p, "both channels togth:   %s                     %s\n",
-				(c0&0x80) ? "no" : "yes",
-				(c1&0x80) ? "no" : "yes" );
-	} else {
-        /*p = sprintf(p,"--------------drive0------drive1-------drive0------drive1----");*/
-		p += sprintf(p, "DMA enabled:    %s         %s          %s         %s\n",
-				(c0&0x20) ? "yes" : "no ",
-				(c0&0x40) ? "yes" : "no ",
-				(c1&0x20) ? "yes" : "no ",
-				(c1&0x40) ? "yes" : "no " );
-	}
- 
-	return (char *)p;
-}
-
-static int via_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	/*
-	 * print what /proc/via displays,
-	 * if required from DISPLAY_APOLLO_TIMINGS
-	 */
-	char *p = buffer;
-	/* Parameter of chipset : */
-
-	/* Miscellaneous control 1 */
-	p = print_apollo_chipset_control1(buffer, bmide_dev);
-
-	/* Miscellaneous control 2 */
-	p = print_apollo_chipset_control2(p, bmide_dev);
-	/* Parameters of drives: */
-
-	/* Header */
-	p += sprintf(p, "------------------Primary IDE------------Secondary IDE-----\n");
-	p = print_apollo_chipset_control3(p, bmide_dev, 0);
-	p = print_apollo_ide_config(p, bmide_dev);
-	p += sprintf(p, "--------------drive0------drive1-------drive0------drive1----\n");
-	p = print_apollo_chipset_control3(p, bmide_dev, 1);
-	p = print_apollo_drive_config(p, bmide_dev);
- 
-	return p-buffer;	/* hoping it is less than 4K... */
-}
-
-#endif  /* defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-/*
- *  Used to set Fifo configuration via kernel command line:
- */
-
-byte via_proc = 0;
-byte fifoconfig = 0;
-static byte newfifo = 0;
-
-/* Used to just intialize once Fifo configuration */
-static short int done = 0;
-
-/*
- *  Set VIA Chipset Timings for (U)DMA modes enabled.
- *
- *  VIA Apollo chipset has complete support for
- *  setting up the timing parameters.
- */
-static void set_via_timings (ide_hwif_t *hwif)
-{
-	struct pci_dev  *dev = hwif->pci_dev;
-	byte post  = hwif->channel ? 0x30 : 0xc0;
-	byte flush = hwif->channel ? 0x50 : 0xa0;
-	int mask = hwif->channel ? ((newfifo & 0x60) ? 0 : 1) :
-				   (((newfifo & 0x60) == 0x60) ? 1 : 0);
-	byte via_config = 0;
-	int rc = 0, errors = 0;
-
-	printk("%s: VIA Bus-Master ", hwif->name);
-
-	/*
-	 * setting IDE read prefetch buffer and IDE post write buffer.
-	 * (This feature allows prefetched reads and post writes).
-	 */
-	if ((rc = pci_read_config_byte(dev, 0x41, &via_config))) 
-		errors++;
-
-	if (mask) {
-		if ((rc = pci_write_config_byte(dev, 0x41, via_config & ~post)))
-			errors++;
-	} else {
-		if ((rc = pci_write_config_byte(dev, 0x41, via_config | post)))
-			errors++;
-	}
-
-	/*
-	 * setting Channel read and End-of-sector FIFO flush.
-	 * (This feature ensures that FIFO flush is enabled:
-	 *  - for read DMA when interrupt asserts the given channel.
-	 *  - at the end of each sector for the given channel.)
-	 */
-	if ((rc = pci_read_config_byte(dev, 0x46, &via_config)))
-		errors++;
-
-	if (mask) {
-		if ((rc = pci_write_config_byte(dev, 0x46, via_config & ~flush)))
-			errors++;
-	} else {
-		if ((rc = pci_write_config_byte(dev, 0x46, via_config | flush)))
-			errors++;
-	}
-
-	if (!hwif->dma_base)
-		printk("Config %s. No DMA Enabled\n",
-			errors ? "ERROR":"Success");
-	else
-		printk("(U)DMA Timing Config %s\n",
-			errors ? "ERROR" : "Success");
-}
-
-/*
- *  Sets VIA 82cxxx FIFO configuration:
- *  This chipsets gets a splitable fifo. This can be driven either by command
- *  line option (eg "splitfifo=2,2,3" which asks this driver to switch all the 
- *  16 fifo levels to the second drive, and give it a threshold of 3 for (u)dma 
- *  triggering.
- */
-
-static int via_set_fifoconfig(ide_hwif_t *hwif)
-{
-	byte fifo;
-	unsigned int timings;
-	struct pci_dev  *dev = hwif->pci_dev;
-
-	/* read port configuration */
-	if (pci_read_config_dword(dev, 0x40, &timings))
-		return 1;
-   
-	/* first read actual fifo config: */
-	if (pci_read_config_byte(dev, 0x43, &fifo))
-		return 1;
-
-	/* keep 4 and 7 bit as they seem to differ between chipsets flavors... */
-	newfifo = fifo & 0x90;
-
-	if (fifoconfig) {
-		/* we received a config request from kernel command line: */
-		newfifo |= fifoconfig & 0x6f;
-	} else {
-		/* If ever just one channel is unused, allocate all fifo levels to it
-		 * and give it a 3/4 threshold for (u)dma transfers.
-		 * Otherwise, share it evenly between channels:
-		 */
-		if ((timings & 3) == 2) {
-			/* only primary channel is  enabled
-			 * 16 buf. to prim. chan. thresh=3/4
-			 */
-			newfifo |= 0x06;
-		} else if ((timings & 3) == 1) {
-			/* only secondary channel is enabled!
-			 * 16 buffers to sec. ch. thresh=3/4
-			 */
-			newfifo |= 0x69;
-		} else {
-			/* fifo evenly distributed: */
-			newfifo |= 0x2a;
-		}
-	}
-
-	/* write resulting configuration to chipset: */
-	if (pci_write_config_byte(dev, 0x43, newfifo))
-		return 1;
-
-	/* and then reread it to get the actual one */
-	if (pci_read_config_byte(dev, 0x43, &newfifo))
-		return 1;
-
-	/* print a kernel report: */
-	printk("Split FIFO Configuration: %s Primary buffers, threshold = %s\n",
-		((newfifo & 0x60) == 0x60)	? " 0" :
-		((newfifo & 0x60)		? " 8" : "16"),
-		!(newfifo & 0x0c)		? "1" :
-		(!(newfifo & 0x08)		? "3/4" :
-		(newfifo & 0x04)		? "1/4" : "1/2"));
-
-	printk("                          %s Second. buffers, threshold = %s\n",
-		((newfifo & 0x60) == 0x60)	? "16" :
-		((newfifo & 0x60)		? " 8" : " 0"),
-		!(newfifo & 0x03)		? "1" :
-		(!(newfifo & 0x02)		? "3/4" :
-		(newfifo & 0x01)		? "1/4" : "1/2"));
-	return 0;
-}
-
-#ifdef CONFIG_VIA82CXXX_TUNING
-
-static int via82cxxx_tune_chipset (ide_drive_t *drive, byte speed)
-{
-	struct hd_driveid *id	= drive->id;
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned long dma_base	= hwif->dma_base;
-	byte unit		= (drive->select.b.unit & 0x01);
-	int drive_number	= ((hwif->channel ? 2 : 0) + unit);
-
-	byte ata2_pci		= 0x00;
-	byte ata3_pci		= 0x00;
-	byte timing		= 0x00;
-	byte ultra		= 0x00;
-	int			err;
-
-	int bus_speed		= ide_system_bus_speed();
-
-	switch(drive_number) {
-		case 0: ata2_pci = 0x48; ata3_pci = 0x50; break;
-		case 1: ata2_pci = 0x49; ata3_pci = 0x51; break;
-		case 2: ata2_pci = 0x4a; ata3_pci = 0x52; break;
-		case 3: ata2_pci = 0x4b; ata3_pci = 0x53; break;
-		default:
-			return err;
-	}
-
-	pci_read_config_byte(dev, ata2_pci, &timing);
-	pci_read_config_byte(dev, ata3_pci, &ultra);
-
-	switch(speed) {
-		case XFER_UDMA_4:
-		case XFER_UDMA_3:
-		case XFER_UDMA_2:
-		case XFER_UDMA_1:
-		case XFER_UDMA_0:
-		case XFER_MW_DMA_2:
-		case XFER_MW_DMA_1:
-		case XFER_MW_DMA_0:
-		case XFER_SW_DMA_2:
-		case XFER_SW_DMA_1:
-		case XFER_SW_DMA_0:
-		case XFER_PIO_4:
-		case XFER_PIO_3:
-		case XFER_PIO_2:
-		case XFER_PIO_1:
-		case XFER_PIO_0:
-		case XFER_PIO_SLOW:
-		default:
-			break;
-	}
-
-	pci_write_config_byte(dev, ata2_pci, timing);
-	pci_write_config_byte(dev, ata3_pci, ultra);
-
-	err = ide_config_drive_speed(drive, speed);
-
-	return(err);
-}
-
-static int config_chipset_for_dma (ide_drive_t *drive)
-{
-	struct hd_driveid *id	= drive->id;
-	byte speed		= 0x00;
-	int  rval;
-
-	if ((id->dma_ultra & 0x0010) && (HWIF(drive)->udma_four)) {
-		speed = XFER_UDMA_4;
-	} else if ((id->dma_ultra & 0x0008) && (HWIF(drive)->udma_four)) {
-		speed = XFER_UDMA_3;
-	} else if (id->dma_ultra & 0x0004) {
-		speed = XFER_UDMA_2;
-	} else if (id->dma_ultra & 0x0002) {
-		speed = XFER_UDMA_1;
-	} else if (id->dma_ultra & 0x0001) {
-		speed = XFER_UDMA_0;
-	} else if (id->dma_mword & 0x0004) {
-		speed = XFER_MW_DMA_2;
-	} else if (id->dma_mword & 0x0002) {
-		speed = XFER_MW_DMA_1;
-	} else if (id->dma_mword & 0x0001) {
-		speed = XFER_MW_DMA_0;
-	} else if (id->dma_1word & 0x0004) {
-		speed = XFER_SW_DMA_2;
-	} else if (id->dma_1word & 0x0002) {
-		speed = XFER_SW_DMA_1;
-	} else if (id->dma_1word & 0x0001) {
-		speed = XFER_SW_DMA_0;
-	} else {
-		return ((int) ide_dma_off_quietly);
-	}
-
-	(void) via82cxxx_tune_chipset(drive, speed);
-
-	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
-	return rval;
-}
-
-static void config_chipset_for_pio (ide_drive_t *drive)
-{
-	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
-	unsigned short xfer_pio = drive->id->eide_pio_modes;
-	byte			timing, speed, pio;
-
-	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
-
-	if (xfer_pio> 4)
-		xfer_pio = 0;
-
-	if (drive->id->eide_pio_iordy > 0) {
-		for (xfer_pio = 5;
-			xfer_pio>0 &&
-			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
-			xfer_pio--);
-	} else {
-		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
-			   (drive->id->eide_pio_modes & 2) ? 0x04 :
-			   (drive->id->eide_pio_modes & 1) ? 0x03 :
-			   (drive->id->tPIO & 2) ? 0x02 :
-			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
-	}
-
-	timing = (xfer_pio >= pio) ? xfer_pio : pio;
-
-	switch(timing) {
-		case 4: speed = XFER_PIO_4;break;
-		case 3: speed = XFER_PIO_3;break;
-		case 2: speed = XFER_PIO_2;break;
-		case 1: speed = XFER_PIO_1;break;
-		default:
-			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;                        break;
-	}
-	(void) via82cxxx_tune_chipset(drive, speed);
-}
-
-static void via82cxxx_tune_drive (ide_drive_t *drive, byte pio)
-{
-	byte speed;
-	switch(pio) {
-		case 4:		speed = XFER_PIO_4;break;
-		case 3:		speed = XFER_PIO_3;break;
-		case 2:		speed = XFER_PIO_2;break;
-		case 1:		speed = XFER_PIO_1;break;
-		default:	speed = XFER_PIO_0;break;
-	}
-	(void) via82cxxx_tune_chipset(drive, speed);
-}
-
-static int config_drive_xfer_rate (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
-
-	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
-			goto fast_ata_pio;
-		}
-		dma_func = ide_dma_off_quietly;
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & 0x001F) {
-				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive);
-				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & 0x0007) ||
-			    (id->dma_1word & 0x0007)) {
-				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive);
-				if (dma_func != ide_dma_on)
-					goto no_dma_set;
-			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			if (id->eide_dma_time > 150) {
-				goto no_dma_set;
-			}
-			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive);
-			if (dma_func != ide_dma_on)
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
-no_dma_set:
-		config_chipset_for_pio(drive);
-	}
-	return HWIF(drive)->dmaproc(dma_func, drive);
-}
-
-int via82cxxx_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
-{
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
-}
-#endif /* CONFIG_VIA82CXXX_TUNING */
-
-unsigned int __init pci_init_via82cxxx (struct pci_dev *dev, const char *name)
-{
-	struct pci_dev *host;
-	struct pci_dev *isa;
-	int i, j, ata33, ata66;
-
-	byte revision = 0;
-
-	for (i = 0; i < arraysize (ApolloHostChipInfo) && !host_dev; i++) {
-		host = pci_find_device (PCI_VENDOR_ID_VIA,
-					ApolloHostChipInfo[i].host_id,
-					NULL);
-		if (!host)
-			continue;
-
-		host_dev = host;
-		printk(ApolloHostChipInfo[i].name);
-
-		for (j = 0; j < arraysize (ApolloISAChipInfo) && !isa_dev; j++) {
-			if (ApolloISAChipInfo[j].host_id !=
-			    ApolloHostChipInfo[i].host_id)
-				continue;
-
-			isa = pci_find_device (PCI_VENDOR_ID_VIA,
-					ApolloISAChipInfo[j].isa_id,
-					NULL);
-			if (!isa)
-				continue;
-
-			isa_dev = isa;
-
-			ata33 = 1;
-			ata66 = 0;
-
-			if (ApolloISAChipInfo[j].flags & VIA_FLAG_CHECK_REV) {
-				pci_read_config_byte(isa_dev, 0x0d, &revision);
-				ata33 = (revision >= 0x20) ? 1 : 0;
-			} else if (ApolloISAChipInfo[j].flags & VIA_FLAG_ATA_66) {
-				ata33 = 0;
-				ata66 = 1;
-			}
-
-			if (ata33 | ata66)
-				printk(" Chipset Core ATA-%s", ata66 ? "66" : "33");
-		}
-		printk("\n");
-	}
-
-#if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS)
-	via_proc = 1;
-	bmide_dev = dev;
-	via_display_info = &via_get_info;
-#endif /* DISPLAY_VIA_TIMINGS &&  CONFIG_PROC_FS*/
-
-	return 0;
-}
-
-unsigned int __init ata66_via82cxxx (ide_hwif_t *hwif)
-{
-	/* (Jeff Garzik) FIXME!!! for MVP4 */
-	return 0;
-}
-
-void __init ide_init_via82cxxx (ide_hwif_t *hwif)
-{
-	set_via_timings(hwif);
-
-#ifdef CONFIG_VIA82CXXX_TUNING
-	hwif->tuneproc = &via82cxxx_tune_drive;
-	if (hwif->dma_base) {
-		hwif->dmaproc = &via82cxxx_dmaproc;
-	} else {
-		hwif->autodma = 0;
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-	}
-#endif /* CONFIG_VIA82CXXX_TUNING */
-}
-
-/*
- *  ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long)
- *  checks if channel "channel" of if hwif is dma
- *  capable or not, according to kernel command line,
- *  and the new fifo settings.
- *  It calls "ide_setup_dma" on capable mainboards, and
- *  bypasses the setup if not capable.
- */
-
-void ide_dmacapable_via82cxxx (ide_hwif_t *hwif, unsigned long dmabase)
-{
-	if (!done) {
-		via_set_fifoconfig(hwif);
-		done = 1;
-	}
-
-	/*
-	 * check if any fifo is available for requested port:
-	 */
-	if (((hwif->channel == 0) && ((newfifo & 0x60) == 0x60)) ||
-	    ((hwif->channel == 1) && ((newfifo & 0x60) == 0x00))) {
-		printk("    %s: VP_IDE Bus-Master DMA disabled (FIFO setting)\n", hwif->name);
-	} else {
-		ide_setup_dma(hwif, dmabase, 8);
-	}
-}
diff -u --recursive --new-file v2.3.51/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v2.3.51/linux/drivers/cdrom/aztcd.c	Wed Feb 16 17:03:51 2000
+++ linux/drivers/cdrom/aztcd.c	Mon Mar 13 09:43:36 2000
@@ -1726,10 +1726,9 @@
 	               { printk("aztcd: no AZTECH CD-ROM drive found\n");
                          return -EIO;
 	               } 
-	            for (count = 0; count < AZT_TIMEOUT; count++); 
-	               { count=count*2;          /* delay a bit */
-	                 count=count/2;
-	               }                        
+	            
+	            for (count = 0; count < AZT_TIMEOUT; count++);
+	            
 	            if ((st=getAztStatus())==-1)
 	               { printk("aztcd: Drive Status Error Status=%x\n",st);
                          return -EIO;
diff -u --recursive --new-file v2.3.51/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.3.51/linux/drivers/char/Config.in	Fri Mar 10 16:40:42 2000
+++ linux/drivers/char/Config.in	Sat Mar 11 11:27:14 2000
@@ -219,7 +219,7 @@
    fi
    dep_tristate '  Zoran ZR36057/36060 Video For Linux' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV $CONFIG_PCI
    dep_tristate '    Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN
-   dep_tristate '  Zoran ZR36120/36125 Video For Linux' CONFIG_VIDEO_ZR36120 $CONFIG_VIDEO_DEV $CONFIG_PCI
+   dep_tristate '  Zoran ZR36120/36125 Video For Linux' CONFIG_VIDEO_ZR36120 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C
 fi
 
 endmenu
diff -u --recursive --new-file v2.3.51/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.3.51/linux/drivers/char/bttv.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/char/bttv.c	Sun Mar 12 19:12:09 2000
@@ -719,12 +719,14 @@
 
 /* ----------------------------------------------------------------------- */
 
+/* for some vendors it is just the PCI ID */
 static struct VENDOR {
 	int id;
 	char *name;
 } vendors[] = {
 	{ 0x0001, "ATI Technologies Inc" },
 	{ 0x10b4, "STB Systems Inc" },
+	{ 0x1118, "Terratec" },
 	{ 0x13eb, "Hauppauge Computer Works Inc" },
 	{ 0x1461, "Avermedia" },
 	{ 0x1850, "Chronos" },
@@ -743,6 +745,7 @@
 } cards[] = {
 	{ 0x0001, 0x1002, BTTV_HAUPPAUGE878,  "TV Wonder" },
 	{ 0x10b4, 0x2636, BTTV_HAUPPAUGE878,  "???" },
+	{ 0x1118, 0x153b, BTTV_TERRATVALUE,   "TV Value" },
 	{ 0x13eb, 0x0070, BTTV_HAUPPAUGE878,  "WinTV" },
 	{ 0x1461, 0x0002, BTTV_AVERMEDIA98,   "TVCapture 98" },
 	{ 0x1850, 0x1851, BTTV_CHRONOS_VS2,   "Video Shuttle II" },
@@ -2018,6 +2021,7 @@
                 btv->gbuf[i].stat = GBUFFER_UNUSED;
 
         burst(0);
+	set_pll(btv);
         btv->user++;
 	up(&btv->lock);
         MOD_INC_USE_COUNT;
@@ -2235,6 +2239,7 @@
 		if (btv->win.norm != v.mode) {
 			btv->win.norm = v.mode;
                         down(&btv->lock);
+			set_pll(btv);
 			make_vbitab(btv);
 			bt848_set_winsize(btv);
 			up(&btv->lock);
@@ -2866,6 +2871,7 @@
 static int radio_open(struct video_device *dev, int flags)
 {
 	struct bttv *btv = (struct bttv *)(dev-1);
+	unsigned long v;
 
         down(&btv->lock);
 	if (btv->user)
@@ -2873,6 +2879,8 @@
 	btv->user++;
 
 	btv->radio = 1;
+	v = 400*16;
+	call_i2c_clients(btv,VIDIOCSFREQ,&v);
 	call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
 	bt848_muxsel(btv,0);
 	up(&btv->lock);
diff -u --recursive --new-file v2.3.51/linux/drivers/char/bttv.h linux/drivers/char/bttv.h
--- v2.3.51/linux/drivers/char/bttv.h	Tue Mar  7 14:32:25 2000
+++ linux/drivers/char/bttv.h	Sun Mar 12 19:12:09 2000
@@ -21,7 +21,7 @@
 #ifndef _BTTV_H_
 #define _BTTV_H_
 
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,21) 
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,22) 
 
 #include <linux/types.h>
 #include <linux/wait.h>
diff -u --recursive --new-file v2.3.51/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.3.51/linux/drivers/char/console.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/char/console.c	Sun Mar 12 19:18:55 2000
@@ -369,7 +369,7 @@
 static void update_attr(int currcons)
 {
 	attr = build_attr(currcons, color, intensity, blink, underline, reverse ^ decscnm);
-	video_erase_char = (build_attr(currcons, color, 1, 0, 0, decscnm) << 8) | ' ';
+	video_erase_char = (build_attr(currcons, color, 1, blink, 0, decscnm) << 8) | ' ';
 }
 
 /* Note: inverting the screen twice should revert to the original state */
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/auth.c linux/drivers/char/drm/auth.c
--- v2.3.51/linux/drivers/char/drm/auth.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/auth.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* auth.c -- IOCTLs for authentication -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:31:48 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -23,9 +22,9 @@
  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
- * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/bufs.c linux/drivers/char/drm/bufs.c
--- v2.3.51/linux/drivers/char/drm/bufs.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/bufs.c	Tue Mar 14 09:27:31 2000
@@ -1,8 +1,7 @@
 /* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Dec  3 12:11:11 1999 by faith@precisioninsight.com
  *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,13 +23,12 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.8 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
 #define __NO_VERSION__
-#include <linux/config.h>
 #include "drmP.h"
 #include "linux/un.h"
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/context.c linux/drivers/char/drm/context.c
--- v2.3.51/linux/drivers/char/drm/context.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/context.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:32:09 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/dma.c linux/drivers/char/drm/dma.c
--- v2.3.51/linux/drivers/char/drm/dma.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/dma.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* dma.c -- DMA IOCTL and function support -*- linux-c -*-
  * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:39 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.7 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/drawable.c linux/drivers/char/drm/drawable.c
--- v2.3.51/linux/drivers/char/drm/drawable.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/drawable.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* drawable.c -- IOCTLs for drawables -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:03 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/drm.h linux/drivers/char/drm/drm.h
--- v2.3.51/linux/drivers/char/drm/drm.h	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/drm.h	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* drm.h -- Header for Direct Rendering Manager -*- linux-c -*-
  * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 17:11:19 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All rights reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.46 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  * Acknowledgements:
  * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/drmP.h linux/drivers/char/drm/drmP.h
--- v2.3.51/linux/drivers/char/drm/drmP.h	Tue Dec 14 01:27:24 1999
+++ linux/drivers/char/drm/drmP.h	Tue Mar 14 18:36:43 2000
@@ -1,6 +1,5 @@
 /* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
  * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 16:06:49 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All rights reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.58 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  * 
  */
 
@@ -50,6 +49,10 @@
 #ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
 #endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <asm/spinlock.h>
+#include <linux/poll.h>
+#endif
 #include "drm.h"
 
 #define DRM_DEBUG_CODE 2	  /* Include debugging code (if > 1, then
@@ -475,6 +478,7 @@
 extern ssize_t	     drm_read(struct file *filp, char *buf, size_t count,
 			      loff_t *off);
 extern int	     drm_write_string(drm_device_t *dev, const char *s);
+extern unsigned int  drm_poll(struct file *filp, struct poll_table_struct *wait);
 
 				/* Mapping support (vm.c) */
 #if LINUX_VERSION_CODE < 0x020317
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/fops.c linux/drivers/char/drm/fops.c
--- v2.3.51/linux/drivers/char/drm/fops.c	Tue Dec 14 01:27:24 1999
+++ linux/drivers/char/drm/fops.c	Tue Mar 14 09:27:31 2000
@@ -1,8 +1,7 @@
 /* fops.c -- File operations for DRM -*- linux-c -*-
  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Dec  3 10:26:26 1999 by faith@precisioninsight.com
  *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,8 +23,9 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.3 1999/08/20 15:36:45 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ *    Daryll Strauss <daryll@precisioninsight.com>
  *
  */
 
@@ -220,5 +220,15 @@
 #endif
 	DRM_DEBUG("waking\n");
 	wake_up_interruptible(&dev->buf_readers);
+	return 0;
+}
+
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+	drm_file_t   *priv = filp->private_data;
+	drm_device_t *dev  = priv->dev;
+
+	poll_wait(filp, &dev->buf_readers, wait);
+	if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
 	return 0;
 }
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/gamma_dma.c linux/drivers/char/drm/gamma_dma.c
--- v2.3.51/linux/drivers/char/drm/gamma_dma.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/gamma_dma.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*-
  * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:37 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.9 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/gamma_drv.c linux/drivers/char/drm/gamma_drv.c
--- v2.3.51/linux/drivers/char/drm/gamma_drv.c	Tue Jan  4 13:57:16 2000
+++ linux/drivers/char/drm/gamma_drv.c	Tue Mar 14 09:27:31 2000
@@ -1,8 +1,7 @@
 /* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:36 1999 by faith@precisioninsight.com
  *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,12 +23,11 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.17 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
-#include <linux/config.h>
 #include "drmP.h"
 #include "gamma_drv.h"
 EXPORT_SYMBOL(gamma_init);
@@ -52,6 +50,7 @@
 	mmap:	 drm_mmap,
 	read:	 drm_read,
 	fasync:	 drm_fasync,
+	poll:	 drm_poll,
 };
 
 static struct miscdevice      gamma_misc = {
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/gamma_drv.h linux/drivers/char/drm/gamma_drv.h
--- v2.3.51/linux/drivers/char/drm/gamma_drv.h	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/gamma_drv.h	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*-
  * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:24:27 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All rights reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  * 
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/init.c linux/drivers/char/drm/init.c
--- v2.3.51/linux/drivers/char/drm/init.c	Tue Jan  4 13:57:16 2000
+++ linux/drivers/char/drm/init.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* init.c -- Setup/Cleanup for DRM -*- linux-c -*-
  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.3 1999/08/20 15:07:01 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/ioctl.c linux/drivers/char/drm/ioctl.c
--- v2.3.51/linux/drivers/char/drm/ioctl.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/ioctl.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* ioctl.c -- IOCTL processing for DRM -*- linux-c -*-
  * Created: Fri Jan  8 09:01:26 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/lists.c linux/drivers/char/drm/lists.c
--- v2.3.51/linux/drivers/char/drm/lists.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/lists.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* lists.c -- Buffer list handling routines -*- linux-c -*-
  * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 16:04:44 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.3 1999/08/20 15:07:02 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
@@ -154,7 +153,7 @@
 	buf->list	= DRM_LIST_FREE;
 	do {
 		old       = bl->next;
-		bl->next  = old;
+		buf->next = old;
 		prev      = cmpxchg(&bl->next, old, buf);
 		if (++count > DRM_LOOPING_LIMIT) {
 			DRM_ERROR("Looping\n");
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/lock.c linux/drivers/char/drm/lock.c
--- v2.3.51/linux/drivers/char/drm/lock.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/lock.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* lock.c -- IOCTLs for locking -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 16:04:44 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/memory.c linux/drivers/char/drm/memory.c
--- v2.3.51/linux/drivers/char/drm/memory.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/memory.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* memory.c -- Memory management wrappers for DRM -*- linux-c -*-
  * Created: Thu Feb  4 14:00:34 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 10:28:18 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.4 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/proc.c linux/drivers/char/drm/proc.c
--- v2.3.51/linux/drivers/char/drm/proc.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/proc.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* proc.c -- /proc support for DRM -*- linux-c -*-
  * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
- * Revised: Fri Dec  3 09:44:16 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.4 1999/08/20 15:36:46 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
@@ -79,26 +78,26 @@
 	struct proc_dir_entry *ent;
 	int		      i, j;
 
-	drm_root = create_proc_entry("graphics", S_IFDIR, NULL);
+	drm_root = create_proc_entry("dri", S_IFDIR, NULL);
 	if (!drm_root) {
-		DRM_ERROR("Cannot create /proc/graphics\n");
+		DRM_ERROR("Cannot create /proc/dri\n");
 		return -1;
 	}
 
 				/* Instead of doing this search, we should
-				   add some global support for /proc/graphics. */
+				   add some global support for /proc/dri. */
 	for (i = 0; i < 8; i++) {
-		sprintf(drm_slot_name, "graphics/%d", i);
+		sprintf(drm_slot_name, "dri/%d", i);
 		drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL);
 		if (!drm_dev_root) {
 			DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name);
-			remove_proc_entry("graphics", NULL);
+			remove_proc_entry("dri", NULL);
 		}
 		if (drm_dev_root->nlink == 2) break;
 		drm_dev_root = NULL;
 	}
 	if (!drm_dev_root) {
-		DRM_ERROR("Cannot find slot in /proc/graphics\n");
+		DRM_ERROR("Cannot find slot in /proc/dri\n");
 		return -1;
 	}
 
@@ -112,7 +111,7 @@
 				remove_proc_entry(drm_proc_list[i].name,
 						  drm_dev_root);
 			remove_proc_entry(drm_slot_name, NULL);
-			remove_proc_entry("graphics", NULL);
+			remove_proc_entry("dri", NULL);
 			return -1;
 		}
 		ent->read_proc = drm_proc_list[i].f;
@@ -135,7 +134,7 @@
 			}
 			remove_proc_entry(drm_slot_name, NULL);
 		}
-		remove_proc_entry("graphics", NULL);
+		remove_proc_entry("dri", NULL);
 		remove_proc_entry(DRM_NAME, NULL);
 	}
 	drm_root = drm_dev_root = NULL;
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/tdfx_context.c linux/drivers/char/drm/tdfx_context.c
--- v2.3.51/linux/drivers/char/drm/tdfx_context.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/tdfx_context.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*-
  * Created: Thu Oct  7 10:50:22 1999 by faith@precisioninsight.com
- * Revised: Sat Oct  9 23:39:56 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI$
- * $XFree86$
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/tdfx_drv.c linux/drivers/char/drm/tdfx_drv.c
--- v2.3.51/linux/drivers/char/drm/tdfx_drv.c	Tue Jan  4 13:57:16 2000
+++ linux/drivers/char/drm/tdfx_drv.c	Tue Mar 14 09:27:31 2000
@@ -1,8 +1,7 @@
 /* tdfx.c -- tdfx driver -*- linux-c -*-
  * Created: Thu Oct  7 10:38:32 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:35 1999 by faith@precisioninsight.com
  *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -23,17 +22,15 @@
  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
- * 
- * $PI$
- * $XFree86$
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ *    Daryll Strauss <daryll@precisioninsight.com>
  *
  */
 
-#include <linux/config.h>
 #include "drmP.h"
 #include "tdfx_drv.h"
-EXPORT_SYMBOL(tdfx_init);
-EXPORT_SYMBOL(tdfx_cleanup);
 
 #define TDFX_NAME	 "tdfx"
 #define TDFX_DESC	 "tdfx"
@@ -53,6 +50,7 @@
 	mmap:	 drm_mmap,
 	read:	 drm_read,
 	fasync:	 drm_fasync,
+	poll:	 drm_poll,
 };
 
 static struct miscdevice      tdfx_misc = {
@@ -542,6 +540,12 @@
 #endif
 		}
         }
+
+	if (lock.context != tdfx_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+
         DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
 
 #if DRM_DMA_HISTOGRAM
@@ -582,6 +586,11 @@
 		}
 	}
 	
+	if (lock.context != tdfx_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/tdfx_drv.h linux/drivers/char/drm/tdfx_drv.h
--- v2.3.51/linux/drivers/char/drm/tdfx_drv.h	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/tdfx_drv.h	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*-
  * Created: Thu Oct  7 10:40:04 1999 by faith@precisioninsight.com
- * Revised: Sat Oct  9 23:38:19 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All rights reserved.
@@ -24,8 +23,9 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI$
- * $XFree86$
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ *    Daryll Strauss <daryll@precisioninsight.com>
  * 
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/drm/vm.c linux/drivers/char/drm/vm.c
--- v2.3.51/linux/drivers/char/drm/vm.c	Wed Dec  8 14:11:25 1999
+++ linux/drivers/char/drm/vm.c	Tue Mar 14 09:27:31 2000
@@ -1,6 +1,5 @@
 /* vm.c -- Memory mapping for DRM -*- linux-c -*-
  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 16:54:35 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.7 1999/08/21 02:48:34 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/generic_serial.c linux/drivers/char/generic_serial.c
--- v2.3.51/linux/drivers/char/generic_serial.c	Tue Feb  1 01:35:43 2000
+++ linux/drivers/char/generic_serial.c	Tue Mar 14 17:54:42 2000
@@ -18,90 +18,12 @@
 #include <linux/serial.h>
 #include <linux/mm.h>
 #include <asm/semaphore.h>
-#include <linux/version.h>
-
-
-#if LINUX_VERSION_CODE < 0x020100    /* Less than 2.1.0 */
-#define TWO_ZERO
-#else
-#if LINUX_VERSION_CODE < 0x020200   /* less than 2.2.x */
-#warning "Please use a 2.2.x kernel. "
-#else
-#if LINUX_VERSION_CODE < 0x020300   /* less than 2.2.x */
-#define TWO_TWO
-#else
-#define TWO_THREE
-#endif
-#endif
-#endif
-
-#ifdef TWO_ZERO
-
-/* Here is the section that makes the 2.2 compatible driver source 
-   work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2, 
-   and provide for compatibility stuff here if possible. */
-
-/* Some 200 days (on intel) */
-#define MAX_SCHEDULE_TIMEOUT     ((long)(~0UL>>1))
-
-
-#ifndef MODULE
-
-#define copy_to_user(a,b,c)          memcpy_tofs(a,b,c)
-
-static inline int copy_from_user(void *to,const void *from, int c) 
-{
-	memcpy_fromfs(to, from, c);
-	return 0;
-}
-
-
-#define capable(x)                   suser()
-
-#define queue_task                   queue_task_irq_off
-#define tty_flip_buffer_push(tty)    queue_task(&tty->flip.tqueue, &tq_timer)
-#define signal_pending(current)      (current->signal & ~current->blocked)
-#define schedule_timeout(to)         do {current->timeout = jiffies + (to);schedule ();} while (0)
-#define time_after(t1,t2)            (((long)t1-t2) > 0)
-
-#define test_and_set_bit(nr, addr)   set_bit(nr, addr)
-#define test_and_clear_bit(nr, addr) clear_bit(nr, addr)
-
-/* Not yet implemented on 2.0 */
-#define ASYNC_SPD_SHI  -1
-#define ASYNC_SPD_WARP -1
-
-
-
-/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it
-   to the "name" field that does exist. As long as the assignments are
-   done in the right order, there is nothing to worry about. */
-#define driver_name           name 
-
-
-/* Should be in a header somewhere. */
-#define TTY_HW_COOK_OUT       14 /* Flag to tell ntty what we can handle */
-#define TTY_HW_COOK_IN        15 /* in hardware - output and input       */
-#endif
-
-#endif
-
-#ifndef TWO_ZERO
-/* This include is new with 2.2  (and required!) */
 #include <asm/uaccess.h>
-#endif
-
-#ifndef TWO_THREE
-/* These are new in 2.3. The source now uses 2.3 syntax, and here is 
-   the compatibility define... */
-#define wait_queue_head_t struct wait_queue *
-#define DECLARE_MUTEX(name) struct semaphore name = MUTEX
-#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL }
-
-#endif
-
-#include "generic_serial.h"
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/generic_serial.h>
 
+#define DEBUG 
 
 static char *                  tmp_buf; 
 static DECLARE_MUTEX(tmp_buf_sem);
@@ -118,8 +40,6 @@
 #define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n")
 #define func_exit()  gs_dprintk (GS_DEBUG_FLOW, "gs: exit  " __FUNCTION__ "\n")
 
-
-
 #if NEW_WRITE_LOCKING
 #define DECL      /* Nothing */
 #define LOCKIT    down (& port->port_write_sem);
@@ -130,6 +50,28 @@
 #define RELEASEIT restore_flags (flags)
 #endif
 
+#define RS_EVENT_WRITE_WAKEUP	1
+
+#ifdef DEBUG
+static void my_hd (unsigned char *addr, int len)
+{
+	int i, j, ch;
+
+	for (i=0;i<len;i+=16) {
+		printk ("%08x ", (int) addr+i);
+		for (j=0;j<16;j++) {
+			printk ("%02x %s", addr[j+i], (j==7)?" ":"");
+		}
+		for (j=0;j<16;j++) {
+			ch = addr[j+i];
+			printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
+		}
+		printk ("\n");
+	}
+}
+#else
+#define my_hd(addr,len) 
+#endif
 
 
 void gs_put_char(struct tty_struct * tty, unsigned char ch)
@@ -393,16 +335,19 @@
 	if (!tty) return 0;
 	port = tty->driver_data;
 
+	if (!port->rd) return 0;
+	if (!port->rd->chars_in_buffer) return 0;
+
 	func_exit ();
 	return port->xmit_cnt + port->rd->chars_in_buffer (port);
 }
 
 
-static void gs_wait_tx_flushed (void * ptr, int timeout) 
+static int gs_wait_tx_flushed (void * ptr, int timeout) 
 {
 	struct gs_port *port = ptr;
 	long end_jiffies;
-	int jiffies_to_transmit, charsleft;
+	int jiffies_to_transmit, charsleft = 0, rv = 0;
 	int to, rcib;
 
 	func_enter();
@@ -416,7 +361,7 @@
 	if (!port || port->xmit_cnt < 0 || !port->xmit_buf) {
 		gs_dprintk (GS_DEBUG_FLUSH, "ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0.\n");
 	func_exit();
-		return;  /* This is an error which we don't know how to handle. */
+		return -EINVAL;  /* This is an error which we don't know how to handle. */
 	}
 	gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 1\n");
 
@@ -426,8 +371,8 @@
 
 	if(rcib <= 0) {
 		gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n");
-	func_exit();
-	return;
+		func_exit();
+		return rv;
 	}
 	gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 3\n");
 
@@ -460,14 +405,18 @@
 
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(jiffies_to_transmit);
-		if (signal_pending (current))
+		if (signal_pending (current)) {
+			gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: "); 
+			rv = -EINTR;
 			break;
+		}
 	}
 
 	gs_dprintk (GS_DEBUG_FLUSH, "charsleft = %d.\n", charsleft); 
 	current->state = TASK_RUNNING;
 
 	func_exit();
+	return rv;
 }
 
 
@@ -541,7 +490,7 @@
 void gs_shutdown_port (struct gs_port *port)
 {
 	long flags;
-
+	func_enter();
 	if (!(port->flags & ASYNC_INITIALIZED))
 		return;
 
@@ -560,6 +509,7 @@
 
 	port->flags &= ~ASYNC_INITIALIZED;
 	restore_flags (flags);
+	func_exit();
 }
 
 
@@ -746,8 +696,10 @@
 		func_exit();
 		return;
 	}
+
 	if (!port->tty) {
-		printk (KERN_WARNING "gs: Odd: port->tty is NULL\n");
+		/* This seems to happen when this is called from vhangup. */
+		gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->tty is NULL\n");
 		port->tty = tty;
 	}
 
@@ -771,6 +723,7 @@
 		port->count = 0;
 	}
 	if (port->count) {
+		gs_dprintk(GS_DEBUG_CLOSE, "gs_close: count: %d\n", port->count);
 		restore_flags(flags);
 		func_exit ();
 		return;
@@ -802,6 +755,7 @@
 
 	port->rd->disable_rx_interrupts (port);
 
+	/* close has no way of returning "EINTR", so discard return value */
 	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
 		gs_wait_tx_flushed (port, port->closing_wait); 
 
@@ -812,8 +766,12 @@
 	if (tty->ldisc.flush_buffer)
 		tty->ldisc.flush_buffer(tty);
 	tty->closing = 0;
+
 	port->event = 0;
+	port->rd->close (port);
+	port->rd->shutdown_port (port);
 	port->tty = 0;
+
 	if (port->blocked_open) {
 		if (port->close_delay) {
 			current->state = TASK_INTERRUPTIBLE;
@@ -825,8 +783,6 @@
 	                 ASYNC_CLOSING | ASYNC_INITIALIZED);
 	wake_up_interruptible(&port->close_wait);
 
-	port->rd->close (port);
-	port->rd->shutdown_port (port);
 	restore_flags(flags);
 	func_exit ();
 }
@@ -842,7 +798,7 @@
                      struct termios * old_termios)
 {
 	struct gs_port *port = tty->driver_data;
-	int baudrate, tmp;
+	int baudrate, tmp, rv;
 	struct termios *tiosp;
 
 	func_enter();
@@ -867,7 +823,7 @@
 		   && (tiosp->c_line  == old_termios->c_line)
 		   && (memcmp(tiosp->c_cc, old_termios->c_cc, NCC) == 0)) {
 			gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: optimized away\n");
-			return;
+			return /* 0 */;
 		}
 	} else 
 		gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: no old_termios: "
@@ -923,9 +879,11 @@
 
 	/* We should really wait for the characters to be all sent before
 	   changing the settings. -- CAL */
-	gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
+	rv = gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
+	if (rv < 0) return /* rv */;
 
-	port->rd->set_real_termios(port);
+	rv = port->rd->set_real_termios(port);
+	if (rv < 0) return /* rv */;
 
 	if ((!old_termios || 
 	     (old_termios->c_cflag & CRTSCTS)) &&
@@ -943,7 +901,7 @@
 #endif
 
 	func_exit();
-	return;
+	return /* 0 */;
 }
 
 
@@ -1067,3 +1025,15 @@
 	copy_to_user(sp, &sio, sizeof(struct serial_struct));
 }
 
+
+#ifdef MODULE
+int init_module (void)
+{
+  return 0;
+}
+
+int cleanup_module (void)
+{
+  return 0;
+}
+#endif
diff -u --recursive --new-file v2.3.51/linux/drivers/char/generic_serial.h linux/drivers/char/generic_serial.h
--- v2.3.51/linux/drivers/char/generic_serial.h	Tue Feb  1 01:35:43 2000
+++ linux/drivers/char/generic_serial.h	Wed Dec 31 16:00:00 1969
@@ -1,104 +0,0 @@
-/*
- *  generic_serial.h
- *
- *  Copyright (C) 1998 R.E.Wolff@BitWizard.nl
- *
- *  written for the SX serial driver.
- *     Contains the code that should be shared over all the serial drivers.
- *
- *  Version 0.1 -- December, 1998.
- */
-
-#ifndef GENERIC_SERIAL_H
-#define GENERIC_SERIAL_H
-
-#define RS_EVENT_WRITE_WAKEUP 0
-
-struct real_driver {
-  void                    (*disable_tx_interrupts) (void *);
-  void                    (*enable_tx_interrupts) (void *);
-  void                    (*disable_rx_interrupts) (void *);
-  void                    (*enable_rx_interrupts) (void *);
-  int                     (*get_CD) (void *);
-  void                    (*shutdown_port) (void*);
-  void                    (*set_real_termios) (void*);
-  int                     (*chars_in_buffer) (void*);
-  void                    (*close) (void*);
-  void                    (*hungup) (void*);
-  void                    (*getserial) (void*, struct serial_struct *sp);
-};
-
-
-
-struct gs_port {
-  int                     magic;
-  unsigned char           *xmit_buf; 
-  int                     xmit_head;
-  int                     xmit_tail;
-  int                     xmit_cnt;
-  /*  struct semaphore        port_write_sem; */
-  int                     flags;
-  struct termios          normal_termios;
-  struct termios          callout_termios;
-  wait_queue_head_t       open_wait;
-  wait_queue_head_t       close_wait;
-  long                    session;
-  long                    pgrp;
-  int                     count;
-  int                     blocked_open;
-  struct tty_struct       *tty;
-  int                     event;
-  unsigned short          closing_wait;
-  int                     close_delay;
-  struct real_driver      *rd;
-  int                     wakeup_chars;
-  int                     baud_base;
-  int                     baud;
-  int                     custom_divisor;
-};
-
-
-/* Flags */
-/* Warning: serial.h defines some ASYNC_ flags, they say they are "only"
-   used in serial.c, but they are also used in all other serial drivers. 
-   Make sure they don't clash with these here... */
-#define GS_TX_INTEN      0x00800000
-#define GS_RX_INTEN      0x00400000
-#define GS_ACTIVE        0x00200000
-
-
-
-#define GS_TYPE_NORMAL   1
-#define GS_TYPE_CALLOUT  2
-
-
-#define GS_DEBUG_FLUSH   0x00000001
-#define GS_DEBUG_BTR     0x00000002
-#define GS_DEBUG_TERMIOS 0x00000004
-#define GS_DEBUG_STUFF   0x00000008
-#define GS_DEBUG_CLOSE   0x00000010
-#define GS_DEBUG_FLOW    0x00000020
-
-
-void gs_put_char(struct tty_struct *tty, unsigned char ch);
-int  gs_write(struct tty_struct *tty, int from_user, 
-             const unsigned char *buf, int count);
-int  gs_write_room(struct tty_struct *tty);
-int  gs_chars_in_buffer(struct tty_struct *tty);
-void gs_flush_buffer(struct tty_struct *tty);
-void gs_flush_chars(struct tty_struct *tty);
-void gs_stop(struct tty_struct *tty);
-void gs_start(struct tty_struct *tty);
-void gs_hangup(struct tty_struct *tty);
-void gs_do_softint(void *private_);
-int  block_til_ready(void *port, struct file *filp);
-void gs_close(struct tty_struct *tty, struct file *filp);
-void gs_set_termios (struct tty_struct * tty, 
-                     struct termios * old_termios);
-int  gs_init_port(struct gs_port *port);
-int  gs_setserial(struct gs_port *port, struct serial_struct *sp);
-void gs_getserial(struct gs_port *port, struct serial_struct *sp);
-
-extern int gs_debug;
-
-#endif
diff -u --recursive --new-file v2.3.51/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.3.51/linux/drivers/char/mem.c	Fri Mar 10 16:40:42 2000
+++ linux/drivers/char/mem.c	Mon Mar 13 10:21:50 2000
@@ -26,9 +26,6 @@
 #include <asm/io.h>
 #include <asm/pgalloc.h>
 
-#ifdef CONFIG_VIDEO_BT848
-extern int i2c_init(void);
-#endif
 #ifdef CONFIG_I2C
 extern int i2c_init_all(void);
 #endif
@@ -52,7 +49,6 @@
 #endif
 #ifdef CONFIG_FB
 extern void fbmem_init(void);
-extern void fbconsole_init(void);
 #endif
 #ifdef CONFIG_PROM_CONSOLE
 extern void prom_con_init(void);
@@ -618,7 +614,6 @@
 #endif
 #if defined (CONFIG_FB)
 	fbmem_init();
-	fbconsole_init();
 #endif
 #if defined (CONFIG_PROM_CONSOLE)
 	prom_con_init();
@@ -661,9 +656,6 @@
 #endif
 #ifdef CONFIG_FTAPE
 	ftape_init();
-#endif
-#ifdef CONFIG_VIDEO_BT848
-	i2c_init();
 #endif
 #if defined(CONFIG_ADB)
 	adbdev_init();
diff -u --recursive --new-file v2.3.51/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.3.51/linux/drivers/char/misc.c	Sat Feb 26 22:31:44 2000
+++ linux/drivers/char/misc.c	Tue Mar 14 17:54:42 2000
@@ -122,6 +122,22 @@
 	open:		misc_open,
 };
 
+/**
+ *	misc_register	-	register a miscellaneous device
+ *	@misc: device structure
+ *	
+ *	Register a miscellaneous device with the kernel. If the minor
+ *	number is set to MISC_DYNAMIC_MINOR a minor number is assigned
+ *	and placed in the minor field of the structure. For other cases
+ *	the minor number requested is used.
+ *
+ *	The structure passed is linked into the kernel and may not be
+ *	destroyed until it has been unregistered
+ *
+ *	A zero is returned on success and a negative errno code for
+ *	failure.
+ */
+ 
 int misc_register(struct miscdevice * misc)
 {
 	static devfs_handle_t devfs_handle = NULL;
@@ -156,6 +172,16 @@
 	misc->next->prev = misc;
 	return 0;
 }
+
+/**
+ *	misc_deregister - unregister a miscellaneous device
+ *	@misc: device to unregister
+ *
+ *	Unregister a miscellaneous device that was previously
+ *	successfully registered with misc_register. Success
+ *	is indicated by a zero return, a negative errno code
+ *	indicates an error.
+ */
 
 int misc_deregister(struct miscdevice * misc)
 {
diff -u --recursive --new-file v2.3.51/linux/drivers/char/mixcomwd.c linux/drivers/char/mixcomwd.c
--- v2.3.51/linux/drivers/char/mixcomwd.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/mixcomwd.c	Sun Mar 12 19:18:55 2000
@@ -24,10 +24,13 @@
  * Version 0.3.1 (99/06/22):
  *		- allow module removal while internal timer is active,
  *		  print warning about probable reset
+ *
+ * Version 0.4 (99/11/15):
+ *		- support for one more type board
  *	
  */
 
-#define VERSION "0.3.1" 
+#define VERSION "0.4" 
   
 #include <linux/module.h>
 #include <linux/config.h>
@@ -46,11 +49,13 @@
 static int mixcomwd_ioports[] = { 0x180, 0x280, 0x380, 0x000 };
 
 #define MIXCOM_WATCHDOG_OFFSET 0xc10
-#define MIXCOM_ID1 0x11
-#define MIXCOM_ID2 0x13
+#define MIXCOM_ID 0x11
+#define FLASHCOM_WATCHDOG_OFFSET 0x4
+#define FLASHCOM_ID 0x18
 
 static int mixcomwd_opened;
-static int mixcomwd_port;
+
+static int watchdog_port;
 
 #ifndef CONFIG_WATCHDOG_NOWAYOUT
 static int mixcomwd_timer_alive;
@@ -59,7 +64,7 @@
 
 static void mixcomwd_ping(void)
 {
-	outb_p(55,mixcomwd_port+MIXCOM_WATCHDOG_OFFSET);
+	outb_p(55,watchdog_port);
 	return;
 }
 
@@ -183,40 +188,61 @@
 {
 	int id;
 
-	if(check_region(port,1)) {
+	if(check_region(port+MIXCOM_WATCHDOG_OFFSET,1)) {
 		return 0;
 	}
 	
 	id=inb_p(port + MIXCOM_WATCHDOG_OFFSET) & 0x3f;
-	if(id!=MIXCOM_ID1 && id!=MIXCOM_ID2) {
+	if(id!=MIXCOM_ID) {
 		return 0;
 	}
 	return 1;
 }
 
-
+static int __init flashcom_checkcard(int port)
+{
+	int id;
+	
+	if(check_region(port + FLASHCOM_WATCHDOG_OFFSET,1)) {
+		return 0;
+	}
+	
+	id=inb_p(port + FLASHCOM_WATCHDOG_OFFSET);
+ 	if(id!=FLASHCOM_ID) {
+		return 0;
+	}
+ 	return 1;
+ }
+ 
 void __init mixcomwd_init(void)
 {
 	int i;
 	int found=0;
 
-	for (i = 0; mixcomwd_ioports[i] != 0; i++) {
+	for (i = 0; !found && mixcomwd_ioports[i] != 0; i++) {
 		if (mixcomwd_checkcard(mixcomwd_ioports[i])) {
 			found = 1;
-			mixcomwd_port = mixcomwd_ioports[i];
-			break;
+			watchdog_port = mixcomwd_ioports[i] + MIXCOM_WATCHDOG_OFFSET;
 		}
 	}
-
+	
+	/* The FlashCOM card can be set up at 0x300 -> 0x378, in 0x8 jumps */
+	for (i = 0x300; !found && i < 0x380; i+=0x8) {
+		if (flashcom_checkcard(i)) {
+			found = 1;
+			watchdog_port = i + FLASHCOM_WATCHDOG_OFFSET;
+		}
+	}
+	
 	if (!found) {
 		printk("mixcomwd: No card detected, or port not available.\n");
 		return;
 	}
 
-	request_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1,"MixCOM watchdog");
-	
+	request_region(watchdog_port,1,"MixCOM watchdog");
+		
 	misc_register(&mixcomwd_miscdev);
-	printk("MixCOM watchdog driver v%s, MixCOM card at 0x%3x\n",VERSION,mixcomwd_port);
+	printk(KERN_INFO "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",VERSION,watchdog_port);
 }	
 
 #ifdef MODULE
@@ -236,7 +262,7 @@
 		mixcomwd_timer_alive=0;
 	}
 #endif
-	release_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1);
+	release_region(watchdog_port,1);
 	misc_deregister(&mixcomwd_miscdev);
 }
 #endif
diff -u --recursive --new-file v2.3.51/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.3.51/linux/drivers/char/n_tty.c	Fri Oct 22 13:21:48 1999
+++ linux/drivers/char/n_tty.c	Sun Mar 12 19:18:55 2000
@@ -17,6 +17,10 @@
  * 
  * This file may be redistributed under the terms of the GNU Public
  * License.
+ *
+ * 2000/01/20   Fixed SMP locking on put_tty_queue using bits of 
+ *		the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu>
+ *		who actually finally proved there really was a race.
  */
 
 #include <linux/types.h>
@@ -59,11 +63,18 @@
 
 static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
 {
+	unsigned long flags;
+	/*
+	 *	The problem of stomping on the buffers ends here.
+	 *	Why didn't anyone see this one comming? --AJK
+	*/
+	spin_lock_irqsave(&tty->read_lock, flags);
 	if (tty->read_cnt < N_TTY_BUF_SIZE) {
 		tty->read_buf[tty->read_head] = c;
 		tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
 		tty->read_cnt++;
 	}
+	spin_unlock_irqrestore(&tty->read_lock, flags);
 }
 
 /* 
@@ -86,7 +97,11 @@
  */
 static void reset_buffer_flags(struct tty_struct *tty)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&tty->read_lock, flags);
 	tty->read_head = tty->read_tail = tty->read_cnt = 0;
+	spin_unlock_irqrestore(&tty->read_lock, flags);
 	tty->canon_head = tty->canon_data = tty->erasing = 0;
 	memset(&tty->read_flags, 0, sizeof tty->read_flags);
 	check_unthrottle(tty);
@@ -114,14 +129,19 @@
  */
 ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
 {
-	if (tty->icanon) {
-		if (!tty->canon_data) return 0;
+	unsigned long flags;
+	ssize_t n = 0;
 
-		return (tty->canon_head > tty->read_tail) ?
+	spin_lock_irqsave(&tty->read_lock, flags);
+	if (!tty->icanon) {
+		n = tty->read_cnt;
+	} else if (tty->canon_data) {
+		n = (tty->canon_head > tty->read_tail) ?
 			tty->canon_head - tty->read_tail :
 			tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
 	}
-	return tty->read_cnt;
+	spin_unlock_irqrestore(&tty->read_lock, flags);
+	return n;
 }
 
 /*
@@ -283,6 +303,7 @@
 {
 	enum { ERASE, WERASE, KILL } kill_type;
 	int head, seen_alnums;
+	unsigned long flags;
 
 	if (tty->read_head == tty->canon_head) {
 		/* opost('\a', tty); */		/* what do you think? */
@@ -294,15 +315,19 @@
 		kill_type = WERASE;
 	else {
 		if (!L_ECHO(tty)) {
+			spin_lock_irqsave(&tty->read_lock, flags);
 			tty->read_cnt -= ((tty->read_head - tty->canon_head) &
 					  (N_TTY_BUF_SIZE - 1));
 			tty->read_head = tty->canon_head;
+			spin_unlock_irqrestore(&tty->read_lock, flags);
 			return;
 		}
 		if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
+			spin_lock_irqsave(&tty->read_lock, flags);
 			tty->read_cnt -= ((tty->read_head - tty->canon_head) &
 					  (N_TTY_BUF_SIZE - 1));
 			tty->read_head = tty->canon_head;
+			spin_unlock_irqrestore(&tty->read_lock, flags);
 			finish_erasing(tty);
 			echo_char(KILL_CHAR(tty), tty);
 			/* Add a newline if ECHOK is on and ECHOKE is off. */
@@ -324,8 +349,10 @@
 			else if (seen_alnums)
 				break;
 		}
+		spin_lock_irqsave(&tty->read_lock, flags);
 		tty->read_head = head;
 		tty->read_cnt--;
+		spin_unlock_irqrestore(&tty->read_lock, flags);
 		if (L_ECHO(tty)) {
 			if (L_ECHOPRT(tty)) {
 				if (!tty->erasing) {
@@ -658,11 +685,13 @@
 	char *f, flags = TTY_NORMAL;
 	int	i;
 	char	buf[64];
+	unsigned long cpuflags;
 
 	if (!tty->read_buf)
 		return;
 
 	if (tty->real_raw) {
+		spin_lock_irqsave(&tty->read_lock, cpuflags);
 		i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
 				   N_TTY_BUF_SIZE - tty->read_head));
 		memcpy(tty->read_buf + tty->read_head, cp, i);
@@ -676,6 +705,7 @@
 		memcpy(tty->read_buf + tty->read_head, cp, i);
 		tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
 		tty->read_cnt += i;
+		spin_unlock_irqrestore(&tty->read_lock, cpuflags);
 	} else {
 		for (i=count, p = cp, f = fp; i; i--, p++) {
 			if (f)
@@ -850,15 +880,20 @@
 {
 	int retval;
 	ssize_t n;
+	unsigned long flags;
 
 	retval = 0;
+	spin_lock_irqsave(&tty->read_lock, flags);
 	n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
+	spin_unlock_irqrestore(&tty->read_lock, flags);
 	if (n) {
 		mb();
 		retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
 		n -= retval;
+		spin_lock_irqsave(&tty->read_lock, flags);
 		tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
 		tty->read_cnt -= n;
+		spin_unlock_irqrestore(&tty->read_lock, flags);
 		*b += n;
 		*nr -= n;
 	}
@@ -875,6 +910,7 @@
 	ssize_t retval = 0;
 	ssize_t size;
 	long timeout;
+	unsigned long flags;
 
 do_it_again:
 
@@ -993,9 +1029,11 @@
 				eol = test_and_clear_bit(tty->read_tail,
 						&tty->read_flags);
 				c = tty->read_buf[tty->read_tail];
+				spin_lock_irqsave(&tty->read_lock, flags);
 				tty->read_tail = ((tty->read_tail+1) &
 						  (N_TTY_BUF_SIZE-1));
 				tty->read_cnt--;
+				spin_unlock_irqrestore(&tty->read_lock, flags);
 
 				if (!eol || (c != __DISABLED_CHAR)) {
 					put_user(c, b++);
@@ -1094,7 +1132,9 @@
 				nr -= num;
 				if (nr == 0)
 					break;
+				current->state = TASK_RUNNING;
 				get_user(c, b);
+				current->state = TASK_INTERRUPTIBLE;
 				if (opost(c, tty) < 0)
 					break;
 				b++; nr--;
@@ -1102,7 +1142,9 @@
 			if (tty->driver.flush_chars)
 				tty->driver.flush_chars(tty);
 		} else {
+			current->state = TASK_RUNNING;
 			c = tty->driver.write(tty, 1, b, nr);
+			current->state = TASK_INTERRUPTIBLE;
 			if (c < 0) {
 				retval = c;
 				goto break_out;
diff -u --recursive --new-file v2.3.51/linux/drivers/char/ppdev.c linux/drivers/char/ppdev.c
--- v2.3.51/linux/drivers/char/ppdev.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/char/ppdev.c	Tue Mar 14 17:54:42 2000
@@ -537,6 +537,17 @@
 	unsigned int minor = MINOR (inode->i_rdev);
 	struct pp_struct *pp = file->private_data;
 
+	if (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT) {
+		if (!(pp->flags & PP_CLAIMED)) {
+			parport_claim_or_block (pp->pdev);
+			pp->flags |= PP_CLAIMED;
+		}
+		parport_negotiate (pp->pdev->port, IEEE1284_MODE_COMPAT);
+		printk (KERN_DEBUG CHRDEV
+			"%x: negotiated back to compatibility mode because "
+			"user-space forgot\n", minor);
+	}
+
 	if (pp->flags & PP_CLAIMED) {
 		parport_release (pp->pdev);
 		printk (KERN_DEBUG CHRDEV "%x: released pardevice because "
diff -u --recursive --new-file v2.3.51/linux/drivers/char/pty.c linux/drivers/char/pty.c
--- v2.3.51/linux/drivers/char/pty.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/char/pty.c	Sun Mar 12 19:18:55 2000
@@ -174,7 +174,9 @@
 		}
 		up(&tty->flip.pty_sem);
 	} else {
-		c = MIN(count, to->ldisc.receive_room(to));
+		c = to->ldisc.receive_room(to);
+		if (c > count)
+			c = count;
 		to->ldisc.receive_buf(to, buf, 0, c);
 	}
 	
diff -u --recursive --new-file v2.3.51/linux/drivers/char/radio-gemtek.c linux/drivers/char/radio-gemtek.c
--- v2.3.51/linux/drivers/char/radio-gemtek.c	Mon Oct  4 15:49:29 1999
+++ linux/drivers/char/radio-gemtek.c	Mon Mar 13 09:43:36 2000
@@ -266,7 +266,7 @@
 {
 	if(io==-1)
 	{
-		printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (or io=0x248 for the combined sound/radiocard)\n");
+		printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n");
 		return -EINVAL;
 	}
 
@@ -299,7 +299,7 @@
 MODULE_AUTHOR("Jonas Munsin");
 MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
 MODULE_PARM(io, "i");
-MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (or 0x248 for the combined sound/radiocard))");
+MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard)).");
 
 EXPORT_NO_SYMBOLS;
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.3.51/linux/drivers/char/serial.c	Sat Feb 26 22:31:45 2000
+++ linux/drivers/char/serial.c	Tue Mar 14 17:54:42 2000
@@ -1648,8 +1648,13 @@
 		serial_outp(info, UART_FCR, fcr); 	/* set fcr */
 	serial_outp(info, UART_LCR, cval);		/* reset DLAB */
 	info->LCR = cval;				/* Save LCR */
-	if (info->state->type != PORT_16750)
+ 	if (info->state->type != PORT_16750) {
+ 		if (fcr & UART_FCR_ENABLE_FIFO) {
+ 			/* emulated UARTs (Lucent Venus 167x) need two steps */
+ 			serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ 		}
 		serial_outp(info, UART_FCR, fcr); 	/* set fcr */
+ 	}
 	restore_flags(flags);
 }
 
@@ -4515,9 +4520,24 @@
 }
 
 /*
- * register_serial and unregister_serial allows for serial ports to be
+ * register_serial and unregister_serial allows for 16x50 serial ports to be
  * configured at run-time, to support PCMCIA modems.
  */
+ 
+/**
+ *	register_serial - configure a 16x50 serial port at runtime
+ *	@req: request structure
+ *
+ *	Configure the serial port specified by the request. If the
+ *	port exists and is in use an error is returned. If the port
+ *	is not currently in the table it is added.
+ *
+ *	The port is then probed and if neccessary the IRQ is autodetected
+ *	If this fails an error is returned.
+ *
+ *	On success the port is ready to use and the line number is returned.	
+ */
+ 
 int register_serial(struct serial_struct *req)
 {
 	int i;
@@ -4575,7 +4595,7 @@
 	if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))
 		state->irq = detect_uart_irq(state);
 
-       printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
+	printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
 	      state->line + SERIAL_DEV_OFFSET,
 	      state->iomem_base ? "iomem" : "port",
 	      state->iomem_base ? (unsigned long)state->iomem_base :
@@ -4587,6 +4607,15 @@
 			   callout_driver.minor_start + state->line);
 	return state->line + SERIAL_DEV_OFFSET;
 }
+
+/**
+ *	unregister_serial - deconfigure a 16x50 serial port
+ *	@line: line to deconfigure
+ *
+ *	The port specified is deconfigured and its resources are freed. Any
+ *	user of the port is disconnected as if carrier was dropped. Line is
+ *	the port number returned by register_serial.
+ */
 
 void unregister_serial(int line)
 {
diff -u --recursive --new-file v2.3.51/linux/drivers/char/sx.c linux/drivers/char/sx.c
--- v2.3.51/linux/drivers/char/sx.c	Thu Feb 10 17:11:08 2000
+++ linux/drivers/char/sx.c	Sun Mar 12 19:18:55 2000
@@ -4,7 +4,7 @@
  *  This driver will also support the older SI, and XIO cards.
  *
  *
- *   (C) 1998 R.E.Wolff@BitWizard.nl
+ *   (C) 1998 - 2000  R.E.Wolff@BitWizard.nl
  *
  *  Simon Allen (simonallen@cix.compulink.co.uk) wrote a previous
  *  version of this driver. Some fragments may have been copied. (none
@@ -33,6 +33,16 @@
  *
  * Revision history:
  * $Log: sx.c,v $
+ * Revision 1.32  2000/03/07 90:00:00  wolff,pvdl
+ * - Fixed some sx_dprintk typos
+ * - added detection for an invalid board/module configuration
+ *
+ * Revision 1.31  2000/03/06 12:00:00  wolff,pvdl
+ * - Added support for EISA
+ *
+ * Revision 1.30  2000/01/21 17:43:06  wolff
+ * - Added support for SX+
+ *
  * Revision 1.26  1999/08/05 15:22:14  wolff
  * - Port to 2.3.x
  * - Reformatted to Linus' liking.
@@ -185,8 +195,8 @@
  * */
 
 
-#define RCS_ID "$Id: sx.c,v 1.26 1999/08/05 15:22:14 wolff Exp $"
-#define RCS_REV "$Revision: 1.26 $"
+#define RCS_ID "$Id: sx.c,v 1.32 2000/03/07 17:01:02 wolff, pvdl Exp $"
+#define RCS_REV "$Revision: 1.32 $"
 
 
 #include <linux/module.h>
@@ -221,6 +231,9 @@
 
 #include "sxboards.h"
 #include "sxwindow.h"
+#include <linux/generic_serial.h>
+#include <linux/compatmac.h>
+#include "sx.h"
 
 
 /* I don't think that this driver can handle more than 256 ports on
@@ -228,149 +241,16 @@
    if you want more than 4 boards.  */
 
 
-/* ************************************************************** */
-/* * This section can be removed when 2.0 becomes outdated....  * */
-/* ************************************************************** */
-
-
-#if LINUX_VERSION_CODE < 0x020100    /* Less than 2.1.0 */
-#define TWO_ZERO
-#else
-#if LINUX_VERSION_CODE < 0x020200   /* less than 2.2.x */
-#warning "Please use a 2.2.x kernel. "
-#else
-#if LINUX_VERSION_CODE < 0x020300   /* less than 2.3.x */
-#define TWO_TWO
-#else
-#define TWO_THREE
-#endif
-#endif
-#endif
-
-#ifdef TWO_ZERO
-
-/* Here is the section that makes the 2.2 compatible driver source 
-   work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2, 
-   and provide for compatibility stuff here if possible. */
-
-#include <linux/bios32.h>
-
-#define Get_user(a,b)                a = get_user(b)
-#define Put_user(a,b)                0,put_user(a,b)
-#define copy_to_user(a,b,c)          memcpy_tofs(a,b,c)
-
-static inline int copy_from_user(void *to,const void *from, int c) 
-{
-  memcpy_fromfs(to, from, c);
-  return 0;
-}
-
-#define pci_present                  pcibios_present
-#define pci_read_config_word         pcibios_read_config_word
-#define pci_read_config_dword        pcibios_read_config_dword
-
-static inline unsigned char get_irq (unsigned char bus, unsigned char fn)
-{
-	unsigned char t; 
-	pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &t);
-	return t;
-}
-
-static inline void *ioremap(unsigned long base, long length)
-{
-	if (base < 0x100000) return (void *)base;
-	return vremap (base, length);
-}
-
-#define my_iounmap(x, b)             (((long)x<0x100000)?0:vfree ((void*)x))
-
-#define capable(x)                   suser()
-
-#define queue_task                   queue_task_irq_off
-#define tty_flip_buffer_push(tty)    queue_task(&tty->flip.tqueue, &tq_timer)
-#define signal_pending(current)      (current->signal & ~current->blocked)
-#define schedule_timeout(to)         do {current->timeout = jiffies + (to);schedule ();} while (0)
-#define time_after(t1,t2)            (((long)t1-t2) > 0)
-
-
-#define test_and_set_bit(nr, addr)   set_bit(nr, addr)
-#define test_and_clear_bit(nr, addr) clear_bit(nr, addr)
-
-/* Not yet implemented on 2.0 */
-#define ASYNC_SPD_SHI  -1
-#define ASYNC_SPD_WARP -1
-
-
-/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it
-   to the "name" field that does exist. As long as the assignments are
-   done in the right order, there is nothing to worry about. */
-#define driver_name           name 
-
-/* Should be in a header somewhere. They are in tty.h on 2.2 */
-#define TTY_HW_COOK_OUT       14 /* Flag to tell ntty what we can handle */
-#define TTY_HW_COOK_IN        15 /* in hardware - output and input       */
-
-/* The return type of a "close" routine. */
-#define INT                   void
-#define NO_ERROR              /* Nothing */
-
-#else
-
-/* The 2.2.x compatibility section. */
-#include <asm/uaccess.h>
-
-
-#define Get_user(a,b)         get_user(a,b)
-#define Put_user(a,b)         put_user(a,b)
-#define get_irq(pdev)         pdev->irq
-
-#define INT                   int
-#define NO_ERROR              0
-
-#define my_iounmap(x,b)       (iounmap((char *)(b)))
-
-#endif
-
-#ifndef TWO_THREE
-/* These are new in 2.3. The source now uses 2.3 syntax, and here is 
-   the compatibility define... */
-#define wait_queue_head_t struct wait_queue *
-#define DECLARE_MUTEX(name) struct semaphore name = MUTEX
-#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL }
-
-#endif
-
-#undef RS_EVENT_WRITE_WAKEUP
-#define RS_EVENT_WRITE_WAKEUP	0
-
-
-#include "generic_serial.h"
-#include "sx.h"
-
-
-/* ************************************************************** */
-/* *                End of compatibility section..              * */
-/* ************************************************************** */
-
-
 
 /* Why the hell am I defining these here? */
 #define SX_TYPE_NORMAL 1
 #define SX_TYPE_CALLOUT 2
 
-#ifndef SX_NORMAL_MAJOR
-/* This allows overriding on the compiler commandline, or in a "major.h" 
-   include or something like that */
-#define SX_NORMAL_MAJOR  32
-#define SX_CALLOUT_MAJOR 33
-#endif
-
 #ifndef PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8
 #define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000
 #endif
 
 
-
 /* Configurable options: 
    (Don't be too sure that it'll work if you toggle them) */
 
@@ -410,19 +290,17 @@
 static void sx_enable_rx_interrupts (void * ptr); 
 static int  sx_get_CD (void * ptr); 
 static void sx_shutdown_port (void * ptr);
-static void sx_set_real_termios (void  *ptr);
+static int  sx_set_real_termios (void  *ptr);
 static void sx_hungup (void  *ptr);
 static void sx_close (void  *ptr);
 static int sx_chars_in_buffer (void * ptr);
 static int sx_init_board (struct sx_board *board);
 static int sx_init_portstructs (int nboards, int nports);
 static int sx_fw_ioctl (struct inode *inode, struct file *filp,
-		         unsigned int cmd, unsigned long arg);
+                        unsigned int cmd, unsigned long arg);
 static int sx_fw_open(struct inode *inode, struct file *filp);
 static INT sx_fw_release(struct inode *inode, struct file *filp);
 static int sx_init_drivers(void);
-void my_hd (unsigned char *addr, int len);
-
 
 
 static struct tty_driver sx_driver, sx_callout_driver;
@@ -458,11 +336,13 @@
 int sx_maxints = 100;
 
 /* These are the only open spaces in my computer. Yours may have more
-   or less.... */
+   or less.... -- REW 
+   duh: Card at 0xa0000 is possible on HP Netserver?? -- pvdl
+*/
 int sx_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000, 
                        0xc8000, 0xd8000, 0xe8000};
 int si_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000, 
-                       0xc8000, 0xd8000, 0xe8000};
+                       0xc8000, 0xd8000, 0xe8000, 0xa0000};
 
 #define NR_SX_ADDRS (sizeof(sx_probe_addrs)/sizeof (int))
 #define NR_SI_ADDRS (sizeof(si_probe_addrs)/sizeof (int))
@@ -474,6 +354,8 @@
 
 #ifndef TWO_ZERO
 #ifdef MODULE
+MODULE_PARM(sx_probe_addrs, "i");
+MODULE_PARM(si_probe_addrs, "i");
 MODULE_PARM(sx_poll, "i");
 MODULE_PARM(sx_slowpoll, "i");
 MODULE_PARM(sx_maxints, "i");
@@ -580,6 +462,27 @@
 #define TIMEOUT_2 1000000
 
 
+#ifdef DEBUG
+static void my_hd (unsigned char *addr, int len)
+{
+	int i, j, ch;
+
+	for (i=0;i<len;i+=16) {
+		printk ("%08x ", (int) addr+i);
+		for (j=0;j<16;j++) {
+			printk ("%02x %s", addr[j+i], (j==7)?" ":"");
+		}
+		for (j=0;j<16;j++) {
+			ch = addr[j+i];
+			printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
+		}
+		printk ("\n");
+	}
+}
+#endif
+
+
+
 /* This needs redoing for Alpha -- REW -- Done. */
 
 inline void write_sx_byte (struct sx_board *board, int offset, u8 byte)
@@ -675,6 +578,8 @@
 			printk (KERN_INFO "sx: Card doesn't respond to reset....\n");
 			return 0;
 		}
+	} else if (IS_EISA_BOARD(board)) {
+		outb(board->irq<<4, board->eisa_base+0xc02);
 	} else {
 		/* Gory details of the SI/ISA board */
 		write_sx_byte (board, SI2_ISA_RESET,    SI2_ISA_RESET_SET);
@@ -746,9 +651,12 @@
 {
 	if (IS_SX_BOARD (board)) {
 		write_sx_byte (board, SX_CONFIG, SX_CONF_BUSEN);
+	} else if (IS_EISA_BOARD(board)) {
+		write_sx_byte(board, SI2_EISA_OFF, SI2_EISA_VAL);
+		outb((board->irq<<4)|4, board->eisa_base+0xc02);
 	} else {
 		/* Don't bug me about the clear_set. 
-		   I haven't the foggiest idea what it's about -- REW*/
+		   I haven't the foggiest idea what it's about -- REW */
 		write_sx_byte (board, SI2_ISA_RESET,    SI2_ISA_RESET_CLEAR);
 		write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET);
 	}
@@ -769,6 +677,8 @@
 		write_sx_byte (board, SX_CONFIG, SX_IRQ_REG_VAL (board) | 
 		                                 SX_CONF_BUSEN | 
 		                                 SX_CONF_HOSTIRQ);
+	} else if (IS_EISA_BOARD(board)) {
+		inb(board->eisa_base+0xc03);  
 	} else {
 		switch (board->irq) {
 		case 11:write_sx_byte (board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_SET);break;
@@ -834,6 +744,18 @@
 	return module_type >> 4;
 }
 
+static void sx_reconfigure_port(struct sx_port *port)
+{
+	if (sx_read_channel_byte (port, hi_hstat) == HS_IDLE_OPEN) {
+		if (sx_send_command (port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) {
+			printk (KERN_WARNING "sx: Sent reconfigure command, but card didn't react.\n");
+		}
+	} else {
+		sx_dprintk (SX_DEBUG_TERMIOS, 
+		            "sx: Not sending reconfigure: port isn't open (%02x).\n", 
+		            sx_read_channel_byte (port, hi_hstat));
+	}	
+}
 
 static void sx_setsignals (struct sx_port *port, int dtr, int rts)
 {
@@ -954,7 +876,7 @@
 /* Simon Allen's version of this routine was 225 lines long. 85 is a lot
    better. -- REW */
 
-static void sx_set_real_termios (void *ptr)
+static int sx_set_real_termios (void *ptr)
 {
 	struct sx_port *port = ptr;
 
@@ -1008,16 +930,7 @@
 	sx_write_channel_byte (port, hi_txoff, STOP_CHAR  (port->gs.tty));
 	sx_write_channel_byte (port, hi_rxoff, STOP_CHAR  (port->gs.tty));
 
-	if (sx_read_channel_byte (port, hi_hstat) == HS_IDLE_OPEN) {
-		if (sx_send_command (port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) {
-			printk (KERN_WARNING "sx: Sent reconfigure command, but card didn't react.\n");
-		}
-	} else {
-		sx_dprintk (SX_DEBUG_TERMIOS, 
-		            "sx: Not sending reconfigure: port isn't open (%02x).\n", 
-		            sx_read_channel_byte (port, hi_hstat));
-	}
-
+	sx_reconfigure_port(port);
 
 	/* Tell line discipline whether we will do input cooking */
 	if(I_OTHER(port->gs.tty)) {
@@ -1045,6 +958,7 @@
 	            O_OTHER(port->gs.tty));
 	/* port->c_dcd = sx_get_CD (port); */
 	func_exit ();
+	return 0;
 }
 
 
@@ -1102,7 +1016,7 @@
 
 		/* Don't copy pas the end of the source buffer */
 		if (c > SERIAL_XMIT_SIZE - port->gs.xmit_tail) 
-                	c = SERIAL_XMIT_SIZE - port->gs.xmit_tail;
+			c = SERIAL_XMIT_SIZE - port->gs.xmit_tail;
 
 		sx_dprintk (SX_DEBUG_TRANSMIT, " %d(%d) \n", 
 		            c, SERIAL_XMIT_SIZE- port->gs.xmit_tail);
@@ -1331,6 +1245,9 @@
 		sx_write_board_word (board, cc_int_pending, 0);
 		if (IS_SX_BOARD (board)) {
 			write_sx_byte (board, SX_RESET_IRQ, 1);
+		} else if (IS_EISA_BOARD(board)) {
+			inb(board->eisa_base+0xc03);
+			write_sx_word(board, 8, 0); 
 		} else {
 			write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_CLEAR);
 			write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET);
@@ -1474,6 +1391,7 @@
 	port->gs.flags &= ~ GS_ACTIVE;
 	if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
 		sx_setsignals (port, 0, 0);
+		sx_reconfigure_port(port);
 	}
 
 	func_exit();
@@ -1711,7 +1629,7 @@
 
 
 static int sx_fw_ioctl (struct inode *inode, struct file *filp,
-		         unsigned int cmd, unsigned long arg)
+                        unsigned int cmd, unsigned long arg)
 {
 	int rc = 0;
 	int *descr = (int *)arg, i;
@@ -1754,7 +1672,11 @@
 		board = &boards[arg];
 		break;
 	case SXIO_GET_TYPE:
-		rc = IS_SX_BOARD (board)? SX_TYPE_SX:SX_TYPE_SI;
+		rc = -ENOENT; /* If we manage to miss one, return error. */
+		if (IS_SX_BOARD (board)) rc = SX_TYPE_SX;
+		if (IS_CF_BOARD (board)) rc = SX_TYPE_CF;
+		if (IS_SI_BOARD (board)) rc = SX_TYPE_SI;
+		if (IS_EISA_BOARD (board)) rc = SX_TYPE_SI;
 		sx_dprintk (SX_DEBUG_FIRMWARE, "returning type= %d\n", rc);
 		break;
 	case SXIO_DO_RAMTEST:
@@ -1786,7 +1708,7 @@
 			for (i=0;i<nbytes;i += SX_CHUNK_SIZE) {
 				copy_from_user (tmp, (char *)data+i, 
 				                (i+SX_CHUNK_SIZE>nbytes)?nbytes-i:SX_CHUNK_SIZE);
-				memcpy_toio    ((char *) (board->base + offset + i), tmp, 
+				memcpy_toio    ((char *) (board->base2 + offset + i), tmp, 
 				                (i+SX_CHUNK_SIZE>nbytes)?nbytes-i:SX_CHUNK_SIZE);
 			}
 
@@ -1830,6 +1752,9 @@
 	case SXIO_GETGSDEBUG:
 		rc = gs_debug;
 		break;
+	case SXIO_GETNPORTS:
+		rc = sx_nports;
+		break;
 	default:
 		printk (KERN_WARNING "Unknown ioctl on firmware device (%x).\n", cmd);
 		break;
@@ -1886,6 +1811,7 @@
 			Get_user(ival, (unsigned int *) arg);
 			sx_setsignals(port, ((ival & TIOCM_DTR) ? 1 : -1),
 			                     ((ival & TIOCM_RTS) ? 1 : -1));
+			sx_reconfigure_port(port);
 		}
 		break;
 	case TIOCMBIC:
@@ -1894,6 +1820,7 @@
 			Get_user(ival, (unsigned int *) arg);
 			sx_setsignals(port, ((ival & TIOCM_DTR) ? 0 : -1),
 			                     ((ival & TIOCM_RTS) ? 0 : -1));
+			sx_reconfigure_port(port);
 		}
 		break;
 	case TIOCMSET:
@@ -1902,6 +1829,7 @@
 			Get_user(ival, (unsigned int *) arg);
 			sx_setsignals(port, ((ival & TIOCM_DTR) ? 1 : 0),
 			                     ((ival & TIOCM_RTS) ? 1 : 0));
+			sx_reconfigure_port(port);
 		}
 		break;
 
@@ -1980,13 +1908,17 @@
 
 	board->flags |= SX_BOARD_INITIALIZED;
 
+	if (read_sx_byte (board, 0))
+		/* CF boards may need this. */
+		write_sx_byte(board,0, 0);
+
 	/* This resets the processor again, to make sure it didn't do any
 	   foolish things while we were downloading the image */
 	if (!sx_reset (board))
 		return 0;
 
 	sx_start_board (board);
-
+	udelay (10);
 	if (!sx_busy_wait_neq (board, 0, 0xff, 0)) {
 		printk (KERN_ERR "sx: Ooops. Board won't initialize.\n");
 		return 0;
@@ -2050,7 +1982,8 @@
 			chans=0;
 			break;
 		}
-		if (IS_SI_BOARD(board) && (mod_compat_type(type) == 4)) {
+		if ((IS_EISA_BOARD(board) || 
+		     IS_SI_BOARD(board)) && (mod_compat_type(type) == 4)) {
 			printk (KERN_ERR "sx: This is an invalid configuration.\n"
 			        "Don't use SXDCs on an SI/XIO adapter.\n");
 			chans=0;
@@ -2147,52 +2080,56 @@
 	int i;
 
 	func_enter();
-	sx_dprintk (SX_DEBUG_PROBE, "Going to verify vpd prom at %x.\n", 
-	            board->base + SX_VPD_ROM);
 
-	if (sx_debug & SX_DEBUG_PROBE)
-		my_hd ((char *)(board->base + SX_VPD_ROM), 0x40);
+	if (!IS_CF_BOARD (board)) {    
+		sx_dprintk (SX_DEBUG_PROBE, "Going to verify vpd prom at %x.\n", 
+		            board->base + SX_VPD_ROM);
 
-	p = (char *) &vpdp;
-	for (i=0;i< sizeof (struct vpd_prom);i++)
-		*p++ = read_sx_byte (board, SX_VPD_ROM + i*2);
+		if (sx_debug & SX_DEBUG_PROBE)
+			my_hd ((char *)(board->base + SX_VPD_ROM), 0x40);
 
-	if (sx_debug & SX_DEBUG_PROBE)
-		my_hd ((char *)&vpdp, 0x20);
+		p = (char *) &vpdp;
+		for (i=0;i< sizeof (struct vpd_prom);i++)
+			*p++ = read_sx_byte (board, SX_VPD_ROM + i*2);
 
-	sx_dprintk (SX_DEBUG_PROBE, "checking identifier...\n");
+		if (sx_debug & SX_DEBUG_PROBE)
+			my_hd ((char *)&vpdp, 0x20);
 
-	if (strncmp (vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) {
-		sx_dprintk (SX_DEBUG_PROBE, "Got non-SX identifier: '%s'\n", 
-		            vpdp.identifier); 
-		return 0;
+		sx_dprintk (SX_DEBUG_PROBE, "checking identifier...\n");
+
+		if (strncmp (vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) {
+			sx_dprintk (SX_DEBUG_PROBE, "Got non-SX identifier: '%s'\n", 
+			            vpdp.identifier); 
+			return 0;
+		}
 	}
 
 	printheader ();
 
-	printk (KERN_DEBUG "sx: Found an SX board at %x\n", board->hw_base);
-	printk (KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, uniq ID:%08x, ", 
-	        vpdp.hwrev, vpdp.hwass, vpdp.uniqid);
-	printk (           "Manufactured: %d/%d\n", 
-	        1970 + vpdp.myear, vpdp.mweek);
-
-
-	if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_PCI_UNIQUEID1) &&
-	    (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) {
-		/* This might be a bit harsh. This was the primary reason the
-		   SX/ISA card didn't work at first... */
-		printk (KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA card. Sorry: giving up.\n");
-		return (0);
-	}
-
-	if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) == SX_ISA_UNIQUEID1) {
-		if (board->base & 0x8000) {
-			printk (KERN_WARNING "sx: Warning: There may be hardware problems with the card at %x.\n", board->base);
-			printk (KERN_WARNING "sx: Read sx.txt for more info.\n");
+	if (!IS_CF_BOARD (board)) {
+		printk (KERN_DEBUG "sx: Found an SX board at %x\n", board->hw_base);
+		printk (KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, uniq ID:%08x, ", 
+		        vpdp.hwrev, vpdp.hwass, vpdp.uniqid);
+		printk (           "Manufactured: %d/%d\n", 
+		        1970 + vpdp.myear, vpdp.mweek);
+
+
+		if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_PCI_UNIQUEID1) &&
+		    (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) {
+			/* This might be a bit harsh. This was the primary reason the
+			   SX/ISA card didn't work at first... */
+			printk (KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA card. Sorry: giving up.\n");
+			return (0);
+		}
+
+		if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) == SX_ISA_UNIQUEID1) {
+			if (board->base & 0x8000) {
+				printk (KERN_WARNING "sx: Warning: There may be hardware problems with the card at %x.\n", board->base);
+				printk (KERN_WARNING "sx: Read sx.txt for more info.\n");
+			}
 		}
 	}
 
-
 	board->nports = -1;
 
 	/* This resets the processor, and keeps it off the bus. */
@@ -2225,9 +2162,11 @@
 	if (sx_debug & SX_DEBUG_PROBE)
 		my_hd ((char *)(board->base + SI2_ISA_ID_BASE), 0x8);
 
-	for (i=0;i<8;i++) {
-		if ((read_sx_byte (board, SI2_ISA_ID_BASE+7-i) & 7) != i) {
-			return 0;
+	if (!IS_EISA_BOARD(board)) {
+		for (i=0;i<8;i++) {
+			if ((read_sx_byte (board, SI2_ISA_ID_BASE+7-i) & 7) != i) {
+				return 0;
+			}
 		}
 	}
 
@@ -2449,7 +2388,7 @@
 	unsigned int t;
 
 #define CNTRL_REG_OFFSET        0x50
-#define CNTRL_REG_GOODVALUE     0x00260000
+#define CNTRL_REG_GOODVALUE     0x18260000
 
 	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &hwbase);
 	hwbase &= PCI_BASE_ADDRESS_MEM_MASK;
@@ -2472,6 +2411,7 @@
 {
 	int i;
 	int found = 0;
+	int eisa_slot;
 	struct sx_board *board;
 
 #ifdef CONFIG_PCI
@@ -2518,22 +2458,35 @@
 			tshort = (tint >> 16) & 0xffff;
 			sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x.\n", tint);
 			/* sx_dprintk (SX_DEBUG_PROBE, "pdev = %d/%d	(%x)\n", pdev, tint); */ 
-			if (tshort != 0x0200) {
+			if ((tshort != 0x0200) && (tshort != 0x0300)) {
 				sx_dprintk (SX_DEBUG_PROBE, "But it's not an SX card (%d)...\n", 
 				            tshort);
 				continue;
 			}
 			board = &boards[found];
 
-			pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &tint);
+			board->flags &= ~SX_BOARD_TYPE;
+			board->flags |= (tshort == 0x200)?SX_PCI_BOARD:
+			                                  SX_CFPCI_BOARD;
+
+			/* CF boards use base address 3.... */
+			if (IS_CF_BOARD (board))
+				pci_read_config_dword(pdev, PCI_BASE_ADDRESS_3,
+				                      &tint);
+			else
+				pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2,
+				                      &tint);
 			board->hw_base = tint & PCI_BASE_ADDRESS_MEM_MASK;
-			board->base = (ulong) ioremap(board->hw_base, SX_WINDOW_LEN);
+			board->base2 = 
+			board->base = (ulong) ioremap(board->hw_base, WINDOW_LEN (board));
+			/* Most of the stuff on the CF board is offset by
+			   0x18000 ....  */
+			if (IS_CF_BOARD (board)) board->base += 0x18000;
+
 			board->irq = get_irq (pdev);
-			board->flags &= ~SX_BOARD_TYPE;
-			board->flags |=  SX_PCI_BOARD;
 
-			sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x/%x(%d).\n", 
-			            tint, boards[found].base, board->irq);
+			sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x/%x(%d) %x.\n", 
+			            tint, boards[found].base, board->irq, board->flags);
 
 			if (probe_sx (board)) {
 				found++;
@@ -2547,6 +2500,7 @@
 	for (i=0;i<NR_SX_ADDRS;i++) {
 		board = &boards[found];
 		board->hw_base = sx_probe_addrs[i];
+		board->base2 =
 		board->base = (ulong) ioremap(board->hw_base, SX_WINDOW_LEN);
 		board->flags &= ~SX_BOARD_TYPE;
 		board->flags |=	SX_ISA_BOARD;
@@ -2562,6 +2516,7 @@
 	for (i=0;i<NR_SI_ADDRS;i++) {
 		board = &boards[found];
 		board->hw_base = si_probe_addrs[i];
+		board->base2 =
 		board->base = (ulong) ioremap(board->hw_base, SI2_ISA_WINDOW_LEN);
 		board->flags &= ~SX_BOARD_TYPE;
 		board->flags |=  SI_ISA_BOARD;
@@ -2574,6 +2529,34 @@
 		}
 	}
 
+        sx_dprintk(SX_DEBUG_PROBE, "Probing for EISA cards\n");
+        for(eisa_slot=0x1000; eisa_slot<0x10000; eisa_slot+=0x1000)
+        {
+                if((inb(eisa_slot+0xc80)==0x4d) &&
+                   (inb(eisa_slot+0xc81)==0x98))
+                {
+			sx_dprintk(SX_DEBUG_PROBE, "%s : Signature found in EISA slot %d, Product %d Rev %d\n",
+			                        "XIO", (eisa_slot>>12), inb(eisa_slot+0xc82), inb(eisa_slot+0xc83));
+
+			board = &boards[found];
+			board->eisa_base = eisa_slot;
+			board->flags &= ~SX_BOARD_TYPE;
+			board->flags |= SI_EISA_BOARD;
+
+			board->hw_base = (((inb(0xc01+eisa_slot) << 8) + inb(0xc00+eisa_slot)) << 16);
+			board->base2 =
+			board->base = (ulong) ioremap(board->hw_base, SI2_EISA_WINDOW_LEN);
+
+			sx_dprintk(SX_DEBUG_PROBE, "IO hw_base address: %x\n", board->hw_base);
+			sx_dprintk(SX_DEBUG_PROBE, "base: %x\n", board->base);
+			board->irq = inb(board->eisa_base+0xc02)>>4; 
+			sx_dprintk(SX_DEBUG_PROBE, "IRQ: %d\n", board->irq);
+			
+			probe_si(board);
+
+			found++;
+		}
+	}
 	if (found) {
 		printk (KERN_INFO "sx: total of %d boards detected.\n", found);
 
@@ -2588,7 +2571,7 @@
 }
 
 
-
+#ifdef MODULE
 void cleanup_module(void)
 {
 	int i; 
@@ -2622,52 +2605,4 @@
 	kfree (sx_termios_locked);
 	func_exit();
 }
-
-
-#ifdef DEBUG
-void my_hd (unsigned char *addr, int len)
-{
-	int i, j, ch;
-
-	for (i=0;i<len;i+=16) {
-		printk ("%08x ", (int) addr+i);
-		for (j=0;j<16;j++) {
-			printk ("%02x %s", addr[j+i], (j==7)?" ":"");
-		}
-		for (j=0;j<16;j++) {
-			ch = addr[j+i];
-			printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
-		}
-		printk ("\n");
-	}
-}
 #endif
-
-#ifdef MODULE
-#undef func_enter
-#undef func_exit
-
-#include "generic_serial.c"
-#endif
-
-
-/*
- * Anybody who knows why this doesn't work for me, please tell me -- REW.
- * Snatched from scsi.c (fixed one spelling error):
- * Overrides for Emacs so that we follow Linus' tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff -u --recursive --new-file v2.3.51/linux/drivers/char/sx.h linux/drivers/char/sx.h
--- v2.3.51/linux/drivers/char/sx.h	Thu Aug  5 14:47:44 1999
+++ linux/drivers/char/sx.h	Sun Mar 12 19:18:55 2000
@@ -37,7 +37,9 @@
 struct sx_board {
   int magic;
   unsigned int base;
+  unsigned int base2;
   unsigned int hw_base;
+  int eisa_base;
   int port_base; /* Number of the first port */
   struct sx_port *ports;
   int nports;
@@ -65,19 +67,27 @@
 #define MOD_RS232DB25MALE 0x0a
 #endif
 
-
-#define SX_BOARD_PRESENT     0x00000001
+#define SI_ISA_BOARD         0x00000001
 #define SX_ISA_BOARD         0x00000002
 #define SX_PCI_BOARD         0x00000004
-#define SI_ISA_BOARD         0x00000008
-#define SX_BOARD_INITIALIZED 0x00000010
-#define SX_IRQ_ALLOCATED     0x00000020
+#define SX_CFPCI_BOARD       0x00000008
+#define SX_CFISA_BOARD       0x00000010
+#define SI_EISA_BOARD        0x00000020
+
+#define SX_BOARD_PRESENT     0x00001000
+#define SX_BOARD_INITIALIZED 0x00002000
+#define SX_IRQ_ALLOCATED     0x00004000
+
+#define SX_BOARD_TYPE        0x000000ff
 
-#define SX_BOARD_TYPE (SX_ISA_BOARD|SX_PCI_BOARD|SI_ISA_BOARD)
+#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_CFPCI_BOARD | \
+                                            SX_ISA_BOARD | SX_CFISA_BOARD))
 
-#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_ISA_BOARD))
 #define IS_SI_BOARD(board) (board->flags & SI_ISA_BOARD)
 
+#define IS_EISA_BOARD(board) (board->flags & SI_EISA_BOARD)
+
+#define IS_CF_BOARD(board) (board->flags & (SX_CFISA_BOARD | SX_CFPCI_BOARD))
 
 #define SERIAL_TYPE_NORMAL 1
 
@@ -168,6 +178,7 @@
 #define SXIO_DO_RAMTEST     SPXL(0x07)
 #define SXIO_SETGSDEBUG     SPXL(0x08)
 #define SXIO_GETGSDEBUG     SPXL(0x09)
+#define SXIO_GETNPORTS      SPXL(0x0a)
 
 
 #ifndef SXCTL_MISC_MINOR 
@@ -175,6 +186,19 @@
 #define SXCTL_MISC_MINOR    167
 #endif
 
+#ifndef SX_NORMAL_MAJOR
+/* This allows overriding on the compiler commandline, or in a "major.h" 
+   include or something like that */
+#define SX_NORMAL_MAJOR  32
+#define SX_CALLOUT_MAJOR 33
+#endif
+
+
 #define SX_TYPE_SX          0x01
 #define SX_TYPE_SI          0x02
+#define SX_TYPE_CF          0x03
+
+
+#define WINDOW_LEN(board) (IS_CF_BOARD(board)?0x20000:SX_WINDOW_LEN)
+/*                         Need a #define for ^^^^^^^ !!! */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/char/sxboards.h linux/drivers/char/sxboards.h
--- v2.3.51/linux/drivers/char/sxboards.h	Thu Aug  5 14:47:44 1999
+++ linux/drivers/char/sxboards.h	Sun Mar 12 19:18:55 2000
@@ -159,6 +159,7 @@
 
 #define		SI2_EISA_OFF	 	0x42
 #define		SI2_EISA_VAL	 	0x01
+#define		SI2_EISA_WINDOW_LEN     0x10000
 
 /*****************************************************************************
 ***********************************         **********************************
diff -u --recursive --new-file v2.3.51/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.3.51/linux/drivers/char/tty_io.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/char/tty_io.c	Sun Mar 12 19:18:55 2000
@@ -1979,6 +1979,7 @@
 	tty->tq_hangup.routine = do_tty_hangup;
 	tty->tq_hangup.data = tty;
 	sema_init(&tty->atomic_read, 1);
+	spin_lock_init(&tty->read_lock);
 	INIT_LIST_HEAD(&tty->tty_files);
 }
 
@@ -2318,6 +2319,9 @@
 #endif
 #ifdef CONFIG_SX
 	sx_init();
+#endif
+#ifdef CONFIG_RIO
+	rio_init();
 #endif
 #ifdef CONFIG_8xx
         rs_8xx_init();
diff -u --recursive --new-file v2.3.51/linux/drivers/char/videodev.c linux/drivers/char/videodev.c
--- v2.3.51/linux/drivers/char/videodev.c	Tue Mar  7 14:32:25 2000
+++ linux/drivers/char/videodev.c	Mon Mar 13 09:43:36 2000
@@ -24,10 +24,9 @@
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/videodev.h>
+#include <linux/init.h>
 
-#if LINUX_VERSION_CODE >= 0x020100
 #include <asm/uaccess.h>
-#endif
 #include <asm/system.h>
 
 #include <linux/kmod.h>
@@ -174,20 +173,11 @@
  *	image ?
  */
  
-#if LINUX_VERSION_CODE >= 0x020100
 static long long video_lseek(struct file * file,
 			  long long offset, int origin)
 {
 	return -ESPIPE;
 }
-#else
-static long long video_lseek(struct inode *inode, struct file * file,
-			     long long offset, int origin)
-{
-	return -ESPIPE;
-}
-#endif
-
 
 static int video_ioctl(struct inode *inode, struct file *file,
 	unsigned int cmd, unsigned long arg)
@@ -210,16 +200,9 @@
  */
  
  
-#if LINUX_VERSION_CODE >= 0x020100
 int video_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
-#else
-static int video_mmap(struct inode * ino, struct file * file,
-		      struct vm_area_struct * vma)
-{
-	struct video_device *vfl=video_device[MINOR(ino->i_rdev)];
-#endif
 	if(vfl->mmap)
 		return vfl->mmap(vfl, (char *)vma->vm_start, 
 				(unsigned long)(vma->vm_end-vma->vm_start));
@@ -228,8 +211,28 @@
 
 extern struct file_operations video_fops;
 
-/*
- *	Video For Linux device drivers request registration here.
+/**
+ *	video_register_device - register video4linux devices
+ *	@vfd: Video device structure we want to register
+ *	@type: type of device to register
+ *	FIXME: needs a semaphore on 2.3.x
+ *	
+ *	The registration code assigns minor numbers based on the type
+ *	requested. -ENFILE is returned in all the device slots for this
+ *	catetory are full. If not then the minor field is set and the
+ *	driver initialize function is called (if non NULL).
+ *
+ *	Zero is returned on success.
+ *
+ *	Valid types are
+ *
+ *	VFL_TYPE_GRABBER - A frame grabber
+ *
+ *	VFL_TYPE_VTX - A teletext device
+ *
+ *	VFL_TYPE_VBI - Vertical blank data (undecoded)
+ *
+ *	VFL_TYPE_RADIO - A radio card	
  */
  
 int video_register_device(struct video_device *vfd, int type)
@@ -288,10 +291,14 @@
 				}
 			}
 			sprintf (name, "v4l/%s%d", name_base, i - base);
+			/*
+			 *	Start the device root only. Anything else
+			 *	has serious privacy issues.
+			 */
 			vfd->devfs_handle =
 			    devfs_register (NULL, name, 0, DEVFS_FL_DEFAULT,
 					    VIDEO_MAJOR, vfd->minor,
-					    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+					    S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
 					    &video_fops, NULL);
 			return 0;
 		}
@@ -299,8 +306,12 @@
 	return -ENFILE;
 }
 
-/*
- *	Unregister an unused video for linux device
+/**
+ *	video_unregister_device - unregister a video4linux device
+ *	@vfd: the device to unregister
+ *
+ *	This unregisters the passed device and deassigns the minor
+ *	number. Future open calls will be met with errors.
  */
  
 void video_unregister_device(struct video_device *vfd)
@@ -322,16 +333,14 @@
 	mmap:		video_mmap,
 	open:		video_open,
 	release:	video_release,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 	poll:		video_poll,
-#endif
 };
 
 /*
  *	Initialise video for linux
  */
  
-int videodev_init(void)
+int __init videodev_init(void)
 {
 	struct video_init *vfli = video_init_list;
 	
@@ -365,15 +374,10 @@
 	devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture");
 }
 
-
-
-
-
-
-
 #endif
 
-#if LINUX_VERSION_CODE >= 0x020100
 EXPORT_SYMBOL(video_register_device);
 EXPORT_SYMBOL(video_unregister_device);
-#endif
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("Device registrar for Video4Linux drivers");
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/Config.in linux/drivers/ide/Config.in
--- v2.3.51/linux/drivers/ide/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/Config.in	Sun Mar 12 19:49:24 2000
@@ -0,0 +1,153 @@
+#
+# IDE ATA ATAPI Block device driver configuration
+#
+mainmenu_option next_comment
+comment 'IDE, ATA and ATAPI Block devices'
+
+dep_tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE $CONFIG_IDE
+comment 'Please see Documentation/ide.txt for help/info on IDE drives'
+if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+   dep_bool '  Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE $CONFIG_X86
+   define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_IDE
+
+   dep_tristate '  Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
+   dep_mbool '    Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE $CONFIG_BLK_DEV_IDEDISK
+   dep_tristate '  PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA
+   dep_tristate '  Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
+   dep_tristate '  Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
+   dep_tristate '  Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
+   dep_tristate '  SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
+
+   comment 'IDE chipset support/bugfixes'
+   if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+      dep_bool '  CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86
+      dep_bool '    CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640
+      dep_bool '  ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP
+      if [ "$CONFIG_PCI" = "y" ]; then
+	 dep_bool '  RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86
+	 bool '  Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
+	 if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
+	    bool '    Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
+	    bool '    Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
+	    bool '    Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
+	    dep_bool '      Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI
+	    define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
+	    define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PCI_AUTO
+	    define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL $CONFIG_EXPERIMENTAL
+	    dep_bool '      ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
+	    dep_bool '      Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
+	    dep_bool '    AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_mbool '      AEC6210 Tuning support (WIP)' CONFIG_AEC6210_TUNING $CONFIG_BLK_DEV_AEC6210 $CONFIG_IDEDMA_PCI_WIP
+	    dep_bool '    ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_bool '    AMD Viper support' CONFIG_BLK_DEV_AMD7409 $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_mbool '      AMD Viper ATA-66 Override (WIP)' CONFIG_AMD7409_OVERRIDE $CONFIG_BLK_DEV_AMD7409 $CONFIG_IDEDMA_PCI_WIP
+	    dep_bool '    CMD64X chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_mbool '      CMD64X chipset RAID (WIP)' CONFIG_CMD64X_RAID $CONFIG_BLK_DEV_CMD64X $CONFIG_IDEDMA_PCI_WIP
+	    dep_bool '    CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+	    dep_bool '    Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDEDMA_PCI
+  	    dep_bool '    HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_mbool '      HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA $CONFIG_BLK_DEV_HPT34X $CONFIG_IDEDMA_PCI_WIP
+	    dep_bool '    HPT366 chipset support' CONFIG_BLK_DEV_HPT366 $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_bool '      HPT366 Fast Interrupts (WIP)' CONFIG_HPT366_FIP $CONFIG_BLK_DEV_HPT366 $CONFIG_IDEDMA_PCI_WIP
+	    dep_mbool '      HPT366 mode Three (WIP)' CONFIG_HPT366_MODE3 $CONFIG_BLK_DEV_HPT366 $CONFIG_IDEDMA_PCI_WIP
+	    if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then
+	       dep_mbool '    Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDEDMA_PCI
+	       dep_mbool '      PIIXn Tuning support' CONFIG_PIIX_TUNING $CONFIG_BLK_DEV_PIIX $CONFIG_IDEDMA_PCI_AUTO
+	    fi
+	    dep_bool '    NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+	    dep_bool '    OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL
+	    dep_bool '    PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_bool '      Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX
+	    dep_mbool '      Special Mode Feature (WIP)' CONFIG_PDC202XX_MASTER $CONFIG_BLK_DEV_PDC202XX $CONFIG_IDEDMA_PCI_WIP
+	    dep_bool '    SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
+	    dep_bool '    Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+	    dep_bool '    VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+         fi
+	 if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
+	    bool '    Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
+	 fi
+      fi
+      if [ "$CONFIG_PMAC" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
+	 bool '    Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
+	 dep_bool '      PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC $CONFIG_BLK_DEV_IDE_PMAC
+	 dep_bool '        Use DMA by default' CONFIG_IDEDMA_PMAC_AUTO $CONFIG_BLK_DEV_IDEDMA_PMAC
+	 define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PMAC
+	 define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PMAC_AUTO
+      fi
+      if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+	 dep_bool '    ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_ARCH_ACORN
+	 dep_bool '      ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS $CONFIG_BLK_DEV_IDE_ICSIDE
+	 dep_bool '        Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO $CONFIG_BLK_DEV_IDEDMA_ICS
+	 define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS
+	 define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_ICS_AUTO
+	 dep_bool '    RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN
+      fi
+      if [ "$CONFIG_AMIGA" = "y" ]; then
+	 dep_bool '    Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA
+	 dep_mbool '      Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL
+      fi
+      if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+	 dep_mbool '    Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA $CONFIG_ZORRO $CONFIG_EXPERIMENTAL
+      fi
+      if [ "$CONFIG_ATARI" = "y" ]; then
+	 dep_bool '    Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE $CONFIG_ATARI
+      fi
+      if [ "$CONFIG_MAC" = "y" ]; then
+	 dep_bool '    Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE $CONFIG_MAC
+      fi
+
+      bool '  Other IDE chipset support' CONFIG_IDE_CHIPSETS
+      if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
+	 comment 'Note: most of these also require special kernel boot parameters'
+	 bool '    Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
+	 bool '    ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
+	 bool '    DTC-2278 support' CONFIG_BLK_DEV_DTC2278
+	 bool '    Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
+	 if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+	    bool '    PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
+	 fi
+	 bool '    QDI QD6580 support' CONFIG_BLK_DEV_QD6580
+	 bool '    UMC-8672 support' CONFIG_BLK_DEV_UMC8672
+      fi
+   fi
+else
+   bool 'Old hard disk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
+   define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_ONLY
+fi
+
+# if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
+#      "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
+#      "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
+#    define_bool CONFIG_BLK_DEV_IDEDMA y
+#    if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
+#         "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
+#         "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
+#       define_bool CONFIG_IDEDMA_AUTO y
+#    fi
+# else
+#    define_bool CONFIG_BLK_DEV_IDEDMA n
+#    define_bool CONFIG_IDEDMA_AUTO n
+# fi
+
+if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
+     "$CONFIG_BLK_DEV_AEC6210" = "y" -o \
+     "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \
+     "$CONFIG_BLK_DEV_AMD7409" = "y" -o \
+     "$CONFIG_BLK_DEV_CMD640" = "y" -o \
+     "$CONFIG_BLK_DEV_CMD64X" = "y" -o \
+     "$CONFIG_BLK_DEV_CS5530" = "y" -o \
+     "$CONFIG_BLK_DEV_CY82C693" = "y" -o \
+     "$CONFIG_BLK_DEV_HPT34X" = "y" -o \
+     "$CONFIG_BLK_DEV_HPT366" = "y" -o \
+     "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
+     "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
+     "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \
+     "$CONFIG_BLK_DEV_PIIX" = "y" -o \
+     "$CONFIG_BLK_DEV_SIS5513" = "y" -o \
+     "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
+   define_bool CONFIG_BLK_DEV_IDE_MODES y
+else
+   define_bool CONFIG_BLK_DEV_IDE_MODES n
+fi
+
+endmenu
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/Makefile linux/drivers/ide/Makefile
--- v2.3.51/linux/drivers/ide/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/Makefile	Sun Mar 12 19:49:24 2000
@@ -0,0 +1,238 @@
+#
+# Makefile for the kernel ata, atapi, and ide block device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+#
+# Note : at this point, these files are compiled on all systems.
+# In the future, some of these should be built conditionally.
+#
+
+SUB_DIRS     :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := ide.a
+L_OBJS   := ide-geometry.o
+M_OBJS   :=
+MOD_LIST_NAME := IDE_MODULES
+LX_OBJS :=
+MX_OBJS :=
+
+ifeq ($(CONFIG_BLK_DEV_AEC6210),y)
+IDE_OBJS += aec6210.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
+IDE_OBJS += ali14xx.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI15X3),y)
+IDE_OBJS += alim15x3.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_AMD7409),y)
+IDE_OBJS += amd7409.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_BUDDHA),y)
+IDE_OBJS += buddha.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD640),y)
+IDE_OBJS += cmd640.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD64X),y)
+IDE_OBJS += cmd64x.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CS5530),y)
+IDE_OBJS += cs5530.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CY82C693),y)
+IDE_OBJS += cy82c693.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
+IDE_OBJS += dtc2278.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_FALCON_IDE),y)
+IDE_OBJS += falconide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_GAYLE),y)
+IDE_OBJS += gayle.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_Q40IDE),y)
+IDE_OBJS += q40ide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HD),y)
+L_OBJS += hd.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HPT34X),y)
+IDE_OBJS += hpt34x.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HPT366),y)
+IDE_OBJS += hpt366.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
+IDE_OBJS += ht6560b.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
+IDE_OBJS += icside.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEDMA),y)
+IDE_OBJS += ide-dma.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEPCI),y)
+IDE_OBJS += ide-pci.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ISAPNP),y)
+IDE_OBJS += ide-pnp.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_PMAC),y)
+IDE_OBJS += ide-pmac.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_MAC_IDE),y)
+IDE_OBJS += macide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_NS87415),y)
+IDE_OBJS += ns87415.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
+IDE_OBJS += opti621.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PDC202XX),y)
+IDE_OBJS += pdc202xx.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PDC4030),y)
+IDE_OBJS += pdc4030.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PIIX),y)
+IDE_OBJS += piix.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_QD6580),y)
+IDE_OBJS += qd6580.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
+IDE_OBJS += rapide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
+IDE_OBJS += rz1000.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SIS5513),y)
+IDE_OBJS += sis5513.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SL82C105),y)
+IDE_OBJS += sl82c105.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_TRM290),y)
+IDE_OBJS += trm290.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
+IDE_OBJS += umc8672.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_VIA82CXXX),y)
+IDE_OBJS += via82cxxx.o
+endif
+
+### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored
+
+ifeq ($(CONFIG_PROC_FS),y)
+IDE_OBJS += ide-proc.o
+endif
+  
+###Collect
+
+ifeq ($(CONFIG_BLK_DEV_IDE),y)
+  LX_OBJS += ide.o ide-features.o
+  L_OBJS += ide-probe.o $(IDE_OBJS)
+else
+  ifeq ($(CONFIG_BLK_DEV_IDE),m)
+  MIX_OBJS += ide.o ide-features.o $(IDE_OBJS)
+  M_OBJS += ide-mod.o ide-probe-mod.o
+  endif
+endif
+
+############
+
+ifeq ($(CONFIG_BLK_DEV_IDECS),y)
+L_OBJS += ide-cs.o
+else
+  ifeq ($(CONFIG_BLK_DEV_IDECS),m)
+  M_OBJS += ide-cs.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
+L_OBJS += ide-disk.o
+else
+  ifeq ($(CONFIG_BLK_DEV_IDEDISK),m)
+  M_OBJS += ide-disk.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDECD),y)
+L_OBJS += ide-cd.o
+else
+  ifeq ($(CONFIG_BLK_DEV_IDECD),m)
+  M_OBJS += ide-cd.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
+L_OBJS += ide-tape.o
+else
+  ifeq ($(CONFIG_BLK_DEV_IDETAPE),m)
+  M_OBJS += ide-tape.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
+L_OBJS += ide-floppy.o
+else
+  ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),m)
+  M_OBJS += ide-floppy.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+ide-mod.o: ide.o ide-features.o $(IDE_OBJS)
+	$(LD) $(LD_RFLAG) -r -o $@ ide.o ide-features.o $(IDE_OBJS)
+
+ide-probe-mod.o: ide-probe.o ide-geometry.o
+	$(LD) $(LD_RFLAG) -r -o $@ ide-probe.o ide-geometry.o
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/aec6210.c linux/drivers/ide/aec6210.c
--- v2.3.51/linux/drivers/ide/aec6210.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/aec6210.c	Wed Mar  8 11:40:25 2000
@@ -0,0 +1,372 @@
+/*
+ * linux/drivers/block/aec6210.c		Version 0.05	Feb. 10, 2000
+ *
+ * Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ *  pio 0 ::       40: 00 07 00 00 00 00 00 00 02 07 a6 04 00 02 00 02
+ *  pio 1 ::       40: 0a 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02
+ *  pio 2 ::       40: 08 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02
+ *  pio 3 ::       40: 03 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *  pio 4 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *  dma 0 ::       40: 0a 07 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *  dma 1 ::       40: 02 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *  dma 2 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *                 50: ff ff ff ff 00 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 0 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *                 50: ff ff ff ff 01 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 1 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *                 50: ff ff ff ff 01 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 2 ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *                 50: ff ff ff ff 02 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * auto   ::       40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ *                 50: ff ff ff ff 02 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * auto   ::       40: 01 04 01 04 01 04 01 04 02 05 a6 cf 00 02 00 02
+ *                 50: ff ff ff ff aa 06 04 00 00 00 00 00 00 00 00 00
+ *
+ *                 NO-Devices
+ *                 40: 00 00 00 00 00 00 00 00 02 05 a6 00 00 02 00 02
+ *                 50: ff ff ff ff 00 06 00 00 00 00 00 00 00 00 00 00
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#define ACARD_DEBUG_DRIVE_INFO		0
+
+#undef DISPLAY_AEC6210_TIMINGS
+
+#if defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int aec6210_get_info(char *, char **, off_t, int);
+extern int (*aec6210_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int aec6210_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p = buffer;
+
+	u32 bibma = bmide_dev->resource[4].start;
+	u8 c0 = 0, c1 = 0;
+	
+	p += sprintf(p, "\n                                AEC6210 Chipset.\n");
+
+        /*
+         * at that point bibma+0x2 et bibma+0xa are byte registers
+         * to investigate:
+         */
+	c0 = inb_p((unsigned short)bibma + 0x02);
+	c1 = inb_p((unsigned short)bibma + 0x0a);
+
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	p += sprintf(p, "                %sabled                         %sabled\n",
+			(c0&0x80) ? "dis" : " en",
+			(c1&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
+			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+	p += sprintf(p, "UDMA\n");
+	p += sprintf(p, "DMA\n");
+	p += sprintf(p, "PIO\n");
+	return p-buffer;/* => must be less than 4k! */
+}
+#endif	/* defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte aec6210_proc = 0;
+
+#ifdef CONFIG_AEC6210_TUNING
+
+struct chipset_bus_clock_list_entry {
+	byte		xfer_speed;
+	unsigned short	chipset_settings;
+	byte		ultra_settings;
+};
+
+struct chipset_bus_clock_list_entry aec6210_base [] = {
+	{	XFER_UDMA_2,	0x0401,	0x02	},
+	{	XFER_UDMA_1,	0x0401,	0x01	},
+	{	XFER_UDMA_0,	0x0401,	0x01	},
+
+	{	XFER_MW_DMA_2,	0x0401,	0x00	},
+	{	XFER_MW_DMA_1,	0x0402,	0x00	},
+	{	XFER_MW_DMA_0,	0x070a,	0x00	},
+
+	{	XFER_PIO_4,	0x0401,	0x00	},
+	{	XFER_PIO_3,	0x0403,	0x00	},
+	{	XFER_PIO_2,	0x0708,	0x00	},
+	{	XFER_PIO_1,	0x070a,	0x00	},
+	{	XFER_PIO_0,	0x0700,	0x00	},
+	{	0,		0x0000,	0x00	}
+};
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * TO DO: active tuning and correction of cards without a bios.
+ */
+
+static unsigned short pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
+{
+	for ( ; chipset_table->xfer_speed ; chipset_table++)
+		if (chipset_table->xfer_speed == speed) {
+			return chipset_table->chipset_settings;
+		}
+	return 0x0000;
+}
+
+static byte pci_bus_clock_list_ultra (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
+{
+	for ( ; chipset_table->xfer_speed ; chipset_table++)
+		if (chipset_table->xfer_speed == speed) {
+			return chipset_table->ultra_settings;
+		}
+	return 0x00;
+}
+
+static int aec6210_tune_chipset (ide_drive_t *drive, byte speed)
+{
+	ide_hwif_t *hwif	= HWIF(drive);
+
+	int			err;
+	byte			drive_pci;
+	unsigned short		drive_conf = 0x0000;
+	byte			ultra = 0x00, ultra_conf = 0x00;
+	byte			tmp1 = 0x00, tmp2 = 0x00;
+
+	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+
+	switch(drive_number) {
+		case 0:		drive_pci = 0x40; break;
+		case 1:		drive_pci = 0x42; break;
+		case 2:		drive_pci = 0x44; break;
+		case 3:		drive_pci = 0x46; break;
+		default:	return -1;
+	}
+
+	pci_read_config_word(HWIF(drive)->pci_dev, drive_pci, &drive_conf);
+	drive_conf = pci_bus_clock_list(speed, aec6210_base);
+	pci_write_config_word(HWIF(drive)->pci_dev, drive_pci, drive_conf);
+
+	pci_read_config_byte(HWIF(drive)->pci_dev, 0x54, &ultra);
+	tmp1 = ((0x00 << (2*drive_number)) | (ultra & ~(3 << (2*drive_number))));
+	ultra_conf = pci_bus_clock_list_ultra(speed, aec6210_base);
+	tmp2 = ((ultra_conf << (2*drive_number)) | (tmp1 & ~(3 << (2*drive_number))));
+	pci_write_config_byte(HWIF(drive)->pci_dev, 0x54, tmp2);
+
+	err = ide_config_drive_speed(drive, speed);
+
+#if ACARD_DEBUG_DRIVE_INFO
+	printk("%s: %s drive%d 0x04%x 0x02%x 0x02%x 0x02%x 0x02%x\n",
+		drive->name, ide_xfer_verbose(speed), drive_number,
+		drive_conf, ultra, tmp1, ultra_conf, tmp2);
+#endif /* ACARD_DEBUG_DRIVE_INFO */
+
+	return(err);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+	struct hd_driveid *id = drive->id;
+	byte speed = -1;
+
+	if (drive->media != ide_disk)
+		return ((int) ide_dma_off_quietly);
+
+	if (((id->dma_ultra & 0x0010) ||
+	     (id->dma_ultra & 0x0008) ||
+	     (id->dma_ultra & 0x0004)) && (ultra)) {
+		speed = XFER_UDMA_2;
+	} else if ((id->dma_ultra & 0x0002) && (ultra)) {
+		speed = XFER_UDMA_1;
+	} else if ((id->dma_ultra & 0x0001) && (ultra)) {
+		speed = XFER_UDMA_0;
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+	} else if (id->dma_1word & 0x0004) {
+		speed = XFER_SW_DMA_2;
+	} else if (id->dma_1word & 0x0002) {
+		speed = XFER_SW_DMA_1;
+	} else if (id->dma_1word & 0x0001) {
+		speed = XFER_SW_DMA_0;
+	} else {
+		return ((int) ide_dma_off_quietly);
+	}
+	(void) aec6210_tune_chipset(drive, speed);
+
+	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_off :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+}
+
+static void aec6210_tune_drive (ide_drive_t *drive, byte pio)
+{
+	byte speed;
+
+	switch(pio) {
+		case 5:
+			speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
+		case 4:
+			speed = XFER_PIO_4; break;
+		case 3:
+			speed = XFER_PIO_3; break;
+		case 2:
+			speed = XFER_PIO_2; break;
+		case 1:
+			speed = XFER_PIO_1; break;
+		default:
+			speed = XFER_PIO_0; break;
+	}
+	(void) aec6210_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_dma_action_t dma_func = ide_dma_on;
+
+	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
+		}
+		dma_func = ide_dma_off_quietly;
+		if (id->field_valid & 4) {
+			if (id->dma_ultra & 0x001F) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive, 1);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if ((id->dma_mword & 0x0007) ||
+			    (id->dma_1word & 0x0007)) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive, 0);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
+			}
+		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+			if (id->eide_dma_time > 150) {
+				goto no_dma_set;
+			}
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive, 0);
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+		aec6210_tune_drive(drive, 5);
+	}
+	return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * aec6210_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+int aec6210_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			return config_drive_xfer_rate(drive);
+		default:
+			break;
+	}
+	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+}
+#endif /* CONFIG_AEC6210_TUNING */
+
+unsigned int __init pci_init_aec6210 (struct pci_dev *dev, const char *name)
+{
+	if (dev->resource[PCI_ROM_RESOURCE].start) {
+		pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+		printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
+	}
+
+#if defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS)
+	aec6210_proc = 1;
+	bmide_dev = dev;
+	aec6210_display_info = &aec6210_get_info;
+#endif /* DISPLAY_AEC6210_TIMINGS && CONFIG_PROC_FS */
+
+	return dev->irq;
+}
+
+void __init ide_init_aec6210 (ide_hwif_t *hwif)
+{
+#ifdef CONFIG_AEC6210_TUNING
+	hwif->tuneproc = &aec6210_tune_drive;
+
+	if (hwif->dma_base) {
+		hwif->dmaproc = &aec6210_dmaproc;
+	} else {
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+#endif /* CONFIG_AEC6210_TUNING */
+}
+
+void __init ide_dmacapable_aec6210 (ide_hwif_t *hwif, unsigned long dmabase)
+{
+	byte dma_new	= 0;
+	byte dma_old	= inb(dmabase+2);
+	byte reg54h	= 0;
+	byte masterdma	= hwif->channel ? 0x30 : 0x03;
+	byte slavedma	= hwif->channel ? 0xc0 : 0x0c;
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+
+	dma_new = dma_old;
+
+	pci_read_config_byte(hwif->pci_dev, 0x54, &reg54h);
+
+	if (reg54h & masterdma)	dma_new |= 0x20;
+	if (reg54h & slavedma)	dma_new |= 0x40;
+	if (dma_new != dma_old)	outb(dma_new, dmabase+2);
+
+	__restore_flags(flags);	/* local CPU only */
+
+	ide_setup_dma(hwif, dmabase, 8);
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ali14xx.c linux/drivers/ide/ali14xx.c
--- v2.3.51/linux/drivers/ide/ali14xx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ali14xx.c	Fri Mar  3 12:54:44 2000
@@ -0,0 +1,225 @@
+/*
+ *  linux/drivers/block/ali14xx.c		Version 0.03	Feb 09, 1996
+ *
+ *  Copyright (C) 1996  Linus Torvalds & author (see below)
+ */
+
+/*
+ * ALI M14xx chipset EIDE controller
+ *
+ * Works for ALI M1439/1443/1445/1487/1489 chipsets.
+ *
+ * Adapted from code developed by derekn@vw.ece.cmu.edu.  -ml
+ * Derek's notes follow:
+ *
+ * I think the code should be pretty understandable,
+ * but I'll be happy to (try to) answer questions.
+ *
+ * The critical part is in the setupDrive function.  The initRegisters
+ * function doesn't seem to be necessary, but the DOS driver does it, so
+ * I threw it in.
+ *
+ * I've only tested this on my system, which only has one disk.  I posted
+ * it to comp.sys.linux.hardware, so maybe some other people will try it
+ * out.
+ *
+ * Derek Noonburg  (derekn@ece.cmu.edu)
+ * 95-sep-26
+ *
+ * Update 96-jul-13:
+ *
+ * I've since upgraded to two disks and a CD-ROM, with no trouble, and
+ * I've also heard from several others who have used it successfully.
+ * This driver appears to work with both the 1443/1445 and the 1487/1489
+ * chipsets.  I've added support for PIO mode 4 for the 1487.  This
+ * seems to work just fine on the 1443 also, although I'm not sure it's
+ * advertised as supporting mode 4.  (I've been running a WDC AC21200 in
+ * mode 4 for a while now with no trouble.)  -Derek
+ */
+
+#undef REALLY_SLOW_IO           /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/* port addresses for auto-detection */
+#define ALI_NUM_PORTS 4
+static int ports[ALI_NUM_PORTS] __initdata = {0x074, 0x0f4, 0x034, 0x0e4};
+
+/* register initialization data */
+typedef struct { byte reg, data; } RegInitializer;
+
+static RegInitializer initData[] __initdata = {
+	{0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00},
+	{0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f},
+	{0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
+	{0x29, 0x00}, {0x2a, 0x00}, {0x2f, 0x00}, {0x2b, 0x00},
+	{0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x30, 0x00},
+	{0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0xff},
+	{0x35, 0x03}, {0x00, 0x00}
+};
+
+#define ALI_MAX_PIO 4
+
+/* timing parameter registers for each drive */
+static struct { byte reg1, reg2, reg3, reg4; } regTab[4] = {
+	{0x03, 0x26, 0x04, 0x27},     /* drive 0 */
+	{0x05, 0x28, 0x06, 0x29},     /* drive 1 */
+	{0x2b, 0x30, 0x2c, 0x31},     /* drive 2 */
+	{0x2d, 0x32, 0x2e, 0x33},     /* drive 3 */
+};
+
+static int basePort = 0;	/* base port address */
+static int regPort = 0;		/* port for register number */
+static int dataPort = 0;	/* port for register data */
+static byte regOn;	/* output to base port to access registers */
+static byte regOff;	/* output to base port to close registers */
+
+/*------------------------------------------------------------------------*/
+
+/*
+ * Read a controller register.
+ */
+static inline byte inReg (byte reg)
+{
+	outb_p(reg, regPort);
+	return inb(dataPort);
+}
+
+/*
+ * Write a controller register.
+ */
+static void outReg (byte data, byte reg)
+{
+	outb_p(reg, regPort);
+	outb_p(data, dataPort);
+}
+
+/*
+ * Set PIO mode for the specified drive.
+ * This function computes timing parameters
+ * and sets controller registers accordingly.
+ */
+static void ali14xx_tune_drive (ide_drive_t *drive, byte pio)
+{
+	int driveNum;
+	int time1, time2;
+	byte param1, param2, param3, param4;
+	unsigned long flags;
+	ide_pio_data_t d;
+	int bus_speed = ide_system_bus_speed();
+
+	pio = ide_get_best_pio_mode(drive, pio, ALI_MAX_PIO, &d);
+
+	/* calculate timing, according to PIO mode */
+	time1 = d.cycle_time;
+	time2 = ide_pio_timings[pio].active_time;
+	param3 = param1 = (time2 * bus_speed + 999) / 1000;
+	param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1;
+	if (pio < 3) {
+		param3 += 8;
+		param4 += 8;
+	}
+	printk("%s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d\n",
+		drive->name, pio, time1, time2, param1, param2, param3, param4);
+
+	/* stuff timing parameters into controller registers */
+	driveNum = (HWIF(drive)->index << 1) + drive->select.b.unit;
+	save_flags(flags);	/* all CPUs */
+	cli();			/* all CPUs */
+	outb_p(regOn, basePort);
+	outReg(param1, regTab[driveNum].reg1);
+	outReg(param2, regTab[driveNum].reg2);
+	outReg(param3, regTab[driveNum].reg3);
+	outReg(param4, regTab[driveNum].reg4);
+	outb_p(regOff, basePort);
+	restore_flags(flags);	/* all CPUs */
+}
+
+/*
+ * Auto-detect the IDE controller port.
+ */
+static int __init findPort (void)
+{
+	int i;
+	byte t;
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	for (i = 0; i < ALI_NUM_PORTS; ++i) {
+		basePort = ports[i];
+		regOff = inb(basePort);
+		for (regOn = 0x30; regOn <= 0x33; ++regOn) {
+			outb_p(regOn, basePort);
+			if (inb(basePort) == regOn) {
+				regPort = basePort + 4;
+				dataPort = basePort + 8;
+				t = inReg(0) & 0xf0;
+				outb_p(regOff, basePort);
+				__restore_flags(flags);	/* local CPU only */
+				if (t != 0x50)
+					return 0;
+				return 1;  /* success */
+			}
+		}
+		outb_p(regOff, basePort);
+	}
+	__restore_flags(flags);	/* local CPU only */
+	return 0;
+}
+
+/*
+ * Initialize controller registers with default values.
+ */
+static int __init initRegisters (void) {
+	RegInitializer *p;
+	byte t;
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	outb_p(regOn, basePort);
+	for (p = initData; p->reg != 0; ++p)
+		outReg(p->data, p->reg);
+	outb_p(0x01, regPort);
+	t = inb(regPort) & 0x01;
+	outb_p(regOff, basePort);
+	__restore_flags(flags);	/* local CPU only */
+	return t;
+}
+
+void __init init_ali14xx (void)
+{
+	/* auto-detect IDE controller port */
+	if (!findPort()) {
+		printk("\nali14xx: not found");
+		return;
+	}
+
+	printk("\nali14xx: base= 0x%03x, regOn = 0x%02x", basePort, regOn);
+	ide_hwifs[0].chipset = ide_ali14xx;
+	ide_hwifs[1].chipset = ide_ali14xx;
+	ide_hwifs[0].tuneproc = &ali14xx_tune_drive;
+	ide_hwifs[1].tuneproc = &ali14xx_tune_drive;
+	ide_hwifs[0].mate = &ide_hwifs[1];
+	ide_hwifs[1].mate = &ide_hwifs[0];
+	ide_hwifs[1].channel = 1;
+
+	/* initialize controller registers */
+	if (!initRegisters()) {
+		printk("\nali14xx: Chip initialization failed");
+		return;
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/alim15x3.c linux/drivers/ide/alim15x3.c
--- v2.3.51/linux/drivers/ide/alim15x3.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/alim15x3.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,689 @@
+/*
+ * linux/drivers/block/alim15x3.c		Version 0.08	Jan. 14, 2000
+ *
+ *  Copyright (C) 1998-2000 Michel Aubry, Maintainer
+ *  Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer
+ *
+ *  Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
+ *  May be copied or modified under the terms of the GNU General Public License
+ *
+ *  (U)DMA capable version of ali 1533/1543(C), 1535(D)
+ *
+ *  version: 1.0 beta2 (Sep. 2, 1999)
+ *	e-mail your problems to cjtsai@ali.com.tw
+ *
+ **********************************************************************
+ *  9/7/99 --Parts from the above author are included and need to be
+ *  converted into standard interface, once I finish the thought.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+#define DISPLAY_ALI_TIMINGS
+
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int ali_get_info(char *buffer, char **addr, off_t offset, int count);
+extern int (*ali_display_info)(char *, char **, off_t, int);  /* ide-proc.c */
+struct pci_dev *bmide_dev;
+
+char *fifo[4] = {
+	"FIFO Off",
+	"FIFO On ",
+	"DMA mode",
+	"PIO mode" };
+
+char *udmaT[8] = {
+	"1.5T",
+	"  2T",
+	"2.5T",
+	"  3T",
+	"3.5T",
+	"  4T",
+	"  6T",
+	"  8T"
+};
+
+char *channel_status[8] = {
+	"OK            ",
+	"busy          ",
+	"DRQ           ",
+	"DRQ busy      ",
+	"error         ",
+	"error busy    ",
+	"error DRQ     ",
+	"error DRQ busy"
+};
+
+static int ali_get_info(char *buffer, char **addr, off_t offset, int count)
+{
+	byte reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1;
+	unsigned int bibma;
+	byte c0, c1;
+	byte rev, tmp;
+	char *p = buffer;
+	char *q;
+
+	/* fetch rev. */
+	pci_read_config_byte(bmide_dev, 0x08, &rev);
+	if (rev >= 0xc1)	/* M1543C or newer */
+		udmaT[7] = " ???";
+	else
+		fifo[3]  = "   ???  ";
+
+	/* first fetch bibma: */
+	pci_read_config_dword(bmide_dev, 0x20, &bibma);
+	bibma = (bibma & 0xfff0) ;
+	/*
+	 * at that point bibma+0x2 et bibma+0xa are byte
+	 * registers to investigate:
+	 */
+	c0 = inb((unsigned short)bibma + 0x02);
+	c1 = inb((unsigned short)bibma + 0x0a);
+
+	p += sprintf(p,
+		"\n                                Ali M15x3 Chipset.\n");
+	p += sprintf(p,
+		"                                ------------------\n");
+	pci_read_config_byte(bmide_dev, 0x78, &reg53h);
+	p += sprintf(p, "PCI Clock: %d.\n", reg53h);
+
+	pci_read_config_byte(bmide_dev, 0x53, &reg53h);
+	p += sprintf(p,
+		"CD_ROM FIFO:%s, CD_ROM DMA:%s\n",
+		(reg53h & 0x02) ? "Yes" : "No ",
+		(reg53h & 0x01) ? "Yes" : "No " );
+	pci_read_config_byte(bmide_dev, 0x74, &reg53h);
+	p += sprintf(p,
+		"FIFO Status: contains %d Words, runs%s%s\n\n",
+		(reg53h & 0x3f),
+		(reg53h & 0x40) ? " OVERWR" : "",
+		(reg53h & 0x80) ? " OVERRD." : "." );
+
+	p += sprintf(p,
+		"-------------------primary channel-------------------secondary channel---------\n\n");
+
+	pci_read_config_byte(bmide_dev, 0x09, &reg53h);
+	p += sprintf(p,
+		"channel status:       %s                               %s\n",
+		(reg53h & 0x20) ? "On " : "Off",
+		(reg53h & 0x10) ? "On " : "Off" );
+
+	p += sprintf(p,
+		"both channels togth:  %s                               %s\n",
+		(c0&0x80) ? "No " : "Yes",
+		(c1&0x80) ? "No " : "Yes" );
+
+	pci_read_config_byte(bmide_dev, 0x76, &reg53h);
+	p += sprintf(p,
+		"Channel state:        %s                    %s\n",
+		channel_status[reg53h & 0x07],
+		channel_status[(reg53h & 0x70) >> 4] );
+
+	pci_read_config_byte(bmide_dev, 0x58, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x5c, &reg5yh);
+	p += sprintf(p,
+		"Add. Setup Timing:    %dT                                %dT\n",
+		(reg5xh & 0x07) ? (reg5xh & 0x07) : 8,
+		(reg5yh & 0x07) ? (reg5yh & 0x07) : 8 );
+
+	pci_read_config_byte(bmide_dev, 0x59, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x5d, &reg5yh);
+	p += sprintf(p,
+		"Command Act. Count:   %dT                                %dT\n"
+		"Command Rec. Count:   %dT                               %dT\n\n",
+		(reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
+		(reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8, 
+		(reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
+		(reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16 );
+
+	p += sprintf(p,
+		"----------------drive0-----------drive1------------drive0-----------drive1------\n\n");
+	p += sprintf(p,
+		"DMA enabled:      %s              %s               %s              %s\n",
+		(c0&0x20) ? "Yes" : "No ",
+		(c0&0x40) ? "Yes" : "No ",
+		(c1&0x20) ? "Yes" : "No ",
+		(c1&0x40) ? "Yes" : "No " );
+
+	pci_read_config_byte(bmide_dev, 0x54, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x55, &reg5yh);
+	q = "FIFO threshold:   %2d Words         %2d Words          %2d Words         %2d Words\n";
+	if (rev < 0xc1) {
+		if ((rev == 0x20) && (pci_read_config_byte(bmide_dev, 0x4f, &tmp), (tmp &= 0x20))) {
+			p += sprintf(p, q, 8, 8, 8, 8);
+		} else {
+			p += sprintf(p, q,
+				(reg5xh & 0x03) + 12,
+				((reg5xh & 0x30)>>4) + 12,
+				(reg5yh & 0x03) + 12,
+				((reg5yh & 0x30)>>4) + 12 );
+		}
+	} else {
+		p += sprintf(p, q,
+			(tmp = (reg5xh & 0x03)) ? (tmp << 3) : 4,
+			(tmp = ((reg5xh & 0x30)>>4)) ? (tmp << 3) : 4,
+			(tmp = (reg5yh & 0x03)) ? (tmp << 3) : 4,
+			(tmp = ((reg5yh & 0x30)>>4)) ? (tmp << 3) : 4 );
+	}
+
+#if 0
+	p += sprintf(p, 
+		"FIFO threshold:   %2d Words         %2d Words          %2d Words         %2d Words\n",
+		(reg5xh & 0x03) + 12,
+		((reg5xh & 0x30)>>4) + 12,
+		(reg5yh & 0x03) + 12,
+		((reg5yh & 0x30)>>4) + 12 );
+#endif
+
+	p += sprintf(p,
+		"FIFO mode:        %s         %s          %s         %s\n",
+		fifo[((reg5xh & 0x0c) >> 2)],
+		fifo[((reg5xh & 0xc0) >> 6)],
+		fifo[((reg5yh & 0x0c) >> 2)],
+		fifo[((reg5yh & 0xc0) >> 6)] );
+
+	pci_read_config_byte(bmide_dev, 0x5a, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x5b, &reg5xh1);
+	pci_read_config_byte(bmide_dev, 0x5e, &reg5yh);
+	pci_read_config_byte(bmide_dev, 0x5f, &reg5yh1);
+
+	p += sprintf(p,/*
+		"------------------drive0-----------drive1------------drive0-----------drive1------\n")*/
+		"Dt RW act. Cnt    %2dT              %2dT               %2dT              %2dT\n"
+		"Dt RW rec. Cnt    %2dT              %2dT               %2dT              %2dT\n\n",
+		(reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
+		(reg5xh1 & 0x70) ? ((reg5xh1 & 0x70) >> 4) : 8,
+		(reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
+		(reg5yh1 & 0x70) ? ((reg5yh1 & 0x70) >> 4) : 8,
+		(reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
+		(reg5xh1 & 0x0f) ? (reg5xh1 & 0x0f) : 16,
+		(reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16,
+		(reg5yh1 & 0x0f) ? (reg5yh1 & 0x0f) : 16 );
+
+	p += sprintf(p,
+		"-----------------------------------UDMA Timings--------------------------------\n\n");
+
+	pci_read_config_byte(bmide_dev, 0x56, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x57, &reg5yh);
+	p += sprintf(p,
+		"UDMA:             %s               %s                %s               %s\n"
+		"UDMA timings:     %s             %s              %s             %s\n\n",
+		(reg5xh & 0x08) ? "OK" : "No",
+		(reg5xh & 0x80) ? "OK" : "No",
+		(reg5yh & 0x08) ? "OK" : "No",
+		(reg5yh & 0x80) ? "OK" : "No",
+		udmaT[(reg5xh & 0x07)],
+		udmaT[(reg5xh & 0x70) >> 4],
+		udmaT[reg5yh & 0x07],
+		udmaT[(reg5yh & 0x70) >> 4] );
+
+	return p-buffer; /* => must be less than 4k! */
+}
+#endif  /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+static byte m5229_revision	= 0;
+static byte chip_is_1543c_e	= 0;
+static byte cable_80_pin[2]	= { 0, 0 };
+
+byte ali_proc = 0;
+static struct pci_dev *isa_dev;
+
+static void ali15x3_tune_drive (ide_drive_t *drive, byte pio)
+{
+	ide_pio_data_t d;
+	ide_hwif_t *hwif = HWIF(drive);
+	struct pci_dev *dev = hwif->pci_dev;
+	int s_time, a_time, c_time;
+	byte s_clc, a_clc, r_clc;
+	unsigned long flags;
+	int bus_speed = ide_system_bus_speed();
+	int port = hwif->index ? 0x5c : 0x58;
+	int portFIFO = hwif->channel ? 0x55 : 0x54;
+	byte cd_dma_fifo = 0;
+
+	pio = ide_get_best_pio_mode(drive, pio, 5, &d);
+	s_time = ide_pio_timings[pio].setup_time;
+	a_time = ide_pio_timings[pio].active_time;
+	if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
+		s_clc = 0;
+	if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8)
+		a_clc = 0;
+	c_time = ide_pio_timings[pio].cycle_time;
+
+#if 0
+	if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16)
+		r_clc = 0;
+#endif
+
+	if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) {
+		r_clc = 1;
+	} else {
+		if (r_clc >= 16)
+			r_clc = 0;
+	}
+	__save_flags(flags);
+	__cli();
+	
+	/* 
+	 * PIO mode => ATA FIFO on, ATAPI FIFO off
+	 */
+	pci_read_config_byte(dev, portFIFO, &cd_dma_fifo);
+	if (drive->media==ide_disk) {
+		if (hwif->index) {
+			pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0x0F) | 0x50);
+		} else {
+			pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0xF0) | 0x05);
+		}
+	} else {
+		if (hwif->index) {
+			pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0x0F);
+		} else {
+			pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0xF0);
+		}
+	}
+	
+	pci_write_config_byte(dev, port, s_clc);
+	pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc);
+	__restore_flags(flags);
+
+	/*
+	 * setup   active  rec
+	 * { 70,   165,    365 },   PIO Mode 0
+	 * { 50,   125,    208 },   PIO Mode 1
+	 * { 30,   100,    110 },   PIO Mode 2
+	 * { 30,   80,     70  },   PIO Mode 3 with IORDY
+	 * { 25,   70,     25  },   PIO Mode 4 with IORDY  ns
+	 * { 20,   50,     30  }    PIO Mode 5 with IORDY (nonstandard)
+	 */
+
+}
+
+static int ali15x3_tune_chipset (ide_drive_t *drive, byte speed)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	byte unit		= (drive->select.b.unit & 0x01);
+	byte tmpbyte		= 0x00;
+	int m5229_udma		= hwif->channel? 0x57 : 0x56;
+	int err			= 0;
+
+	if (speed < XFER_UDMA_0) {
+		byte ultra_enable	= (unit) ? 0x7f : 0xf7;
+		/*
+		 * clear "ultra enable" bit
+		 */
+		pci_read_config_byte(dev, m5229_udma, &tmpbyte);
+		tmpbyte &= ultra_enable;
+		pci_write_config_byte(dev, m5229_udma, tmpbyte);
+	}
+
+	err = ide_config_drive_speed(drive, speed);
+
+	if (speed >= XFER_SW_DMA_0) {
+		unsigned long dma_base = hwif->dma_base;
+
+		outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+	}
+
+	if (speed >= XFER_UDMA_0) {
+		pci_read_config_byte(dev, m5229_udma, &tmpbyte);
+		tmpbyte &= (0x0f << ((1-unit) << 2));
+		/*
+		 * enable ultra dma and set timing
+		 */
+		tmpbyte |= ((0x08 | (4-speed)) << (unit << 2));
+		pci_write_config_byte(dev, m5229_udma, tmpbyte);
+		if (speed >= XFER_UDMA_3) {
+			pci_read_config_byte(dev, 0x4b, &tmpbyte);
+			tmpbyte |= 1;
+			pci_write_config_byte(dev, 0x4b, tmpbyte);
+		}
+	}
+
+	return (err);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra33)
+{
+	struct hd_driveid *id	= drive->id;
+	ide_hwif_t *hwif	= HWIF(drive);
+	byte speed		= 0x00;
+	byte ultra66		= ((hwif->udma_four) && (id->hw_config & 0x2000)) ? 1 : 0;
+	int  rval;
+
+	if ((id->dma_ultra & 0x0010) && (ultra66) && (ultra33)) {
+		speed = XFER_UDMA_4;
+	} else if ((id->dma_ultra & 0x0008) && (ultra66) && (ultra33)) {
+		speed = XFER_UDMA_3;
+	} else if ((id->dma_ultra & 0x0004) && (ultra33)) {
+		speed = XFER_UDMA_2;
+	} else if ((id->dma_ultra & 0x0002) && (ultra33)) {
+		speed = XFER_UDMA_1;
+	} else if ((id->dma_ultra & 0x0001) && (ultra33)) {
+		speed = XFER_UDMA_0;
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+	} else if (id->dma_1word & 0x0004) {
+		speed = XFER_SW_DMA_2;
+	} else if (id->dma_1word & 0x0002) {
+		speed = XFER_SW_DMA_1;
+	} else if (id->dma_1word & 0x0001) {
+		speed = XFER_SW_DMA_0;
+	} else {
+		return ((int) ide_dma_off_quietly);
+	}
+
+	(void) ali15x3_tune_chipset(drive, speed);
+
+	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+
+	return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	ali15x3_tune_drive(drive, 5);
+}
+
+
+static byte ali15x3_can_ultra (ide_drive_t *drive)
+{
+	struct hd_driveid *id	= drive->id;
+
+#if 0
+	if (m5229_revision < 0x20) {
+#else
+	if (m5229_revision <= 0x20) {
+#endif
+		return 0;
+	} else if ((m5229_revision < 0xC2) &&
+		   ((drive->media!=ide_disk) ||
+		    (chip_is_1543c_e &&
+		     strstr(id->model, "WDC ")))) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+static int ali15x3_config_drive_for_dma(ide_drive_t *drive)
+{
+	struct hd_driveid *id		= drive->id;
+	ide_hwif_t *hwif		= HWIF(drive);
+	ide_dma_action_t dma_func	= ide_dma_on;
+	byte can_ultra_dma		= ali15x3_can_ultra(drive);
+
+	if ((m5229_revision<=0x20) && (drive->media!=ide_disk))
+		return hwif->dmaproc(ide_dma_off_quietly, drive);
+
+	if ((id != NULL) && ((id->capability & 1) != 0) && hwif->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
+		}
+		dma_func = ide_dma_off_quietly;
+		if ((id->field_valid & 4) && (m5229_revision >= 0xC2)) {
+			if (id->dma_ultra & 0x001F) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive, can_ultra_dma);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if ((id->dma_mword & 0x0007) ||
+			    (id->dma_1word & 0x0007)) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive, can_ultra_dma);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
+			}
+		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+			if (id->eide_dma_time > 150) {
+				goto no_dma_set;
+			}
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive, can_ultra_dma);
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+		config_chipset_for_pio(drive);
+	}
+	return hwif->dmaproc(dma_func, drive);
+}
+
+static int ali15x3_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch(func) {
+		case ide_dma_check:
+			return ali15x3_config_drive_for_dma(drive);
+		case ide_dma_write:
+			if ((m5229_revision < 0xC2) && (drive->media != ide_disk))
+				return 1;	/* try PIO instead of DMA */
+			break;
+		default:
+			break;
+	}
+	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name)
+{
+	unsigned long fixdma_base = dev->resource[4].start;
+
+	pci_read_config_byte(dev, PCI_REVISION_ID, &m5229_revision);
+
+	isa_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
+
+	if (!fixdma_base || fixdma_base == PCI_BASE_ADDRESS_IO_MASK) {
+		/*
+		 *
+		 */
+	} else {
+		/*
+		 * enable DMA capable bit, and "not" simplex only
+		 */
+		outb(inb(fixdma_base+2) & 0x60, fixdma_base+2);
+
+		if (inb(fixdma_base+2) & 0x80)
+			printk("%s: simplex device: DMA will fail!!\n", name);
+	}
+
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+	ali_proc = 1;
+	bmide_dev = dev;
+	ali_display_info = &ali_get_info;
+#endif  /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+	return 0;
+}
+
+unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif)
+{
+	struct pci_dev *dev	= hwif->pci_dev;
+	byte ata66mask		= hwif->channel ? 0x02 : 0x01;
+	unsigned int ata66	= 0;
+
+	unsigned long flags;
+	byte tmpbyte;
+
+	__save_flags(flags);
+	__cli();
+
+	if (m5229_revision >= 0xC2) {
+		/*
+		 * 1543C-B?, 1535, 1535D, 1553
+		 * Note 1: not all "motherboard" support this detection
+		 * Note 2: if no udma 66 device, the detection may "error".
+		 *         but in this case, we will not set the device to
+		 *         ultra 66, the detection result is not important
+		 */
+
+		/*
+		 * enable "Cable Detection", m5229, 0x4b, bit3
+		 */
+		pci_read_config_byte(dev, 0x4b, &tmpbyte);
+		pci_write_config_byte(dev, 0x4b, tmpbyte | 0x08);
+
+		/*
+		 * set south-bridge's enable bit, m1533, 0x79
+		 */
+		pci_read_config_byte(isa_dev, 0x79, &tmpbyte);
+		if (m5229_revision == 0xC2) {
+			/*
+			 * 1543C-B0 (m1533, 0x79, bit 2)
+			 */
+			pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x04);
+		} else if (m5229_revision == 0xC3) {
+			/*
+			 * 1553/1535 (m1533, 0x79, bit 1)
+			 */
+			pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x02);
+		}
+		/*
+		 * Ultra66 cable detection (from Host View)
+		 * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin
+		 */
+		pci_read_config_byte(dev, 0x4a, &tmpbyte);
+		/*
+		 * 0x4a, bit0 is 0 => primary channel
+		 * has 80-pin (from host view)
+		 */
+		if (!(tmpbyte & 0x01)) cable_80_pin[0] = 1;
+		/*
+		 * 0x4a, bit1 is 0 => secondary channel
+		 * has 80-pin (from host view)
+		 */
+		if (!(tmpbyte & 0x02)) cable_80_pin[1] = 1;
+	} else {
+		/*
+		 * revision 0x20 (1543-E, 1543-F)
+		 * revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E)
+		 * clear CD-ROM DMA write bit, m5229, 0x4b, bit 7
+		 */
+		pci_read_config_byte(dev, 0x4b, &tmpbyte);
+		/*
+		 * clear bit 7
+		 */
+		pci_write_config_byte(dev, 0x4b, tmpbyte & 0x7F);
+		/*
+		 * check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010
+		 */
+		pci_read_config_byte(isa_dev, 0x5e, &tmpbyte);
+		chip_is_1543c_e = ((tmpbyte & 0x1e) == 0x12) ? 1: 0;
+	}
+
+	/*
+	 * CD_ROM DMA on (m5229, 0x53, bit0)
+	 *      Enable this bit even if we want to use PIO
+	 * PIO FIFO off (m5229, 0x53, bit1)
+	 *      The hardware will use 0x54h and 0x55h to control PIO FIFO
+	 */
+	pci_read_config_byte(dev, 0x53, &tmpbyte);
+	tmpbyte = (tmpbyte & (~0x02)) | 0x01;
+
+	pci_write_config_byte(dev, 0x53, tmpbyte);
+
+	/*
+	 * Ultra66 cable detection (from Host View)
+	 * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin
+	 *
+	 * 0x4a, bit0 is 0 => primary channel
+	 * has 80-pin (from host view)
+	 *
+	 * 0x4a, bit1 is 0 => secondary channel
+	 * has 80-pin (from host view)
+	 */
+	pci_read_config_byte(dev, 0x4a, &tmpbyte);
+	ata66 = (!(tmpbyte & ata66mask)) ? 0 : 1;
+	__restore_flags(flags);
+
+	return(ata66);
+}
+
+void __init ide_init_ali15x3 (ide_hwif_t *hwif)
+{
+	byte ideic, inmir;
+	byte irq_routing_table[] = { -1,  9, 3, 10, 4,  5, 7,  6,
+				      1, 11, 0, 12, 0, 14, 0, 15 };
+
+	hwif->irq = hwif->channel ? 15 : 14;
+
+	if (isa_dev) {
+		/*
+		 * read IDE interface control
+		 */
+		pci_read_config_byte(isa_dev, 0x58, &ideic);
+
+		/* bit0, bit1 */
+		ideic = ideic & 0x03;
+
+		/* get IRQ for IDE Controller */
+		if ((hwif->channel && ideic == 0x03) || (!hwif->channel && !ideic)) {
+			/*
+			 * get SIRQ1 routing table
+			 */
+			pci_read_config_byte(isa_dev, 0x44, &inmir);
+			inmir = inmir & 0x0f;
+			hwif->irq = irq_routing_table[inmir];
+		} else if (hwif->channel && !(ideic & 0x01)) {
+			/*
+			 * get SIRQ2 routing table
+			 */
+			pci_read_config_byte(isa_dev, 0x75, &inmir);
+			inmir = inmir & 0x0f;
+			hwif->irq = irq_routing_table[inmir];
+		}
+	}
+
+	hwif->tuneproc = &ali15x3_tune_drive;
+	if ((hwif->dma_base) && (m5229_revision >= 0x20)) {
+		/*
+		 * M1543C or newer for DMAing
+		 */
+		hwif->dmaproc = &ali15x3_dmaproc;
+		hwif->autodma = 1;
+	} else {
+		hwif->autodma = 0;
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+	return;
+}
+
+void ide_dmacapable_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase)
+{
+	if ((dmabase) && (m5229_revision < 0x20)) {
+		return;
+	}
+	ide_setup_dma(hwif, dmabase, 8);
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/amd7409.c linux/drivers/ide/amd7409.c
--- v2.3.51/linux/drivers/ide/amd7409.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/amd7409.c	Mon Feb 28 07:18:20 2000
@@ -0,0 +1,411 @@
+/*
+ * linux/drivers/block/amd7409.c		Version 0.03	Feb. 10, 2000
+ *
+ * Copyright (C) 2000			Andre Hedrick <andre@suse.com>
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#define DISPLAY_VIPER_TIMINGS
+
+#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int amd7409_get_info(char *, char **, off_t, int);
+extern int (*amd7409_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int amd7409_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p = buffer;
+	u32 bibma = bmide_dev->resource[4].start;
+	u8 c0 = 0, c1 = 0;
+
+	/*
+	 * at that point bibma+0x2 et bibma+0xa are byte registers
+	 * to investigate:
+	 */
+	c0 = inb_p((unsigned short)bibma + 0x02);
+	c1 = inb_p((unsigned short)bibma + 0x0a);
+
+	p += sprintf(p, "\n                                AMD 7409 VIPER Chipset.\n");
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	p += sprintf(p, "                %sabled                         %sabled\n",
+			(c0&0x80) ? "dis" : " en",
+			(c1&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
+			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+	p += sprintf(p, "UDMA\n");
+	p += sprintf(p, "DMA\n");
+	p += sprintf(p, "PIO\n");
+
+	return p-buffer;	/* => must be less than 4k! */
+}
+#endif  /* defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte amd7409_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * Here is where all the hard work goes to program the chipset.
+ *
+ */
+static int amd7409_tune_chipset (ide_drive_t *drive, byte speed)
+{
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	int err			= 0;
+	byte unit		= (drive->select.b.unit & 0x01);
+	int drive_number	= ((HWIF(drive)->channel ? 2 : 0) + unit);
+	unsigned long dma_base	= hwif->dma_base;
+	byte drive_pci		= 0x00;
+	byte drive_pci2		= 0x00;
+	byte ultra_timing	= 0x00;
+	byte dma_pio_timing	= 0x00;
+	byte pio_timing		= 0x00;
+
+        switch (drive_number) {
+		case 0: drive_pci = 0x53; drive_pci2 = 0x4b; break;
+		case 1: drive_pci = 0x52; drive_pci2 = 0x4a; break;
+		case 2: drive_pci = 0x51; drive_pci2 = 0x49; break;
+		case 3: drive_pci = 0x50; drive_pci2 = 0x48; break;
+		default:
+                        return ((int) ide_dma_off_quietly);
+        }
+
+	pci_read_config_byte(dev, drive_pci, &ultra_timing);
+	pci_read_config_byte(dev, drive_pci2, &dma_pio_timing);
+	pci_read_config_byte(dev, 0x4c, &pio_timing);
+
+#ifdef DEBUG
+	printk("%s: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ",
+		drive->name, ultra_timing, dma_pio_timing, pio_timing);
+#endif
+
+	ultra_timing &= ~0xC7;
+	dma_pio_timing &= ~0xFF;
+	pio_timing &= ~(0x03 << drive_number);
+
+#ifdef DEBUG
+	printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ",
+		ultra_timing, dma_pio_timing, pio_timing);
+#endif
+
+	switch(speed) {
+		case XFER_UDMA_4:
+			ultra_timing |= 0x45;
+			dma_pio_timing |= 0x20;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_UDMA_3:
+			ultra_timing |= 0x44;
+			dma_pio_timing |= 0x20;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_UDMA_2:
+			ultra_timing |= 0x40;
+			dma_pio_timing |= 0x20;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_UDMA_1:
+			ultra_timing |= 0x41;
+			dma_pio_timing |= 0x20;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_UDMA_0:
+			ultra_timing |= 0x42;
+			dma_pio_timing |= 0x20;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_MW_DMA_2:
+			dma_pio_timing |= 0x20;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_MW_DMA_1:
+			dma_pio_timing |= 0x21;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_MW_DMA_0:
+			dma_pio_timing |= 0x77;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_PIO_4:
+			dma_pio_timing |= 0x20;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_PIO_3:
+			dma_pio_timing |= 0x22;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_PIO_2:
+			dma_pio_timing |= 0x42;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_PIO_1:
+			dma_pio_timing |= 0x65;
+			pio_timing |= (0x03 << drive_number);
+			break;
+		case XFER_PIO_0:
+		default:
+			dma_pio_timing |= 0xA8;
+			pio_timing |= (0x03 << drive_number);
+			break;
+        }
+
+	pci_write_config_byte(dev, drive_pci, ultra_timing);
+	pci_write_config_byte(dev, drive_pci2, dma_pio_timing);
+	pci_write_config_byte(dev, 0x4c, pio_timing);
+
+#ifdef DEBUG
+	printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x\n",
+		ultra_timing, dma_pio_timing, pio_timing);
+#endif
+
+	if (speed > XFER_PIO_4) {
+		outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+	} else {
+		outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
+	}
+	err = ide_config_drive_speed(drive, speed);
+	return (err);
+}
+
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS.
+ */
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id	= drive->id;
+	byte udma_66		= ((id->hw_config & 0x2000) &&
+				   (HWIF(drive)->udma_four)) ? 1 : 0;
+	byte speed		= 0x00;
+	int  rval;
+
+	if ((id->dma_ultra & 0x0010) && (udma_66)) {
+		speed = XFER_UDMA_4;
+	} else if ((id->dma_ultra & 0x0008) && (udma_66)) {
+		speed = XFER_UDMA_3;
+	} else if (id->dma_ultra & 0x0004) {
+		speed = XFER_UDMA_2;
+	} else if (id->dma_ultra & 0x0002) {
+		speed = XFER_UDMA_1;
+	} else if (id->dma_ultra & 0x0001) {
+		speed = XFER_UDMA_0;
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+        } else {
+		return ((int) ide_dma_off_quietly);
+	}
+
+	(void) amd7409_tune_chipset(drive, speed);
+
+	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+
+	return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+	unsigned short xfer_pio	= drive->id->eide_pio_modes;
+	byte			timing, speed, pio;
+
+	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 :
+			   (drive->id->tPIO & 2) ? 0x02 :
+			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+	switch(timing) {
+		case 4: speed = XFER_PIO_4;break;
+		case 3: speed = XFER_PIO_3;break;
+		case 2: speed = XFER_PIO_2;break;
+		case 1: speed = XFER_PIO_1;break;
+		default:
+			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+			break;
+	}
+	(void) amd7409_tune_chipset(drive, speed);
+}
+
+static void amd7409_tune_drive (ide_drive_t *drive, byte pio)
+{
+	byte speed;
+	switch(pio) {
+		case 4:		speed = XFER_PIO_4;break;
+		case 3:		speed = XFER_PIO_3;break;
+		case 2:		speed = XFER_PIO_2;break;
+		case 1:		speed = XFER_PIO_1;break;
+		default:	speed = XFER_PIO_0;break;
+	}
+	(void) amd7409_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_dma_action_t dma_func = ide_dma_on;
+
+	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
+		}
+		dma_func = ide_dma_off_quietly;
+		if (id->field_valid & 4) {
+			if (id->dma_ultra & 0x001F) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if (id->dma_mword & 0x0007) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
+			}
+		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+			if (id->eide_dma_time > 150) {
+				goto no_dma_set;
+			}
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive);
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+
+		config_chipset_for_pio(drive);
+	}
+	return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * amd7409_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+
+int amd7409_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			return config_drive_xfer_rate(drive);
+		default:
+			break;
+	}
+	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_amd7409 (struct pci_dev *dev, const char *name)
+{
+	unsigned long fixdma_base = dev->resource[4].start;
+
+	if (!fixdma_base || fixdma_base == PCI_BASE_ADDRESS_IO_MASK) {
+		/*
+		 *
+		 */
+	} else {
+		/*
+		 * enable DMA capable bit, and "not" simplex only
+		 */
+		outb(inb(fixdma_base+2) & 0x60, fixdma_base+2);
+
+		if (inb(fixdma_base+2) & 0x80)
+			printk("%s: simplex device: DMA will fail!!\n", name);
+	}
+#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
+	amd7409_proc = 1;
+	bmide_dev = dev;
+	amd7409_display_info = &amd7409_get_info;
+#endif /* DISPLAY_VIPER_TIMINGS && CONFIG_PROC_FS */
+
+	return 0;
+}
+
+unsigned int __init ata66_amd7409 (ide_hwif_t *hwif)
+{
+#ifdef CONFIG_AMD7409_OVERRIDE
+	byte ata66 = 1;
+#else
+	byte ata66 = 0;
+#endif /* CONFIG_AMD7409_OVERRIDE */
+
+#if 0
+	pci_read_config_byte(hwif->pci_dev, 0x48, &ata66);
+	return ((ata66 & 0x02) ? 0 : 1);
+#endif
+	return ata66;
+}
+
+void __init ide_init_amd7409 (ide_hwif_t *hwif)
+{
+	hwif->tuneproc = &amd7409_tune_drive;
+	if (hwif->dma_base) {
+		hwif->dmaproc = &amd7409_dmaproc;
+	} else {
+		hwif->autodma = 0;
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+}
+
+void ide_dmacapable_amd7409 (ide_hwif_t *hwif, unsigned long dmabase)
+{
+	ide_setup_dma(hwif, dmabase, 8);
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/buddha.c linux/drivers/ide/buddha.c
--- v2.3.51/linux/drivers/ide/buddha.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/buddha.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,165 @@
+/*
+ *  linux/drivers/block/buddha.c -- Amiga Buddha and Catweasel IDE Driver
+ *
+ *	Copyright (C) 1997 by Geert Uytterhoeven
+ *
+ *  This driver was written by based on the specifications in README.buddha.
+ *
+ *  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.
+ *
+ *  TODO:
+ *    - test it :-)
+ *    - tune the timings using the speed-register
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/zorro.h>
+#include <linux/ide.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+
+    /*
+     *  The Buddha has 2 IDE interfaces, the Catweasel has 3
+     */
+
+#define BUDDHA_NUM_HWIFS	2
+#define CATWEASEL_NUM_HWIFS	3
+
+
+    /*
+     *  Bases of the IDE interfaces (relative to the board address)
+     */
+
+#define BUDDHA_BASE1	0x800
+#define BUDDHA_BASE2	0xa00
+#define BUDDHA_BASE3	0xc00
+
+static const u_int __init buddha_bases[CATWEASEL_NUM_HWIFS] = {
+    BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3
+};
+
+
+    /*
+     *  Offsets from one of the above bases
+     */
+
+#define BUDDHA_DATA	0x00
+#define BUDDHA_ERROR	0x06		/* see err-bits */
+#define BUDDHA_NSECTOR	0x0a		/* nr of sectors to read/write */
+#define BUDDHA_SECTOR	0x0e		/* starting sector */
+#define BUDDHA_LCYL	0x12		/* starting cylinder */
+#define BUDDHA_HCYL	0x16		/* high byte of starting cyl */
+#define BUDDHA_SELECT	0x1a		/* 101dhhhh , d=drive, hhhh=head */
+#define BUDDHA_STATUS	0x1e		/* see status-bits */
+#define BUDDHA_CONTROL	0x11a
+
+static int __init buddha_offsets[IDE_NR_PORTS] = {
+    BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL,
+    BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL
+};
+
+
+    /*
+     *  Other registers
+     */
+
+#define BUDDHA_IRQ1	0xf00		/* MSB = 1, Harddisk is source of */
+#define BUDDHA_IRQ2	0xf40		/* interrupt */
+#define BUDDHA_IRQ3	0xf80
+
+static const int __init buddha_irqports[CATWEASEL_NUM_HWIFS] = {
+    BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3
+};
+
+#define BUDDHA_IRQ_MR	0xfc0		/* master interrupt enable */
+
+
+    /*
+     *  Board information
+     */
+
+static u_long buddha_board = 0;
+static int buddha_num_hwifs = -1;
+
+
+    /*
+     *  Check and acknowledge the interrupt status
+     */
+
+static int buddha_ack_intr(ide_hwif_t *hwif)
+{
+    unsigned char ch;
+
+    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+    if (!(ch & 0x80))
+	return 0;
+    return 1;
+}
+
+
+    /*
+     *  Any Buddha or Catweasel boards present?
+     */
+
+static int __init find_buddha(void)
+{
+    struct zorro_dev *z = NULL;
+
+    buddha_num_hwifs = 0;
+    while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
+	unsigned long board;
+	const char *name;
+	if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) {
+	    buddha_num_hwifs = BUDDHA_NUM_HWIFS;
+	    name = "Buddha IDE Interface";
+	} else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL) {
+	    buddha_num_hwifs = CATWEASEL_NUM_HWIFS;
+	    name = "Catweasel IDE Interface and Floppy Controller";
+	} else
+	    continue;
+	board = z->resource.start;
+	if (!request_mem_region(board+BUDDHA_BASE1, 0x800, "IDE"))
+	    continue;
+	strcpy(z->name, name);
+	buddha_board = ZTWO_VADDR(board);
+	/* write to BUDDHA_IRQ_MR to enable the board IRQ */
+	*(char *)(buddha_board+BUDDHA_IRQ_MR) = 0;
+	break;
+    }
+    return buddha_num_hwifs;
+}
+
+
+    /*
+     *  Probe for a Buddha or Catweasel IDE interface
+     *  We support only _one_ of them, no multiple boards!
+     */
+
+void __init buddha_init(void)
+{
+    hw_regs_t hw;
+    int i, index;
+
+    if (buddha_num_hwifs < 0 && !find_buddha())
+	return;
+
+    for (i = 0; i < buddha_num_hwifs; i++) {
+	ide_setup_ports(&hw, (ide_ioreg_t)(buddha_board+buddha_bases[i]),
+			buddha_offsets, 0,
+			(ide_ioreg_t)(buddha_board+buddha_irqports[i]),
+			buddha_ack_intr, IRQ_AMIGA_PORTS);
+	index = ide_register_hw(&hw, NULL);
+	if (index != -1)
+	    printk("ide%d: %s IDE interface\n", index,
+		   buddha_num_hwifs == BUDDHA_NUM_HWIFS ? "Buddha" :
+		   					  "Catweasel");
+    }
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/cmd640.c linux/drivers/ide/cmd640.c
--- v2.3.51/linux/drivers/ide/cmd640.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/cmd640.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,855 @@
+/*
+ *  linux/drivers/block/cmd640.c		Version 1.02  Sep 01, 1996
+ *
+ *  Copyright (C) 1995-1996  Linus Torvalds & authors (see below)
+ */
+
+/*
+ *  Original authors:	abramov@cecmow.enet.dec.com (Igor Abramov)
+ *  			mlord@pobox.com (Mark Lord)
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
+ *
+ *  This file provides support for the advanced features and bugs
+ *  of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
+ *
+ *  These chips are basically fucked by design, and getting this driver
+ *  to work on every motherboard design that uses this screwed chip seems
+ *  bloody well impossible.  However, we're still trying.
+ *
+ *  Version 0.97 worked for everybody.
+ *
+ *  User feedback is essential.  Many thanks to the beta test team:
+ *
+ *  A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com,
+ *  bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz,
+ *  chrisc@dbass.demon.co.uk, dalecki@namu26.Num.Math.Uni-Goettingen.de,
+ *  derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de,
+ *  flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net,
+ *  j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net,
+ *  kerouac@ssnet.com, meskes@informatik.rwth-aachen.de, hzoli@cs.elte.hu,
+ *  peter@udgaard.isgtec.com, phil@tazenda.demon.co.uk, roadcapw@cfw.com,
+ *  s0033las@sun10.vsz.bme.hu, schaffer@tam.cornell.edu, sjd@slip.net,
+ *  steve@ei.org, ulrpeg@bigcomm.gun.de, ism@tardis.ed.ac.uk, mack@cray.com
+ *  liug@mama.indstate.edu, and others.
+ *
+ *  Version 0.01	Initial version, hacked out of ide.c,
+ *			and #include'd rather than compiled separately.
+ *			This will get cleaned up in a subsequent release.
+ *
+ *  Version 0.02	Fixes for vlb initialization code, enable prefetch
+ *			for versions 'B' and 'C' of chip by default,
+ *			some code cleanup.
+ *
+ *  Version 0.03	Added reset of secondary interface,
+ *			and black list for devices which are not compatible
+ *			with prefetch mode. Separate function for setting
+ *			prefetch is added, possibly it will be called some
+ *			day from ioctl processing code.
+ *
+ *  Version 0.04	Now configs/compiles separate from ide.c
+ *
+ *  Version 0.05	Major rewrite of interface timing code.
+ *			Added new function cmd640_set_mode to set PIO mode
+ *			from ioctl call. New drives added to black list.
+ *
+ *  Version 0.06	More code cleanup. Prefetch is enabled only for
+ *			detected hard drives, not included in prefetch
+ *			black list.
+ *
+ *  Version 0.07	Changed to more conservative drive tuning policy.
+ *			Unknown drives, which report PIO < 4 are set to
+ *			(reported_PIO - 1) if it is supported, or to PIO0.
+ *			List of known drives extended by info provided by
+ *			CMD at their ftp site.
+ *
+ *  Version 0.08	Added autotune/noautotune support.
+ *
+ *  Version 0.09	Try to be smarter about 2nd port enabling.
+ *  Version 0.10	Be nice and don't reset 2nd port.
+ *  Version 0.11	Try to handle more wierd situations.
+ *
+ *  Version 0.12	Lots of bug fixes from Laszlo Peter
+ *			irq unmasking disabled for reliability.
+ *			try to be even smarter about the second port.
+ *			tidy up source code formatting.
+ *  Version 0.13	permit irq unmasking again.
+ *  Version 0.90	massive code cleanup, some bugs fixed.
+ *			defaults all drives to PIO mode0, prefetch off.
+ *			autotune is OFF by default, with compile time flag.
+ *			prefetch can be turned OFF/ON using "hdparm -p8/-p9"
+ *			 (requires hdparm-3.1 or newer)
+ *  Version 0.91	first release to linux-kernel list.
+ *  Version 0.92	move initial reg dump to separate callable function
+ *			change "readahead" to "prefetch" to avoid confusion
+ *  Version 0.95	respect original BIOS timings unless autotuning.
+ *			tons of code cleanup and rearrangement.
+ *			added CONFIG_BLK_DEV_CMD640_ENHANCED option
+ *			prevent use of unmask when prefetch is on
+ *  Version 0.96	prevent use of io_32bit when prefetch is off
+ *  Version 0.97	fix VLB secondary interface for sjd@slip.net
+ *			other minor tune-ups:  0.96 was very good.
+ *  Version 0.98	ignore PCI version when disabled by BIOS
+ *  Version 0.99	display setup/active/recovery clocks with PIO mode
+ *  Version 1.00	Mmm.. cannot depend on PCMD_ENA in all systems
+ *  Version 1.01	slow/fast devsel can be selected with "hdparm -p6/-p7"
+ *			 ("fast" is necessary for 32bit I/O in some systems)
+ *  Version 1.02	fix bug that resulted in slow "setup times"
+ *			 (patch courtesy of Zoltan Hidvegi)
+ */
+
+#undef REALLY_SLOW_IO		/* most systems can safely undef this */
+#define CMD640_PREFETCH_MASKS 1
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/*
+ * This flag is set in ide.c by the parameter:  ide0=cmd640_vlb
+ */
+int cmd640_vlb = 0;
+
+/*
+ * CMD640 specific registers definition.
+ */
+
+#define VID		0x00
+#define DID		0x02
+#define PCMD		0x04
+#define   PCMD_ENA	0x01
+#define PSTTS		0x06
+#define REVID		0x08
+#define PROGIF		0x09
+#define SUBCL		0x0a
+#define BASCL		0x0b
+#define BaseA0		0x10
+#define BaseA1		0x14
+#define BaseA2		0x18
+#define BaseA3		0x1c
+#define INTLINE		0x3c
+#define INPINE		0x3d
+
+#define	CFR		0x50
+#define   CFR_DEVREV		0x03
+#define   CFR_IDE01INTR		0x04
+#define	  CFR_DEVID		0x18
+#define	  CFR_AT_VESA_078h	0x20
+#define	  CFR_DSA1		0x40
+#define	  CFR_DSA0		0x80
+
+#define CNTRL		0x51
+#define	  CNTRL_DIS_RA0		0x40
+#define   CNTRL_DIS_RA1		0x80
+#define	  CNTRL_ENA_2ND		0x08
+
+#define	CMDTIM		0x52
+#define	ARTTIM0		0x53
+#define	DRWTIM0		0x54
+#define ARTTIM1 	0x55
+#define DRWTIM1		0x56
+#define ARTTIM23	0x57
+#define   ARTTIM23_DIS_RA2	0x04
+#define   ARTTIM23_DIS_RA3	0x08
+#define DRWTIM23	0x58
+#define BRST		0x59
+
+/*
+ * Registers and masks for easy access by drive index:
+ */
+static byte prefetch_regs[4]  = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
+static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
+
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+
+static byte arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
+static byte drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
+
+/*
+ * Current cmd640 timing values for each drive.
+ * The defaults for each are the slowest possible timings.
+ */
+static byte setup_counts[4]    = {4, 4, 4, 4};     /* Address setup count (in clocks) */
+static byte active_counts[4]   = {16, 16, 16, 16}; /* Active count   (encoded) */
+static byte recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
+
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+/*
+ * These are initialized to point at the devices we control
+ */
+static ide_hwif_t  *cmd_hwif0, *cmd_hwif1;
+static ide_drive_t *cmd_drives[4];
+
+/*
+ * Interface to access cmd640x registers
+ */
+static unsigned int cmd640_key;
+static void (*put_cmd640_reg)(unsigned short reg, byte val);
+static byte (*get_cmd640_reg)(unsigned short reg);
+
+/*
+ * This is read from the CFR reg, and is used in several places.
+ */
+static unsigned int cmd640_chip_version;
+
+/*
+ * The CMD640x chip does not support DWORD config write cycles, but some
+ * of the BIOSes use them to implement the config services.
+ * Therefore, we must use direct IO instead.
+ */
+
+/* PCI method 1 access */
+
+static void put_cmd640_reg_pci1 (unsigned short reg, byte val)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
+	outb_p(val, (reg & 3) | 0xcfc);
+	restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci1 (unsigned short reg)
+{
+	byte b;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
+	b = inb_p((reg & 3) | 0xcfc);
+	restore_flags(flags);
+	return b;
+}
+
+/* PCI method 2 access (from CMD datasheet) */
+
+static void put_cmd640_reg_pci2 (unsigned short reg, byte val)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(0x10, 0xcf8);
+	outb_p(val, cmd640_key + reg);
+	outb_p(0, 0xcf8);
+	restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci2 (unsigned short reg)
+{
+	byte b;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(0x10, 0xcf8);
+	b = inb_p(cmd640_key + reg);
+	outb_p(0, 0xcf8);
+	restore_flags(flags);
+	return b;
+}
+
+/* VLB access */
+
+static void put_cmd640_reg_vlb (unsigned short reg, byte val)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(reg, cmd640_key);
+	outb_p(val, cmd640_key + 4);
+	restore_flags(flags);
+}
+
+static byte get_cmd640_reg_vlb (unsigned short reg)
+{
+	byte b;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(reg, cmd640_key);
+	b = inb_p(cmd640_key + 4);
+	restore_flags(flags);
+	return b;
+}
+
+static int __init match_pci_cmd640_device (void)
+{
+	const byte ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
+	unsigned int i;
+	for (i = 0; i < 4; i++) {
+		if (get_cmd640_reg(i) != ven_dev[i])
+			return 0;
+	}
+#ifdef STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT
+	if ((get_cmd640_reg(PCMD) & PCMD_ENA) == 0) {
+		printk("ide: cmd640 on PCI disabled by BIOS\n");
+		return 0;
+	}
+#endif /* STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT */
+	return 1; /* success */
+}
+
+/*
+ * Probe for CMD640x -- pci method 1
+ */
+static int __init probe_for_cmd640_pci1 (void)
+{
+	get_cmd640_reg = get_cmd640_reg_pci1;
+	put_cmd640_reg = put_cmd640_reg_pci1;
+	for (cmd640_key = 0x80000000; cmd640_key <= 0x8000f800; cmd640_key += 0x800) {
+		if (match_pci_cmd640_device())
+			return 1; /* success */
+	}
+	return 0;
+}
+
+/*
+ * Probe for CMD640x -- pci method 2
+ */
+static int __init probe_for_cmd640_pci2 (void)
+{
+	get_cmd640_reg = get_cmd640_reg_pci2;
+	put_cmd640_reg = put_cmd640_reg_pci2;
+	for (cmd640_key = 0xc000; cmd640_key <= 0xcf00; cmd640_key += 0x100) {
+		if (match_pci_cmd640_device())
+			return 1; /* success */
+	}
+	return 0;
+}
+
+/*
+ * Probe for CMD640x -- vlb
+ */
+static int __init probe_for_cmd640_vlb (void)
+{
+	byte b;
+
+	get_cmd640_reg = get_cmd640_reg_vlb;
+	put_cmd640_reg = put_cmd640_reg_vlb;
+	cmd640_key = 0x178;
+	b = get_cmd640_reg(CFR);
+	if (b == 0xff || b == 0x00 || (b & CFR_AT_VESA_078h)) {
+		cmd640_key = 0x78;
+		b = get_cmd640_reg(CFR);
+		if (b == 0xff || b == 0x00 || !(b & CFR_AT_VESA_078h))
+			return 0;
+	}
+	return 1; /* success */
+}
+
+/*
+ *  Returns 1 if an IDE interface/drive exists at 0x170,
+ *  Returns 0 otherwise.
+ */
+static int __init secondary_port_responding (void)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	outb_p(0x0a, 0x170 + IDE_SELECT_OFFSET);	/* select drive0 */
+	udelay(100);
+	if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x0a) {
+		outb_p(0x1a, 0x170 + IDE_SELECT_OFFSET); /* select drive1 */
+		udelay(100);
+		if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x1a) {
+			restore_flags(flags);
+			return 0; /* nothing responded */
+		}
+	}
+	restore_flags(flags);
+	return 1; /* success */
+}
+
+#ifdef CMD640_DUMP_REGS
+/*
+ * Dump out all cmd640 registers.  May be called from ide.c
+ */
+void cmd640_dump_regs (void)
+{
+	unsigned int reg = cmd640_vlb ? 0x50 : 0x00;
+
+	/* Dump current state of chip registers */
+	printk("ide: cmd640 internal register dump:");
+	for (; reg <= 0x59; reg++) {
+		if (!(reg & 0x0f))
+			printk("\n%04x:", reg);
+		printk(" %02x", get_cmd640_reg(reg));
+	}
+	printk("\n");
+}
+#endif
+
+/*
+ * Check whether prefetch is on for a drive,
+ * and initialize the unmask flags for safe operation.
+ */
+static void __init check_prefetch (unsigned int index)
+{
+	ide_drive_t *drive = cmd_drives[index];
+	byte b = get_cmd640_reg(prefetch_regs[index]);
+
+	if (b & prefetch_masks[index]) {	/* is prefetch off? */
+		drive->no_unmask = 0;
+		drive->no_io_32bit = 1;
+		drive->io_32bit = 0;
+	} else {
+#if CMD640_PREFETCH_MASKS
+		drive->no_unmask = 1;
+		drive->unmask = 0;
+#endif
+		drive->no_io_32bit = 0;
+	}
+}
+
+/*
+ * Figure out which devices we control
+ */
+static void __init setup_device_ptrs (void)
+{
+	unsigned int i;
+
+	cmd_hwif0 = &ide_hwifs[0]; /* default, if not found below */
+	cmd_hwif1 = &ide_hwifs[1]; /* default, if not found below */
+	for (i = 0; i < MAX_HWIFS; i++) {
+		ide_hwif_t *hwif = &ide_hwifs[i];
+		if (hwif->chipset == ide_unknown || hwif->chipset == ide_generic) {
+			if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0)
+				cmd_hwif0 = hwif;
+			else if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
+				cmd_hwif1 = hwif;
+		}
+	}
+	cmd_drives[0] = &cmd_hwif0->drives[0];
+	cmd_drives[1] = &cmd_hwif0->drives[1];
+	cmd_drives[2] = &cmd_hwif1->drives[0];
+	cmd_drives[3] = &cmd_hwif1->drives[1];
+}
+
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+
+/*
+ * Sets prefetch mode for a drive.
+ */
+static void set_prefetch_mode (unsigned int index, int mode)
+{
+	ide_drive_t *drive = cmd_drives[index];
+	int reg = prefetch_regs[index];
+	byte b;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	b = get_cmd640_reg(reg);
+	if (mode) {	/* want prefetch on? */
+#if CMD640_PREFETCH_MASKS
+		drive->no_unmask = 1;
+		drive->unmask = 0;
+#endif
+		drive->no_io_32bit = 0;
+		b &= ~prefetch_masks[index];	/* enable prefetch */
+	} else {
+		drive->no_unmask = 0;
+		drive->no_io_32bit = 1;
+		drive->io_32bit = 0;
+		b |= prefetch_masks[index];	/* disable prefetch */
+	}
+	put_cmd640_reg(reg, b);
+	restore_flags(flags);
+}
+
+/*
+ * Dump out current drive clocks settings
+ */
+static void display_clocks (unsigned int index)
+{
+	byte active_count, recovery_count;
+
+	active_count = active_counts[index];
+	if (active_count == 1)
+		++active_count;
+	recovery_count = recovery_counts[index];
+	if (active_count > 3 && recovery_count == 1)
+		++recovery_count;
+	if (cmd640_chip_version > 1)
+		recovery_count += 1;  /* cmd640b uses (count + 1)*/
+	printk(", clocks=%d/%d/%d\n", setup_counts[index], active_count, recovery_count);
+}
+
+/*
+ * Pack active and recovery counts into single byte representation
+ * used by controller
+ */
+inline static byte pack_nibbles (byte upper, byte lower)
+{
+	return ((upper & 0x0f) << 4) | (lower & 0x0f);
+}
+
+/*
+ * This routine retrieves the initial drive timings from the chipset.
+ */
+static void __init retrieve_drive_counts (unsigned int index)
+{
+	byte b;
+
+	/*
+	 * Get the internal setup timing, and convert to clock count
+	 */
+	b = get_cmd640_reg(arttim_regs[index]) & ~0x3f;
+	switch (b) {
+		case 0x00: b = 4; break;
+		case 0x80: b = 3; break;
+		case 0x40: b = 2; break;
+		default:   b = 5; break;
+	}
+	setup_counts[index] = b;
+
+	/*
+	 * Get the active/recovery counts
+	 */
+	b = get_cmd640_reg(drwtim_regs[index]);
+	active_counts[index]   = (b >> 4)   ? (b >> 4)   : 0x10;
+	recovery_counts[index] = (b & 0x0f) ? (b & 0x0f) : 0x10;
+}
+
+
+/*
+ * This routine writes the prepared setup/active/recovery counts
+ * for a drive into the cmd640 chipset registers to active them.
+ */
+static void program_drive_counts (unsigned int index)
+{
+	unsigned long flags;
+	byte setup_count    = setup_counts[index];
+	byte active_count   = active_counts[index];
+	byte recovery_count = recovery_counts[index];
+
+	/*
+	 * Set up address setup count and drive read/write timing registers.
+	 * Primary interface has individual count/timing registers for
+	 * each drive.  Secondary interface has one common set of registers,
+	 * so we merge the timings, using the slowest value for each timing.
+	 */
+	if (index > 1) {
+		unsigned int mate;
+		if (cmd_drives[mate = index ^ 1]->present) {
+			if (setup_count < setup_counts[mate])
+				setup_count = setup_counts[mate];
+			if (active_count < active_counts[mate])
+				active_count = active_counts[mate];
+			if (recovery_count < recovery_counts[mate])
+				recovery_count = recovery_counts[mate];
+		}
+	}
+
+	/*
+	 * Convert setup_count to internal chipset representation
+	 */
+	switch (setup_count) {
+		case 4:	 setup_count = 0x00; break;
+		case 3:	 setup_count = 0x80; break;
+		case 1:
+		case 2:	 setup_count = 0x40; break;
+		default: setup_count = 0xc0; /* case 5 */
+	}
+
+	/*
+	 * Now that everything is ready, program the new timings
+	 */
+	save_flags (flags);
+	cli();
+	/*
+	 * Program the address_setup clocks into ARTTIM reg,
+	 * and then the active/recovery counts into the DRWTIM reg
+	 * (this converts counts of 16 into counts of zero -- okay).
+	 */
+	setup_count |= get_cmd640_reg(arttim_regs[index]) & 0x3f;
+	put_cmd640_reg(arttim_regs[index], setup_count);
+	put_cmd640_reg(drwtim_regs[index], pack_nibbles(active_count, recovery_count));
+	restore_flags(flags);
+}
+
+/*
+ * Set a specific pio_mode for a drive
+ */
+static void cmd640_set_mode (unsigned int index, byte pio_mode, unsigned int cycle_time)
+{
+	int setup_time, active_time, recovery_time, clock_time;
+	byte setup_count, active_count, recovery_count, recovery_count2, cycle_count;
+	int bus_speed = ide_system_bus_speed();
+
+	if (pio_mode > 5)
+		pio_mode = 5;
+	setup_time  = ide_pio_timings[pio_mode].setup_time;
+	active_time = ide_pio_timings[pio_mode].active_time;
+	recovery_time = cycle_time - (setup_time + active_time);
+	clock_time = 1000 / bus_speed;
+	cycle_count = (cycle_time + clock_time - 1) / clock_time;
+
+	setup_count = (setup_time + clock_time - 1) / clock_time;
+
+	active_count = (active_time + clock_time - 1) / clock_time;
+	if (active_count < 2)
+		active_count = 2; /* minimum allowed by cmd640 */
+
+	recovery_count = (recovery_time + clock_time - 1) / clock_time;
+	recovery_count2 = cycle_count - (setup_count + active_count);
+	if (recovery_count2 > recovery_count)
+		recovery_count = recovery_count2;
+	if (recovery_count < 2)
+		recovery_count = 2; /* minimum allowed by cmd640 */
+	if (recovery_count > 17) {
+		active_count += recovery_count - 17;
+		recovery_count = 17;
+	}
+	if (active_count > 16)
+		active_count = 16; /* maximum allowed by cmd640 */
+	if (cmd640_chip_version > 1)
+		recovery_count -= 1;  /* cmd640b uses (count + 1)*/
+	if (recovery_count > 16)
+		recovery_count = 16; /* maximum allowed by cmd640 */
+
+	setup_counts[index]    = setup_count;
+	active_counts[index]   = active_count;
+	recovery_counts[index] = recovery_count;
+
+	/*
+	 * In a perfect world, we might set the drive pio mode here
+	 * (using WIN_SETFEATURE) before continuing.
+	 *
+	 * But we do not, because:
+	 *	1) this is the wrong place to do it (proper is do_special() in ide.c)
+	 * 	2) in practice this is rarely, if ever, necessary
+	 */
+	program_drive_counts (index);
+}
+
+/*
+ * Drive PIO mode selection:
+ */
+static void cmd640_tune_drive (ide_drive_t *drive, byte mode_wanted)
+{
+	byte b;
+	ide_pio_data_t  d;
+	unsigned int index = 0;
+
+	while (drive != cmd_drives[index]) {
+		if (++index > 3) {
+			printk("%s: bad news in cmd640_tune_drive\n", drive->name);
+			return;
+		}
+	}
+	switch (mode_wanted) {
+		case 6: /* set fast-devsel off */
+		case 7: /* set fast-devsel on */
+			mode_wanted &= 1;
+			b = get_cmd640_reg(CNTRL) & ~0x27;
+			if (mode_wanted)
+				b |= 0x27;
+			put_cmd640_reg(CNTRL, b);
+			printk("%s: %sabled cmd640 fast host timing (devsel)\n", drive->name, mode_wanted ? "en" : "dis");
+			return;
+
+		case 8: /* set prefetch off */
+		case 9: /* set prefetch on */
+			mode_wanted &= 1;
+			set_prefetch_mode(index, mode_wanted);
+			printk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
+			return;
+	}
+
+	(void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
+	cmd640_set_mode (index, d.pio_mode, d.cycle_time);
+
+	printk ("%s: selected cmd640 PIO mode%d (%dns)%s",
+		drive->name,
+		d.pio_mode,
+		d.cycle_time,
+		d.overridden ? " (overriding vendor mode)" : "");
+	display_clocks(index);
+	return;
+}
+
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+/*
+ * Probe for a cmd640 chipset, and initialize it if found.  Called from ide.c
+ */
+int __init ide_probe_for_cmd640x (void)
+{
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+	int second_port_toggled = 0;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+	int second_port_cmd640 = 0;
+	const char *bus_type, *port2;
+	unsigned int index;
+	byte b, cfr;
+
+	if (cmd640_vlb && probe_for_cmd640_vlb()) {
+		bus_type = "VLB";
+	} else {
+		cmd640_vlb = 0;
+		if (probe_for_cmd640_pci1())
+			bus_type = "PCI (type1)";
+		else if (probe_for_cmd640_pci2())
+			bus_type = "PCI (type2)";
+		else
+			return 0;
+	}
+	/*
+	 * Undocumented magic (there is no 0x5b reg in specs)
+	 */
+	put_cmd640_reg(0x5b, 0xbd);
+	if (get_cmd640_reg(0x5b) != 0xbd) {
+		printk("ide: cmd640 init failed: wrong value in reg 0x5b\n");
+		return 0;
+	}
+	put_cmd640_reg(0x5b, 0);
+
+#ifdef CMD640_DUMP_REGS
+	CMD640_DUMP_REGS;
+#endif
+
+	/*
+	 * Documented magic begins here
+	 */
+	cfr = get_cmd640_reg(CFR);
+	cmd640_chip_version = cfr & CFR_DEVREV;
+	if (cmd640_chip_version == 0) {
+		printk ("ide: bad cmd640 revision: %d\n", cmd640_chip_version);
+		return 0;
+	}
+
+	/*
+	 * Initialize data for primary port
+	 */
+	setup_device_ptrs ();
+	printk("%s: buggy cmd640%c interface on %s, config=0x%02x\n",
+	       cmd_hwif0->name, 'a' + cmd640_chip_version - 1, bus_type, cfr);
+	cmd_hwif0->chipset = ide_cmd640;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+	cmd_hwif0->tuneproc = &cmd640_tune_drive;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+	/*
+	 * Ensure compatibility by always using the slowest timings
+	 * for access to the drive's command register block,
+	 * and reset the prefetch burstsize to default (512 bytes).
+	 *
+	 * Maybe we need a way to NOT do these on *some* systems?
+	 */
+	put_cmd640_reg(CMDTIM, 0);
+	put_cmd640_reg(BRST, 0x40);
+
+	/*
+	 * Try to enable the secondary interface, if not already enabled
+	 */
+	if (cmd_hwif1->noprobe) {
+		port2 = "not probed";
+	} else {
+		b = get_cmd640_reg(CNTRL);
+		if (secondary_port_responding()) {
+			if ((b & CNTRL_ENA_2ND)) {
+				second_port_cmd640 = 1;
+				port2 = "okay";
+			} else if (cmd640_vlb) {
+				second_port_cmd640 = 1;
+				port2 = "alive";
+			} else
+				port2 = "not cmd640";
+		} else {
+			put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
+			if (secondary_port_responding()) {
+				second_port_cmd640 = 1;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+				second_port_toggled = 1;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+				port2 = "enabled";
+			} else {
+				put_cmd640_reg(CNTRL, b); /* restore original setting */
+				port2 = "not responding";
+			}
+		}
+	}
+
+	/*
+	 * Initialize data for secondary cmd640 port, if enabled
+	 */
+	if (second_port_cmd640) {
+		cmd_hwif0->serialized = 1;
+		cmd_hwif1->serialized = 1;
+		cmd_hwif1->chipset = ide_cmd640;
+		cmd_hwif0->mate = cmd_hwif1;
+		cmd_hwif1->mate = cmd_hwif0;
+		cmd_hwif1->channel = 1;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+		cmd_hwif1->tuneproc = &cmd640_tune_drive;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+	}
+	printk("%s: %sserialized, secondary interface %s\n", cmd_hwif1->name,
+		cmd_hwif0->serialized ? "" : "not ", port2);
+
+	/*
+	 * Establish initial timings/prefetch for all drives.
+	 * Do not unnecessarily disturb any prior BIOS setup of these.
+	 */
+	for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) {
+		ide_drive_t *drive = cmd_drives[index];
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+		if (drive->autotune || ((index > 1) && second_port_toggled)) {
+	 		/*
+	 		 * Reset timing to the slowest speed and turn off prefetch.
+			 * This way, the drive identify code has a better chance.
+			 */
+			setup_counts    [index] = 4;	/* max possible */
+			active_counts   [index] = 16;	/* max possible */
+			recovery_counts [index] = 16;	/* max possible */
+			program_drive_counts (index);
+			set_prefetch_mode (index, 0);
+			printk("cmd640: drive%d timings/prefetch cleared\n", index);
+		} else {
+			/*
+			 * Record timings/prefetch without changing them.
+			 * This preserves any prior BIOS setup.
+			 */
+			retrieve_drive_counts (index);
+			check_prefetch (index);
+			printk("cmd640: drive%d timings/prefetch(%s) preserved",
+				index, drive->no_io_32bit ? "off" : "on");
+			display_clocks(index);
+		}
+#else
+		/*
+		 * Set the drive unmask flags to match the prefetch setting
+		 */
+		check_prefetch (index);
+		printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
+			index, drive->no_io_32bit ? "off" : "on");
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+	}
+
+#ifdef CMD640_DUMP_REGS
+	CMD640_DUMP_REGS;
+#endif
+	return 1;
+}
+
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/cmd64x.c linux/drivers/ide/cmd64x.c
--- v2.3.51/linux/drivers/ide/cmd64x.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/cmd64x.c	Wed Mar  8 11:40:25 2000
@@ -0,0 +1,721 @@
+/* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16
+ *
+ * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
+ *           Note, this driver is not used at all on other systems because
+ *           there the "BIOS" has done all of the following already.
+ *           Due to massive hardware bugs, UltraDMA is only supported
+ *           on the 646U2 and not on the 646U.
+ *
+ * Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 1998       David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999-2000  Andre Hedrick (andre@suse.com)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include "ide_modes.h"
+
+#ifndef SPLIT_BYTE
+#define SPLIT_BYTE(B,H,L)	((H)=(B>>4), (L)=(B-((B>>4)<<4)))
+#endif
+
+#define CMD_DEBUG 0
+
+#if CMD_DEBUG
+#define cmdprintk(x...)	printk(##x)
+#else
+#define cmdprintk(x...)
+#endif
+
+/*
+ * CMD64x specific registers definition.
+ */
+
+#define CNTRL		0x51
+#define	  CNTRL_DIS_RA0		0x40
+#define   CNTRL_DIS_RA1		0x80
+#define	  CNTRL_ENA_2ND		0x08
+
+#define	CMDTIM		0x52
+#define	ARTTIM0		0x53
+#define	DRWTIM0		0x54
+#define ARTTIM1 	0x55
+#define DRWTIM1		0x56
+#define ARTTIM23	0x57
+#define   ARTTIM23_DIS_RA2	0x04
+#define   ARTTIM23_DIS_RA3	0x08
+#define ARTTIM2		0x57
+#define ARTTIM3		0x57
+#define DRWTIM23	0x58
+#define DRWTIM2		0x58
+#define BRST		0x59
+#define DRWTIM3		0x5b
+
+#define BMIDECR0	0x70
+#define MRDMODE		0x71
+#define BMIDESR0	0x72
+#define UDIDETCR0	0x73
+#define DTPR0		0x74
+#define BMIDECR1	0x78
+#define BMIDECSR	0x79
+#define BMIDESR1	0x7A
+#define UDIDETCR1	0x7B
+#define DTPR1		0x7C
+
+#undef DISPLAY_CMD64X_TIMINGS
+
+#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int cmd64x_get_info(char *, char **, off_t, int);
+extern int (*cmd64x_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p = buffer;
+	u8 reg53 = 0, reg54 = 0, reg55 = 0, reg56 = 0;	/* primary */
+	u8 reg57 = 0, reg58 = 0, reg5b;			/* secondary */
+	u8 reg72 = 0, reg73 = 0;			/* primary */
+	u8 reg7a = 0, reg7b = 0;			/* secondary */
+	u8 hi_byte = 0, lo_byte = 0;
+
+	switch(bmide_dev->device) {
+		case PCI_DEVICE_ID_CMD_648:
+			p += sprintf(p, "\n                                CMD648 Chipset.\n");
+			break;
+		case PCI_DEVICE_ID_CMD_646:
+			p += sprintf(p, "\n                                CMD646 Chipset.\n");
+			break;
+		case PCI_DEVICE_ID_CMD_643:
+			p += sprintf(p, "\n                                CMD643 Chipset.\n");
+			break;
+		default:
+			p += sprintf(p, "\n                                CMD64? Chipse.\n");
+			break;
+	}
+	(void) pci_read_config_byte(bmide_dev, ARTTIM0,   &reg53);
+	(void) pci_read_config_byte(bmide_dev, DRWTIM0,   &reg54);
+	(void) pci_read_config_byte(bmide_dev, ARTTIM1,   &reg55);
+	(void) pci_read_config_byte(bmide_dev, DRWTIM1,   &reg56);
+	(void) pci_read_config_byte(bmide_dev, ARTTIM2,   &reg57);
+	(void) pci_read_config_byte(bmide_dev, DRWTIM2,   &reg58);
+	(void) pci_read_config_byte(bmide_dev, DRWTIM3,   &reg5b);
+	(void) pci_read_config_byte(bmide_dev, BMIDESR0,  &reg72);
+	(void) pci_read_config_byte(bmide_dev, UDIDETCR0, &reg73);
+	(void) pci_read_config_byte(bmide_dev, BMIDESR1,  &reg7a);
+	(void) pci_read_config_byte(bmide_dev, UDIDETCR1, &reg7b);
+
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	p += sprintf(p, "                %sabled                         %sabled\n",
+			(reg72&0x80) ? "dis" : " en", (reg7a&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
+			(reg72&0x20) ? "yes" : "no ", (reg72&0x40) ? "yes" : "no ", (reg7a&0x20) ? "yes" : "no ", (reg7a&0x40) ? "yes" : "no " );
+	p += sprintf(p, "UDMA enabled:   %s              %s             %s               %s\n",
+			(reg73&0x01) ? "yes" : "no ", (reg73&0x02) ? "yes" : "no ", (reg7b&0x01) ? "yes" : "no ", (reg7b&0x02) ? "yes" : "no " );
+	p += sprintf(p, "UDMA enabled:   %s                %s               %s                 %s\n",
+			(reg73&0x15) ? "4" : (reg73&0x25) ? "3" : (reg73&0x11) ? "2" : (reg73&0x21) ? "1" : (reg73&0x31) ? "0" : "X",
+			(reg73&0x4A) ? "4" : (reg73&0x8A) ? "3" : (reg73&0x42) ? "2" : (reg73&0x82) ? "1" : (reg73&0xC2) ? "0" : "X",
+			(reg7b&0x15) ? "4" : (reg7b&0x25) ? "3" : (reg7b&0x11) ? "2" : (reg7b&0x21) ? "1" : (reg7b&0x31) ? "0" : "X",
+			(reg7b&0x4A) ? "4" : (reg7b&0x8A) ? "3" : (reg7b&0x42) ? "2" : (reg7b&0x82) ? "1" : (reg7b&0xC2) ? "0" : "X" );
+	p += sprintf(p, "DMA enabled:    %s                %s               %s                 %s\n",
+			(reg73&0x10) ? "2" : (reg73&0x20) ? "1" : (reg73&0x30) ? "0" : "X",
+			(reg73&0x40) ? "2" : (reg73&0x80) ? "1" : (reg73&0xC0) ? "0" : "X",
+			(reg7b&0x10) ? "2" : (reg7b&0x20) ? "1" : (reg7b&0x30) ? "0" : "X",
+			(reg7b&0x40) ? "2" : (reg7b&0x80) ? "1" : (reg7b&0xC0) ? "0" : "X" );
+	p += sprintf(p, "PIO\n");
+
+	SPLIT_BYTE(reg53, hi_byte, lo_byte);
+	p += sprintf(p, "ARTTIM0   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg53, hi_byte, lo_byte);
+	SPLIT_BYTE(reg54, hi_byte, lo_byte);
+	p += sprintf(p, "DRWTIM0   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg54, hi_byte, lo_byte);
+	SPLIT_BYTE(reg55, hi_byte, lo_byte);
+	p += sprintf(p, "ARTTIM1   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg55, hi_byte, lo_byte);
+	SPLIT_BYTE(reg56, hi_byte, lo_byte);
+	p += sprintf(p, "DRWTIM1   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg56, hi_byte, lo_byte);
+	SPLIT_BYTE(reg57, hi_byte, lo_byte);
+	p += sprintf(p, "ARTTIM23  = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg57, hi_byte, lo_byte);
+	SPLIT_BYTE(reg58, hi_byte, lo_byte);
+	p += sprintf(p, "DRWTIM2   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg58, hi_byte, lo_byte);
+	SPLIT_BYTE(reg5b, hi_byte, lo_byte);
+	p += sprintf(p, "DRWTIM3   = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg5b, hi_byte, lo_byte);
+	SPLIT_BYTE(reg73, hi_byte, lo_byte);
+	p += sprintf(p, "UDIDETCR0 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg73, hi_byte, lo_byte);
+	SPLIT_BYTE(reg7b, hi_byte, lo_byte);
+	p += sprintf(p, "UDIDETCR1 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg7b, hi_byte, lo_byte);
+
+	return p-buffer;	/* => must be less than 4k! */
+}
+#endif	/* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte cmd64x_proc = 0;
+
+/*
+ * Registers and masks for easy access by drive index:
+ */
+#if 0
+static byte prefetch_regs[4]  = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
+static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
+#endif
+
+/*
+ * This routine writes the prepared setup/active/recovery counts
+ * for a drive into the cmd646 chipset registers to active them.
+ */
+static void program_drive_counts (ide_drive_t *drive, int setup_count, int active_count, int recovery_count)
+{
+	unsigned long flags;
+	ide_drive_t *drives = HWIF(drive)->drives;
+	byte temp_b;
+	static const byte setup_counts[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
+	static const byte recovery_counts[] =
+		{15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
+	static const byte arttim_regs[2][2] = {
+			{ ARTTIM0, ARTTIM1 },
+			{ ARTTIM23, ARTTIM23 }
+		};
+	static const byte drwtim_regs[2][2] = {
+			{ DRWTIM0, DRWTIM1 },
+			{ DRWTIM2, DRWTIM3 }
+		};
+	int channel = (int) HWIF(drive)->channel;
+	int slave = (drives != drive);  /* Is this really the best way to determine this?? */
+
+	cmdprintk("program_drive_count parameters = s(%d),a(%d),r(%d),p(%d)\n", setup_count,
+		active_count, recovery_count, drive->present);
+	/*
+	 * Set up address setup count registers.
+	 * Primary interface has individual count/timing registers for
+	 * each drive.  Secondary interface has one common set of registers,
+	 * for address setup so we merge these timings, using the slowest
+	 * value.
+	 */
+	if (channel) {
+		drive->drive_data = setup_count;
+		setup_count = IDE_MAX(drives[0].drive_data, drives[1].drive_data);
+		cmdprintk("Secondary interface, setup_count = %d\n", setup_count);
+	}
+
+	/*
+	 * Convert values to internal chipset representation
+	 */
+	setup_count = (setup_count > 5) ? 0xc0 : (int) setup_counts[setup_count];
+	active_count &= 0xf; /* Remember, max value is 16 */
+	recovery_count = (int) recovery_counts[recovery_count];
+
+	cmdprintk("Final values = %d,%d,%d\n", setup_count, active_count, recovery_count);
+
+	/*
+	 * Now that everything is ready, program the new timings
+	 */
+	__save_flags (flags);
+	__cli();
+	/*
+	 * Program the address_setup clocks into ARTTIM reg,
+	 * and then the active/recovery counts into the DRWTIM reg
+	 */
+	(void) pci_read_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave], &temp_b);
+	(void) pci_write_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave],
+		((byte) setup_count) | (temp_b & 0x3f));
+	(void) pci_write_config_byte(HWIF(drive)->pci_dev, drwtim_regs[channel][slave],
+		(byte) ((active_count << 4) | recovery_count));
+	cmdprintk ("Write %x to %x\n", ((byte) setup_count) | (temp_b & 0x3f), arttim_regs[channel][slave]);
+	cmdprintk ("Write %x to %x\n", (byte) ((active_count << 4) | recovery_count), drwtim_regs[channel][slave]);
+	__restore_flags(flags);
+}
+
+/*
+ * Attempts to set the interface PIO mode.
+ * The preferred method of selecting PIO modes (e.g. mode 4) is 
+ * "echo 'piomode:4' > /proc/ide/hdx/settings".  Special cases are
+ * 8: prefetch off, 9: prefetch on, 255: auto-select best mode.
+ * Called with 255 at boot time.
+ */
+static void cmd64x_tuneproc (ide_drive_t *drive, byte mode_wanted)
+{
+	int setup_time, active_time, recovery_time, clock_time, pio_mode, cycle_time;
+	byte recovery_count2, cycle_count;
+	int setup_count, active_count, recovery_count;
+	int bus_speed = ide_system_bus_speed();
+	/*byte b;*/
+	ide_pio_data_t  d;
+
+	switch (mode_wanted) {
+		case 8: /* set prefetch off */
+		case 9: /* set prefetch on */
+			mode_wanted &= 1;
+			/*set_prefetch_mode(index, mode_wanted);*/
+			cmdprintk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
+			return;
+	}
+
+	mode_wanted = ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
+	pio_mode = d.pio_mode;
+	cycle_time = d.cycle_time;
+
+	/*
+	 * I copied all this complicated stuff from cmd640.c and made a few minor changes.
+	 * For now I am just going to pray that it is correct.
+	 */
+	if (pio_mode > 5)
+		pio_mode = 5;
+	setup_time  = ide_pio_timings[pio_mode].setup_time;
+	active_time = ide_pio_timings[pio_mode].active_time;
+	recovery_time = cycle_time - (setup_time + active_time);
+	clock_time = 1000 / bus_speed;
+	cycle_count = (cycle_time + clock_time - 1) / clock_time;
+
+	setup_count = (setup_time + clock_time - 1) / clock_time;
+
+	active_count = (active_time + clock_time - 1) / clock_time;
+
+	recovery_count = (recovery_time + clock_time - 1) / clock_time;
+	recovery_count2 = cycle_count - (setup_count + active_count);
+	if (recovery_count2 > recovery_count)
+		recovery_count = recovery_count2;
+	if (recovery_count > 16) {
+		active_count += recovery_count - 16;
+		recovery_count = 16;
+	}
+	if (active_count > 16)
+		active_count = 16; /* maximum allowed by cmd646 */
+
+	/*
+	 * In a perfect world, we might set the drive pio mode here
+	 * (using WIN_SETFEATURE) before continuing.
+	 *
+	 * But we do not, because:
+	 *	1) this is the wrong place to do it (proper is do_special() in ide.c)
+	 * 	2) in practice this is rarely, if ever, necessary
+	 */
+	program_drive_counts (drive, setup_count, active_count, recovery_count);
+
+	cmdprintk("%s: selected cmd646 PIO mode%d : %d (%dns)%s, clocks=%d/%d/%d\n",
+		drive->name, pio_mode, mode_wanted, cycle_time,
+		d.overridden ? " (overriding vendor mode)" : "",
+		setup_count, active_count, recovery_count);
+}
+
+static int tune_chipset_for_dma (ide_drive_t *drive, byte speed)
+{
+#if 0
+	struct hd_driveid *id	= drive->id;
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	unsigned long dma_base	= hwif->dma_base;
+	byte unit		= (drive->select.b.unit & 0x01);
+
+	u8 reg72 = 0, reg73 = 0;	/* primary */
+	u8 reg7a = 0, reg7b = 0;	/* secondary */
+	u8 pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0;
+	u8 pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0;
+	u8 regU = (hwif->channel) ? 2 : 0;
+	u8 regD = (hwif->channel) ? 2 : 0;
+
+	(void) pci_read_config_byte(dev, BMIDESR0,  &reg72);
+	(void) pci_read_config_byte(dev, UDIDETCR0, &reg73);
+	(void) pci_read_config_byte(dev, BMIDESR1,  &reg7a);
+	(void) pci_read_config_byte(dev, UDIDETCR1, &reg7b);
+
+	switch(speed) {
+		case XFER_UDMA_4:
+			pciU = unit ? 0x4A : 0x15;
+		case XFER_UDMA_3:
+			pciU = unit ? 0x8A : 0x25;
+		case XFER_UDMA_2:
+			pciU = unit ? 0x42 : 0x11;
+		case XFER_UDMA_1:
+			pciU = unit ? 0x82 : 0x21;
+		case XFER_UDMA_0:
+			pciU = unit ? 0xC2 : 0x31
+(reg73&0x15)?"4":(reg73&0x25)?"3":(reg73&0x11)?"2":(reg73&0x21)?"1":(reg73&0x31)?"0":"X",
+(reg73&0x4A)?"4":(reg73&0x8A)?"3":(reg73&0x42)?"2":(reg73&0x82)?"1":(reg73&0xC2)?"0":"X",
+(reg7b&0x15)?"4":(reg7b&0x25)?"3":(reg7b&0x11)?"2":(reg7b&0x21)?"1":(reg7b&0x31)?"0":"X",
+(reg7b&0x4A)?"4":(reg7b&0x8A)?"3":(reg7b&0x42)?"2":(reg7b&0x82)?"1":(reg7b&0xC2)?"0":"X",
+
+		case XFER_MW_DMA_2:
+			pciD = unit ? 0x40 : 0x10;
+		case XFER_MW_DMA_1:
+			pciD = unit ? 0x80 : 0x20;
+		case XFER_MW_DMA_0:
+			pciD = unit ? 0xC0 : 0x30;
+		case XFER_SW_DMA_2:
+		case XFER_SW_DMA_1:
+		case XFER_SW_DMA_0:
+(reg73&0x10)?"2":(reg73&0x20)?"1":(reg73&0x30)?"0":"X",
+(reg73&0x40)?"2":(reg73&0x80)?"1":(reg73&0xC0)?"0":"X",
+(reg7b&0x10)?"2":(reg7b&0x20)?"1":(reg7b&0x30)?"0":"X",
+(reg7b&0x40)?"2":(reg7b&0x80)?"1":(reg7b&0xC0)?"0":"X" );
+
+		default:
+			return 1;
+	}
+
+	(void) ide_config_drive_speed(drive, speed);
+	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+#endif
+	return 0;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+{
+	byte speed	= 0x00;
+	byte set_pio	= ide_get_best_pio_mode(drive, 4, 5, NULL);	
+
+	cmd64x_tuneproc(drive, set_pio);
+	speed = XFER_PIO_0 + set_pio;
+	if (set_speed)
+		(void) ide_config_drive_speed(drive, speed);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
+{
+	struct hd_driveid *id	= drive->id;
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	unsigned long dma_base	= hwif->dma_base;
+
+ 	byte unit		= (drive->select.b.unit & 0x01);
+	byte speed		= 0x00;
+	byte set_pio		= 0x00;
+	byte udma_timing_bits	= 0x00;
+	byte udma_33		= ((rev >= 0x05) || (ultra_66)) ? 1 : 0;
+	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+	/* int drive_number	= ((hwif->channel ? 2 : 0) + unit); */
+	int rval;
+
+	switch(dev->device) {
+		case PCI_DEVICE_ID_CMD_643:
+		case PCI_DEVICE_ID_CMD_646:
+		case PCI_DEVICE_ID_CMD_648:
+		default:
+			break;
+	}
+
+	if (drive->media != ide_disk) {
+		cmdprintk("CMD64X: drive->media != ide_disk at double check, inital check failed!!\n");
+		return ((int) ide_dma_off);
+	}
+
+	/* UltraDMA only supported on PCI646U and PCI646U2,
+	 * which correspond to revisions 0x03, 0x05 and 0x07 respectively.
+	 * Actually, although the CMD tech support people won't
+	 * tell me the details, the 0x03 revision cannot support
+	 * UDMA correctly without hardware modifications, and even
+	 * then it only works with Quantum disks due to some
+	 * hold time assumptions in the 646U part which are fixed
+	 * in the 646U2.
+	 * So we only do UltraDMA on revision 0x05 and 0x07 chipsets.
+	 */
+
+	if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
+		speed = XFER_UDMA_4;
+		udma_timing_bits = 0x10;	/* 2 clock */
+	} else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
+		speed = XFER_UDMA_3;
+		udma_timing_bits = 0x20;	/* 3 clock */
+	} else if ((id->dma_ultra & 0x0004) && (udma_33)) {
+		speed = XFER_UDMA_2;
+		udma_timing_bits = 0x10;	/* 2 clock */
+	} else if ((id->dma_ultra & 0x0002) && (udma_33)) {
+		speed = XFER_UDMA_1;
+		udma_timing_bits = 0x20;	/* 3 clock */
+	} else if ((id->dma_ultra & 0x0001) && (udma_33)) {
+		speed = XFER_UDMA_0;
+		udma_timing_bits = 0x30;	/* 4 clock */
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+	} else if (id->dma_1word & 0x0004) {
+		speed = XFER_SW_DMA_2;
+	} else if (id->dma_1word & 0x0002) {
+		speed = XFER_SW_DMA_1;
+	} else if (id->dma_1word & 0x0001) {
+		speed = XFER_SW_DMA_0;
+	} else {
+		set_pio = 1;
+	}
+
+	config_chipset_for_pio(drive, set_pio);
+
+	if (set_pio)
+		return ((int) ide_dma_off_quietly);
+
+#if 1
+	/*
+	 * This the alternate access method. :-(
+	 * The correct method is to directly setup the pci-config space.
+	 */ 
+	(void) ide_config_drive_speed(drive, speed);
+	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+
+	if (speed >= XFER_UDMA_0) {
+		byte udma_ctrl = inb(dma_base + 3);
+		/* Put this channel into UDMA mode. */
+		udma_ctrl |= (1 << unit);
+		udma_ctrl &= ~(0x04 << unit);
+		if (udma_66)
+			udma_ctrl |= (0x04 << unit);
+		udma_ctrl &= ~(0x30 << (unit * 2));
+		udma_ctrl |=  (udma_timing_bits << (unit * 2));
+		outb(udma_ctrl, dma_base+3);
+	}
+#endif
+
+	if (tune_chipset_for_dma(drive, speed))
+		return ((int) ide_dma_off);
+
+	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+
+	return rval;
+}
+
+static int cmd64x_config_drive_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id	= drive->id;
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	unsigned int class_rev	= 0;
+	byte can_ultra_33	= 0;
+	byte can_ultra_66	= 0;
+	ide_dma_action_t dma_func = ide_dma_on;
+
+	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+	class_rev &= 0xff;	
+
+	switch(dev->device) {
+		case PCI_DEVICE_ID_CMD_643:
+			can_ultra_33 = 1;
+			can_ultra_66 = 0;
+			break;
+		case PCI_DEVICE_ID_CMD_646:
+			can_ultra_33 = (class_rev >= 0x05) ? 1 : 0;
+			can_ultra_66 = 0;
+			break;
+		case PCI_DEVICE_ID_CMD_648:
+			can_ultra_33 = 1;
+			can_ultra_66 = 1;
+			break;
+		default:
+			return hwif->dmaproc(ide_dma_off, drive);
+	}
+
+	if ((id != NULL) && ((id->capability & 1) != 0) &&
+	    hwif->autodma && (drive->media == ide_disk)) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
+		}
+		dma_func = ide_dma_off_quietly;
+		if ((id->field_valid & 4) && (can_ultra_33)) {
+			if (id->dma_ultra & 0x001F) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive, class_rev, can_ultra_66);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if ((id->dma_mword & 0x0007) ||
+			    (id->dma_1word & 0x0007)) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive, class_rev, 0);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
+			}
+		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+			if (id->eide_dma_time > 150) {
+				goto no_dma_set;
+			}
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive, class_rev, 0);
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+		config_chipset_for_pio(drive, 1);
+	}
+	return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+static int cmd64x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			return cmd64x_config_drive_for_dma(drive);
+		default:
+			break;
+	}
+	/* Other cases are done by generic IDE-DMA code. */
+	return ide_dmaproc(func, drive);
+}
+
+/*
+ * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old
+ * event order for DMA transfers.
+ */
+static int cmd646_1_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned long dma_base = hwif->dma_base;
+	byte dma_stat;
+
+	if (func == ide_dma_end) {
+		drive->waiting_for_dma = 0;
+		dma_stat = inb(dma_base+2);		/* get DMA status */
+		outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+		outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+		ide_destroy_dmatable(drive);		/* and free any DMA resources */
+		return (dma_stat & 7) != 4;		/* verify good DMA status */
+	}
+
+	/* Other cases are done by generic IDE-DMA code. */
+	return cmd64x_dmaproc(func, drive);
+}
+
+unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name)
+{
+	unsigned char mrdmode;
+	unsigned int class_rev;
+
+	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+	class_rev &= 0xff;
+
+#if 0
+	if (dev->resource[PCI_ROM_RESOURCE].start)
+		pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+#endif
+
+	switch(dev->device) {
+		case PCI_DEVICE_ID_CMD_643:
+			break;
+		case PCI_DEVICE_ID_CMD_646:
+			printk("%s: chipset revision 0x%02X, ", name, class_rev);
+			switch(class_rev) {
+				case 0x07:
+				case 0x05:
+					printk("UltraDMA Capable");
+					break;
+				case 0x03:
+					printk("MultiWord DMA Force Limited");
+					break;
+				case 0x01:
+				default:
+					printk("MultiWord DMA Limited, IRQ workaround enabled");
+					break;
+				}
+			printk("\n");
+                        break;
+		case PCI_DEVICE_ID_CMD_648:
+			break;
+		default:
+			break;
+	}
+
+	/* Set a good latency timer and cache line size value. */
+	(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+#ifdef __sparc_v9__
+	(void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
+#endif
+
+	/* Setup interrupts. */
+	(void) pci_read_config_byte(dev, MRDMODE, &mrdmode);
+	mrdmode &= ~(0x30);
+	(void) pci_write_config_byte(dev, MRDMODE, mrdmode);
+
+	/* Use MEMORY READ LINE for reads.
+	 * NOTE: Although not mentioned in the PCI0646U specs,
+	 *       these bits are write only and won't be read
+	 *       back as set or not.  The PCI0646U2 specs clarify
+	 *       this point.
+	 */
+	(void) pci_write_config_byte(dev, MRDMODE, mrdmode | 0x02);
+
+	/* Set reasonable active/recovery/address-setup values. */
+	(void) pci_write_config_byte(dev, ARTTIM0,  0x40);
+	(void) pci_write_config_byte(dev, DRWTIM0,  0x3f);
+	(void) pci_write_config_byte(dev, ARTTIM1,  0x40);
+	(void) pci_write_config_byte(dev, DRWTIM1,  0x3f);
+#ifdef __i386__
+	(void) pci_write_config_byte(dev, ARTTIM23, 0x1c);
+#else
+	(void) pci_write_config_byte(dev, ARTTIM23, 0x5c);
+#endif
+	(void) pci_write_config_byte(dev, DRWTIM23, 0x3f);
+	(void) pci_write_config_byte(dev, DRWTIM3,  0x3f);
+
+#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
+	cmd64x_proc = 1;
+	bmide_dev = dev;
+	cmd64x_display_info = &cmd64x_get_info;
+#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */
+
+	return 0;
+}
+
+unsigned int __init ata66_cmd64x (ide_hwif_t *hwif)
+{
+	byte ata66 = 0;
+	byte mask = (hwif->channel) ? 0x02 : 0x01;
+
+	pci_read_config_byte(hwif->pci_dev, BMIDECSR, &ata66);
+	return (ata66 & mask) ? 1 : 0;
+}
+
+void __init ide_init_cmd64x (ide_hwif_t *hwif)
+{
+	struct pci_dev *dev	= hwif->pci_dev;
+	unsigned int class_rev;
+
+	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+	class_rev &= 0xff;
+
+	hwif->tuneproc = &cmd64x_tuneproc;
+	hwif->drives[0].autotune = 1;
+	hwif->drives[0].autotune = 1;
+
+	if (!hwif->dma_base)
+		return;
+
+	switch(dev->device) {
+		case PCI_DEVICE_ID_CMD_643:
+			hwif->dmaproc = &cmd64x_dmaproc;
+			break;
+		case PCI_DEVICE_ID_CMD_646:
+			hwif->chipset = ide_cmd646;
+			if (class_rev == 0x01) {
+				hwif->dmaproc = &cmd646_1_dmaproc;
+			} else {
+				hwif->dmaproc = &cmd64x_dmaproc;
+			}
+			break;
+		case PCI_DEVICE_ID_CMD_648:
+			hwif->dmaproc = &cmd64x_dmaproc;
+			break;
+		default:
+			break;
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/cs5530.c linux/drivers/ide/cs5530.c
--- v2.3.51/linux/drivers/ide/cs5530.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/cs5530.c	Mon Feb 28 07:18:20 2000
@@ -0,0 +1,361 @@
+/*
+ * linux/drivers/block/cs5530.c			Version 0.5	Feb 13, 2000
+ *
+ * Copyright (C) 2000			Mark Lord <mlord@pobox.com>
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * Development of this chipset driver was funded
+ * by the nice folks at National Semiconductor.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "ide_modes.h"
+
+#define DISPLAY_CS5530_TIMINGS
+
+#if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int cs5530_get_info(char *, char **, off_t, int);
+extern int (*cs5530_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int cs5530_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p = buffer;
+	u32 bibma = bmide_dev->resource[4].start;
+	u8  c0 = 0, c1 = 0;
+
+	/*
+	 * at that point bibma+0x2 et bibma+0xa are byte registers
+	 * to investigate:
+	 */
+
+	c0 = inb_p((unsigned short)bibma + 0x02);
+	c1 = inb_p((unsigned short)bibma + 0x0a);
+
+	p += sprintf(p, "\n                                Cyrix 5530 Chipset.\n");
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	p += sprintf(p, "                %sabled                         %sabled\n",
+			(c0&0x80) ? "dis" : " en",
+			(c1&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
+			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+	p += sprintf(p, "UDMA\n");
+	p += sprintf(p, "DMA\n");
+	p += sprintf(p, "PIO\n");
+
+	return p-buffer;
+}
+#endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */
+
+byte cs5530_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * Set a new transfer mode at the drive
+ */
+int cs5530_set_xfer_mode (ide_drive_t *drive, byte mode)
+{
+	int error = 0;
+
+	printk("%s: cs5530_set_xfer_mode(%s)\n", drive->name, ide_xfer_verbose(mode));
+	error = ide_config_drive_speed(drive, mode);
+
+	return error;
+}
+
+/*
+ * Here are the standard PIO mode 0-4 timings for each "format".
+ * Format-0 uses fast data reg timings, with slower command reg timings.
+ * Format-1 uses fast timings for all registers, but won't work with all drives.
+ */
+static unsigned int cs5530_pio_timings[2][5] =
+	{{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010},
+	 {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}};
+
+/*
+ * After chip reset, the PIO timings are set to 0x0000e132, which is not valid.
+ */
+#define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132)
+#define CS5530_BASEREG(hwif)	(((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20))
+
+/*
+ * cs5530_tuneproc() handles selection/setting of PIO modes
+ * for both the chipset and drive.
+ *
+ * The ide_init_cs5530() routine guarantees that all drives
+ * will have valid default PIO timings set up before we get here.
+ */
+static void cs5530_tuneproc (ide_drive_t *drive, byte pio)	/* pio=255 means "autotune" */
+{
+	ide_hwif_t	*hwif = HWIF(drive);
+	unsigned int	format, basereg = CS5530_BASEREG(hwif);
+	static byte	modes[5] = {XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4};
+
+	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+	if (!cs5530_set_xfer_mode(drive, modes[pio])) {
+		format = (inl(basereg+4) >> 31) & 1;
+		outl(cs5530_pio_timings[format][pio], basereg+(drive->select.b.unit<<3));
+	}
+}
+
+/*
+ * cs5530_config_dma() handles selection/setting of DMA/UDMA modes
+ * for both the chipset and drive.
+ */
+static int cs5530_config_dma (ide_drive_t *drive)
+{
+	int			udma_ok = 1, mode = 0;
+	ide_hwif_t		*hwif = HWIF(drive);
+	int			unit = drive->select.b.unit;
+	ide_drive_t		*mate = &hwif->drives[unit^1];
+	struct hd_driveid	*id = drive->id;
+	unsigned int		basereg, reg, timings;
+
+
+	/*
+	 * Default to DMA-off in case we run into trouble here.
+	 */
+	(void)hwif->dmaproc(ide_dma_off_quietly, drive);	/* turn off DMA while we fiddle */
+	outb(inb(hwif->dma_base+2)&~(unit?0x40:0x20), hwif->dma_base+2); /* clear DMA_capable bit */
+
+	/*
+	 * The CS5530 specifies that two drives sharing a cable cannot
+	 * mix UDMA/MDMA.  It has to be one or the other, for the pair,
+	 * though different timings can still be chosen for each drive.
+	 * We could set the appropriate timing bits on the fly,
+	 * but that might be a bit confusing.  So, for now we statically
+	 * handle this requirement by looking at our mate drive to see
+	 * what it is capable of, before choosing a mode for our own drive.
+	 */
+	if (mate->present) {
+		struct hd_driveid *mateid = mate->id;
+		if (mateid && (mateid->capability & 1) && !hwif->dmaproc(ide_dma_bad_drive, mate)) {
+			if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7))
+				udma_ok = 1;
+			else if ((mateid->field_valid & 2) && (mateid->dma_mword & 7))
+				udma_ok = 0;
+			else
+				udma_ok = 1;
+		}
+	}
+
+	/*
+	 * Now see what the current drive is capable of,
+	 * selecting UDMA only if the mate said it was ok.
+	 */
+	if (id && (id->capability & 1) && hwif->autodma && !hwif->dmaproc(ide_dma_bad_drive, drive)) {
+		if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) {
+			if      (id->dma_ultra & 4)
+				mode = XFER_UDMA_2;
+			else if (id->dma_ultra & 2)
+				mode = XFER_UDMA_1;
+			else if (id->dma_ultra & 1)
+				mode = XFER_UDMA_0;
+		}
+		if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) {
+			if      (id->dma_mword & 4)
+				mode = XFER_MW_DMA_2;
+			else if (id->dma_mword & 2)
+				mode = XFER_MW_DMA_1;
+			else if (id->dma_mword & 1)
+				mode = XFER_MW_DMA_0;
+		}
+	}
+
+	/*
+	 * Tell the drive to switch to the new mode; abort on failure.
+	 */
+	if (!mode || cs5530_set_xfer_mode(drive, mode))
+		return 1;	/* failure */
+
+	/*
+	 * Now tune the chipset to match the drive:
+	 */
+	switch (mode) {
+		case XFER_UDMA_0:	timings = 0x00921250; break;
+		case XFER_UDMA_1:	timings = 0x00911140; break;
+		case XFER_UDMA_2:	timings = 0x00911030; break;
+		case XFER_MW_DMA_0:	timings = 0x00077771; break;
+		case XFER_MW_DMA_1:	timings = 0x00012121; break;
+		case XFER_MW_DMA_2:	timings = 0x00002020; break;
+		default:
+			printk("%s: cs5530_config_dma: huh? mode=%02x\n", drive->name, mode);
+			return 1;	/* failure */
+	}
+	basereg = CS5530_BASEREG(hwif);
+	reg = inl(basereg+4);			/* get drive0 config register */
+	timings |= reg & 0x80000000;		/* preserve PIO format bit */
+	if (unit == 0) {			/* are we configuring drive0? */
+		outl(timings, basereg+4);	/* write drive0 config register */
+	} else {
+		if (timings & 0x00100000)
+			reg |=  0x00100000;	/* enable UDMA timings for both drives */
+		else
+			reg &= ~0x00100000;	/* disable UDMA timings for both drives */
+		outl(reg,     basereg+4);	/* write drive0 config register */
+		outl(timings, basereg+12);	/* write drive1 config register */
+	}
+	outb(inb(hwif->dma_base+2)|(unit?0x40:0x20), hwif->dma_base+2);	/* set DMA_capable bit */
+
+	/*
+	 * Finally, turn DMA on in software, and exit.
+	 */
+	return hwif->dmaproc(ide_dma_on, drive);	/* success */
+}
+
+/*
+ * This is a CS5530-specific wrapper for the standard ide_dmaproc().
+ * We need it for our custom "ide_dma_check" function.
+ * All other requests are forwarded to the standard ide_dmaproc().
+ */
+int cs5530_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			return cs5530_config_dma(drive);
+		default:
+			break;
+	}
+	/* Other cases are done by generic IDE-DMA code. */
+	return ide_dmaproc(func, drive);
+}
+
+/*
+ * Initialize the cs5530 bridge for reliable IDE DMA operation.
+ */
+unsigned int __init pci_init_cs5530 (struct pci_dev *dev, const char *name)
+{
+	struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
+	unsigned short pcicmd = 0;
+	unsigned long flags;
+
+	pci_for_each_dev (dev) {
+		if (dev->vendor == PCI_VENDOR_ID_CYRIX) {
+			switch (dev->device) {
+				case PCI_DEVICE_ID_CYRIX_PCI_MASTER:
+					master_0 = dev;
+					break;
+				case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
+					cs5530_0 = dev;
+					break;
+			}
+		}
+	}
+	if (!master_0) {
+		printk("%s: unable to locate PCI MASTER function\n", name);
+		return 0;
+	}
+	if (!cs5530_0) {
+		printk("%s: unable to locate CS5530 LEGACY function\n", name);
+		return 0;
+	}
+
+	save_flags(flags);
+	cli();	/* all CPUs (there should only be one CPU with this chipset) */
+
+	/*
+	 * Enable BusMaster and MemoryWriteAndInvalidate for the cs5530:
+	 * -->  OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530
+	 */
+	pci_read_config_word (cs5530_0, PCI_COMMAND, &pcicmd);
+	pci_write_config_word(cs5530_0, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE);
+
+	/*
+	 * Set PCI CacheLineSize to 16-bytes:
+	 * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530
+	 */
+	pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04);
+
+	/*
+	 * Disable trapping of UDMA register accesses (Win98 hack):
+	 * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530
+	 */
+	pci_write_config_word(cs5530_0, 0xd0, 0x5006);
+
+	/*
+	 * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus:
+	 * The other settings are what is necessary to get the register
+	 * into a sane state for IDE DMA operation.
+	 */
+	pci_write_config_byte(master_0, 0x40, 0x1e);
+
+	/* 
+	 * Set max PCI burst size (16-bytes seems to work best):
+	 *	   16bytes: set bit-1 at 0x41 (reg value of 0x16)
+	 *	all others: clear bit-1 at 0x41, and do:
+	 *	  128bytes: OR 0x00 at 0x41
+	 *	  256bytes: OR 0x04 at 0x41
+	 *	  512bytes: OR 0x08 at 0x41
+	 *	 1024bytes: OR 0x0c at 0x41
+	 */
+	pci_write_config_byte(master_0, 0x41, 0x14);
+
+	/*
+	 * These settings are necessary to get the chip
+	 * into a sane state for IDE DMA operation.
+	 */
+	pci_write_config_byte(master_0, 0x42, 0x00);
+	pci_write_config_byte(master_0, 0x43, 0xc1);
+
+	restore_flags(flags);
+
+#if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS)
+	cs5530_proc = 1;
+	bmide_dev = dev;
+	cs5530_display_info = &cs5530_get_info;
+#endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */
+
+	return 0;
+}
+
+/*
+ * This gets invoked by the IDE driver once for each channel,
+ * and performs channel-specific pre-initialization before drive probing.
+ */
+void __init ide_init_cs5530 (ide_hwif_t *hwif)
+{
+	if (hwif->mate)
+		hwif->serialized = hwif->mate->serialized = 1;
+	if (!hwif->dma_base) {
+		hwif->autodma = 0;
+	} else {
+		unsigned int basereg, d0_timings;
+
+		hwif->dmaproc  = &cs5530_dmaproc;
+		hwif->tuneproc = &cs5530_tuneproc;
+		basereg = CS5530_BASEREG(hwif);
+		d0_timings = inl(basereg+0);
+		if (CS5530_BAD_PIO(d0_timings)) {	/* PIO timings not initialized? */
+			outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+0);
+			if (!hwif->drives[0].autotune)
+				hwif->drives[0].autotune = 1;	/* needs autotuning later */
+		}
+		if (CS5530_BAD_PIO(inl(basereg+8))) {	/* PIO timings not initialized? */
+			outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+8);
+			if (!hwif->drives[1].autotune)
+				hwif->drives[1].autotune = 1;	/* needs autotuning later */
+		}
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/cy82c693.c linux/drivers/ide/cy82c693.c
--- v2.3.51/linux/drivers/ide/cy82c693.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/cy82c693.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,441 @@
+/*
+ * linux/drivers/block/cy82c693.c		Version 0.34	Dec. 13, 1999
+ *
+ *  Copyright (C) 1998-99 Andreas S. Krebs (akrebs@altavista.net), Maintainer
+ *  Copyright (C) 1998-99 Andre Hedrick, Integrater
+ *
+ * CYPRESS CY82C693 chipset IDE controller
+ *
+ * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards.
+ * Writting the driver was quite simple, since most of the job is
+ * done by the generic pci-ide support. 
+ * The hard part was finding the CY82C693's datasheet on Cypress's
+ * web page :-(. But Altavista solved this problem :-).
+ *
+ *
+ * Notes:
+ * - I recently got a 16.8G IBM DTTA, so I was able to test it with
+ *   a large and fast disk - the results look great, so I'd say the
+ *   driver is working fine :-)
+ *   hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA 
+ * - this is my first linux driver, so there's probably a lot  of room 
+ *   for optimizations and bug fixing, so feel free to do it.
+ * - use idebus=xx parameter to set PCI bus speed - needed to calc
+ *   timings for PIO modes (default will be 40)
+ * - if using PIO mode it's a good idea to set the PIO mode and 
+ *   32-bit I/O support (if possible), e.g. hdparm -p2 -c1 /dev/hda
+ * - I had some problems with my IBM DHEA with PIO modes < 2
+ *   (lost interrupts) ?????
+ * - first tests with DMA look okay, they seem to work, but there is a
+ *   problem with sound - the BusMaster IDE TimeOut should fixed this
+ *
+ *
+ * History:
+ * AMH@1999-08-24: v0.34 init_cy82c693_chip moved to pci_init_cy82c693
+ * ASK@1999-01-23: v0.33 made a few minor code clean ups
+ *                       removed DMA clock speed setting by default
+ *                       added boot message
+ * ASK@1998-11-01: v0.32 added support to set BusMaster IDE TimeOut
+ *                       added support to set DMA Controller Clock Speed
+ * ASK@1998-10-31: v0.31 fixed problem with setting to high DMA modes on some drive
+ * ASK@1998-10-29: v0.3 added support to set DMA modes
+ * ASK@1998-10-28: v0.2 added support to set PIO modes
+ * ASK@1998-10-27: v0.1 first version - chipset detection
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/* the current version */
+#define CY82_VERSION	"CY82C693U driver v0.34 99-13-12 Andreas S. Krebs (akrebs@altavista.net)"
+
+/*
+ *	The following are used to debug the driver.
+ */
+#define	CY82C693_DEBUG_LOGS	0
+#define	CY82C693_DEBUG_INFO	0
+
+/* define CY82C693_SETDMA_CLOCK to set DMA Controller Clock Speed to ATCLK */
+#undef CY82C693_SETDMA_CLOCK
+
+/*
+ * note: the value for busmaster timeout is tricky and i got it by trial and error !
+ *       using a to low value will cause DMA timeouts and drop IDE performance
+ *       using a to high value will cause audio playback to scatter
+ *       if you know a better value or how to calc it, please let me know 
+ */
+#define BUSMASTER_TIMEOUT	0x50	/* twice the value written in cy82c693ub datasheet */
+/*
+ * the value above was tested on my machine and it seems to work okay
+ */
+
+/* here are the offset definitions for the registers */
+#define CY82_IDE_CMDREG		0x04
+#define CY82_IDE_ADDRSETUP	0x48
+#define CY82_IDE_MASTER_IOR	0x4C	
+#define CY82_IDE_MASTER_IOW	0x4D	
+#define CY82_IDE_SLAVE_IOR	0x4E	
+#define CY82_IDE_SLAVE_IOW	0x4F
+#define CY82_IDE_MASTER_8BIT	0x50	
+#define CY82_IDE_SLAVE_8BIT	0x51	
+
+#define CY82_INDEX_PORT		0x22
+#define CY82_DATA_PORT		0x23
+
+#define CY82_INDEX_CTRLREG1	0x01
+#define CY82_INDEX_CHANNEL0	0x30
+#define CY82_INDEX_CHANNEL1	0x31
+#define CY82_INDEX_TIMEOUT	0x32
+
+/* the max PIO mode - from datasheet */
+#define CY82C693_MAX_PIO	4
+
+/* the min and max PCI bus speed in MHz - from datasheet */
+#define CY82C963_MIN_BUS_SPEED	25
+#define CY82C963_MAX_BUS_SPEED	33
+
+/* the struct for the PIO mode timings */
+typedef struct pio_clocks_s {
+        byte	address_time;		/* Address setup (clocks) */
+	byte	time_16r;		/* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */
+	byte	time_16w;		/* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */
+	byte	time_8;			/* clocks for 8bit (0xF0=Active/data, 0x0F=Recovery) */
+} pio_clocks_t;
+
+/*
+ * calc clocks using bus_speed
+ * returns (rounded up) time in bus clocks for time in ns
+ */
+static int calc_clk (int time, int bus_speed)
+{
+	int clocks;
+
+	clocks = (time*bus_speed+999)/1000 -1;
+
+	if (clocks < 0)
+		clocks = 0;
+
+	if (clocks > 0x0F)
+		clocks = 0x0F;
+
+	return clocks;
+}
+
+/*
+ * compute the values for the clock registers for PIO
+ * mode and pci_clk [MHz] speed
+ *
+ * NOTE: for mode 0,1 and 2 drives 8-bit IDE command control registers are used
+ *       for mode 3 and 4 drives 8 and 16-bit timings are the same
+ *
+ */ 
+static void compute_clocks (byte pio, pio_clocks_t *p_pclk)
+{
+	int clk1, clk2;
+	int bus_speed;
+
+	bus_speed = ide_system_bus_speed();	/* get speed of PCI bus */
+	/* we don't check against CY82C693's min and max speed,
+	 * so you can play with the idebus=xx parameter
+	 */
+
+	if (pio > CY82C693_MAX_PIO)
+		pio = CY82C693_MAX_PIO;
+
+	/* let's calc the address setup time clocks */
+	p_pclk->address_time = (byte)calc_clk(ide_pio_timings[pio].setup_time, bus_speed);
+
+	/* let's calc the active and recovery time clocks */
+	clk1 = calc_clk(ide_pio_timings[pio].active_time, bus_speed);
+
+	/* calc recovery timing */
+	clk2 =	ide_pio_timings[pio].cycle_time -
+		ide_pio_timings[pio].active_time -
+		ide_pio_timings[pio].setup_time;
+
+	clk2 = calc_clk(clk2, bus_speed);
+
+	clk1 = (clk1<<4)|clk2;	/* combine active and recovery clocks */
+
+	/* note: we use the same values for 16bit IOR and IOW
+         *	those are all the same, since I don't have other
+	 *	timings than those from ide_modes.h
+	 */
+
+	p_pclk->time_16r = (byte)clk1;
+	p_pclk->time_16w = (byte)clk1;
+
+	/* what are good values for 8bit ?? */
+	p_pclk->time_8 = (byte)clk1;
+}
+
+/*
+ * set DMA mode a specific channel for CY82C693
+ */
+static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single)
+{
+        byte index;
+	byte data;
+
+        if (mode>2)	/* make sure we set a valid mode */
+		mode = 2;
+			   
+	if (mode > drive->id->tDMA)  /* to be absolutly sure we have a valid mode */
+		mode = drive->id->tDMA;
+	
+        index = (HWIF(drive)->channel==0) ? CY82_INDEX_CHANNEL0 : CY82_INDEX_CHANNEL1;
+
+#if CY82C693_DEBUG_LOGS
+       	/* for debug let's show the previous values */
+
+	OUT_BYTE(index, CY82_INDEX_PORT);
+	data = IN_BYTE(CY82_DATA_PORT);
+
+	printk (KERN_INFO "%s (ch=%d, dev=%d): DMA mode is %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, (data&0x3), ((data>>2)&1));
+#endif /* CY82C693_DEBUG_LOGS */
+
+	data = (byte)mode|(byte)(single<<2);
+
+	OUT_BYTE(index, CY82_INDEX_PORT);
+	OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+	printk (KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, mode, single);
+#endif /* CY82C693_DEBUG_INFO */
+
+	/* 
+	 * note: below we set the value for Bus Master IDE TimeOut Register
+	 * I'm not absolutly sure what this does, but it solved my problem
+	 * with IDE DMA and sound, so I now can play sound and work with
+	 * my IDE driver at the same time :-)
+	 *
+	 * If you know the correct (best) value for this register please
+	 * let me know - ASK
+	 */
+
+	data = BUSMASTER_TIMEOUT;
+	OUT_BYTE(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT);
+	OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO	
+	printk (KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n", drive->name, data);
+#endif /* CY82C693_DEBUG_INFO */
+}
+
+/* 
+ * used to set DMA mode for CY82C693 (single and multi modes)
+ */
+static int cy82c693_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+	/*
+	 * if the function is dma on, set dma mode for drive everything
+	 * else is done by the defaul func
+	 */
+	if (func == ide_dma_on) {
+		struct hd_driveid *id = drive->id;
+
+#if CY82C693_DEBUG_INFO
+		printk (KERN_INFO "dma_on: %s\n", drive->name);
+#endif /* CY82C693_DEBUG_INFO */
+
+		if (id != NULL) {		
+                       /* Enable DMA on any drive that has DMA (multi or single) enabled */
+                       if (id->field_valid & 2) {       /* regular DMA */
+			       int mmode, smode;
+
+			       mmode = id->dma_mword & (id->dma_mword >> 8);
+			       smode = id->dma_1word & (id->dma_1word >> 8);
+			       		      
+		               if (mmode != 0)
+				     cy82c693_dma_enable(drive, (mmode >> 1), 0); /* enable multi */
+			       else if (smode != 0)
+				     cy82c693_dma_enable(drive, (smode >> 1), 1); /* enable single */
+			}
+		}
+	}
+        return ide_dmaproc(func, drive);
+}
+
+/*
+ * tune ide drive - set PIO mode
+ */
+static void cy82c693_tune_drive (ide_drive_t *drive, byte pio)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	struct pci_dev *dev = hwif->pci_dev;
+	pio_clocks_t pclk;
+	unsigned int addrCtrl;
+
+	/* select primary or secondary channel */
+	if (hwif->index > 0) {  /* drive is on the secondary channel */
+		dev = pci_find_slot(dev->bus->number, dev->devfn+1);
+		if (!dev) {
+			printk(KERN_ERR "%s: tune_drive: Cannot find secondary interface!\n", drive->name);
+			return;
+		}
+	}
+
+#if CY82C693_DEBUG_LOGS
+	/* for debug let's show the register values */
+	
+       	if (drive->select.b.unit == 0) {
+		/*
+		 * get master drive registers               	
+		 * address setup control register
+		 * is 32 bit !!!
+		 */ 
+	  	pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);                
+		addrCtrl &= 0x0F;
+
+		/* now let's get the remaining registers */
+		pci_read_config_byte(dev, CY82_IDE_MASTER_IOR, &pclk.time_16r);
+		pci_read_config_byte(dev, CY82_IDE_MASTER_IOW, &pclk.time_16w);
+		pci_read_config_byte(dev, CY82_IDE_MASTER_8BIT, &pclk.time_8);
+	} else {
+		/*
+		 * set slave drive registers
+		 * address setup control register
+		 * is 32 bit !!!
+		 */ 
+		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+
+		addrCtrl &= 0xF0;
+		addrCtrl >>= 4;
+
+		/* now let's get the remaining registers */
+		pci_read_config_byte(dev, CY82_IDE_SLAVE_IOR, &pclk.time_16r);
+		pci_read_config_byte(dev, CY82_IDE_SLAVE_IOW, &pclk.time_16w);
+		pci_read_config_byte(dev, CY82_IDE_SLAVE_8BIT, &pclk.time_8);
+	}
+
+	printk (KERN_INFO "%s (ch=%d, dev=%d): PIO timing is (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->channel, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
+#endif /* CY82C693_DEBUG_LOGS */
+
+        /* first let's calc the pio modes */
+	pio = ide_get_best_pio_mode(drive, pio, CY82C693_MAX_PIO, NULL);
+
+#if CY82C693_DEBUG_INFO
+	printk (KERN_INFO "%s: Selected PIO mode %d\n", drive->name, pio);
+#endif /* CY82C693_DEBUG_INFO */
+
+        compute_clocks(pio, &pclk);  /* let's calc the values for this PIO mode */
+
+	/* now let's write  the clocks registers */
+	if (drive->select.b.unit == 0) {
+		/*
+		 * set master drive
+		 * address setup control register
+		 * is 32 bit !!!
+		 */ 
+		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+		
+		addrCtrl &= (~0xF);
+		addrCtrl |= (unsigned int)pclk.address_time;
+		pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
+
+		/* now let's set the remaining registers */
+		pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
+		pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
+		pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
+		
+		addrCtrl &= 0xF;
+	} else {
+		/*
+		 * set slave drive
+		 * address setup control register
+		 * is 32 bit !!!
+		 */ 
+		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+
+		addrCtrl &= (~0xF0);
+		addrCtrl |= ((unsigned int)pclk.address_time<<4);
+		pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
+
+		/* now let's set the remaining registers */
+		pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r);
+		pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w);
+		pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8);
+
+		addrCtrl >>= 4;
+		addrCtrl &= 0xF;
+	}	
+
+#if CY82C693_DEBUG_INFO
+	printk (KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->channel, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
+#endif /* CY82C693_DEBUG_INFO */
+}
+
+/*
+ * this function is called during init and is used to setup the cy82c693 chip
+ */
+/*
+ * FIXME! "pci_init_cy82c693" really should replace
+ * the "init_cy82c693_chip", it is the correct location to tinker/setup
+ * the device prior to INIT.
+ */
+
+unsigned int __init pci_init_cy82c693(struct pci_dev *dev, const char *name)
+{
+#ifdef CY82C693_SETDMA_CLOCK
+        byte data;
+#endif /* CY82C693_SETDMA_CLOCK */ 
+
+	/* write info about this verion of the driver */
+	printk (KERN_INFO CY82_VERSION "\n");
+
+#ifdef CY82C693_SETDMA_CLOCK
+       /* okay let's set the DMA clock speed */
+        
+        OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
+        data = IN_BYTE(CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+	printk (KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", name, data);
+#endif /* CY82C693_DEBUG_INFO */
+
+        /*
+	 * for some reason sometimes the DMA controller
+	 * speed is set to ATCLK/2 ???? - we fix this here
+	 * 
+	 * note: i don't know what causes this strange behaviour,
+	 *       but even changing the dma speed doesn't solve it :-(
+	 *       the ide performance is still only half the normal speed 
+	 * 
+	 *       if anybody knows what goes wrong with my machine, please
+	 *       let me know - ASK
+         */
+
+	data |= 0x03;
+
+        OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
+        OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+	printk (KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", name, data);
+#endif /* CY82C693_DEBUG_INFO */
+
+#endif /* CY82C693_SETDMA_CLOCK */
+	return 0;
+}
+
+/*
+ * the init function - called for each ide channel once
+ */
+void __init ide_init_cy82c693(ide_hwif_t *hwif)
+{
+	hwif->chipset = ide_cy82c693;
+	hwif->tuneproc = &cy82c693_tune_drive;
+	if (hwif->dma_base) {
+		hwif->dmaproc = &cy82c693_dmaproc;
+	} else {
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/dtc2278.c linux/drivers/ide/dtc2278.c
--- v2.3.51/linux/drivers/ide/dtc2278.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/dtc2278.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,133 @@
+/*
+ *  linux/drivers/block/dtc2278.c		Version 0.02	Feb 10, 1996
+ *
+ *  Copyright (C) 1996  Linus Torvalds & author (see below)
+ */
+
+#undef REALLY_SLOW_IO           /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/*
+ * Changing this #undef to #define may solve start up problems in some systems.
+ */
+#undef ALWAYS_SET_DTC2278_PIO_MODE
+
+/*
+ * From: andy@cercle.cts.com (Dyan Wile)
+ *
+ * Below is a patch for DTC-2278 - alike software-programmable controllers
+ * The code enables the secondary IDE controller and the PIO4 (3?) timings on
+ * the primary (EIDE). You may probably have to enable the 32-bit support to
+ * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
+ * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
+ * filesystem  corrupted with -u1, but under heavy disk load only :-)
+ *
+ * This card is now forced to use the "serialize" feature,
+ * and irq-unmasking is disallowed.  If io_32bit is enabled,
+ * it must be done for BOTH drives on each interface.
+ *
+ * This code was written for the DTC2278E, but might work with any of these:
+ *
+ * DTC2278S has only a single IDE interface.
+ * DTC2278D has two IDE interfaces and is otherwise identical to the S version.
+ * DTC2278E also has serial ports and a printer port
+ * DTC2278EB: has onboard BIOS, and "works like a charm" -- Kent Bradford <kent@theory.caltech.edu>
+ *
+ * There may be a fourth controller type. The S and D versions use the
+ * Winbond chip, and I think the E version does also.
+ *
+ */
+
+static void sub22 (char b, char c)
+{
+	int i;
+
+	for(i = 0; i < 3; ++i) {
+		inb(0x3f6);
+		outb_p(b,0xb0);
+		inb(0x3f6);
+		outb_p(c,0xb4);
+		inb(0x3f6);
+		if(inb(0xb4) == c) {
+			outb_p(7,0xb0);
+			inb(0x3f6);
+			return;	/* success */
+		}
+	}
+}
+
+static void tune_dtc2278 (ide_drive_t *drive, byte pio)
+{
+	unsigned long flags;
+
+	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+
+	if (pio >= 3) {
+		save_flags(flags);	/* all CPUs */
+		cli();			/* all CPUs */
+		/*
+		 * This enables PIO mode4 (3?) on the first interface
+		 */
+		sub22(1,0xc3);
+		sub22(0,0xa0);
+		restore_flags(flags);	/* all CPUs */
+	} else {
+		/* we don't know how to set it back again.. */
+	}
+
+	/*
+	 * 32bit I/O has to be enabled for *both* drives at the same time.
+	 */
+	drive->io_32bit = 1;
+	HWIF(drive)->drives[!drive->select.b.unit].io_32bit = 1;
+}
+
+void __init init_dtc2278 (void)
+{
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	/*
+	 * This enables the second interface
+	 */
+	outb_p(4,0xb0);
+	inb(0x3f6);
+	outb_p(0x20,0xb4);
+	inb(0x3f6);
+#ifdef ALWAYS_SET_DTC2278_PIO_MODE
+	/*
+	 * This enables PIO mode4 (3?) on the first interface
+	 * and may solve start-up problems for some people.
+	 */
+	sub22(1,0xc3);
+	sub22(0,0xa0);
+#endif
+	__restore_flags(flags);	/* local CPU only */
+
+	ide_hwifs[0].serialized = 1;
+	ide_hwifs[1].serialized = 1;
+	ide_hwifs[0].chipset = ide_dtc2278;
+	ide_hwifs[1].chipset = ide_dtc2278;
+	ide_hwifs[0].tuneproc = &tune_dtc2278;
+	ide_hwifs[0].drives[0].no_unmask = 1;
+	ide_hwifs[0].drives[1].no_unmask = 1;
+	ide_hwifs[1].drives[0].no_unmask = 1;
+	ide_hwifs[1].drives[1].no_unmask = 1;
+	ide_hwifs[0].mate = &ide_hwifs[1];
+	ide_hwifs[1].mate = &ide_hwifs[0];
+	ide_hwifs[1].channel = 1;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/falconide.c linux/drivers/ide/falconide.c
--- v2.3.51/linux/drivers/ide/falconide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/falconide.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,66 @@
+/*
+ *  linux/drivers/block/falconide.c -- Atari Falcon IDE Driver
+ *
+ *     Created 12 Jul 1997 by Geert Uytterhoeven
+ *
+ *  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 <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_stdma.h>
+
+
+    /*
+     *  Base of the IDE interface
+     */
+
+#define ATA_HD_BASE	0xfff00000
+
+    /*
+     *  Offsets from the above base
+     */
+
+#define ATA_HD_DATA	0x00
+#define ATA_HD_ERROR	0x05		/* see err-bits */
+#define ATA_HD_NSECTOR	0x09		/* nr of sectors to read/write */
+#define ATA_HD_SECTOR	0x0d		/* starting sector */
+#define ATA_HD_LCYL	0x11		/* starting cylinder */
+#define ATA_HD_HCYL	0x15		/* high byte of starting cyl */
+#define ATA_HD_SELECT	0x19		/* 101dhhhh , d=drive, hhhh=head */
+#define ATA_HD_STATUS	0x1d		/* see status-bits */
+#define ATA_HD_CONTROL	0x39
+
+static int __init falconide_offsets[IDE_NR_PORTS] = {
+    ATA_HD_DATA, ATA_HD_ERROR, ATA_HD_NSECTOR, ATA_HD_SECTOR, ATA_HD_LCYL,
+    ATA_HD_HCYL, ATA_HD_SELECT, ATA_HD_STATUS, ATA_HD_CONTROL, -1
+};
+
+
+    /*
+     *  Probe for a Falcon IDE interface
+     */
+
+void __init falconide_init(void)
+{
+    if (MACH_IS_ATARI && ATARIHW_PRESENT(IDE)) {
+	hw_regs_t hw;
+	int index;
+
+	ide_setup_ports(&hw, (ide_ioreg_t)ATA_HD_BASE, falconide_offsets,
+			0, 0, NULL, IRQ_MFP_IDE);
+	index = ide_register_hw(&hw, NULL);
+
+	if (index != -1)
+	    printk("ide%d: Falcon IDE interface\n", index);
+    }
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/gayle.c linux/drivers/ide/gayle.c
--- v2.3.51/linux/drivers/ide/gayle.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/gayle.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,169 @@
+/*
+ *  linux/drivers/block/gayle.c -- Amiga Gayle IDE Driver
+ *
+ *     Created 9 Jul 1997 by Geert Uytterhoeven
+ *
+ *  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 <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+
+    /*
+     *  Bases of the IDE interfaces
+     */
+
+#define GAYLE_BASE_4000	0xdd2020	/* A4000/A4000T */
+#define GAYLE_BASE_1200	0xda0000	/* A1200/A600 */
+
+    /*
+     *  Offsets from one of the above bases
+     */
+
+#define GAYLE_DATA	0x00
+#define GAYLE_ERROR	0x06		/* see err-bits */
+#define GAYLE_NSECTOR	0x0a		/* nr of sectors to read/write */
+#define GAYLE_SECTOR	0x0e		/* starting sector */
+#define GAYLE_LCYL	0x12		/* starting cylinder */
+#define GAYLE_HCYL	0x16		/* high byte of starting cyl */
+#define GAYLE_SELECT	0x1a		/* 101dhhhh , d=drive, hhhh=head */
+#define GAYLE_STATUS	0x1e		/* see status-bits */
+#define GAYLE_CONTROL	0x101a
+
+static int __init gayle_offsets[IDE_NR_PORTS] = {
+    GAYLE_DATA, GAYLE_ERROR, GAYLE_NSECTOR, GAYLE_SECTOR, GAYLE_LCYL,
+    GAYLE_HCYL, GAYLE_SELECT, GAYLE_STATUS, -1, -1
+};
+
+
+    /*
+     *  These are at different offsets from the base
+     */
+
+#define GAYLE_IRQ_4000	0xdd3020	/* MSB = 1, Harddisk is source of */
+#define GAYLE_IRQ_1200	0xda9000	/* interrupt */
+
+
+    /*
+     *  Offset of the secondary port for IDE doublers
+     *  Note that GAYLE_CONTROL is NOT available then!
+     */
+
+#define GAYLE_NEXT_PORT	0x1000
+
+#ifndef CONFIG_BLK_DEV_IDEDOUBLER
+#define GAYLE_NUM_HWIFS		1
+#define GAYLE_NUM_PROBE_HWIFS	GAYLE_NUM_HWIFS
+#define GAYLE_HAS_CONTROL_REG	1
+#else /* CONFIG_BLK_DEV_IDEDOUBLER */
+#define GAYLE_NUM_HWIFS		2
+#define GAYLE_NUM_PROBE_HWIFS	(ide_doubler ? GAYLE_NUM_HWIFS : \
+					       GAYLE_NUM_HWIFS-1)
+#define GAYLE_HAS_CONTROL_REG	(!ide_doubler)
+int ide_doubler = 0;	/* support IDE doublers? */
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
+
+    /*
+     *  Check and acknowledge the interrupt status
+     */
+
+static int gayle_ack_intr_a4000(ide_hwif_t *hwif)
+{
+    unsigned char ch;
+
+    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+    if (!(ch & 0x80))
+	return 0;
+    return 1;
+}
+
+static int gayle_ack_intr_a1200(ide_hwif_t *hwif)
+{
+    unsigned char ch;
+
+    ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+    if (!(ch & 0x80))
+	return 0;
+    (void)inb(hwif->io_ports[IDE_STATUS_OFFSET]);
+    outb(0x7c | (ch & 0x03), hwif->io_ports[IDE_IRQ_OFFSET]);
+    return 1;
+}
+
+    /*
+     *  Probe for a Gayle IDE interface (and optionally for an IDE doubler)
+     */
+
+void __init gayle_init(void)
+{
+    int a4000, i;
+
+    if (!MACH_IS_AMIGA)
+	return;
+
+    if (!(a4000 = AMIGAHW_PRESENT(A4000_IDE)) && !AMIGAHW_PRESENT(A1200_IDE))
+	return;
+
+    for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) {
+	ide_ioreg_t base, ctrlport, irqport;
+	ide_ack_intr_t *ack_intr;
+	hw_regs_t hw;
+	int index;
+
+	if (a4000) {
+	    base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_4000);
+	    irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_4000);
+	    ack_intr = gayle_ack_intr_a4000;
+	} else {
+	    base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_1200);
+	    irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_1200);
+	    ack_intr = gayle_ack_intr_a1200;
+	}
+
+	if (GAYLE_HAS_CONTROL_REG)
+	    ctrlport = base + GAYLE_CONTROL;
+	else
+	    ctrlport = 0;
+
+	base += i*GAYLE_NEXT_PORT;
+
+	ide_setup_ports(&hw, base, gayle_offsets,
+			ctrlport, irqport, ack_intr, IRQ_AMIGA_PORTS);
+
+	index = ide_register_hw(&hw, NULL);
+	if (index != -1) {
+	    switch (i) {
+		case 0:
+		    printk("ide%d: Gayle IDE interface (A%d style)\n", index,
+			   a4000 ? 4000 : 1200);
+		    break;
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+		case 1:
+		    printk("ide%d: IDE doubler\n", index);
+		    break;
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+	    }
+	}
+#if 1 /* TESTING */
+	if (i == 1) {
+	    volatile u_short *addr = (u_short *)base;
+	    u_short data;
+	    printk("+++ Probing for IDE doubler... ");
+	    *addr = 0xffff;
+	    data = *addr;
+	    printk("probe returned 0x%02x (PLEASE REPORT THIS!!)\n", data);
+	}
+#endif /* TESTING */
+    }
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/hd.c linux/drivers/ide/hd.c
--- v2.3.51/linux/drivers/ide/hd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/hd.c	Wed Feb 16 15:42:05 2000
@@ -0,0 +1,883 @@
+/*
+ *  linux/drivers/block/hd.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * This is the low-level hd interrupt support. It traverses the
+ * request-list, using interrupts to jump between functions. As
+ * all the functions are called within interrupts, we may not
+ * sleep. Special care is recommended.
+ * 
+ *  modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ *
+ *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ *  in the early extended-partition checks and added DM partitions
+ *
+ *  IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ *  and general streamlining by Mark Lord.
+ *
+ *  Removed 99% of above. Use Mark's ide driver for those options.
+ *  This is now a lightweight ST-506 driver. (Paul Gortmaker)
+ *
+ *  Modified 1995 Russell King for ARM processor.
+ */
+  
+/* Uncomment the following if you want verbose error reports. */
+/* #define VERBOSE_ERRORS */
+  
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/init.h>
+#include <linux/blkpg.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define MAJOR_NR HD_MAJOR
+#include <linux/blk.h>
+
+#ifdef __arm__
+#undef  HD_IRQ
+#endif
+#include <asm/irq.h>
+#ifdef __arm__
+#define HD_IRQ IRQ_HARDDISK
+#endif
+
+static int revalidate_hddisk(kdev_t, int);
+
+#define	HD_DELAY	0
+
+#define MAX_ERRORS     16	/* Max read/write errors/sector */
+#define RESET_FREQ      8	/* Reset controller every 8th retry */
+#define RECAL_FREQ      4	/* Recalibrate every 4th retry */
+#define MAX_HD		2
+
+#define STAT_OK		(READY_STAT|SEEK_STAT)
+#define OK_STATUS(s)	(((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
+
+static void recal_intr(void);
+static void bad_rw_intr(void);
+
+static char recalibrate[MAX_HD];
+static char special_op[MAX_HD];
+static int access_count[MAX_HD];
+static char busy[MAX_HD];
+static DECLARE_WAIT_QUEUE_HEAD(busy_wait);
+
+static int reset;
+static int hd_error;
+
+#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
+
+/*
+ *  This struct defines the HD's and their types.
+ */
+struct hd_i_struct {
+	unsigned int head,sect,cyl,wpcom,lzone,ctl;
+};
+	
+#ifdef HD_TYPE
+static struct hd_i_struct hd_info[] = { HD_TYPE };
+static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
+#else
+static struct hd_i_struct hd_info[MAX_HD];
+static int NR_HD;
+#endif
+
+static struct hd_struct hd[MAX_HD<<6];
+static int hd_sizes[MAX_HD<<6];
+static int hd_blocksizes[MAX_HD<<6];
+static int hd_hardsectsizes[MAX_HD<<6];
+
+#if (HD_DELAY > 0)
+unsigned long last_req;
+
+unsigned long read_timer(void)
+{
+	unsigned long t, flags;
+	int i;
+
+	save_flags(flags);
+	cli();
+	t = jiffies * 11932;
+    	outb_p(0, 0x43);
+	i = inb_p(0x40);
+	i |= inb(0x40) << 8;
+	restore_flags(flags);
+	return(t - i);
+}
+#endif
+
+void __init hd_setup(char *str, int *ints)
+{
+	int hdind = 0;
+
+	if (ints[0] != 3)
+		return;
+	if (hd_info[0].head != 0)
+		hdind=1;
+	hd_info[hdind].head = ints[2];
+	hd_info[hdind].sect = ints[3];
+	hd_info[hdind].cyl = ints[1];
+	hd_info[hdind].wpcom = 0;
+	hd_info[hdind].lzone = ints[1];
+	hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
+	NR_HD = hdind+1;
+}
+
+static void dump_status (const char *msg, unsigned int stat)
+{
+	unsigned long flags;
+	char devc;
+
+	devc = !QUEUE_EMPTY ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
+	save_flags (flags);
+	sti();
+#ifdef VERBOSE_ERRORS
+	printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
+	if (stat & BUSY_STAT)	printk("Busy ");
+	if (stat & READY_STAT)	printk("DriveReady ");
+	if (stat & WRERR_STAT)	printk("WriteFault ");
+	if (stat & SEEK_STAT)	printk("SeekComplete ");
+	if (stat & DRQ_STAT)	printk("DataRequest ");
+	if (stat & ECC_STAT)	printk("CorrectedError ");
+	if (stat & INDEX_STAT)	printk("Index ");
+	if (stat & ERR_STAT)	printk("Error ");
+	printk("}\n");
+	if ((stat & ERR_STAT) == 0) {
+		hd_error = 0;
+	} else {
+		hd_error = inb(HD_ERROR);
+		printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);
+		if (hd_error & BBD_ERR)		printk("BadSector ");
+		if (hd_error & ECC_ERR)		printk("UncorrectableError ");
+		if (hd_error & ID_ERR)		printk("SectorIdNotFound ");
+		if (hd_error & ABRT_ERR)	printk("DriveStatusError ");
+		if (hd_error & TRK0_ERR)	printk("TrackZeroNotFound ");
+		if (hd_error & MARK_ERR)	printk("AddrMarkNotFound ");
+		printk("}");
+		if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
+			printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
+				inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
+			if (!QUEUE_EMPTY)
+				printk(", sector=%ld", CURRENT->sector);
+		}
+		printk("\n");
+	}
+#else
+	printk("hd%c: %s: status=0x%02x.\n", devc, msg, stat & 0xff);
+	if ((stat & ERR_STAT) == 0) {
+		hd_error = 0;
+	} else {
+		hd_error = inb(HD_ERROR);
+		printk("hd%c: %s: error=0x%02x.\n", devc, msg, hd_error & 0xff);
+	}
+#endif	/* verbose errors */
+	restore_flags (flags);
+}
+
+void check_status(void)
+{
+	int i = inb_p(HD_STATUS);
+
+	if (!OK_STATUS(i)) {
+		dump_status("check_status", i);
+		bad_rw_intr();
+	}
+}
+
+static int controller_busy(void)
+{
+	int retries = 100000;
+	unsigned char status;
+
+	do {
+		status = inb_p(HD_STATUS);
+	} while ((status & BUSY_STAT) && --retries);
+	return status;
+}
+
+static int status_ok(void)
+{
+	unsigned char status = inb_p(HD_STATUS);
+
+	if (status & BUSY_STAT)
+		return 1;	/* Ancient, but does it make sense??? */
+	if (status & WRERR_STAT)
+		return 0;
+	if (!(status & READY_STAT))
+		return 0;
+	if (!(status & SEEK_STAT))
+		return 0;
+	return 1;
+}
+
+static int controller_ready(unsigned int drive, unsigned int head)
+{
+	int retry = 100;
+
+	do {
+		if (controller_busy() & BUSY_STAT)
+			return 0;
+		outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
+		if (status_ok())
+			return 1;
+	} while (--retry);
+	return 0;
+}
+
+static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
+		unsigned int head,unsigned int cyl,unsigned int cmd,
+		void (*intr_addr)(void))
+{
+	unsigned short port;
+
+#if (HD_DELAY > 0)
+	while (read_timer() - last_req < HD_DELAY)
+		/* nothing */;
+#endif
+	if (reset)
+		return;
+	if (!controller_ready(drive, head)) {
+		reset = 1;
+		return;
+	}
+	SET_INTR(intr_addr);
+	outb_p(hd_info[drive].ctl,HD_CMD);
+	port=HD_DATA;
+	outb_p(hd_info[drive].wpcom>>2,++port);
+	outb_p(nsect,++port);
+	outb_p(sect,++port);
+	outb_p(cyl,++port);
+	outb_p(cyl>>8,++port);
+	outb_p(0xA0|(drive<<4)|head,++port);
+	outb_p(cmd,++port);
+}
+
+static void hd_request (void);
+
+static int drive_busy(void)
+{
+	unsigned int i;
+	unsigned char c;
+
+	for (i = 0; i < 500000 ; i++) {
+		c = inb_p(HD_STATUS);
+		if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
+			return 0;
+	}
+	dump_status("reset timed out", c);
+	return 1;
+}
+
+static void reset_controller(void)
+{
+	int	i;
+
+	outb_p(4,HD_CMD);
+	for(i = 0; i < 1000; i++) barrier();
+	outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
+	for(i = 0; i < 1000; i++) barrier();
+	if (drive_busy())
+		printk("hd: controller still busy\n");
+	else if ((hd_error = inb(HD_ERROR)) != 1)
+		printk("hd: controller reset failed: %02x\n",hd_error);
+}
+
+static void reset_hd(void)
+{
+	static int i;
+
+repeat:
+	if (reset) {
+		reset = 0;
+		i = -1;
+		reset_controller();
+	} else {
+		check_status();
+		if (reset)
+			goto repeat;
+	}
+	if (++i < NR_HD) {
+		special_op[i] = recalibrate[i] = 1;
+		hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
+			hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
+		if (reset)
+			goto repeat;
+	} else
+		hd_request();
+}
+
+/*
+ * Ok, don't know what to do with the unexpected interrupts: on some machines
+ * doing a reset and a retry seems to result in an eternal loop. Right now I
+ * ignore it, and just set the timeout.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ */
+void unexpected_hd_interrupt(void)
+{
+	unsigned int stat = inb_p(HD_STATUS);
+
+	if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
+		dump_status ("unexpected interrupt", stat);
+		SET_TIMER;
+	}
+}
+
+/*
+ * bad_rw_intr() now tries to be a bit smarter and does things
+ * according to the error returned by the controller.
+ * -Mika Liljeberg (liljeber@cs.Helsinki.FI)
+ */
+static void bad_rw_intr(void)
+{
+	int dev;
+
+	if (QUEUE_EMPTY)
+		return;
+	dev = DEVICE_NR(CURRENT->rq_dev);
+	if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
+		end_request(0);
+		special_op[dev] = recalibrate[dev] = 1;
+	} else if (CURRENT->errors % RESET_FREQ == 0)
+		reset = 1;
+	else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
+		special_op[dev] = recalibrate[dev] = 1;
+	/* Otherwise just retry */
+}
+
+static inline int wait_DRQ(void)
+{
+	int retries = 100000, stat;
+
+	while (--retries > 0)
+		if ((stat = inb_p(HD_STATUS)) & DRQ_STAT)
+			return 0;
+	dump_status("wait_DRQ", stat);
+	return -1;
+}
+
+static void read_intr(void)
+{
+	int i, retries = 100000;
+
+	do {
+		i = (unsigned) inb_p(HD_STATUS);
+		if (i & BUSY_STAT)
+			continue;
+		if (!OK_STATUS(i))
+			break;
+		if (i & DRQ_STAT)
+			goto ok_to_read;
+	} while (--retries > 0);
+	dump_status("read_intr", i);
+	bad_rw_intr();
+	hd_request();
+	return;
+ok_to_read:
+	insw(HD_DATA,CURRENT->buffer,256);
+	CURRENT->sector++;
+	CURRENT->buffer += 512;
+	CURRENT->errors = 0;
+	i = --CURRENT->nr_sectors;
+	--CURRENT->current_nr_sectors;
+#ifdef DEBUG
+	printk("hd%c: read: sector %ld, remaining = %ld, buffer=0x%08lx\n",
+		dev+'a', CURRENT->sector, CURRENT->nr_sectors,
+		(unsigned long) CURRENT->buffer+512));
+#endif
+	if (CURRENT->current_nr_sectors <= 0)
+		end_request(1);
+	if (i > 0) {
+		SET_INTR(&read_intr);
+		return;
+	}
+	(void) inb_p(HD_STATUS);
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
+	if (!QUEUE_EMPTY)
+		hd_request();
+	return;
+}
+
+static void write_intr(void)
+{
+	int i;
+	int retries = 100000;
+
+	do {
+		i = (unsigned) inb_p(HD_STATUS);
+		if (i & BUSY_STAT)
+			continue;
+		if (!OK_STATUS(i))
+			break;
+		if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
+			goto ok_to_write;
+	} while (--retries > 0);
+	dump_status("write_intr", i);
+	bad_rw_intr();
+	hd_request();
+	return;
+ok_to_write:
+	CURRENT->sector++;
+	i = --CURRENT->nr_sectors;
+	--CURRENT->current_nr_sectors;
+	CURRENT->buffer += 512;
+	if (!i || (CURRENT->bh && !SUBSECTOR(i)))
+		end_request(1);
+	if (i > 0) {
+		SET_INTR(&write_intr);
+		outsw(HD_DATA,CURRENT->buffer,256);
+		sti();
+	} else {
+#if (HD_DELAY > 0)
+		last_req = read_timer();
+#endif
+		hd_request();
+	}
+	return;
+}
+
+static void recal_intr(void)
+{
+	check_status();
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
+	hd_request();
+}
+
+/*
+ * This is another of the error-routines I don't know what to do with. The
+ * best idea seems to just set reset, and start all over again.
+ */
+static void hd_times_out(void)
+{
+	unsigned int dev;
+
+	DEVICE_INTR = NULL;
+	if (QUEUE_EMPTY)
+		return;
+	disable_irq(HD_IRQ);
+	sti();
+	reset = 1;
+	dev = DEVICE_NR(CURRENT->rq_dev);
+	printk("hd%c: timeout\n", dev+'a');
+	if (++CURRENT->errors >= MAX_ERRORS) {
+#ifdef DEBUG
+		printk("hd%c: too many errors\n", dev+'a');
+#endif
+		end_request(0);
+	}
+	cli();
+	hd_request();
+	enable_irq(HD_IRQ);
+}
+
+int do_special_op (unsigned int dev)
+{
+	if (recalibrate[dev]) {
+		recalibrate[dev] = 0;
+		hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
+		return reset;
+	}
+	if (hd_info[dev].head > 16) {
+		printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
+		end_request(0);
+	}
+	special_op[dev] = 0;
+	return 1;
+}
+
+/*
+ * The driver enables interrupts as much as possible.  In order to do this,
+ * (a) the device-interrupt is disabled before entering hd_request(),
+ * and (b) the timeout-interrupt is disabled before the sti().
+ *
+ * Interrupts are still masked (by default) whenever we are exchanging
+ * data/cmds with a drive, because some drives seem to have very poor
+ * tolerance for latency during I/O. The IDE driver has support to unmask
+ * interrupts for non-broken hardware, so use that driver if required.
+ */
+static void hd_request(void)
+{
+	unsigned int dev, block, nsect, sec, track, head, cyl;
+
+	if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) return;
+	if (DEVICE_INTR)
+		return;
+repeat:
+	timer_active &= ~(1<<HD_TIMER);
+	sti();
+	INIT_REQUEST;
+	if (reset) {
+		cli();
+		reset_hd();
+		return;
+	}
+	dev = MINOR(CURRENT->rq_dev);
+	block = CURRENT->sector;
+	nsect = CURRENT->nr_sectors;
+	if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
+#ifdef DEBUG
+		if (dev >= (NR_HD<<6))
+			printk("hd: bad minor number: device=%s\n",
+			       kdevname(CURRENT->rq_dev));
+		else
+			printk("hd%c: bad access: block=%d, count=%d\n",
+				(MINOR(CURRENT->rq_dev)>>6)+'a', block, nsect);
+#endif
+		end_request(0);
+		goto repeat;
+	}
+	block += hd[dev].start_sect;
+	dev >>= 6;
+	if (special_op[dev]) {
+		if (do_special_op(dev))
+			goto repeat;
+		return;
+	}
+	sec   = block % hd_info[dev].sect + 1;
+	track = block / hd_info[dev].sect;
+	head  = track % hd_info[dev].head;
+	cyl   = track / hd_info[dev].head;
+#ifdef DEBUG
+	printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx\n",
+		dev+'a', (CURRENT->cmd == READ)?"read":"writ",
+		cyl, head, sec, nsect, (unsigned long) CURRENT->buffer);
+#endif
+	if (CURRENT->cmd == READ) {
+		hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
+		if (reset)
+			goto repeat;
+		return;
+	}
+	if (CURRENT->cmd == WRITE) {
+		hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+		if (reset)
+			goto repeat;
+		if (wait_DRQ()) {
+			bad_rw_intr();
+			goto repeat;
+		}
+		outsw(HD_DATA,CURRENT->buffer,256);
+		return;
+	}
+	panic("unknown hd-command");
+}
+
+static void do_hd_request (request_queue_t * q)
+{
+	disable_irq(HD_IRQ);
+	hd_request();
+	enable_irq(HD_IRQ);
+}
+
+static int hd_ioctl(struct inode * inode, struct file * file,
+	unsigned int cmd, unsigned long arg)
+{
+	struct hd_geometry *loc = (struct hd_geometry *) arg;
+	int dev;
+
+	if ((!inode) || !(inode->i_rdev))
+		return -EINVAL;
+	dev = DEVICE_NR(inode->i_rdev);
+	if (dev >= NR_HD)
+		return -EINVAL;
+	switch (cmd) {
+		case HDIO_GETGEO:
+		{
+			struct hd_geometry g; 
+			if (!loc)  return -EINVAL;
+			g.heads = hd_info[dev].head;
+			g.sectors = hd_info[dev].sect;
+			g.cylinders = hd_info[dev].cyl;
+			g.start = hd[MINOR(inode->i_rdev)].start_sect;
+			return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; 
+		}
+
+         	case BLKGETSIZE:   /* Return device size */
+			if (!arg)  return -EINVAL;
+			return put_user(hd[MINOR(inode->i_rdev)].nr_sects, 
+					(long *) arg);
+
+		case BLKRRPART: /* Re-read partition tables */
+			if (!capable(CAP_SYS_ADMIN))
+				return -EACCES;
+			return revalidate_hddisk(inode->i_rdev, 1);
+
+		case BLKROSET:
+		case BLKROGET:
+		case BLKRASET:
+		case BLKRAGET:
+		case BLKFLSBUF:
+		case BLKPG:
+			return blk_ioctl(inode->i_rdev, cmd, arg);
+
+		default:
+			return -EINVAL;
+	}
+}
+
+static int hd_open(struct inode * inode, struct file * filp)
+{
+	int target;
+	target =  DEVICE_NR(inode->i_rdev);
+
+	if (target >= NR_HD)
+		return -ENODEV;
+	while (busy[target])
+		sleep_on(&busy_wait);
+	access_count[target]++;
+	return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static int hd_release(struct inode * inode, struct file * file)
+{
+        int target =  DEVICE_NR(inode->i_rdev);
+	access_count[target]--;
+	return 0;
+}
+
+extern struct block_device_operations hd_fops;
+
+static struct gendisk hd_gendisk = {
+	MAJOR_NR,	/* Major number */	
+	"hd",		/* Major name */
+	6,		/* Bits to shift to get real from partition */
+	1 << 6,		/* Number of partitions per real */
+	hd,		/* hd struct */
+	hd_sizes,	/* block sizes */
+	0,		/* number */
+	NULL,		/* internal use, not presently used */
+	NULL,		/* next */
+	&hd_fops,       /* file operations */
+};
+	
+static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	void (*handler)(void) = DEVICE_INTR;
+
+	DEVICE_INTR = NULL;
+	timer_active &= ~(1<<HD_TIMER);
+	if (!handler)
+		handler = unexpected_hd_interrupt;
+	handler();
+	sti();
+}
+
+static struct block_device_operations hd_fops = {
+	open:		hd_open,
+	release:	hd_release,
+	ioctl:		hd_ioctl,
+};
+
+/*
+ * This is the hard disk IRQ description. The SA_INTERRUPT in sa_flags
+ * means we run the IRQ-handler with interrupts disabled:  this is bad for
+ * interrupt latency, but anything else has led to problems on some
+ * machines.
+ *
+ * We enable interrupts in some of the routines after making sure it's
+ * safe.
+ */
+static void hd_geninit(void)
+{
+	int drive;
+
+	for(drive=0; drive < (MAX_HD << 6); drive++) {
+		hd_blocksizes[drive] = 1024;
+		hd_hardsectsizes[drive] = 512;
+	}
+	blksize_size[MAJOR_NR] = hd_blocksizes;
+	hardsect_size[MAJOR_NR] = hd_hardsectsizes;
+
+#ifdef __i386__
+	if (!NR_HD) {
+		extern struct drive_info drive_info;
+		unsigned char *BIOS = (unsigned char *) &drive_info;
+		int cmos_disks;
+
+		for (drive=0 ; drive<2 ; drive++) {
+			hd_info[drive].cyl = *(unsigned short *) BIOS;
+			hd_info[drive].head = *(2+BIOS);
+			hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
+			hd_info[drive].ctl = *(8+BIOS);
+			hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
+			hd_info[drive].sect = *(14+BIOS);
+#ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp
+			if (hd_info[drive].cyl && NR_HD == drive)
+				NR_HD++;
+#endif
+			BIOS += 16;
+		}
+
+	/*
+		We query CMOS about hard disks : it could be that 
+		we have a SCSI/ESDI/etc controller that is BIOS
+		compatible with ST-506, and thus showing up in our
+		BIOS table, but not register compatible, and therefore
+		not present in CMOS.
+
+		Furthermore, we will assume that our ST-506 drives
+		<if any> are the primary drives in the system, and 
+		the ones reflected as drive 1 or 2.
+
+		The first drive is stored in the high nibble of CMOS
+		byte 0x12, the second in the low nibble.  This will be
+		either a 4 bit drive type or 0xf indicating use byte 0x19 
+		for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
+
+		Needless to say, a non-zero value means we have 
+		an AT controller hard disk for that drive.
+
+		
+	*/
+
+		if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) {
+			if (cmos_disks & 0x0f)
+				NR_HD = 2;
+			else
+				NR_HD = 1;
+		}
+	}
+#endif /* __i386__ */
+#ifdef __arm__
+	if (!NR_HD) {
+		/* We don't know anything about the drive.  This means
+		 * that you *MUST* specify the drive parameters to the
+		 * kernel yourself.
+		 */
+		printk("hd: no drives specified - use hd=cyl,head,sectors"
+			" on kernel command line\n");
+	}
+#endif
+
+	for (drive=0 ; drive < NR_HD ; drive++) {
+		printk ("hd%c: %ldMB, CHS=%d/%d/%d\n", drive+'a',
+			hd[drive<<6].nr_sects / 2048, hd_info[drive].cyl,
+			hd_info[drive].head, hd_info[drive].sect);
+	}
+	if (!NR_HD)
+		return;
+
+	if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
+		printk("hd: unable to get IRQ%d for the hard disk driver\n",
+			HD_IRQ);
+		NR_HD = 0;
+		return;
+	}
+	request_region(HD_DATA, 8, "hd");
+	request_region(HD_CMD, 1, "hd(cmd)");
+
+	hd_gendisk.nr_real = NR_HD;
+
+	for(drive=0; drive < NR_HD; drive++)
+		register_disk(&hd_gendisk, MKDEV(MAJOR_NR,drive<<6), 1<<6,
+			&hd_fops, hd_info[drive].head * hd_info[drive].sect *
+			hd_info[drive].cyl);
+}
+
+int __init hd_init(void)
+{
+	if (devfs_register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
+		printk("hd: unable to get major %d for hard disk\n",MAJOR_NR);
+		return -1;
+	}
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	read_ahead[MAJOR_NR] = 8;		/* 8 sector (4kB) read-ahead */
+	hd_gendisk.next = gendisk_head;
+	gendisk_head = &hd_gendisk;
+	timer_table[HD_TIMER].fn = hd_times_out;
+	hd_geninit();
+	return 0;
+}
+
+#define DEVICE_BUSY busy[target]
+#define USAGE access_count[target]
+#define CAPACITY (hd_info[target].head*hd_info[target].sect*hd_info[target].cyl)
+/* We assume that the BIOS parameters do not change, so the disk capacity
+   will not change */
+#undef MAYBE_REINIT
+#define GENDISK_STRUCT hd_gendisk
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0.  If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+static int revalidate_hddisk(kdev_t dev, int maxusage)
+{
+	int target;
+	struct gendisk * gdev;
+	int max_p;
+	int start;
+	int i;
+	long flags;
+
+	target = DEVICE_NR(dev);
+	gdev = &GENDISK_STRUCT;
+
+	save_flags(flags);
+	cli();
+	if (DEVICE_BUSY || USAGE > maxusage) {
+		restore_flags(flags);
+		return -EBUSY;
+	}
+	DEVICE_BUSY = 1;
+	restore_flags(flags);
+
+	max_p = gdev->max_p;
+	start = target << gdev->minor_shift;
+
+	for (i=max_p - 1; i >=0 ; i--) {
+		int minor = start + i;
+		kdev_t devi = MKDEV(MAJOR_NR, minor);
+		struct super_block *sb = get_super(devi); 
+
+		sync_dev(devi);
+		if (sb)
+			invalidate_inodes(sb);
+		invalidate_buffers(devi);
+		gdev->part[minor].start_sect = 0;
+		gdev->part[minor].nr_sects = 0;
+	}
+
+#ifdef MAYBE_REINIT
+	MAYBE_REINIT;
+#endif
+
+	grok_partitions(gdev, target, 1<<6, CAPACITY);
+
+	DEVICE_BUSY = 0;
+	wake_up(&busy_wait);
+	return 0;
+}
+
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/hpt34x.c linux/drivers/ide/hpt34x.c
--- v2.3.51/linux/drivers/ide/hpt34x.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/hpt34x.c	Wed Mar  8 11:40:25 2000
@@ -0,0 +1,419 @@
+/*
+ * linux/drivers/block/hpt34x.c		Version 0.29	Feb. 10, 2000
+ *
+ * Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ *
+ * 00:12.0 Unknown mass storage controller:
+ * Triones Technologies, Inc.
+ * Unknown device 0003 (rev 01)
+ *
+ * hde: UDMA 2 (0x0000 0x0002) (0x0000 0x0010)
+ * hdf: UDMA 2 (0x0002 0x0012) (0x0010 0x0030)
+ * hde: DMA 2  (0x0000 0x0002) (0x0000 0x0010)
+ * hdf: DMA 2  (0x0002 0x0012) (0x0010 0x0030)
+ * hdg: DMA 1  (0x0012 0x0052) (0x0030 0x0070)
+ * hdh: DMA 1  (0x0052 0x0252) (0x0070 0x00f0)
+ *
+ * ide-pci.c reference
+ *
+ * Since there are two cards that report almost identically,
+ * the only discernable difference is the values reported in pcicmd.
+ * Booting-BIOS card or HPT363 :: pcicmd == 0x07
+ * Non-bootable card or HPT343 :: pcicmd == 0x05
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "ide_modes.h"
+
+#ifndef SPLIT_BYTE
+#define SPLIT_BYTE(B,H,L)	((H)=(B>>4), (L)=(B-((B>>4)<<4)))
+#endif
+
+#define HPT343_DEBUG_DRIVE_INFO		0
+
+#undef DISPLAY_HPT34X_TIMINGS
+
+#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int hpt34x_get_info(char *, char **, off_t, int);
+extern int (*hpt34x_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int hpt34x_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p = buffer;
+	u32 bibma = bmide_dev->resource[4].start;
+	u8  c0 = 0, c1 = 0;
+
+        /*
+         * at that point bibma+0x2 et bibma+0xa are byte registers
+         * to investigate:
+         */
+	c0 = inb_p((unsigned short)bibma + 0x02);
+	c1 = inb_p((unsigned short)bibma + 0x0a);
+
+	p += sprintf(p, "\n                                HPT34X Chipset.\n");
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	p += sprintf(p, "                %sabled                         %sabled\n",
+			(c0&0x80) ? "dis" : " en",
+			(c1&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
+			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+	p += sprintf(p, "UDMA\n");
+	p += sprintf(p, "DMA\n");
+	p += sprintf(p, "PIO\n");
+
+	return p-buffer;	/* => must be less than 4k! */
+}
+#endif  /* defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte hpt34x_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+static void hpt34x_clear_chipset (ide_drive_t *drive)
+{
+	int drive_number	= ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+	unsigned int reg1	= 0, tmp1 = 0;
+	unsigned int reg2	= 0, tmp2 = 0;
+
+	pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, &reg1);
+	pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, &reg2);
+	tmp1 = ((0x00 << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
+	tmp2 = (reg2 & ~(0x11 << drive_number));
+	pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
+	pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
+}
+
+static int hpt34x_tune_chipset (ide_drive_t *drive, byte speed)
+{
+	int			err;
+	byte			hi_speed, lo_speed;
+	int drive_number	= ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+	unsigned int reg1	= 0, tmp1 = 0;
+	unsigned int reg2	= 0, tmp2 = 0;
+
+	SPLIT_BYTE(speed, hi_speed, lo_speed);
+
+	if (hi_speed & 7) {
+		hi_speed = (hi_speed & 4) ? 0x01 : 0x10;
+	} else {
+		lo_speed <<= 5;
+		lo_speed >>= 5;
+	}
+
+	pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, &reg1);
+	pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, &reg2);
+	tmp1 = ((lo_speed << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
+	tmp2 = ((hi_speed << drive_number) | reg2);
+	err = ide_config_drive_speed(drive, speed);
+	pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
+	pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
+
+#if HPT343_DEBUG_DRIVE_INFO
+	printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \
+		" (0x%02x 0x%02x) 0x%04x\n",
+		drive->name, ide_xfer_verbose(speed),
+		drive_number, reg1, tmp1, reg2, tmp2,
+		hi_speed, lo_speed, err);
+#endif /* HPT343_DEBUG_DRIVE_INFO */
+
+	return(err);
+}
+
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS.  Initally for designed for
+ * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc.
+ */
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+	struct hd_driveid *id	= drive->id;
+	byte speed		= 0x00;
+
+	if (drive->media != ide_disk)
+		return ((int) ide_dma_off_quietly);
+
+	hpt34x_clear_chipset(drive);
+
+	if ((id->dma_ultra & 0x0010) && ultra) {
+		speed = XFER_UDMA_2;
+	} else if ((id->dma_ultra & 0x0008) && ultra) {
+		speed = XFER_UDMA_2;
+	} else if ((id->dma_ultra & 0x0004) && ultra) {
+		speed = XFER_UDMA_2;
+	} else if ((id->dma_ultra & 0x0002) && ultra) {
+		speed = XFER_UDMA_1;
+	} else if ((id->dma_ultra & 0x0001) && ultra) {
+		speed = XFER_UDMA_0;
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+	} else if (id->dma_1word & 0x0004) {
+		speed = XFER_SW_DMA_2;
+	} else if (id->dma_1word & 0x0002) {
+		speed = XFER_SW_DMA_1;
+	} else if (id->dma_1word & 0x0001) {
+		speed = XFER_SW_DMA_0;
+        } else {
+		return ((int) ide_dma_off_quietly);
+	}
+
+	(void) hpt34x_tune_chipset(drive, speed);
+
+	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_off :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+	unsigned short xfer_pio	= drive->id->eide_pio_modes;
+
+	byte			timing, speed, pio;
+
+	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+	switch(timing) {
+		case 4: speed = XFER_PIO_4;break;
+		case 3: speed = XFER_PIO_3;break;
+		case 2: speed = XFER_PIO_2;break;
+		case 1: speed = XFER_PIO_1;break;
+		default:
+			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+			break;
+	}
+	(void) hpt34x_tune_chipset(drive, speed);
+}
+
+static void hpt34x_tune_drive (ide_drive_t *drive, byte pio)
+{
+	byte speed;
+
+	switch(pio) {
+		case 4:		speed = XFER_PIO_4;break;
+		case 3:		speed = XFER_PIO_3;break;
+		case 2:		speed = XFER_PIO_2;break;
+		case 1:		speed = XFER_PIO_1;break;
+		default:	speed = XFER_PIO_0;break;
+	}
+	hpt34x_clear_chipset(drive);
+	(void) hpt34x_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_dma_action_t dma_func = ide_dma_on;
+
+	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
+		}
+		dma_func = ide_dma_off_quietly;
+		if (id->field_valid & 4) {
+			if (id->dma_ultra & 0x0007) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive, 1);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if ((id->dma_mword & 0x0007) ||
+			    (id->dma_1word & 0x0007)) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive, 0);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
+			}
+		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+			if (id->eide_dma_time > 150) {
+				goto no_dma_set;
+			}
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive, 0);
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+		config_chipset_for_pio(drive);
+	}
+
+#ifndef CONFIG_HPT34X_AUTODMA
+	if (dma_func == ide_dma_on)
+		dma_func = ide_dma_off;
+#endif /* CONFIG_HPT34X_AUTODMA */
+
+	return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * hpt34x_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ *
+ * This is specific to the HPT343 UDMA bios-less chipset
+ * and HPT345 UDMA bios chipset (stamped HPT363)
+ * by HighPoint|Triones Technologies, Inc.
+ */
+
+int hpt34x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned long dma_base = hwif->dma_base;
+	unsigned int count, reading = 0;
+	byte dma_stat;
+
+	switch (func) {
+		case ide_dma_check:
+			return config_drive_xfer_rate(drive);
+		case ide_dma_read:
+			reading = 1 << 3;
+		case ide_dma_write:
+			if (!(count = ide_build_dmatable(drive, func)))
+				return 1;	/* try PIO instead of DMA */
+			outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
+			reading |= 0x01;
+			outb(reading, dma_base);		/* specify r/w */
+			outb(inb(dma_base+2)|6, dma_base+2);	/* clear INTR & ERROR flags */
+			drive->waiting_for_dma = 1;
+			if (drive->media != ide_disk)
+				return 0;
+			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);	/* issue cmd to drive */
+			OUT_BYTE((reading == 9) ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+			return 0;
+		case ide_dma_end:	/* returns 1 on error, 0 otherwise */
+			drive->waiting_for_dma = 0;
+			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+			dma_stat = inb(dma_base+2);		/* get DMA status */
+			outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+			ide_destroy_dmatable(drive);		/* purge DMA mappings */
+			return (dma_stat & 7) != 4;		/* verify good DMA status */
+		default:
+			break;
+	}
+	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+}
+
+/*
+ * If the BIOS does not set the IO base addaress to XX00, 343 will fail.
+ */
+#define	HPT34X_PCI_INIT_REG		0x80
+
+unsigned int __init pci_init_hpt34x (struct pci_dev *dev, const char *name)
+{
+	int i = 0;
+	unsigned long hpt34xIoBase = dev->resource[4].start;
+	unsigned short cmd;
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+
+	pci_write_config_byte(dev, HPT34X_PCI_INIT_REG, 0x00);
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+
+	if (cmd & PCI_COMMAND_MEMORY) {
+		if (dev->resource[PCI_ROM_RESOURCE].start) {
+			pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+			printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n", dev->resource[PCI_ROM_RESOURCE].start);
+		}
+		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0);
+	} else {
+		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
+	}
+
+	pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
+	dev->resource[0].start = (hpt34xIoBase + 0x20);
+	dev->resource[1].start = (hpt34xIoBase + 0x34);
+	dev->resource[2].start = (hpt34xIoBase + 0x28);
+	dev->resource[3].start = (hpt34xIoBase + 0x3c);
+	for(i=0; i<4; i++)
+		dev->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
+	/*
+	 * Since 20-23 can be assigned and are R/W, we correct them.
+	 */
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dev->resource[0].start);
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, dev->resource[1].start);
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, dev->resource[2].start);
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, dev->resource[3].start);
+	pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+	__restore_flags(flags);	/* local CPU only */
+
+#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS)
+	hpt34x_proc = 1;
+	bmide_dev = dev;
+	hpt34x_display_info = &hpt34x_get_info;
+#endif /* DISPLAY_HPT34X_TIMINGS && CONFIG_PROC_FS */
+
+	return dev->irq;
+}
+
+void __init ide_init_hpt34x (ide_hwif_t *hwif)
+{
+	hwif->tuneproc = &hpt34x_tune_drive;
+	if (hwif->dma_base) {
+		unsigned short pcicmd = 0;
+
+		pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd);
+		hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0;
+		hwif->dmaproc = &hpt34x_dmaproc;
+	} else {
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/hpt366.c linux/drivers/ide/hpt366.c
--- v2.3.51/linux/drivers/ide/hpt366.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/hpt366.c	Wed Mar  8 11:40:25 2000
@@ -0,0 +1,564 @@
+/*
+ * linux/drivers/block/hpt366.c		Version 0.16	Feb. 10, 2000
+ *
+ * Copyright (C) 1999-2000		Andre Hedrick <andre@suse.com>
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * Thanks to HighPoint Technologies for their assistance, and hardware.
+ * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his
+ * donation of an ABit BP6 mainboard, processor, and memory acellerated
+ * development and support.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#undef DISPLAY_HPT366_TIMINGS
+
+#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#endif  /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+const char *bad_ata66_4[] = {
+	"WDC AC310200R",
+	NULL
+};
+
+const char *bad_ata66_3[] = {
+	"WDC AC310200R",
+	NULL
+};
+
+const char *bad_ata33[] = {
+	"Maxtor 92720U8", "Maxtor 92040U6", "Maxtor 91360U4", "Maxtor 91020U3", "Maxtor 90845U3", "Maxtor 90650U2",
+	"Maxtor 91360D8", "Maxtor 91190D7", "Maxtor 91020D6", "Maxtor 90845D5", "Maxtor 90680D4", "Maxtor 90510D3", "Maxtor 90340D2",
+	"Maxtor 91152D8", "Maxtor 91008D7", "Maxtor 90845D6", "Maxtor 90840D6", "Maxtor 90720D5", "Maxtor 90648D5", "Maxtor 90576D4",
+	"Maxtor 90510D4",
+	"Maxtor 90432D3", "Maxtor 90288D2", "Maxtor 90256D2",
+	"Maxtor 91000D8", "Maxtor 90910D8", "Maxtor 90875D7", "Maxtor 90840D7", "Maxtor 90750D6", "Maxtor 90625D5", "Maxtor 90500D4",
+	"Maxtor 91728D8", "Maxtor 91512D7", "Maxtor 91303D6", "Maxtor 91080D5", "Maxtor 90845D4", "Maxtor 90680D4", "Maxtor 90648D3", "Maxtor 90432D2",
+	NULL
+};
+
+struct chipset_bus_clock_list_entry {
+	byte		xfer_speed;
+	unsigned int	chipset_settings;
+};
+
+struct chipset_bus_clock_list_entry forty_base [] = {
+
+	{	XFER_UDMA_4	,	0x900fd943	},
+	{	XFER_UDMA_3	,	0x900ad943	},
+	{	XFER_UDMA_2	,	0x900bd943	},
+	{	XFER_UDMA_1	,	0x9008d943	},
+	{	XFER_UDMA_0	,	0x9008d943	},
+
+	{	XFER_MW_DMA_2	,	0xa008d943	},
+	{	XFER_MW_DMA_1	,	0xa010d955	},
+	{	XFER_MW_DMA_0	,	0xa010d9fc	},
+
+	{	XFER_PIO_4	,	0xc008d963	},
+	{	XFER_PIO_3	,	0xc010d974	},
+	{	XFER_PIO_2	,	0xc010d997	},
+	{	XFER_PIO_1	,	0xc010d9c7	},
+	{	XFER_PIO_0	,	0xc018d9d9	},
+	{	0		,	0x0120d9d9	}
+};
+
+struct chipset_bus_clock_list_entry thirty_three_base [] = {
+
+	{	XFER_UDMA_4	,	0x90c9a731	},
+	{	XFER_UDMA_3	,	0x90cfa731	},
+	{	XFER_UDMA_2	,	0x90caa731	},
+	{	XFER_UDMA_1	,	0x90cba731	},
+	{	XFER_UDMA_0	,	0x90c8a731	},
+
+	{	XFER_MW_DMA_2	,	0xa0c8a731	},
+	{	XFER_MW_DMA_1	,	0xa0c8a732	},	/* 0xa0c8a733 */
+	{	XFER_MW_DMA_0	,	0xa0c8a797	},
+
+	{	XFER_PIO_4	,	0xc0c8a731	},
+	{	XFER_PIO_3	,	0xc0c8a742	},
+	{	XFER_PIO_2	,	0xc0d0a753	},
+	{	XFER_PIO_1	,	0xc0d0a7a3	},	/* 0xc0d0a793 */
+	{	XFER_PIO_0	,	0xc0d0a7aa	},	/* 0xc0d0a7a7 */
+	{	0		,	0x0120a7a7	}
+};
+
+struct chipset_bus_clock_list_entry twenty_five_base [] = {
+
+	{	XFER_UDMA_4	,	0x90c98521	},
+	{	XFER_UDMA_3	,	0x90cf8521	},
+	{	XFER_UDMA_2	,	0x90cf8521	},
+	{	XFER_UDMA_1	,	0x90cb8521	},
+	{	XFER_UDMA_0	,	0x90cb8521	},
+
+	{	XFER_MW_DMA_2	,	0xa0ca8521	},
+	{	XFER_MW_DMA_1	,	0xa0ca8532	},
+	{	XFER_MW_DMA_0	,	0xa0ca8575	},
+
+	{	XFER_PIO_4	,	0xc0ca8521	},
+	{	XFER_PIO_3	,	0xc0ca8532	},
+	{	XFER_PIO_2	,	0xc0ca8542	},
+	{	XFER_PIO_1	,	0xc0d08572	},
+	{	XFER_PIO_0	,	0xc0d08585	},
+	{	0		,	0x01208585	}
+};
+
+#define HPT366_DEBUG_DRIVE_INFO		0
+#define HPT366_ALLOW_ATA66_4		1
+#define HPT366_ALLOW_ATA66_3		1
+
+#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
+static int hpt366_get_info(char *, char **, off_t, int);
+extern int (*hpt366_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+static struct pci_dev *bmide2_dev;
+
+static int hpt366_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p		= buffer;
+	u32 bibma	= bmide_dev->resource[4].start;
+	u32 bibma2 	= bmide2_dev->resource[4].start;
+	u8  c0 = 0, c1 = 0;
+
+        /*
+         * at that point bibma+0x2 et bibma+0xa are byte registers
+         * to investigate:
+         */
+	c0 = inb_p((unsigned short)bibma + 0x02);
+	if (bmide2_dev)
+		c1 = inb_p((unsigned short)bibma2 + 0x02);
+
+	p += sprintf(p, "\n                                HPT366 Chipset.\n");
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	p += sprintf(p, "                %sabled                         %sabled\n",
+			(c0&0x80) ? "dis" : " en",
+			(c1&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
+			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+	p += sprintf(p, "UDMA\n");
+	p += sprintf(p, "DMA\n");
+	p += sprintf(p, "PIO\n");
+
+	return p-buffer;/* => must be less than 4k! */
+}
+#endif  /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte hpt366_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+byte hpt363_shared_irq = 0;
+byte hpt363_shared_pin = 0;
+
+static int check_in_drive_lists (ide_drive_t *drive, const char **list)
+{
+	struct hd_driveid *id = drive->id;
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("check_in_drive_lists(%s, %p)\n", drive->name, list);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+
+	while (*list) {
+		if (!strcmp(*list++,id->model)) {
+#ifdef DEBUG
+			printk("%s: Broken ASIC, BackSpeeding (U)DMA for %s\n", drive->name, id->model);
+#endif /* DEBUG */
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static unsigned int pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
+{
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("pci_bus_clock_list(speed=0x%02x, table=%p)\n", speed, chipset_table);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	for ( ; chipset_table->xfer_speed ; chipset_table++)
+		if (chipset_table->xfer_speed == speed) {
+#if HPT366_DEBUG_DRIVE_INFO
+			printk("pci_bus_clock_list: found match: 0x%08x\n", chipset_table->chipset_settings);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+			return chipset_table->chipset_settings;
+		}
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("pci_bus_clock_list: using default: 0x%08x\n", 0x01208585);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	return 0x01208585;
+}
+
+static int hpt366_tune_chipset (ide_drive_t *drive, byte speed)
+{
+	int			err;
+#if HPT366_DEBUG_DRIVE_INFO
+	int drive_number	= ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	byte regtime		= (drive->select.b.unit & 0x01) ? 0x44 : 0x40;
+	unsigned int reg1	= 0;
+	unsigned int reg2	= 0;
+
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("hpt366_tune_chipset(%s, speed=0x%02x)\n", drive->name, speed);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+
+	pci_read_config_dword(HWIF(drive)->pci_dev, regtime, &reg1);
+	/* detect bus speed by looking at control reg timing: */
+	switch((reg1 >> 8) & 7) {
+		case 5:
+			reg2 = pci_bus_clock_list(speed, forty_base);
+			break;
+		case 9:
+			reg2 = pci_bus_clock_list(speed, twenty_five_base);
+			break;
+		default:
+			printk("hpt366: assuming 33Mhz PCI bus\n");
+		case 7:
+			reg2 = pci_bus_clock_list(speed, thirty_three_base);
+			break;
+	}
+	/*
+	 * Disable on-chip PIO FIFO/buffer (to avoid problems handling I/O errors later)
+	 */
+	if (speed >= XFER_MW_DMA_0) {
+		reg2 = (reg2 & ~0xc0000000) | (reg1 & 0xc0000000);
+	} else {
+		reg2 = (reg2 & ~0x30070000) | (reg1 & 0x30070000);
+	}	
+	reg2 &= ~0x80000000;
+
+	pci_write_config_dword(HWIF(drive)->pci_dev, regtime, reg2);
+	err = ide_config_drive_speed(drive, speed);
+
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("%s: speed=0x%02x(%s), drive%d, old=0x%08x, new=0x%08x, err=0x%04x\n",
+		drive->name, speed, ide_xfer_verbose(speed),
+		drive_number, reg1, reg2, err);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	return(err);
+}
+
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS.  Initally for designed for
+ * HPT366 UDMA chipset by HighPoint|Triones Technologies, Inc.
+ *
+ * check_in_drive_lists(drive, bad_ata66_4)
+ * check_in_drive_lists(drive, bad_ata66_3)
+ * check_in_drive_lists(drive, bad_ata33)
+ *
+ */
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id	= drive->id;
+	byte speed		= 0x00;
+	byte reg51h		= 0;
+	int  rval;
+
+	if ((id->dma_ultra & 0x0010) &&
+	    (!check_in_drive_lists(drive, bad_ata66_4)) &&
+	    (HPT366_ALLOW_ATA66_4) &&
+	    (HWIF(drive)->udma_four)) {
+		speed = XFER_UDMA_4;
+	} else if ((id->dma_ultra & 0x0008) &&
+		   (!check_in_drive_lists(drive, bad_ata66_3)) &&
+		   (HPT366_ALLOW_ATA66_3) &&
+		   (HWIF(drive)->udma_four)) {
+		speed = XFER_UDMA_3;
+	} else if (id->dma_ultra && (!check_in_drive_lists(drive, bad_ata33))) {
+		if (id->dma_ultra & 0x0004) {
+			speed = XFER_UDMA_2;
+		} else if (id->dma_ultra & 0x0002) {
+			speed = XFER_UDMA_1;
+		} else if (id->dma_ultra & 0x0001) {
+			speed = XFER_UDMA_0;
+		}
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+	} else if (id->dma_1word & 0x0004) {
+		speed = XFER_SW_DMA_2;
+	} else if (id->dma_1word & 0x0002) {
+		speed = XFER_SW_DMA_1;
+	} else if (id->dma_1word & 0x0001) {
+		speed = XFER_SW_DMA_0;
+        } else {
+#if HPT366_DEBUG_DRIVE_INFO
+		printk("%s: config_chipset_for_dma: returning 'ide_dma_off_quietly'\n", drive->name);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+		return ((int) ide_dma_off_quietly);
+	}
+
+	pci_read_config_byte(HWIF(drive)->pci_dev, 0x51, &reg51h);
+
+#ifdef CONFIG_HPT366_FIP
+	/*
+	 * Some drives prefer/allow for the method of handling interrupts.
+	 */
+	if (!(reg51h & 0x80))
+		pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, reg51h|0x80);
+#else /* ! CONFIG_HPT366_FIP */
+	/*
+	 * Disable the "fast interrupt" prediction.
+	 * Instead, always wait for the real interrupt from the drive!
+	 */
+	if (reg51h & 0x80)
+		pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, reg51h & ~0x80);
+#endif /* CONFIG_HPT366_FIP */
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("%s: config_chipset_for_dma:  speed=0x%04x\n", drive->name, speed);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	(void) hpt366_tune_chipset(drive, speed);
+
+	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("%s: config_chipset_for_dma: returning %d (%s)\n", drive->name, rval, rval == ide_dma_on ? "dma_on" : "dma_off");
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+	unsigned short xfer_pio	= drive->id->eide_pio_modes;
+	byte			timing, speed, pio;
+
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("%s: config_chipset_for_pio\n", drive->name);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 :
+			   (drive->id->tPIO & 2) ? 0x02 :
+			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+	switch(timing) {
+		case 4: speed = XFER_PIO_4;break;
+		case 3: speed = XFER_PIO_3;break;
+		case 2: speed = XFER_PIO_2;break;
+		case 1: speed = XFER_PIO_1;break;
+		default:
+			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+			break;
+	}
+#if HPT366_DEBUG_DRIVE_INFO
+	printk("%s: config_chipset_for_pio:  speed=0x%04x\n", drive->name, speed);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+	(void) hpt366_tune_chipset(drive, speed);
+}
+
+static void hpt366_tune_drive (ide_drive_t *drive, byte pio)
+{
+	byte speed;
+	switch(pio) {
+		case 4:		speed = XFER_PIO_4;break;
+		case 3:		speed = XFER_PIO_3;break;
+		case 2:		speed = XFER_PIO_2;break;
+		case 1:		speed = XFER_PIO_1;break;
+		default:	speed = XFER_PIO_0;break;
+	}
+	(void) hpt366_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_dma_action_t dma_func = ide_dma_on;
+
+	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
+		}
+		dma_func = ide_dma_off_quietly;
+		if (id->field_valid & 4) {
+			if (id->dma_ultra & 0x001F) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if ((id->dma_mword & 0x0007) ||
+			    (id->dma_1word & 0x0007)) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
+			}
+		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+			if (id->eide_dma_time > 150) {
+				goto no_dma_set;
+			}
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive);
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+
+		config_chipset_for_pio(drive);
+	}
+	return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * hpt366_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ *
+ * This is specific to the HPT366 UDMA bios chipset
+ * by HighPoint|Triones Technologies, Inc.
+ */
+int hpt366_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	byte reg50h = 0;
+
+	switch (func) {
+		case ide_dma_check:
+			return config_drive_xfer_rate(drive);
+		case ide_dma_lostirq:
+			pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, &reg50h);
+			pci_write_config_byte(HWIF(drive)->pci_dev, 0x50, reg50h|0x03);
+			pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, &reg50h);
+			/* ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); */
+		case ide_dma_timeout:
+		default:
+			break;
+	}
+	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name)
+{
+	byte test = 0;
+
+	if (dev->resource[PCI_ROM_RESOURCE].start)
+		pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+
+	pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &test);
+	if (test != 0x08)
+		pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x08);
+
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &test);
+	if (test != 0x78)
+		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78);
+
+	pci_read_config_byte(dev, PCI_MIN_GNT, &test);
+	if (test != 0x08)
+		pci_write_config_byte(dev, PCI_MIN_GNT, 0x08);
+
+	pci_read_config_byte(dev, PCI_MAX_LAT, &test);
+	if (test != 0x08)
+		pci_write_config_byte(dev, PCI_MAX_LAT, 0x08);
+
+#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
+	if (!hpt366_proc) {
+		hpt366_proc = 1;
+		bmide_dev = dev;
+		hpt366_display_info = &hpt366_get_info;
+	}
+	if ((hpt366_proc) && ((dev->devfn - bmide_dev->devfn) == 1)) {
+		bmide2_dev = dev;
+	}
+#endif /* DISPLAY_HPT366_TIMINGS && CONFIG_PROC_FS */
+
+	return dev->irq;
+}
+
+unsigned int __init ata66_hpt366 (ide_hwif_t *hwif)
+{
+	byte ata66 = 0;
+
+	pci_read_config_byte(hwif->pci_dev, 0x5a, &ata66);
+#ifdef DEBUG
+	printk("HPT366: reg5ah=0x%02x ATA-%s Cable Port%d\n",
+		ata66, (ata66 & 0x02) ? "33" : "66",
+		PCI_FUNC(hwif->pci_dev->devfn));
+#endif /* DEBUG */
+	return ((ata66 & 0x02) ? 0 : 1);
+}
+
+void __init ide_init_hpt366 (ide_hwif_t *hwif)
+{
+	hwif->tuneproc = &hpt366_tune_drive;
+	if (hwif->dma_base) {
+		hwif->dmaproc = &hpt366_dmaproc;
+	} else {
+		hwif->autodma = 0;
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+}
+
+void ide_dmacapable_hpt366 (ide_hwif_t *hwif, unsigned long dmabase)
+{
+	byte masterdma = 0, slavedma = 0;
+	byte dma_new = 0, dma_old = inb(dmabase+2);
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+
+	dma_new = dma_old;
+	pci_read_config_byte(hwif->pci_dev, 0x43, &masterdma);
+	pci_read_config_byte(hwif->pci_dev, 0x47, &slavedma);
+
+	if (masterdma & 0x30)	dma_new |= 0x20;
+	if (slavedma & 0x30)	dma_new |= 0x40;
+	if (dma_new != dma_old) outb(dma_new, dmabase+2);
+
+	__restore_flags(flags);	/* local CPU only */
+
+	ide_setup_dma(hwif, dmabase, 8);
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ht6560b.c linux/drivers/ide/ht6560b.c
--- v2.3.51/linux/drivers/ide/ht6560b.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ht6560b.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,342 @@
+/*
+ *  linux/drivers/block/ht6560b.c		Version 0.07	Feb  1, 2000
+ *
+ *  Copyright (C) 1995-2000  Linus Torvalds & author (see below)
+ */
+
+/*
+ *
+ *  Version 0.01        Initial version hacked out of ide.c
+ *
+ *  Version 0.02        Added support for PIO modes, auto-tune
+ *
+ *  Version 0.03        Some cleanups
+ *
+ *  Version 0.05        PIO mode cycle timings auto-tune using bus-speed
+ *
+ *  Version 0.06        Prefetch mode now defaults no OFF. To set
+ *                      prefetch mode OFF/ON use "hdparm -p8/-p9".
+ *                      Unmask irq is disabled when prefetch mode
+ *                      is enabled.
+ *
+ *  Version 0.07        Trying to fix CD-ROM detection problem.
+ *                      "Prefetch" mode bit OFF for ide disks and
+ *                      ON for anything else.
+ *
+ *
+ *  HT-6560B EIDE-controller support
+ *  To activate controller support use kernel parameter "ide0=ht6560b".
+ *  Use hdparm utility to enable PIO mode support.
+ *
+ *  Author:    Mikko Ala-Fossi            <maf@iki.fi>
+ *             Jan Evert van Grootheest   <janevert@iae.nl>
+ *
+ *  Try:  http://www.maf.iki.fi/~maf/ht6560b/
+ */
+
+#define HT6560B_VERSION "v0.07"
+
+#undef REALLY_SLOW_IO		/* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/* #define DEBUG */  /* remove comments for DEBUG messages */
+
+/*
+ * The special i/o-port that HT-6560B uses to configuration:
+ *    bit0 (0x01): "1" selects secondary interface
+ *    bit2 (0x04): "1" enables FIFO function
+ *    bit5 (0x20): "1" enables prefetched data read function  (???)
+ *
+ * The special i/o-port that HT-6560A uses to configuration:
+ *    bit0 (0x01): "1" selects secondary interface
+ *    bit1 (0x02): "1" enables prefetched data read function
+ *    bit2 (0x04): "0" enables multi-master system	      (?)
+ *    bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time	      (?)
+ */
+#define HT_CONFIG_PORT	  0x3e6
+#define HT_CONFIG(drivea) (byte)(((drivea)->drive_data & 0xff00) >> 8)
+/*
+ * FIFO + PREFETCH (both a/b-model)
+ */
+#define HT_CONFIG_DEFAULT 0x1c /* no prefetch */
+/* #define HT_CONFIG_DEFAULT 0x3c */ /* with prefetch */
+#define HT_SECONDARY_IF	  0x01
+#define HT_PREFETCH_MODE  0x20
+
+/*
+ * ht6560b Timing values:
+ *
+ * I reviewed some assembler source listings of htide drivers and found
+ * out how they setup those cycle time interfacing values, as they at Holtek
+ * call them. IDESETUP.COM that is supplied with the drivers figures out
+ * optimal values and fetches those values to drivers. I found out that
+ * they use IDE_SELECT_REG to fetch timings to the ide board right after
+ * interface switching. After that it was quite easy to add code to
+ * ht6560b.c.
+ *
+ * IDESETUP.COM gave me values 0x24, 0x45, 0xaa, 0xff that worked fine
+ * for hda and hdc. But hdb needed higher values to work, so I guess
+ * that sometimes it is necessary to give higher value than IDESETUP
+ * gives.   [see cmd640.c for an extreme example of this. -ml]
+ *
+ * Perhaps I should explain something about these timing values:
+ * The higher nibble of value is the Recovery Time  (rt) and the lower nibble
+ * of the value is the Active Time  (at). Minimum value 2 is the fastest and
+ * the maximum value 15 is the slowest. Default values should be 15 for both.
+ * So 0x24 means 2 for rt and 4 for at. Each of the drives should have
+ * both values, and IDESETUP gives automatically rt=15 st=15 for CDROMs or
+ * similar. If value is too small there will be all sorts of failures.
+ *
+ * Timing byte consists of
+ *	High nibble:  Recovery Cycle Time  (rt)
+ *	     The valid values range from 2 to 15. The default is 15.
+ *
+ *	Low nibble:   Active Cycle Time	   (at)
+ *	     The valid values range from 2 to 15. The default is 15.
+ *
+ * You can obtain optimized timing values by running Holtek IDESETUP.COM
+ * for DOS. DOS drivers get their timing values from command line, where
+ * the first value is the Recovery Time and the second value is the
+ * Active Time for each drive. Smaller value gives higher speed.
+ * In case of failures you should probably fall back to a higher value.
+ */
+#define HT_TIMING(drivea) (byte)((drivea)->drive_data & 0x00ff)
+#define HT_TIMING_DEFAULT 0xff
+
+/*
+ * This routine handles interface switching for the peculiar hardware design
+ * on the F.G.I./Holtek HT-6560B VLB IDE interface.
+ * The HT-6560B can only enable one IDE port at a time, and requires a
+ * silly sequence (below) whenever we switch between primary and secondary.
+ */
+
+/*
+ * This routine is invoked from ide.c to prepare for access to a given drive.
+ */
+static void ht6560b_selectproc (ide_drive_t *drive)
+{
+	unsigned long flags;
+	static byte current_select = 0;
+	static byte current_timing = 0;
+	byte select, timing;
+	
+	__save_flags (flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	
+	select = HT_CONFIG(drive);
+	timing = HT_TIMING(drive);
+	
+	if (select != current_select || timing != current_timing) {
+		current_select = select;
+		current_timing = timing;
+		if (drive->media != ide_disk || !drive->present)
+			select |= HT_PREFETCH_MODE;
+		(void) inb(HT_CONFIG_PORT);
+		(void) inb(HT_CONFIG_PORT);
+		(void) inb(HT_CONFIG_PORT);
+		(void) inb(HT_CONFIG_PORT);
+		outb(select, HT_CONFIG_PORT);
+		/*
+		 * Set timing for this drive:
+		 */
+		outb(timing, IDE_SELECT_REG);
+		(void) inb(IDE_STATUS_REG);
+#ifdef DEBUG
+		printk("ht6560b: %s: select=%#x timing=%#x\n", drive->name, select, timing);
+#endif
+	}
+	__restore_flags (flags);	/* local CPU only */
+}
+
+/*
+ * Autodetection and initialization of ht6560b
+ */
+static int __init try_to_init_ht6560b(void)
+{
+	byte orig_value;
+	int i;
+	
+	/* Autodetect ht6560b */
+	if ((orig_value=inb(HT_CONFIG_PORT)) == 0xff)
+		return 0;
+	
+	for (i=3;i>0;i--) {
+		outb(0x00, HT_CONFIG_PORT);
+		if (!( (~inb(HT_CONFIG_PORT)) & 0x3f )) {
+			outb(orig_value, HT_CONFIG_PORT);
+			return 0;
+		}
+	}
+	outb(0x00, HT_CONFIG_PORT);
+	if ((~inb(HT_CONFIG_PORT))& 0x3f) {
+		outb(orig_value, HT_CONFIG_PORT);
+		return 0;
+	}
+	/*
+	 * Ht6560b autodetected
+	 */
+	outb(HT_CONFIG_DEFAULT, HT_CONFIG_PORT);
+	outb(HT_TIMING_DEFAULT, 0x1f6);  /* IDE_SELECT_REG */
+	(void) inb(0x1f7);               /* IDE_STATUS_REG */
+	
+	printk("\nht6560b " HT6560B_VERSION
+	       ": chipset detected and initialized"
+#ifdef DEBUG
+	       " with debug enabled"
+#endif
+		);
+	return 1;
+}
+
+static byte ht_pio2timings(ide_drive_t *drive, byte pio)
+{
+	int bus_speed, active_time, recovery_time;
+	int active_cycles, recovery_cycles;
+	ide_pio_data_t d;
+	
+        if (pio) {
+		pio = ide_get_best_pio_mode(drive, pio, 5, &d);
+		
+		/*
+		 *  Just like opti621.c we try to calculate the
+		 *  actual cycle time for recovery and activity
+		 *  according system bus speed.
+		 */
+		bus_speed = ide_system_bus_speed();
+		active_time = ide_pio_timings[pio].active_time;
+		recovery_time = d.cycle_time 
+			- active_time
+			- ide_pio_timings[pio].setup_time;
+		/*
+		 *  Cycle times should be Vesa bus cycles
+		 */
+		active_cycles   = (active_time   * bus_speed + 999) / 1000;
+		recovery_cycles = (recovery_time * bus_speed + 999) / 1000;
+		/*
+		 *  Upper and lower limits
+		 */
+		if (active_cycles   < 2)  active_cycles   = 2;
+		if (recovery_cycles < 2)  recovery_cycles = 2;
+		if (active_cycles   > 15) active_cycles   = 15;
+		if (recovery_cycles > 15) recovery_cycles = 0;  /* 0==16 */
+		
+#ifdef DEBUG
+		printk("ht6560b: drive %s setting pio=%d recovery=%d (%dns) active=%d (%dns)\n", drive->name, pio, recovery_cycles, recovery_time, active_cycles, active_time);
+#endif
+		
+		return (byte)((recovery_cycles << 4) | active_cycles);
+	} else {
+		
+#ifdef DEBUG
+		printk("ht6560b: drive %s setting pio=0\n", drive->name);
+#endif
+		
+		return HT_TIMING_DEFAULT;    /* default setting */
+	}
+}
+
+/*
+ *  Enable/Disable so called prefetch mode
+ */
+static void ht_set_prefetch(ide_drive_t *drive, byte state)
+{
+	unsigned long flags;
+	int t = HT_PREFETCH_MODE << 8;
+	
+	save_flags (flags);	/* all CPUs */
+	cli();		        /* all CPUs */
+	
+	/*
+	 *  Prefetch mode and unmask irq seems to conflict
+	 */
+	if (state) {
+		drive->drive_data |= t;   /* enable prefetch mode */
+		drive->no_unmask = 1;
+		drive->unmask = 0;
+	} else {
+		drive->drive_data &= ~t;  /* disable prefetch mode */
+		drive->no_unmask = 0;
+	}
+	
+	restore_flags (flags);	/* all CPUs */
+	
+#ifdef DEBUG
+	printk("ht6560b: drive %s prefetch mode %sabled\n", drive->name, (state ? "en" : "dis"));
+#endif
+}
+
+static void tune_ht6560b (ide_drive_t *drive, byte pio)
+{
+	unsigned long flags;
+	byte timing;
+	
+	switch (pio) {
+	case 8:         /* set prefetch off */
+	case 9:         /* set prefetch on */
+		ht_set_prefetch(drive, pio & 1);
+		return;
+	}
+	
+	timing = ht_pio2timings(drive, pio);
+	
+	save_flags (flags);	/* all CPUs */
+	cli();		        /* all CPUs */
+	
+	drive->drive_data &= 0xff00;
+	drive->drive_data |= timing;
+	
+	restore_flags (flags);	/* all CPUs */
+	
+#ifdef DEBUG
+	printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing);
+#endif
+}
+
+void __init init_ht6560b (void)
+{
+	int t;
+	
+	if (check_region(HT_CONFIG_PORT,1)) {
+		printk(KERN_ERR "ht6560b: PORT %#x ALREADY IN USE\n", HT_CONFIG_PORT);
+	} else {
+		if (try_to_init_ht6560b()) {
+			request_region(HT_CONFIG_PORT, 1, ide_hwifs[0].name);
+			ide_hwifs[0].chipset = ide_ht6560b;
+			ide_hwifs[1].chipset = ide_ht6560b;
+			ide_hwifs[0].selectproc = &ht6560b_selectproc;
+			ide_hwifs[1].selectproc = &ht6560b_selectproc;
+			ide_hwifs[0].tuneproc = &tune_ht6560b;
+			ide_hwifs[1].tuneproc = &tune_ht6560b;
+			ide_hwifs[0].serialized = 1;  /* is this needed? */
+			ide_hwifs[1].serialized = 1;  /* is this needed? */
+			ide_hwifs[0].mate = &ide_hwifs[1];
+			ide_hwifs[1].mate = &ide_hwifs[0];
+			ide_hwifs[1].channel = 1;
+			
+			/*
+			 * Setting default configurations for drives
+			 */
+			t = (HT_CONFIG_DEFAULT << 8);
+			t |= HT_TIMING_DEFAULT;
+			ide_hwifs[0].drives[0].drive_data = t;
+			ide_hwifs[0].drives[1].drive_data = t;
+			t |= (HT_SECONDARY_IF << 8);
+			ide_hwifs[1].drives[0].drive_data = t;
+			ide_hwifs[1].drives[1].drive_data = t;
+		} else
+			printk(KERN_ERR "ht6560b: not found\n");
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/icside.c linux/drivers/ide/icside.c
--- v2.3.51/linux/drivers/ide/icside.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/icside.c	Thu Feb 17 23:34:46 2000
@@ -0,0 +1,643 @@
+/*
+ * linux/drivers/block/icside.c
+ *
+ * Copyright (c) 1996,1997 Russell King.
+ *
+ * Changelog:
+ *  08-Jun-1996	RMK	Created
+ *  12-Sep-1997	RMK	Added interrupt enable/disable
+ *  17-Apr-1999	RMK	Added support for V6 EASI
+ *  22-May-1999	RMK	Added support for V6 DMA
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * Maximum number of interfaces per card
+ */
+#define MAX_IFS	2
+
+#define ICS_IDENT_OFFSET		0x8a0
+
+#define ICS_ARCIN_V5_INTRSTAT		0x000
+#define ICS_ARCIN_V5_INTROFFSET		0x001
+#define ICS_ARCIN_V5_IDEOFFSET		0xa00
+#define ICS_ARCIN_V5_IDEALTOFFSET	0xae0
+#define ICS_ARCIN_V5_IDESTEPPING	4
+
+#define ICS_ARCIN_V6_IDEOFFSET_1	0x800
+#define ICS_ARCIN_V6_INTROFFSET_1	0x880
+#define ICS_ARCIN_V6_INTRSTAT_1		0x8a4
+#define ICS_ARCIN_V6_IDEALTOFFSET_1	0x8e0
+#define ICS_ARCIN_V6_IDEOFFSET_2	0xc00
+#define ICS_ARCIN_V6_INTROFFSET_2	0xc80
+#define ICS_ARCIN_V6_INTRSTAT_2		0xca4
+#define ICS_ARCIN_V6_IDEALTOFFSET_2	0xce0
+#define ICS_ARCIN_V6_IDESTEPPING	4
+
+struct cardinfo {
+	unsigned int dataoffset;
+	unsigned int ctrloffset;
+	unsigned int stepping;
+};
+
+static struct cardinfo icside_cardinfo_v5 = {
+	ICS_ARCIN_V5_IDEOFFSET,
+	ICS_ARCIN_V5_IDEALTOFFSET,
+	ICS_ARCIN_V5_IDESTEPPING
+};
+
+static struct cardinfo icside_cardinfo_v6_1 = {
+	ICS_ARCIN_V6_IDEOFFSET_1,
+	ICS_ARCIN_V6_IDEALTOFFSET_1,
+	ICS_ARCIN_V6_IDESTEPPING
+};
+
+static struct cardinfo icside_cardinfo_v6_2 = {
+	ICS_ARCIN_V6_IDEOFFSET_2,
+	ICS_ARCIN_V6_IDEALTOFFSET_2,
+	ICS_ARCIN_V6_IDESTEPPING
+};
+
+static const card_ids icside_cids[] = {
+	{ MANU_ICS,  PROD_ICS_IDE  },
+	{ MANU_ICS2, PROD_ICS2_IDE },
+	{ 0xffff, 0xffff }
+};
+
+typedef enum {
+	ics_if_unknown,
+	ics_if_arcin_v5,
+	ics_if_arcin_v6
+} iftype_t;
+
+/* ---------------- Version 5 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int memc_port = (unsigned int)ec->irq_data;
+	outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int memc_port = (unsigned int)ec->irq_data;
+	inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v5 = {
+	icside_irqenable_arcin_v5,
+	icside_irqdisable_arcin_v5,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+
+/* ---------------- Version 6 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: icside_irqprobe(struct expansion_card *ec)
+ * Purpose  : detect an active interrupt from card
+ */
+static int icside_irqpending_arcin_v6(struct expansion_card *ec)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+	return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
+	       inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v6 = {
+	icside_irqenable_arcin_v6,
+	icside_irqdisable_arcin_v6,
+	icside_irqpending_arcin_v6,
+	NULL,
+	NULL,
+	NULL
+};
+
+/* Prototype: icside_identifyif (struct expansion_card *ec)
+ * Purpose  : identify IDE interface type
+ * Notes    : checks the description string
+ */
+static iftype_t icside_identifyif (struct expansion_card *ec)
+{
+	unsigned int addr;
+	iftype_t iftype;
+	int id = 0;
+
+	iftype = ics_if_unknown;
+
+	addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
+
+	id = inb (addr) & 1;
+	id |= (inb (addr + 1) & 1) << 1;
+	id |= (inb (addr + 2) & 1) << 2;
+	id |= (inb (addr + 3) & 1) << 3;
+
+	switch (id) {
+	case 0: /* A3IN */
+		printk("icside: A3IN unsupported\n");
+		break;
+
+	case 1: /* A3USER */
+		printk("icside: A3USER unsupported\n");
+		break;
+
+	case 3:	/* ARCIN V6 */
+		printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
+		iftype = ics_if_arcin_v6;
+		break;
+
+	case 15:/* ARCIN V5 (no id) */
+		printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
+		iftype = ics_if_arcin_v5;
+		break;
+
+	default:/* we don't know - complain very loudly */
+		printk("icside: ***********************************\n");
+		printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
+		printk("icside: ***********************************\n");
+		printk("icside: please report this to linux@arm.linux.org.uk\n");
+		printk("icside: defaulting to ARCIN V5\n");
+		iftype = ics_if_arcin_v5;
+		break;
+	}
+
+	return iftype;
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+/*
+ * SG-DMA support.
+ *
+ * Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers.
+ * There is only one DMA controller per card, which means that only
+ * one drive can be accessed at one time.  NOTE! We do not enforce that
+ * here, but we rely on the main IDE driver spotting that both
+ * interfaces use the same IRQ, which should guarantee this.
+ */
+#define TABLE_SIZE 2048
+
+static int
+icside_build_dmatable(ide_drive_t *drive, int reading)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	struct buffer_head *bh = rq->bh;
+	unsigned long addr, size;
+	unsigned char *virt_addr;
+	unsigned int count = 0;
+	dmasg_t *sg = (dmasg_t *)HWIF(drive)->dmatable_cpu;
+
+	do {
+		if (bh == NULL) {
+			/* paging requests have (rq->bh == NULL) */
+			virt_addr = rq->buffer;
+			addr = virt_to_bus (virt_addr);
+			size = rq->nr_sectors << 9;
+		} else {
+			/* group sequential buffers into one large buffer */
+			virt_addr = bh->b_data;
+			addr = virt_to_bus (virt_addr);
+			size = bh->b_size;
+			while ((bh = bh->b_reqnext) != NULL) {
+				if ((addr + size) != virt_to_bus (bh->b_data))
+					break;
+				size += bh->b_size;
+			}
+		}
+
+		if (addr & 3) {
+			printk("%s: misaligned DMA buffer\n", drive->name);
+			return 0;
+		}
+
+		if (size) {
+			if (reading)
+				dma_cache_inv((unsigned int)virt_addr, size);
+			else
+				dma_cache_wback((unsigned int)virt_addr, size);
+		}
+
+		sg[count].address = addr;
+		sg[count].length = size;
+		if (++count >= (TABLE_SIZE / sizeof(dmasg_t))) {
+			printk("%s: DMA table too small\n", drive->name);
+			return 0;
+		}
+	} while (bh != NULL);
+
+	if (!count)
+		printk("%s: empty DMA table?\n", drive->name);
+
+	return count;
+}
+
+static int
+icside_config_if(ide_drive_t *drive, int xfer_mode)
+{
+	int func = ide_dma_off;
+
+	switch (xfer_mode) {
+	case XFER_MW_DMA_2:
+		/*
+		 * The cycle time is limited to 250ns by the r/w
+		 * pulse width (90ns), however we should still
+		 * have a maximum burst transfer rate of 8MB/s.
+		 */
+		drive->drive_data = 250;
+		break;
+
+	case XFER_MW_DMA_1:
+		drive->drive_data = 250;
+		break;
+
+	case XFER_MW_DMA_0:
+		drive->drive_data = 480;
+		break;
+
+	default:
+		drive->drive_data = 0;
+		break;
+	}
+
+	if (drive->drive_data &&
+	    ide_config_drive_speed(drive, (byte) xfer_mode) == 0)
+		func = ide_dma_on;
+	else
+		drive->drive_data = 480;
+
+	printk("%s: %s selected (peak %dMB/s)\n", drive->name,
+		ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
+
+	return func;
+}
+
+static int
+icside_dma_check(ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_hwif_t *hwif = HWIF(drive);
+	int autodma = hwif->autodma;
+	int xfer_mode = XFER_PIO_2;
+	int func = ide_dma_off_quietly;
+
+	if (!id || !(id->capability & 1) || !autodma)
+		goto out;
+
+	/*
+	 * Consult the list of known "bad" drives
+	 */
+	if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+		func = ide_dma_off;
+		goto out;
+	}
+
+	/*
+	 * Enable DMA on any drive that has multiword DMA
+	 */
+	if (id->field_valid & 2) {
+		if (id->dma_mword & 4) {
+			xfer_mode = XFER_MW_DMA_2;
+			func = ide_dma_on;
+		} else if (id->dma_mword & 2) {
+			xfer_mode = XFER_MW_DMA_1;
+			func = ide_dma_on;
+		} else if (id->dma_mword & 1) {
+			xfer_mode = XFER_MW_DMA_0;
+			func = ide_dma_on;
+		}
+		goto out;
+	}
+
+	/*
+	 * Consult the list of known "good" drives
+	 */
+	if (ide_dmaproc(ide_dma_good_drive, drive)) {
+		if (id->eide_dma_time > 150)
+			goto out;
+		xfer_mode = XFER_MW_DMA_1;
+		func = ide_dma_on;
+	}
+
+out:
+	func = icside_config_if(drive, xfer_mode);
+
+	return hwif->dmaproc(func, drive);
+}
+
+static int
+icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	int count, reading = 0;
+
+	switch (func) {
+	case ide_dma_check:
+		return icside_dma_check(drive);
+
+	case ide_dma_read:
+		reading = 1;
+	case ide_dma_write:
+		count = icside_build_dmatable(drive, reading);
+		if (!count)
+			return 1;
+		disable_dma(hwif->hw.dma);
+
+		/* Route the DMA signals to
+		 * to the correct interface.
+		 */
+		outb(hwif->select_data, hwif->config_data);
+
+		/* Select the correct timing
+		 * for this drive
+		 */
+		set_dma_speed(hwif->hw.dma, drive->drive_data);
+
+		set_dma_sg(hwif->hw.dma, (dmasg_t *)hwif->dmatable_cpu, count);
+		set_dma_mode(hwif->hw.dma, reading ? DMA_MODE_READ
+			     : DMA_MODE_WRITE);
+
+		drive->waiting_for_dma = 1;
+		if (drive->media != ide_disk)
+			return 0;
+
+		ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
+		OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA,
+			 IDE_COMMAND_REG);
+
+	case ide_dma_begin:
+		enable_dma(hwif->hw.dma);
+		return 0;
+
+	case ide_dma_end:
+		drive->waiting_for_dma = 0;
+		disable_dma(hwif->hw.dma);
+		return get_dma_residue(hwif->hw.dma) != 0;
+
+	case ide_dma_test_irq:
+		return inb((unsigned long)hwif->hw.priv) & 1;
+
+	default:
+		return ide_dmaproc(func, drive);
+	}
+}
+
+static unsigned long
+icside_alloc_dmatable(void)
+{
+	static unsigned long dmatable;
+	static unsigned int leftover;
+	unsigned long table;
+
+	if (leftover < TABLE_SIZE) {
+#if PAGE_SIZE == TABLE_SIZE * 2
+		dmatable = __get_free_pages(GFP_KERNEL, 1);
+		leftover = PAGE_SIZE;
+#else
+		dmatable = kmalloc(TABLE_SIZE, GFP_KERNEL);
+		leftover = TABLE_SIZE;
+#endif
+	}
+
+	table = dmatable;
+	if (table) {
+		dmatable += TABLE_SIZE;
+		leftover -= TABLE_SIZE;
+	}
+
+	return table;
+}
+
+static int
+icside_setup_dma(ide_hwif_t *hwif, int autodma)
+{
+	unsigned long table = icside_alloc_dmatable();
+
+	printk("    %s: SG-DMA", hwif->name);
+
+	if (!table)
+		printk(" -- ERROR, unable to allocate DMA table\n");
+	else {
+		hwif->dmatable_cpu = (void *)table;
+		hwif->dmaproc = icside_dmaproc;
+		hwif->autodma = autodma;
+
+		printk(" capable%s\n", autodma ?
+			", auto-enable" : "");
+	}
+
+	return hwif->dmatable_cpu != NULL;
+}
+#endif
+
+static ide_hwif_t *
+icside_find_hwif(unsigned long dataport)
+{
+	ide_hwif_t *hwif;
+	int index;
+
+	for (index = 0; index < MAX_HWIFS; ++index) {
+		hwif = &ide_hwifs[index];
+		if (hwif->hw.io_ports[IDE_DATA_OFFSET] == (ide_ioreg_t)dataport)
+			goto found;
+	}
+
+	for (index = 0; index < MAX_HWIFS; ++index) {
+		hwif = &ide_hwifs[index];
+		if (!hwif->hw.io_ports[IDE_DATA_OFFSET])
+			goto found;
+	}
+
+	return NULL;
+found:
+	return hwif;
+}
+
+static ide_hwif_t *
+icside_setup(unsigned long base, struct cardinfo *info, int irq)
+{
+	unsigned long port = base + info->dataoffset;
+	ide_hwif_t *hwif;
+
+	hwif = icside_find_hwif(base);
+	if (hwif) {
+		int i;
+
+		memset(&hwif->hw, 0, sizeof(hw_regs_t));
+
+		for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+			hwif->hw.io_ports[i] = (ide_ioreg_t)port;
+			port += 1 << info->stepping;
+		}
+		hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset;
+		hwif->hw.irq  = irq;
+		hwif->hw.dma  = NO_DMA;
+		hwif->noprobe = 0;
+		hwif->chipset = ide_acorn;
+	}
+
+	return hwif;
+}
+
+static int icside_register_v5(struct expansion_card *ec, int autodma)
+{
+	unsigned long slot_port;
+	ide_hwif_t *hwif;
+
+	slot_port = ecard_address(ec, ECARD_MEMC, 0);
+
+	ec->irqaddr  = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT);
+	ec->irqmask  = 1;
+	ec->irq_data = (void *)slot_port;
+	ec->ops      = (expansioncard_ops_t *)&icside_ops_arcin_v5;
+
+	/*
+	 * Be on the safe side - disable interrupts
+	 */
+	inb(slot_port + ICS_ARCIN_V5_INTROFFSET);
+
+	hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq);
+
+	return hwif ? 0 : -1;
+}
+
+static int icside_register_v6(struct expansion_card *ec, int autodma)
+{
+	unsigned long slot_port, port;
+	ide_hwif_t *hwif, *mate;
+	int sel = 0;
+
+	slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+	port      = ecard_address(ec, ECARD_EASI, ECARD_FAST);
+
+	if (port == 0)
+		port = slot_port;
+	else
+		sel = 1 << 5;
+
+	outb(sel, slot_port);
+
+	ec->irq_data = (void *)port;
+	ec->ops      = (expansioncard_ops_t *)&icside_ops_arcin_v6;
+
+	/*
+	 * Be on the safe side - disable interrupts
+	 */
+	inb(port + ICS_ARCIN_V6_INTROFFSET_1);
+	inb(port + ICS_ARCIN_V6_INTROFFSET_2);
+
+	hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq);
+	mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+	if (ec->dma != NO_DMA) {
+		if (request_dma(ec->dma, hwif->name))
+			goto no_dma;
+
+		if (hwif) {
+			hwif->config_data = slot_port;
+			hwif->select_data = sel;
+			hwif->hw.dma  = ec->dma;
+			hwif->hw.priv = (void *)
+					(port + ICS_ARCIN_V6_INTRSTAT_1);
+			hwif->channel = 0;
+			icside_setup_dma(hwif, autodma);
+		}
+		if (mate) {
+			mate->config_data = slot_port;
+			mate->select_data = sel | 1;
+			mate->hw.dma  = ec->dma;
+			mate->hw.priv = (void *)
+					(port + ICS_ARCIN_V6_INTRSTAT_2);
+			mate->channel = 1;
+			icside_setup_dma(mate, autodma);
+		}
+	}
+#endif
+
+no_dma:
+	return hwif || mate ? 0 : -1;
+}
+
+int icside_init(void)
+{
+	int autodma = 0;
+
+#ifdef CONFIG_IDEDMA_ICS_AUTO
+	autodma = 1;
+#endif
+
+	ecard_startfind ();
+
+	do {
+		struct expansion_card *ec;
+		int result;
+
+		ec = ecard_find(0, icside_cids);
+		if (ec == NULL)
+			break;
+
+		ecard_claim(ec);
+
+		switch (icside_identifyif(ec)) {
+		case ics_if_arcin_v5:
+			result = icside_register_v5(ec, autodma);
+			break;
+
+		case ics_if_arcin_v6:
+			result = icside_register_v6(ec, autodma);
+			break;
+
+		default:
+			result = -1;
+			break;
+		}
+
+		if (result)
+			ecard_release(ec);
+	} while (1);
+
+	return 0;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c
--- v2.3.51/linux/drivers/ide/ide-cd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-cd.c	Fri Mar  3 12:59:50 2000
@@ -0,0 +1,2729 @@
+/*
+ * linux/drivers/block/ide-cd.c
+ * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
+ * Copyright (C) 1996-1998  Erik Andersen <andersee@debian.org>
+ * Copyright (C) 1998, 1999 Jens Axboe <axboe@image.dk>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * ATAPI CD-ROM driver.  To be used with ide.c.
+ * See Documentation/cdrom/ide-cd for usage information.
+ *
+ * Suggestions are welcome. Patches that work are more welcome though. ;-)
+ * For those wishing to work on this driver, please be sure you download
+ * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI 
+ * (SFF-8020i rev 2.6) standards. These documents can be obtained by 
+ * anonymous ftp from:
+ * ftp://fission.dt.wdc.com/pub/standards/SFF/specs/INF-8020.PDF
+ * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r01.pdf
+ *
+ * Drives that deviate from these standards will be accomodated as much
+ * as possible via compile time or command-line options.  Since I only have
+ * a few drives, you generally need to send me patches...
+ *
+ * ----------------------------------
+ * TO DO LIST:
+ * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on
+ *   boot
+ *
+ * ----------------------------------
+ * 1.00  Oct 31, 1994 -- Initial version.
+ * 1.01  Nov  2, 1994 -- Fixed problem with starting request in
+ *                       cdrom_check_status.
+ * 1.03  Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
+ * (from mlord)       -- minor changes to cdrom_setup()
+ *                    -- renamed ide_dev_s to ide_drive_t, enable irq on command
+ * 2.00  Nov 27, 1994 -- Generalize packet command interface;
+ *                       add audio ioctls.
+ * 2.01  Dec  3, 1994 -- Rework packet command interface to handle devices
+ *                       which send an interrupt when ready for a command.
+ * 2.02  Dec 11, 1994 -- Cache the TOC in the driver.
+ *                       Don't use SCMD_PLAYAUDIO_TI; it's not included
+ *                       in the current version of ATAPI.
+ *                       Try to use LBA instead of track or MSF addressing
+ *                       when possible.
+ *                       Don't wait for READY_STAT.
+ * 2.03  Jan 10, 1995 -- Rewrite block read routines to handle block sizes
+ *                       other than 2k and to move multiple sectors in a
+ *                       single transaction.
+ * 2.04  Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives.
+ *                       Thanks to Nick Saw <cwsaw@pts7.pts.mot.com> for
+ *                       help in figuring this out.  Ditto for Acer and
+ *                       Aztech drives, which seem to have the same problem.
+ * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
+ * 2.05  Jun  8, 1995 -- Don't attempt to retry after an illegal request
+ *                        or data protect error.
+ *                       Use HWIF and DEV_HWIF macros as in ide.c.
+ *                       Always try to do a request_sense after
+ *                        a failed command.
+ *                       Include an option to give textual descriptions
+ *                        of ATAPI errors.
+ *                       Fix a bug in handling the sector cache which
+ *                        showed up if the drive returned data in 512 byte
+ *                        blocks (like Pioneer drives).  Thanks to
+ *                        Richard Hirst <srh@gpt.co.uk> for diagnosing this.
+ *                       Properly supply the page number field in the
+ *                        MODE_SELECT command.
+ *                       PLAYAUDIO12 is broken on the Aztech; work around it.
+ * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
+ *                       (my apologies to Scott, but now ide-cd.c is independent)
+ * 3.00  Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl.
+ *                       Implement CDROMREADAUDIO ioctl (UNTESTED).
+ *                       Use input_ide_data() and output_ide_data().
+ *                       Add door locking.
+ *                       Fix usage count leak in cdrom_open, which happened
+ *                        when a read-write mount was attempted.
+ *                       Try to load the disk on open.
+ *                       Implement CDROMEJECT_SW ioctl (off by default).
+ *                       Read total cdrom capacity during open.
+ *                       Rearrange logic in cdrom_decode_status.  Issue
+ *                        request sense commands for failed packet commands
+ *                        from here instead of from cdrom_queue_packet_command.
+ *                        Fix a race condition in retrieving error information.
+ *                       Suppress printing normal unit attention errors and
+ *                        some drive not ready errors.
+ *                       Implement CDROMVOLREAD ioctl.
+ *                       Implement CDROMREADMODE1/2 ioctls.
+ *                       Fix race condition in setting up interrupt handlers
+ *                        when the `serialize' option is used.
+ * 3.01  Sep  2, 1995 -- Fix ordering of reenabling interrupts in
+ *                        cdrom_queue_request.
+ *                       Another try at using ide_[input,output]_data.
+ * 3.02  Sep 16, 1995 -- Stick total disk capacity in partition table as well.
+ *                       Make VERBOSE_IDE_CD_ERRORS dump failed command again.
+ *                       Dump out more information for ILLEGAL REQUEST errs.
+ *                       Fix handling of errors occurring before the
+ *                        packet command is transferred.
+ *                       Fix transfers with odd bytelengths.
+ * 3.03  Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
+ *                       `DCI-2S10' drives are broken too.
+ * 3.04  Nov 20, 1995 -- So are Vertos drives.
+ * 3.05  Dec  1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
+ * 3.06  Dec 16, 1995 -- Add support needed for partitions.
+ *                       More workarounds for Vertos bugs (based on patches
+ *                        from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
+ *                       Try to eliminate byteorder assumptions.
+ *                       Use atapi_cdrom_subchnl struct definition.
+ *                       Add STANDARD_ATAPI compilation option.
+ * 3.07  Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
+ *                        Vertos 300.
+ *                       Add NO_DOOR_LOCKING configuration option.
+ *                       Handle drive_cmd requests w/NULL args (for hdparm -t).
+ *                       Work around sporadic Sony55e audio play problem.
+ * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
+ *                        problem with "hde=cdrom" with no drive present.  -ml
+ * 3.08  Mar  6, 1996 -- More Vertos workarounds.
+ * 3.09  Apr  5, 1996 -- Add CDROMCLOSETRAY ioctl.
+ *                       Switch to using MSF addressing for audio commands.
+ *                       Reformat to match kernel tabbing style.
+ *                       Add CDROM_GET_UPC ioctl.
+ * 3.10  Apr 10, 1996 -- Fix compilation error with STANDARD_ATAPI.
+ * 3.11  Apr 29, 1996 -- Patch from Heiko Eissfeldt <heiko@colossus.escape.de>
+ *                       to remove redundant verify_area calls.
+ * 3.12  May  7, 1996 -- Rudimentary changer support.  Based on patches
+ *                        from Gerhard Zuber <zuber@berlin.snafu.de>.
+ *                       Let open succeed even if there's no loaded disc.
+ * 3.13  May 19, 1996 -- Fixes for changer code.
+ * 3.14  May 29, 1996 -- Add work-around for Vertos 600.
+ *                        (From Hennus Bergman <hennus@sky.ow.nl>.)
+ * 3.15  July 2, 1996 -- Added support for Sanyo 3 CD changers
+ *                        from Ben Galliart <bgallia@luc.edu> with 
+ *                        special help from Jeff Lightfoot 
+ *                        <jeffml@pobox.com>
+ * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification
+ * 3.16  Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
+ * 3.17  Sep 17, 1996 -- Tweak audio reads for some drives.
+ *                       Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
+ * 3.18  Oct 31, 1996 -- Added module and DMA support.
+ *                       
+ *                       
+ * 4.00  Nov 5, 1996   -- New ide-cd maintainer,
+ *                                 Erik B. Andersen <andersee@debian.org>
+ *                     -- Newer Creative drives don't always set the error
+ *                          register correctly.  Make sure we see media changes
+ *                          regardless.
+ *                     -- Integrate with generic cdrom driver.
+ *                     -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
+ *                          a patch from Ciro Cattuto <>.
+ *                     -- Call set_device_ro.
+ *                     -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ *                          ioctls, based on patch by Erik Andersen
+ *                     -- Add some probes of drive capability during setup.
+ *
+ * 4.01  Nov 11, 1996  -- Split into ide-cd.c and ide-cd.h
+ *                     -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE 
+ *                          ioctls in favor of a generalized approach 
+ *                          using the generic cdrom driver.
+ *                     -- Fully integrated with the 2.1.X kernel.
+ *                     -- Other stuff that I forgot (lots of changes)
+ *
+ * 4.02  Dec 01, 1996  -- Applied patch from Gadi Oxman <gadio@netvision.net.il>
+ *                          to fix the drive door locking problems.
+ *
+ * 4.03  Dec 04, 1996  -- Added DSC overlap support.
+ * 4.04  Dec 29, 1996  -- Added CDROMREADRAW ioclt based on patch 
+ *                          by Ales Makarov (xmakarov@sun.felk.cvut.cz)
+ *
+ * 4.05  Nov 20, 1997  -- Modified to print more drive info on init
+ *                        Minor other changes
+ *                        Fix errors on CDROMSTOP (If you have a "Dolphin",
+ *                          you must define IHAVEADOLPHIN)
+ *                        Added identifier so new Sanyo CD-changer works
+ *                        Better detection if door locking isn't supported
+ *
+ * 4.06  Dec 17, 1997  -- fixed endless "tray open" messages  -ml
+ * 4.07  Dec 17, 1997  -- fallback to set pc->stat on "tray open"
+ * 4.08  Dec 18, 1997  -- spew less noise when tray is empty
+ *                     -- fix speed display for ACER 24X, 18X
+ * 4.09  Jan 04, 1998  -- fix handling of the last block so we return
+ *                         an end of file instead of an I/O error (Gadi)
+ * 4.10  Jan 24, 1998  -- fixed a bug so now changers can change to a new
+ *                         slot when there is no disc in the current slot.
+ *                     -- Fixed a memory leak where info->changer_info was
+ *                         malloc'ed but never free'd when closing the device.
+ *                     -- Cleaned up the global namespace a bit by making more
+ *                         functions static that should already have been.
+ * 4.11  Mar 12, 1998  -- Added support for the CDROM_SELECT_SPEED ioctl
+ *                         based on a patch for 2.0.33 by Jelle Foks 
+ *                         <jelle@scintilla.utwente.nl>, a patch for 2.0.33
+ *                         by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI
+ *                         version, and my own efforts.  -erik
+ *                     -- Fixed a stupid bug which egcs was kind enough to
+ *                         inform me of where "Illegal mode for this track"
+ *                         was never returned due to a comparison on data
+ *                         types of limited range.
+ * 4.12  Mar 29, 1998  -- Fixed bug in CDROM_SELECT_SPEED so write speed is 
+ *                         now set ionly for CD-R and CD-RW drives.  I had 
+ *                         removed this support because it produced errors.
+ *                         It produced errors _only_ for non-writers. duh.
+ * 4.13  May 05, 1998  -- Suppress useless "in progress of becoming ready"
+ *                         messages, since this is not an error.
+ *                     -- Change error messages to be const
+ *                     -- Remove a "\t" which looks ugly in the syslogs
+ * 4.14  July 17, 1998 -- Change to pointing to .ps version of ATAPI spec
+ *                         since the .pdf version doesn't seem to work...
+ *                     -- Updated the TODO list to something more current.
+ *
+ * 4.15  Aug 25, 1998  -- Updated ide-cd.h to respect mechine endianess, 
+ *                         patch thanks to "Eddie C. Dost" <ecd@skynet.be>
+ *
+ * 4.50  Oct 19, 1998  -- New maintainers!
+ *                         Jens Axboe <axboe@image.dk>
+ *                         Chris Zwilling <chris@cloudnet.com>
+ *
+ * 4.51  Dec 23, 1998  -- Jens Axboe <axboe@image.dk>
+ *                      - ide_cdrom_reset enabled since the ide subsystem
+ *                         handles resets fine now. <axboe@image.dk>
+ *                      - Transfer size fix for Samsung CD-ROMs, thanks to
+ *                        "Ville Hallik" <ville.hallik@mail.ee>.
+ *                      - other minor stuff.
+ *
+ * 4.52  Jan 19, 1999  -- Jens Axboe <axboe@image.dk>
+ *                      - Detect DVD-ROM/RAM drives
+ *
+ * 4.53  Feb 22, 1999   - Include other model Samsung and one Goldstar
+ *                         drive in transfer size limit.
+ *                      - Fix the I/O error when doing eject without a medium
+ *                         loaded on some drives.
+ *                      - CDROMREADMODE2 is now implemented through
+ *                         CDROMREADRAW, since many drives don't support
+ *                         MODE2 (even though ATAPI 2.6 says they must).
+ *                      - Added ignore parameter to ide-cd (as a module), eg
+ *                         	insmod ide-cd ignore='hda hdb'
+ *                         Useful when using ide-cd in conjunction with
+ *                         ide-scsi. TODO: non-modular way of doing the
+ *                         same.
+ *
+ * 4.54  Aug 5, 1999	- Support for MMC2 class commands through the generic
+ *			  packet interface to cdrom.c.
+ *			- Unified audio ioctl support, most of it.
+ *			- cleaned up various deprecated verify_area().
+ *			- Added ide_cdrom_packet() as the interface for
+ *			  the Uniform generic_packet().
+ *			- bunch of other stuff, will fill in logs later.
+ *			- report 1 slot for non-changers, like the other
+ *			  cd-rom drivers. don't report select disc for
+ *			  non-changers as well.
+ *			- mask out audio playing, if the device can't do it.
+ *
+ * 4.55  Sep 1, 1999	- Eliminated the rest of the audio ioctls, except
+ *			  for CDROMREADTOC[ENTRY|HEADER]. Some of the drivers
+ *			  use this independently of the actual audio handling.
+ *			  They will disappear later when I get the time to
+ *			  do it cleanly.
+ *			- Minimize the TOC reading - only do it when we
+ *			  know a media change has occured.
+ *			- Moved all the CDROMREADx ioctls to the Uniform layer.
+ *			- Heiko Eissfeldt <heiko@colossus.escape.de> supplied
+ *			  some fixes for CDI.
+ *			- CD-ROM leaving door locked fix from Andries
+ *			  Brouwer <Andries.Brouwer@cwi.nl>
+ *			- Erik Andersen <andersen@xmission.com> unified
+ *			  commands across the various drivers and how
+ *			  sense errors are handled.
+ *
+ * 4.56  Sep 12, 1999	- Removed changer support - it is now in the
+ *			  Uniform layer.
+ *			- Added partition based multisession handling.
+ *			- Mode sense and mode select moved to the
+ *			  Uniform layer.
+ *			- Fixed a problem with WPI CDS-32X drive - it
+ *			  failed the capabilities 
+ *
+ *
+ *************************************************************************/
+ 
+#define IDECD_VERSION "4.56"
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/ide.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "ide-cd.h"
+
+/****************************************************************************
+ * Generic packet command support and error handling routines.
+ */
+
+/* Mark that we've seen a media change, and invalidate our internal
+   buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	
+	CDROM_STATE_FLAGS (drive)->media_changed = 1;
+	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+	info->nsectors_buffered = 0;
+}
+
+
+static
+void cdrom_analyze_sense_data (ide_drive_t *drive, struct request_sense *reqbuf,
+			       struct packet_command *failed_command)
+{
+	if (reqbuf->sense_key == NOT_READY ||
+	    reqbuf->sense_key == UNIT_ATTENTION) {
+		/* Make good and sure we've seen this potential media change.
+		   Some drives (i.e. Creative) fail to present the correct
+		   sense key in the error register. */
+		cdrom_saw_media_change (drive);
+
+
+		/* Don't print not ready or unit attention errors for
+		   READ_SUBCHANNEL.  Workman (and probably other programs)
+		   uses this command to poll the drive, and we don't want
+		   to fill the syslog with useless errors. */
+		if (failed_command &&
+		    failed_command->c[0] == GPCMD_READ_SUBCHANNEL)
+			return;
+	}
+
+	if (reqbuf->error_code == 0x70 && reqbuf->sense_key  == 0x02
+	 && ((reqbuf->asc      == 0x3a && reqbuf->ascq       == 0x00) ||
+	     (reqbuf->asc      == 0x04 && reqbuf->ascq       == 0x01)))
+	{
+		/*
+		 * Suppress the following errors:
+		 * "Medium not present", "in progress of becoming ready",
+		 * and "writing" to keep the noise level down to a dull roar.
+		 */
+		return;
+	}
+
+#if VERBOSE_IDE_CD_ERRORS
+	{
+		int i;
+		const char *s;
+		char buf[80];
+
+		printk ("ATAPI device %s:\n", drive->name);
+		if (reqbuf->error_code==0x70)
+			printk("  Error: ");
+		else if (reqbuf->error_code==0x71)
+			printk("  Deferred Error: ");
+		else
+			printk("  Unknown Error Type: ");
+
+		if ( reqbuf->sense_key < ARY_LEN (sense_key_texts))
+			s = sense_key_texts[reqbuf->sense_key];
+		else
+			s = "bad sense key!";
+
+		printk ("%s -- (Sense key=0x%02x)\n", s, reqbuf->sense_key);
+
+		if (reqbuf->asc == 0x40) {
+			sprintf (buf, "Diagnostic failure on component 0x%02x",
+				 reqbuf->ascq);
+			s = buf;
+		} else {
+			int lo=0, mid, hi=ARY_LEN (sense_data_texts);
+			unsigned long key = (reqbuf->sense_key << 16);
+			key |= (reqbuf->asc << 8);
+			if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
+				key |= reqbuf->ascq;
+			s = NULL;
+
+			while (hi > lo) {
+				mid = (lo + hi) / 2;
+				if (sense_data_texts[mid].asc_ascq == key ||
+				    sense_data_texts[mid].asc_ascq == (0xff0000|key)) {
+					s = sense_data_texts[mid].text;
+					break;
+				}
+				else if (sense_data_texts[mid].asc_ascq > key)
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+		}
+
+		if (s == NULL) {
+			if (reqbuf->asc > 0x80)
+				s = "(vendor-specific error)";
+			else
+				s = "(reserved error code)";
+		}
+
+		printk ("  %s -- (asc=0x%02x, ascq=0x%02x)\n",
+			s, reqbuf->asc, reqbuf->ascq);
+
+		if (failed_command != NULL) {
+
+			int lo=0, mid, hi= ARY_LEN (packet_command_texts);
+			s = NULL;
+
+			while (hi > lo) {
+				mid = (lo + hi) / 2;
+				if (packet_command_texts[mid].packet_command == failed_command->c[0]) {
+					s = packet_command_texts[mid].text;
+					break;
+				}
+				else if (packet_command_texts[mid].packet_command > failed_command->c[0])
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+
+			printk ("  The failed \"%s\" packet command was: \n  \"", s);
+			for (i=0; i<sizeof (failed_command->c); i++)
+				printk ("%02x ", failed_command->c[i]);
+			printk ("\"\n");
+		}
+
+		/* The SKSV bit specifies validity of the sense_key_specific
+		 * in the next two commands. It is bit 7 of the first byte.
+		 * In the case of NOT_READY, if SKSV is set the drive can
+		 * give us nice ETA readings.
+		 */
+		if (reqbuf->sense_key == NOT_READY && (reqbuf->sks[0] & 0x80)) {
+			int progress = (reqbuf->sks[1] << 8 | reqbuf->sks[2]) * 100;
+			printk("  Command is %02d%% complete\n", progress / 0xffff);
+
+		}
+
+		if (reqbuf->sense_key == ILLEGAL_REQUEST &&
+		    (reqbuf->sks[0] & 0x80) != 0) {
+			printk ("  Error in %s byte %d",
+				(reqbuf->sks[0] & 0x40) != 0 ?
+				"command packet" : "command data",
+				(reqbuf->sks[1] << 8) + reqbuf->sks[2]);
+
+			if ((reqbuf->sks[0] & 0x40) != 0)
+				printk (" bit %d", reqbuf->sks[0] & 0x07);
+
+			printk ("\n");
+		}
+	}
+
+#else /* not VERBOSE_IDE_CD_ERRORS */
+
+	/* Suppress printing unit attention and `in progress of becoming ready'
+	   errors when we're not being verbose. */
+
+	if (reqbuf->sense_key == UNIT_ATTENTION ||
+	    (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
+						reqbuf->asc == 0x3a)))
+		return;
+
+	printk ("%s: error code: 0x%02x  sense_key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
+		drive->name,
+		reqbuf->error_code, reqbuf->sense_key,
+		reqbuf->asc, reqbuf->ascq);
+#endif /* not VERBOSE_IDE_CD_ERRORS */
+}
+
+static void cdrom_queue_request_sense (ide_drive_t *drive, 
+				       struct semaphore *sem,
+				       struct packet_command *failed_command)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq;
+	struct packet_command *pc;
+
+	/* Make up a new request to retrieve sense information. */
+	pc = &info->request_sense_pc;
+	memset(pc, 0, sizeof (*pc));
+
+	pc->c[0] = GPCMD_REQUEST_SENSE;
+
+	/* just get the first 18 bytes of the sense info, there might not
+	 * be more available */
+	pc->c[4] = pc->buflen = 18;
+	pc->buffer = (char *)&info->sense_data;
+	pc->sense_data = (struct request_sense *)failed_command;
+
+	/* stuff the sense request in front of our current request */
+	rq = &info->request_sense_request;
+	ide_init_drive_cmd (rq);
+	rq->cmd = REQUEST_SENSE_COMMAND;
+	rq->buffer = (char *)pc;
+	rq->sem = sem;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+
+static void cdrom_end_request (int uptodate, ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+
+	if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) {
+		struct packet_command *pc = (struct packet_command *)
+			                      rq->buffer;
+		cdrom_analyze_sense_data (drive,
+			(struct request_sense *) (pc->buffer - pc->c[4]),
+			(struct packet_command *) pc->sense_data);
+	}
+	if (rq->cmd == READ && !rq->current_nr_sectors)
+		uptodate = 1;
+
+	ide_end_request (uptodate, HWGROUP(drive));
+}
+
+
+/* Returns 0 if the request should be continued.
+   Returns 1 if the request was ended. */
+static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, int good_stat,
+				int *stat_ret)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	int stat, cmd, err, sense_key;
+	struct packet_command *pc = (struct packet_command *) rq->buffer;
+	
+	/* Check for errors. */
+	stat = GET_STAT();
+	*stat_ret = stat;
+
+	if (OK_STAT (stat, good_stat, BAD_R_STAT))
+		return 0;
+
+	/* Get the IDE error register. */
+	err = GET_ERR();
+	sense_key = err >> 4;
+
+	if (rq == NULL)
+		printk ("%s: missing request in cdrom_decode_status\n",
+			drive->name);
+	else {
+		cmd = rq->cmd;
+
+		if (cmd == REQUEST_SENSE_COMMAND) {
+			/* We got an error trying to get sense info
+			   from the drive (probably while trying
+			   to recover from a former error).  Just give up. */
+
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+			*startstop = ide_error (drive, "request sense failure", stat);
+			return 1;
+
+		} else if (cmd == PACKET_COMMAND) {
+			/* All other functions, except for READ. */
+
+			struct semaphore *sem = NULL;
+
+			/* Check for tray open. */
+			if (sense_key == NOT_READY) {
+				cdrom_saw_media_change (drive);
+			} else if (sense_key == UNIT_ATTENTION) {
+				/* Check for media change. */
+				cdrom_saw_media_change (drive);
+				/*printk("%s: media changed\n",drive->name);*/
+				return 0;
+			} else {
+				/* Otherwise, print an error. */
+				ide_dump_status (drive, "packet command error",
+						 stat);
+			}
+			
+			/* Set the error flag and complete the request.
+			   Then, if we have a CHECK CONDITION status,
+			   queue a request sense command.  We must be careful,
+			   though: we don't want the thread in
+			   cdrom_queue_packet_command to wake up until
+			   the request sense has completed.  We do this
+			   by transferring the semaphore from the packet
+			   command request to the request sense request. */
+
+			if ((stat & ERR_STAT) != 0) {
+				sem = rq->sem;
+				rq->sem = NULL;
+			}
+
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+
+			if ((stat & ERR_STAT) != 0)
+				cdrom_queue_request_sense(drive, sem, pc);
+		} else {
+			/* Handle errors from READ requests. */
+
+			if (sense_key == NOT_READY) {
+				/* Tray open. */
+				cdrom_saw_media_change (drive);
+
+				/* Fail the request. */
+				printk ("%s: tray open\n", drive->name);
+				cdrom_end_request (0, drive);
+			} else if (sense_key == UNIT_ATTENTION) {
+				/* Media change. */
+				cdrom_saw_media_change (drive);
+
+				/* Arrange to retry the request.
+				   But be sure to give up if we've retried
+				   too many times. */
+				if (++rq->errors > ERROR_MAX)
+					cdrom_end_request (0, drive);
+			} else if (sense_key == ILLEGAL_REQUEST ||
+				   sense_key == DATA_PROTECT) {
+				/* No point in retrying after an illegal
+				   request or data protect error.*/
+				ide_dump_status (drive, "command error", stat);
+				cdrom_end_request (0, drive);
+			} else if ((err & ~ABRT_ERR) != 0) {
+				/* Go to the default handler
+				   for other errors. */
+				*startstop = ide_error (drive, "cdrom_decode_status", stat);
+				return 1;
+			} else if ((++rq->errors > ERROR_MAX)) {
+				/* We've racked up too many retries.  Abort. */
+				cdrom_end_request (0, drive);
+			}
+
+			/* If we got a CHECK_CONDITION status,
+			   queue a request sense command. */
+			if ((stat & ERR_STAT) != 0)
+				cdrom_queue_request_sense(drive, NULL, NULL);
+		}
+	}
+
+	/* Retry, or handle the next request. */
+	*startstop = ide_stopped;
+	return 1;
+}
+
+static int cdrom_timer_expiry(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *) rq->buffer;
+	unsigned long wait = 0;
+
+	/* blank and format can take an extremly long time to
+	 * complete, if the IMMED bit was not set.
+	 */
+	if (pc->c[0] == GPCMD_BLANK || pc->c[0] == GPCMD_FORMAT_UNIT)
+		wait = 60*60*HZ;
+
+	return wait;
+}
+
+/* Set up the device registers for transferring a packet command on DEV,
+   expecting to later transfer XFERLEN bytes.  HANDLER is the routine
+   which actually transfers the command to the drive.  If this is a
+   drq_interrupt device, this routine will arrange for HANDLER to be
+   called when the interrupt from the drive arrives.  Otherwise, HANDLER
+   will be called immediately after the drive is prepared for the transfer. */
+
+static ide_startstop_t cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
+				       ide_handler_t *handler)
+{
+	ide_startstop_t startstop;
+	struct cdrom_info *info = drive->driver_data;
+
+	/* Wait for the controller to be idle. */
+	if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY))
+		return startstop;
+
+	if (info->dma)
+		info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive);
+
+	/* Set up the controller registers. */
+	OUT_BYTE (info->dma, IDE_FEATURE_REG);
+	OUT_BYTE (0, IDE_NSECTOR_REG);
+	OUT_BYTE (0, IDE_SECTOR_REG);
+
+	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
+	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
+	if (IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+ 
+	if (info->dma)
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+
+	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+		ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+		return ide_started;
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+		return (*handler) (drive);
+	}
+}
+
+/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
+   The device registers must have already been prepared
+   by cdrom_start_packet_command.
+   HANDLER is the interrupt handler to call when the command completes
+   or there's data ready. */
+static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive,
+                                          unsigned char *cmd_buf, int cmd_len,
+					  ide_handler_t *handler)
+{
+	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+		/* Here we should have been called after receiving an interrupt
+		   from the device.  DRQ should how be set. */
+		int stat_dum;
+		ide_startstop_t startstop;
+
+		/* Check for errors. */
+		if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum))
+			return startstop;
+	} else {
+		ide_startstop_t startstop;
+		/* Otherwise, we must wait for DRQ to get set. */
+		if (ide_wait_stat (&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
+			return startstop;
+	}
+
+	/* Arm the interrupt handler. */
+	ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry);
+
+	/* Send the command to the device. */
+	atapi_output_bytes (drive, cmd_buf, cmd_len);
+
+	return ide_started;
+}
+
+
+
+/****************************************************************************
+ * Block read functions.
+ */
+
+/*
+ * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector
+ * buffer.  Once the first sector is added, any subsequent sectors are
+ * assumed to be continuous (until the buffer is cleared).  For the first
+ * sector added, SECTOR is its sector number.  (SECTOR is then ignored until
+ * the buffer is cleared.)
+ */
+static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
+                                  int sectors_to_transfer)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	/* Number of sectors to read into the buffer. */
+	int sectors_to_buffer = MIN (sectors_to_transfer,
+				     (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+				       info->nsectors_buffered);
+
+	char *dest;
+
+	/* If we couldn't get a buffer, don't try to buffer anything... */
+	if (info->buffer == NULL)
+			sectors_to_buffer = 0;
+
+	/* If this is the first sector in the buffer, remember its number. */
+	if (info->nsectors_buffered == 0)
+		info->sector_buffered = sector;
+
+	/* Read the data into the buffer. */
+	dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE;
+	while (sectors_to_buffer > 0) {
+		atapi_input_bytes (drive, dest, SECTOR_SIZE);
+		--sectors_to_buffer;
+		--sectors_to_transfer;
+		++info->nsectors_buffered;
+		dest += SECTOR_SIZE;
+	}
+
+	/* Throw away any remaining data. */
+	while (sectors_to_transfer > 0) {
+		char dum[SECTOR_SIZE];
+		atapi_input_bytes (drive, dum, sizeof (dum));
+		--sectors_to_transfer;
+	}
+}
+
+/*
+ * Check the contents of the interrupt reason register from the cdrom
+ * and attempt to recover if there are problems.  Returns  0 if everything's
+ * ok; nonzero if the request has been terminated.
+ */
+static inline
+int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
+{
+	ireason &= 3;
+	if (ireason == 2) return 0;
+
+	if (ireason == 0) {
+		/* Whoops... The drive is expecting to receive data from us! */
+		printk ("%s: cdrom_read_intr: "
+			"Drive wants to transfer data the wrong way!\n",
+			drive->name);
+
+		/* Throw some data at the drive so it doesn't hang
+		   and quit this request. */
+		while (len > 0) {
+			int dum = 0;
+			atapi_output_bytes (drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+	} else  if (ireason == 1) {
+		/* Some drives (ASUS) seem to tell us that status
+		 * info is available. just get it and ignore.
+		 */
+		GET_STAT();
+		return 0;
+	} else {
+		/* Drive wants a command packet, or invalid ireason... */
+		printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
+			drive->name, ireason);
+	}
+
+	cdrom_end_request (0, drive);
+	return -1;
+}
+
+/*
+ * Interrupt routine.  Called when a read request has completed.
+ */
+static ide_startstop_t cdrom_read_intr (ide_drive_t *drive)
+{
+	int stat;
+	int ireason, len, sectors_to_transfer, nskip;
+	struct cdrom_info *info = drive->driver_data;
+	int i, dma = info->dma, dma_error = 0;
+	ide_startstop_t startstop;
+
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/* Check for errors. */
+	if (dma) {
+		info->dma = 0;
+		if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive)))
+			HWIF(drive)->dmaproc(ide_dma_off, drive);
+	}
+
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
+ 
+	if (dma) {
+		if (!dma_error) {
+			for (i = rq->nr_sectors; i > 0;) {
+				i -= rq->current_nr_sectors;
+				ide_end_request(1, HWGROUP(drive));
+			}
+			return ide_stopped;
+		} else
+			return ide_error (drive, "dma error", stat);
+	}
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = IN_BYTE (IDE_NSECTOR_REG);
+	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+	/* If DRQ is clear, the command has completed. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* If we're not done filling the current buffer, complain.
+		   Otherwise, complete the command normally. */
+		if (rq->current_nr_sectors > 0) {
+			printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
+				drive->name, rq->current_nr_sectors);
+			cdrom_end_request (0, drive);
+		} else
+			cdrom_end_request (1, drive);
+		return ide_stopped;
+	}
+
+	/* Check that the drive is expecting to do the same thing we are. */
+	if (cdrom_read_check_ireason (drive, len, ireason))
+		return ide_stopped;
+
+	/* Assume that the drive will always provide data in multiples
+	   of at least SECTOR_SIZE, as it gets hairy to keep track
+	   of the transfers otherwise. */
+	if ((len % SECTOR_SIZE) != 0) {
+		printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
+			drive->name, len);
+		if (CDROM_CONFIG_FLAGS (drive)->limit_nframes)
+			printk ("  This drive is not supported by this version of the driver\n");
+		else {
+			printk ("  Trying to limit transfer sizes\n");
+			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+		}
+		cdrom_end_request (0, drive);
+		return ide_stopped;
+	}
+
+	/* The number of sectors we need to read from the drive. */
+	sectors_to_transfer = len / SECTOR_SIZE;
+
+	/* First, figure out if we need to bit-bucket
+	   any of the leading sectors. */
+	nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
+		     sectors_to_transfer);
+
+	while (nskip > 0) {
+		/* We need to throw away a sector. */
+		char dum[SECTOR_SIZE];
+		atapi_input_bytes (drive, dum, sizeof (dum));
+
+		--rq->current_nr_sectors;
+		--nskip;
+		--sectors_to_transfer;
+	}
+
+	/* Now loop while we still have data to read from the drive. */
+	while (sectors_to_transfer > 0) {
+		int this_transfer;
+
+		/* If we've filled the present buffer but there's another
+		   chained buffer after it, move on. */
+		if (rq->current_nr_sectors == 0 &&
+		    rq->nr_sectors > 0)
+			cdrom_end_request (1, drive);
+
+		/* If the buffers are full, cache the rest of the data in our
+		   internal buffer. */
+		if (rq->current_nr_sectors == 0) {
+			cdrom_buffer_sectors(drive, rq->sector, sectors_to_transfer);
+			sectors_to_transfer = 0;
+		} else {
+			/* Transfer data to the buffers.
+			   Figure out how many sectors we can transfer
+			   to the current buffer. */
+			this_transfer = MIN (sectors_to_transfer,
+					     rq->current_nr_sectors);
+
+			/* Read this_transfer sectors
+			   into the current buffer. */
+			while (this_transfer > 0) {
+				atapi_input_bytes(drive, rq->buffer, SECTOR_SIZE);
+				rq->buffer += SECTOR_SIZE;
+				--rq->nr_sectors;
+				--rq->current_nr_sectors;
+				++rq->sector;
+				--this_transfer;
+				--sectors_to_transfer;
+			}
+		}
+	}
+
+	/* Done moving data!
+	   Wait for another interrupt. */
+	ide_set_handler(drive, &cdrom_read_intr, WAIT_CMD, NULL);
+	return ide_started;
+}
+
+/*
+ * Try to satisfy some of the current read request from our cached data.
+ * Returns nonzero if the request has been completed, zero otherwise.
+ */
+static int cdrom_read_from_buffer (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/* Can't do anything if there's no buffer. */
+	if (info->buffer == NULL) return 0;
+
+	/* Loop while this request needs data and the next block is present
+	   in our cache. */
+	while (rq->nr_sectors > 0 &&
+	       rq->sector >= info->sector_buffered &&
+	       rq->sector < info->sector_buffered + info->nsectors_buffered) {
+		if (rq->current_nr_sectors == 0)
+			cdrom_end_request (1, drive);
+
+		memcpy (rq->buffer,
+			info->buffer +
+			(rq->sector - info->sector_buffered) * SECTOR_SIZE,
+			SECTOR_SIZE);
+		rq->buffer += SECTOR_SIZE;
+		--rq->current_nr_sectors;
+		--rq->nr_sectors;
+		++rq->sector;
+	}
+
+	/* If we've satisfied the current request,
+	   terminate it successfully. */
+	if (rq->nr_sectors == 0) {
+		cdrom_end_request (1, drive);
+		return -1;
+	}
+
+	/* Move on to the next buffer if needed. */
+	if (rq->current_nr_sectors == 0)
+		cdrom_end_request (1, drive);
+
+	/* If this condition does not hold, then the kluge i use to
+	   represent the number of sectors to skip at the start of a transfer
+	   will fail.  I think that this will never happen, but let's be
+	   paranoid and check. */
+	if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
+	    (rq->sector % SECTORS_PER_FRAME) != 0) {
+		printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+			drive->name, rq->sector);
+		cdrom_end_request (0, drive);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Routine to send a read packet command to the drive.
+ * This is usually called directly from cdrom_start_read.
+ * However, for drq_interrupt devices, it is called from an interrupt
+ * when the drive is ready to accept the command.
+ */
+static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive)
+{
+	struct packet_command pc;
+	struct request *rq = HWGROUP(drive)->rq;
+	int nsect, sector, nframes, frame, nskip;
+
+	/* Number of sectors to transfer. */
+	nsect = rq->nr_sectors;
+
+	/* Starting sector. */
+	sector = rq->sector;
+
+	/* If the requested sector doesn't start on a cdrom block boundary,
+	   we must adjust the start of the transfer so that it does,
+	   and remember to skip the first few sectors.
+	   If the CURRENT_NR_SECTORS field is larger than the size
+	   of the buffer, it will mean that we're to skip a number
+	   of sectors equal to the amount by which CURRENT_NR_SECTORS
+	   is larger than the buffer size. */
+	nskip = (sector % SECTORS_PER_FRAME);
+	if (nskip > 0) {
+		/* Sanity check... */
+		if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS) &&
+			(rq->sector % CD_FRAMESIZE != 0)) {
+			printk ("%s: cdrom_start_read_continuation: buffer botch (%lu)\n",
+				drive->name, rq->current_nr_sectors);
+			cdrom_end_request (0, drive);
+			return ide_stopped;
+		}
+		sector -= nskip;
+		nsect += nskip;
+		rq->current_nr_sectors += nskip;
+	}
+
+	/* Convert from sectors to cdrom blocks, rounding up the transfer
+	   length if needed. */
+	nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
+	frame = sector / SECTORS_PER_FRAME;
+
+	/* Largest number of frames was can transfer at once is 64k-1. For
+	   some drives we need to limit this even more. */
+	nframes = MIN (nframes, (CDROM_CONFIG_FLAGS (drive)->limit_nframes) ?
+		(65534 / CD_FRAMESIZE) : 65535);
+
+	/* Set up the command */
+	memset (&pc.c, 0, sizeof (pc.c));
+	pc.c[0] = GPCMD_READ_10;
+	pc.c[7] = (nframes >> 8);
+	pc.c[8] = (nframes & 0xff);
+	put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
+
+	/* Send the command to the drive and return. */
+	return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
+					      &cdrom_read_intr);
+}
+
+
+#define IDECD_SEEK_THRESHOLD	(1000)			/* 1000 blocks */
+#define IDECD_SEEK_TIMER	(5 * WAIT_MIN_SLEEP)	/* 100 ms */
+#define IDECD_SEEK_TIMEOUT     WAIT_CMD			/* 10 sec */
+
+static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	int stat;
+	static int retry = 10;
+	ide_startstop_t startstop;
+
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
+	CDROM_CONFIG_FLAGS(drive)->seeking = 1;
+
+	if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) {
+		if (--retry == 0) {
+			printk("%s: disabled DSC seek overlap\n", drive->name);
+			drive->dsc_overlap = 0;
+		}
+	}
+	return ide_stopped;
+}
+
+static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive)
+{
+	struct packet_command pc;
+	struct request *rq = HWGROUP(drive)->rq;
+	int sector, frame, nskip;
+
+	sector = rq->sector;
+	nskip = (sector % SECTORS_PER_FRAME);
+	if (nskip > 0)
+		sector -= nskip;
+	frame = sector / SECTORS_PER_FRAME;
+
+	memset (&pc.c, 0, sizeof (pc.c));
+	pc.c[0] = GPCMD_SEEK;
+	put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]);
+	return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr);
+}
+
+static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	info->dma = 0;
+	info->start_seek = jiffies;
+	return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation);
+}
+
+/* Fix up a possibly partially-processed request so that we can
+   start it over entirely, or even put it back on the request queue. */
+static void restore_request (struct request *rq)
+{
+	if (rq->buffer != rq->bh->b_data) {
+		int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
+		rq->buffer = rq->bh->b_data;
+		rq->nr_sectors += n;
+		rq->sector -= n;
+	}
+	rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
+}
+
+/*
+ * Start a read request from the CD-ROM.
+ */
+static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	int minor = MINOR (rq->rq_dev);
+
+	/* If the request is relative to a partition, fix it up to refer to the
+	   absolute address.  */
+	if ((minor & PARTN_MASK) != 0) {
+		rq->sector = block;
+		minor &= ~PARTN_MASK;
+		rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
+	}
+
+	/* We may be retrying this request after an error.  Fix up
+	   any weirdness which might be present in the request packet. */
+	restore_request (rq);
+
+	/* Satisfy whatever we can of this request from our cached sector. */
+	if (cdrom_read_from_buffer(drive))
+		return ide_stopped;
+
+	/* Clear the local sector buffer. */
+	info->nsectors_buffered = 0;
+
+	/* use dma, if possible. */
+	if (drive->using_dma && (rq->sector % SECTORS_PER_FRAME == 0) &&
+				(rq->nr_sectors % SECTORS_PER_FRAME == 0))
+		info->dma = 1;
+	else
+		info->dma = 0;
+
+	/* Start sending the read request to the drive. */
+	return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation);
+}
+
+/****************************************************************************
+ * Execute all other packet commands.
+ */
+
+/* Forward declarations. */
+static int cdrom_lockdoor(ide_drive_t *drive, int lockflag);
+
+/* Interrupt routine for packet command completion. */
+static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
+{
+	int ireason, len, stat, thislen;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
+	struct cdrom_info *info = drive->driver_data;
+	ide_startstop_t startstop;
+
+	pc->sense_data = &info->sense_data;
+
+	/* Check for errors. */
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = IN_BYTE (IDE_NSECTOR_REG);
+	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+	/* If DRQ is clear, the command has completed.
+	   Complain if we still have data left to transfer. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* Some of the trailing request sense fields are optional, and
+		   some drives don't send them.  Sigh. */
+		if (pc->c[0] == GPCMD_REQUEST_SENSE &&
+		    pc->buflen > 0 &&
+		    pc->buflen <= 5) {
+			while (pc->buflen > 0) {
+				*pc->buffer++ = 0;
+				--pc->buflen;
+			}
+		}
+
+		if (pc->buflen == 0)
+			cdrom_end_request (1, drive);
+		else {
+			/* Comment this out, because this always happens 
+			   right after a reset occurs, and it is annoying to 
+			   always print expected stuff.  */
+			/*
+			printk ("%s: cdrom_pc_intr: data underrun %d\n",
+				drive->name, pc->buflen);
+			*/
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+		}
+		return ide_stopped;
+	}
+
+	/* Figure out how much data to transfer. */
+	thislen = pc->buflen;
+	if (thislen > len) thislen = len;
+
+	/* The drive wants to be written to. */
+	if ((ireason & 3) == 0) {
+		/* Transfer the data. */
+		atapi_output_bytes (drive, pc->buffer, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			atapi_output_bytes (drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		pc->buffer += thislen;
+		pc->buflen -= thislen;
+	}
+
+	/* Same drill for reading. */
+	else if ((ireason & 3) == 2) {
+
+		/* Transfer the data. */
+		atapi_input_bytes (drive, pc->buffer, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			atapi_input_bytes (drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		pc->buffer += thislen;
+		pc->buflen -= thislen;
+	} else {
+		printk ("%s: cdrom_pc_intr: The drive "
+			"appears confused (ireason = 0x%2x)\n",
+			drive->name, ireason);
+		pc->stat = 1;
+	}
+
+	/* Now we wait for another interrupt. */
+	ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD, cdrom_timer_expiry);
+	return ide_started;
+}
+
+
+static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+	/* Send the command to the drive and return. */
+	return cdrom_transfer_packet_command (drive, pc->c,
+				       sizeof (pc->c), &cdrom_pc_intr);
+}
+
+
+static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
+{
+	int len;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
+	struct cdrom_info *info = drive->driver_data;
+
+	info->dma = 0;
+	pc->stat = 0;
+	len = pc->buflen;
+
+	/* Start sending the command to the drive. */
+	return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
+}
+
+
+/* Sleep for TIME jiffies.
+   Not to be called from an interrupt handler. */
+static
+void cdrom_sleep (int time)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(time);
+}
+
+static
+int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc)
+{
+	int retries = 10;
+	struct request req;
+
+	/* Start of retry loop. */
+	do {
+		ide_init_drive_cmd (&req);
+		req.cmd = PACKET_COMMAND;
+		req.buffer = (char *)pc;
+		if (ide_do_drive_cmd (drive, &req, ide_wait)) {
+			printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n",
+				drive->name, req.buffer[0], req.buffer[1]);
+			/* FIXME: we should probably abort/retry or something */
+		}
+		if (pc->stat != 0) {
+			/* The request failed.  Retry if it was due to a unit
+			   attention status
+			   (usually means media was changed). */
+			struct request_sense *reqbuf = pc->sense_data;
+
+			if (reqbuf->sense_key == UNIT_ATTENTION)
+				cdrom_saw_media_change (drive);
+			else if (reqbuf->sense_key == NOT_READY &&
+				 reqbuf->asc == 4 && reqbuf->ascq != 4) {
+				/* The drive is in the process of loading
+				   a disk.  Retry, but wait a little to give
+				   the drive time to complete the load. */
+				cdrom_sleep (HZ);
+			} else {
+				/* Otherwise, don't retry. */
+				retries = 0;
+			}
+			--retries;
+		}
+
+		/* End of retry loop. */
+	} while (pc->stat != 0 && retries >= 0);
+
+	/* Return an error if the command failed. */
+	if (pc->stat)
+		return -EIO;
+
+	/* The command succeeded.  If it was anything other than
+	   a request sense, eject, or door lock command,
+	   and we think that the door is presently unlocked, lock it
+	   again. (The door was probably unlocked via an explicit
+	   CDROMEJECT ioctl.) */
+	if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
+	    (pc->c[0] != GPCMD_TEST_UNIT_READY &&
+	     pc->c[0] != GPCMD_REQUEST_SENSE &&
+	     pc->c[0] != GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL &&
+	     pc->c[0] != GPCMD_START_STOP_UNIT &&
+	     pc->c[0] != GPCMD_MODE_SENSE_10 &&
+	     pc->c[0] != GPCMD_MODE_SELECT_10)) {
+		(void) cdrom_lockdoor (drive, 1);
+	}
+	return 0;
+}
+
+/****************************************************************************
+ * cdrom driver request routine.
+ */
+static ide_startstop_t
+ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	ide_startstop_t action;
+	struct cdrom_info *info = drive->driver_data;
+
+	switch (rq->cmd) {
+		case READ: {
+			if (CDROM_CONFIG_FLAGS(drive)->seeking) {
+				unsigned long elpased = jiffies - info->start_seek;
+				int stat = GET_STAT();
+
+				if ((stat & SEEK_STAT) != SEEK_STAT) {
+					if (elpased < IDECD_SEEK_TIMEOUT) {
+						ide_stall_queue(drive, IDECD_SEEK_TIMER);
+						return ide_stopped;
+					}
+					printk ("%s: DSC timeout\n", drive->name);
+				}
+				CDROM_CONFIG_FLAGS(drive)->seeking = 0;
+			}
+			if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap)
+				action = cdrom_start_seek (drive, block);
+			else
+				action = cdrom_start_read (drive, block);
+			info->last_block = block;
+			return action;
+		}
+
+		case PACKET_COMMAND:
+		case REQUEST_SENSE_COMMAND: {
+			return cdrom_do_packet_command(drive);
+		}
+
+		case RESET_DRIVE_COMMAND: {
+			cdrom_end_request(1, drive);
+			return ide_do_reset(drive);
+		}
+
+		default: {
+			printk("ide-cd: bad cmd %d\n", rq -> cmd);
+			cdrom_end_request(0, drive);
+			return ide_stopped;
+		}
+	}
+}
+
+
+
+/****************************************************************************
+ * Ioctl handling.
+ *
+ * Routines which queue packet commands take as a final argument a pointer
+ * to a request_sense struct.  If execution of the command results
+ * in an error with a CHECK CONDITION status, this structure will be filled
+ * with the results of the subsequent request sense command.  The pointer
+ * can also be NULL, in which case no sense information is returned.
+ */
+
+#if ! STANDARD_ATAPI
+static inline
+int bin2bcd (int x)
+{
+	return (x%10) | ((x/10) << 4);
+}
+
+
+static inline
+int bcd2bin (int x)
+{
+	return (x >> 4) * 10 + (x & 0x0f);
+}
+
+static
+void msf_from_bcd (struct atapi_msf *msf)
+{
+	msf->minute = bcd2bin (msf->minute);
+	msf->second = bcd2bin (msf->second);
+	msf->frame  = bcd2bin (msf->frame);
+}
+
+#endif /* not STANDARD_ATAPI */
+
+
+static inline
+void lba_to_msf (int lba, byte *m, byte *s, byte *f)
+{
+	lba += CD_MSF_OFFSET;
+	lba &= 0xffffff;  /* negative lbas use only 24 bits */
+	*m = lba / (CD_SECS * CD_FRAMES);
+	lba %= (CD_SECS * CD_FRAMES);
+	*s = lba / CD_FRAMES;
+	*f = lba % CD_FRAMES;
+}
+
+
+static inline
+int msf_to_lba (byte m, byte s, byte f)
+{
+	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
+}
+
+static int cdrom_check_status (ide_drive_t  *drive)
+{
+	struct packet_command pc;
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+
+	memset(&pc, 0, sizeof(pc));
+
+	pc.c[0] = GPCMD_TEST_UNIT_READY;
+
+#if ! STANDARD_ATAPI
+        /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
+           switch CDs instead of supporting the LOAD_UNLOAD opcode   */
+
+        pc.c[7] = cdi->sanyo_slot % 3;
+#endif /* not STANDARD_ATAPI */
+
+	return cdrom_queue_packet_command(drive, &pc);
+}
+
+
+/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
+static int
+cdrom_lockdoor(ide_drive_t *drive, int lockflag)
+{
+	struct request_sense *sense;
+	struct packet_command pc;
+	int stat;
+
+	/* If the drive cannot lock the door, just pretend. */
+	if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
+		stat = 0;
+	else {
+		memset(&pc, 0, sizeof(pc));
+		pc.c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+		pc.c[4] = (lockflag != 0);
+		stat = cdrom_queue_packet_command (drive, &pc);
+	}
+
+	sense = pc.sense_data;
+
+	/* If we got an illegal field error, the drive
+	   probably cannot lock the door. */
+	if (stat != 0 &&
+	    sense->sense_key == ILLEGAL_REQUEST &&
+	    (sense->asc == 0x24 || sense->asc == 0x20)) {
+		printk ("%s: door locking not supported\n",
+			drive->name);
+		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+		stat = 0;
+	}
+	
+	/* no medium, that's alright. */
+	if (stat != 0 && sense->sense_key == NOT_READY && sense->asc == 0x3a)
+		stat = 0;
+
+	if (stat == 0)
+		CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+
+	return stat;
+}
+
+
+/* Eject the disk if EJECTFLAG is 0.
+   If EJECTFLAG is 1, try to reload the disk. */
+static int cdrom_eject(ide_drive_t *drive, int ejectflag)
+{
+	struct packet_command pc;
+
+	if (CDROM_CONFIG_FLAGS(drive)->no_eject && !ejectflag)
+		return -EDRIVE_CANT_DO_THIS;
+	
+	/* reload fails on some drives, if the tray is locked */
+	if (CDROM_STATE_FLAGS(drive)->door_locked && ejectflag)
+		return 0;
+
+	memset(&pc, 0, sizeof (pc));
+
+	pc.c[0] = GPCMD_START_STOP_UNIT;
+	pc.c[4] = 0x02 + (ejectflag != 0);
+	return cdrom_queue_packet_command (drive, &pc);
+}
+
+static int cdrom_read_capacity(ide_drive_t *drive, unsigned *capacity)
+{
+	struct {
+		__u32 lba;
+		__u32 blocklen;
+	} capbuf;
+
+	int stat;
+	struct packet_command pc;
+
+	memset(&pc, 0, sizeof (pc));
+
+	pc.c[0] = GPCMD_READ_CDVD_CAPACITY;
+	pc.buffer = (char *)&capbuf;
+	pc.buflen = sizeof(capbuf);
+
+	stat = cdrom_queue_packet_command(drive, &pc);
+	if (stat == 0)
+		*capacity = be32_to_cpu(capbuf.lba);
+
+	return stat;
+}
+
+static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
+				int format, char *buf, int buflen)
+{
+	struct packet_command pc;
+
+	memset(&pc, 0, sizeof(pc));
+
+	pc.buffer =  buf;
+	pc.buflen = buflen;
+	pc.c[0] = GPCMD_READ_TOC_PMA_ATIP;
+	pc.c[6] = trackno;
+	pc.c[7] = (buflen >> 8);
+	pc.c[8] = (buflen & 0xff);
+	pc.c[9] = (format << 6);
+
+	if (msf_flag)
+		pc.c[1] = 2;
+
+	return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* Try to read the entire TOC for the disk into our internal buffer. */
+static int cdrom_read_toc (ide_drive_t *drive)
+{
+	int stat, ntracks, i;
+	struct cdrom_info *info = drive->driver_data;
+	struct atapi_toc *toc = info->toc;
+	int minor = drive->select.b.unit << PARTN_BITS;
+	struct {
+		struct atapi_toc_header hdr;
+		struct atapi_toc_entry  ent;
+	} ms_tmp;
+
+	if (toc == NULL) {
+		/* Try to allocate space. */
+		toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
+						    GFP_KERNEL);
+		info->toc = toc;
+		if (toc == NULL) {
+			printk ("%s: No cdrom TOC buffer!\n", drive->name);
+			return -ENOMEM;
+		}
+	}
+
+	/* Check to see if the existing data is still valid.
+	   If it is, just return. */
+	if (CDROM_STATE_FLAGS (drive)->toc_valid)
+		(void) cdrom_check_status(drive);
+
+	if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
+
+	/* First read just the header, so we know how long the TOC is. */
+	stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
+				    sizeof (struct atapi_toc_header));
+	if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+		toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+		toc->hdr.last_track  = bcd2bin (toc->hdr.last_track);
+	}
+#endif  /* not STANDARD_ATAPI */
+
+	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+	if (ntracks <= 0) return -EIO;
+	if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
+
+	/* Now read the whole schmeer. */
+	stat = cdrom_read_tocentry (drive, toc->hdr.first_track, 1, 0, (char *)&toc->hdr,
+				    sizeof (struct atapi_toc_header) +
+				    (ntracks + 1) *
+				    sizeof (struct atapi_toc_entry));
+
+	if (stat && toc->hdr.first_track > 1) {
+		/* Cds with CDI tracks only don't have any TOC entries,
+		   despite of this the returned values are
+		   first_track == last_track = number of CDI tracks + 1,
+		   so that this case is indistinguishable from the same
+		   layout plus an additional audio track.
+		   If we get an error for the regular case, we assume
+		   a CDI without additional audio tracks. In this case
+		   the readable TOC is empty (CDI tracks are not included)
+		   and only holds the Leadout entry. Heiko Eißfeldt */
+		ntracks = 0;
+		stat = cdrom_read_tocentry (drive, CDROM_LEADOUT, 1,
+					    0, (char *)&toc->hdr,
+					    sizeof (struct atapi_toc_header) +
+					   (ntracks+1) *
+					    sizeof (struct atapi_toc_entry));
+		if (stat) {
+			return stat;
+		}
+#if ! STANDARD_ATAPI
+		if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+			toc->hdr.first_track = bin2bcd(CDROM_LEADOUT);
+			toc->hdr.last_track = bin2bcd(CDROM_LEADOUT);
+		} else
+#endif  /* not STANDARD_ATAPI */
+		{
+			toc->hdr.first_track = CDROM_LEADOUT;
+			toc->hdr.last_track = CDROM_LEADOUT;
+		}
+	} else if (stat) {
+		return stat;
+	}
+	if (stat) return stat;
+
+	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+		toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+		toc->hdr.last_track  = bcd2bin (toc->hdr.last_track);
+	}
+#endif  /* not STANDARD_ATAPI */
+
+	for (i=0; i<=ntracks; i++) {
+#if ! STANDARD_ATAPI
+		if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd) {
+			if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd)
+				toc->ent[i].track = bcd2bin (toc->ent[i].track);
+			msf_from_bcd (&toc->ent[i].addr.msf);
+		}
+#endif  /* not STANDARD_ATAPI */
+		toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.minute,
+						   toc->ent[i].addr.msf.second,
+						   toc->ent[i].addr.msf.frame);
+	}
+
+	/* Read the multisession information. */
+	if (toc->hdr.first_track != CDROM_LEADOUT) {
+		/* Read the multisession information. */
+		stat = cdrom_read_tocentry (drive, 0, 1, 1,
+					   (char *)&ms_tmp, sizeof (ms_tmp));
+		if (stat) return stat;
+	} else {
+		ms_tmp.ent.addr.msf.minute = 0;
+		ms_tmp.ent.addr.msf.second = 2;
+		ms_tmp.ent.addr.msf.frame  = 0;
+		ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT;
+	}
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
+		msf_from_bcd (&ms_tmp.ent.addr.msf);
+#endif  /* not STANDARD_ATAPI */
+
+	toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.minute,
+					    ms_tmp.ent.addr.msf.second,
+					    ms_tmp.ent.addr.msf.frame);
+
+	toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+	/* Now try to get the total cdrom capacity. */
+#if 0
+	stat = cdrom_get_last_written(MKDEV(HWIF(drive)->major, minor),
+				     (long *)&toc->capacity);
+	if (stat)
+#endif
+	stat = cdrom_read_capacity (drive, &toc->capacity);
+	if (stat) toc->capacity = 0x1fffff;
+
+	/* for general /dev/cdrom like mounting, one big disc */
+	drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+	HWIF(drive)->gd->sizes[minor] = (toc->capacity * SECTORS_PER_FRAME) >>
+					(BLOCK_SIZE_BITS - 9);
+
+	/* Remember that we've read this stuff. */
+	CDROM_STATE_FLAGS (drive)->toc_valid = 1;
+
+	/* should be "if multisession", but it does no harm. */
+	if (ntracks == 1)
+		return 0;
+
+	/* setup each minor to respond to a session */
+	minor++;
+	i = toc->hdr.first_track;
+	while ((i <= ntracks) && ((minor & CD_PART_MASK) < CD_PART_MAX)) {
+		drive->part[minor & PARTN_MASK].start_sect = 0;
+ 		drive->part[minor & PARTN_MASK].nr_sects =
+			(toc->ent[i].addr.lba *
+			SECTORS_PER_FRAME) << (BLOCK_SIZE_BITS - 9);
+		HWIF(drive)->gd->sizes[minor] = (toc->ent[i].addr.lba *
+			SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
+		i++;
+		minor++;
+	}
+
+	return 0;
+}
+
+
+static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf,
+				 int buflen)
+{
+	struct packet_command pc;
+
+	memset(&pc, 0, sizeof(pc));
+
+	pc.buffer = buf;
+	pc.buflen = buflen;
+	pc.c[0] = GPCMD_READ_SUBCHANNEL;
+	pc.c[1] = 2;     /* MSF addressing */
+	pc.c[2] = 0x40;  /* request subQ data */
+	pc.c[3] = format;
+	pc.c[7] = (buflen >> 8);
+	pc.c[8] = (buflen & 0xff);
+	return cdrom_queue_packet_command(drive, &pc);
+}
+
+/* ATAPI cdrom drives are free to select the speed you request or any slower
+   rate :-( Requesting too fast a speed will _not_ produce an error. */
+static int cdrom_select_speed (ide_drive_t *drive, int speed)
+{
+	struct packet_command pc;
+	memset(&pc, 0, sizeof(pc));
+
+	if (speed == 0)
+		speed = 0xffff; /* set to max */
+	else
+		speed *= 177;   /* Nx to kbytes/s */
+
+	pc.c[0] = GPCMD_SET_SPEED;
+	/* Read Drive speed in kbytes/second MSB */
+	pc.c[2] = (speed >> 8) & 0xff;	
+	/* Read Drive speed in kbytes/second LSB */
+	pc.c[3] = speed & 0xff;
+	if ( CDROM_CONFIG_FLAGS(drive)->cd_r ||
+                   CDROM_CONFIG_FLAGS(drive)->cd_rw ) {
+		/* Write Drive speed in kbytes/second MSB */
+		pc.c[4] = (speed >> 8) & 0xff;
+		/* Write Drive speed in kbytes/second LSB */
+		pc.c[5] = speed & 0xff;
+       }
+
+	return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int cdrom_get_toc_entry(ide_drive_t *drive, int track,
+				struct atapi_toc_entry **ent)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct atapi_toc *toc = info->toc;
+	int ntracks;
+
+	/* Check validity of requested track number. */
+	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+	if (toc->hdr.first_track == CDROM_LEADOUT) ntracks = 0;
+	if (track == CDROM_LEADOUT)
+		*ent = &toc->ent[ntracks];
+	else if (track < toc->hdr.first_track ||
+		 track > toc->hdr.last_track)
+		return -EINVAL;
+	else
+		*ent = &toc->ent[track - toc->hdr.first_track];
+
+	return 0;
+}
+
+
+
+
+
+/* the generic packet interface to cdrom.c */
+static int ide_cdrom_packet(struct cdrom_device_info *cdi,
+			    struct cdrom_generic_command *cgc)
+{
+	struct packet_command pc;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+	/* here we queue the commands from the uniform CD-ROM
+	   layer. the packet must be complete, as we do not
+	   touch it at all. */
+	memset(&pc, 0, sizeof(pc));
+	memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE);
+	pc.buffer = cgc->buffer;
+	pc.buflen = cgc->buflen;
+	cgc->stat = cdrom_queue_packet_command(drive, &pc);
+
+	/* There was an error, assign sense. */
+	if (cgc->stat)
+		cgc->sense = pc.sense_data;
+
+	return cgc->stat;
+}
+
+static
+int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct cdrom_generic_command cgc;
+	char buffer[16];
+	int stat;
+
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
+
+	/* These will be moved into the Uniform layer shortly... */
+	switch (cmd) {
+ 	case CDROMSETSPINDOWN: {
+ 		char spindown;
+ 
+ 		if (copy_from_user(&spindown, (void *) arg, sizeof(char)))
+			return -EFAULT;
+ 
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
+
+ 		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+
+ 		return cdrom_mode_select(cdi, &cgc);
+ 	} 
+ 
+ 	case CDROMGETSPINDOWN: {
+ 		char spindown;
+ 
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
+ 
+ 		spindown = buffer[11] & 0x0f;
+ 
+		if (copy_to_user((void *) arg, &spindown, sizeof (char)))
+			return -EFAULT;
+ 
+ 		return 0;
+ 	}
+  
+	default:
+		return -EINVAL;
+	}
+
+}
+
+static
+int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
+			   unsigned int cmd, void *arg)
+			   
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+
+	switch (cmd) {
+	case CDROMREADTOCHDR: {
+		int stat;
+		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
+		struct atapi_toc *toc;
+
+		/* Make sure our saved TOC is valid. */
+		stat = cdrom_read_toc(drive);
+		if (stat) return stat;
+
+		toc = info->toc;
+		tochdr->cdth_trk0 = toc->hdr.first_track;
+		tochdr->cdth_trk1 = toc->hdr.last_track;
+
+		return 0;
+	}
+
+	case CDROMREADTOCENTRY: {
+		int stat;
+		struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
+		struct atapi_toc_entry *toce;
+
+		stat = cdrom_get_toc_entry (drive, tocentry->cdte_track, &toce);
+		if (stat) return stat;
+
+		tocentry->cdte_ctrl = toce->control;
+		tocentry->cdte_adr  = toce->adr;
+		if (tocentry->cdte_format == CDROM_MSF) {
+			lba_to_msf (toce->addr.lba,
+				   &tocentry->cdte_addr.msf.minute,
+				   &tocentry->cdte_addr.msf.second,
+				   &tocentry->cdte_addr.msf.frame);
+		} else
+			tocentry->cdte_addr.lba = toce->addr.lba;
+
+		return 0;
+	}
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static
+int ide_cdrom_reset (struct cdrom_device_info *cdi)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct request req;
+
+	ide_init_drive_cmd (&req);
+	req.cmd = RESET_DRIVE_COMMAND;
+	return ide_do_drive_cmd (drive, &req, ide_wait);
+}
+
+
+static
+int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+	if (position) {
+		int stat = cdrom_lockdoor (drive, 0);
+		if (stat) return stat;
+	}
+
+	return cdrom_eject(drive, !position);
+}
+
+static
+int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	return cdrom_lockdoor (drive, lock);
+}
+
+#undef __ACER50__
+
+#ifdef __ACER50__
+/*
+ * the buffer struct used by ide_cdrom_get_capabilities()
+ */
+struct get_capabilities_buf {
+	char pad[8];
+	struct atapi_capabilities_page cap;	/* this is 4 bytes short of ATAPI standard */
+	char extra_cap[4];			/* Acer 50X needs the regulation size buffer */
+};
+
+static
+int ide_cdrom_get_capabilities (struct cdrom_device_info *cdi, struct get_capabilities_buf *buf)
+{
+	int stat, attempts = 3, buflen = sizeof(*buf);
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_generic_command cgc;
+
+	/*
+	 * Most drives don't care about the buffer size;
+	 * they return as much info as there's room for.
+	 * But some older drives (?) had trouble with the
+	 * standard size, preferring 4 bytes less.
+	 * And the modern Acer 50X rejects anything smaller
+	 * than the standard size.
+	 */
+	if (!(drive->id && !strcmp(drive->id->model,"ATAPI CD ROM DRIVE 50X MAX")))
+		buflen -= sizeof(buf->extra_cap);	/* for all drives except Acer 50X */
+
+	do { /* we seem to get stat=0x01,err=0x00 the first time (??) */
+		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+		if (stat == 0) {
+			/*
+			 * The ACER/AOpen 24X cdrom has the speed
+			 * fields byte-swapped from the standard.
+			 */
+			if (!(drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4))) {
+				buf->cap.curspeed = ntohs(buf->cap.curspeed);
+				buf->cap.maxspeed = ntohs(buf->cap.maxspeed);
+			}
+			CDROM_STATE_FLAGS (drive)->current_speed = (((unsigned int)buf->cap.curspeed) + (176/2)) / 176;
+			CDROM_CONFIG_FLAGS(drive)->max_speed     = (((unsigned int)buf->cap.maxspeed) + (176/2)) / 176;
+			return 0;
+		}
+	} while (--attempts);
+	return stat;
+}
+#endif /* __ACER50__ */
+
+static
+int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
+{
+#ifndef __ACER50__
+        int stat, attempts = 3;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_generic_command cgc;
+	struct {
+		char pad[8];
+		struct atapi_capabilities_page cap;
+	} buf;
+#else
+	int stat;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_generic_command cgc;
+	struct get_capabilities_buf buf;
+#endif /* __ACER50__ */
+
+	if ((stat = cdrom_select_speed (drive, speed)) < 0)
+		return stat;
+
+	init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
+
+#ifndef __ACER50__
+	/* Now with that done, update the speed fields */
+        do {    /* we seem to get stat=0x01,err=0x00 the first time (??) */
+                if (attempts-- <= 0)
+                        return 0;
+                stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+        } while (stat);
+
+        /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+        if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
+                CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
+                CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+        } else {
+                CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(ntohs(buf.cap.curspeed) + (176/2)) / 176;
+                CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(ntohs(buf.cap.maxspeed) + (176/2)) / 176;
+        }
+#else
+	if (ide_cdrom_get_capabilities(cdi,&buf))
+		return 0;
+#endif /* __ACER50__ */
+
+        cdi->speed = CDROM_STATE_FLAGS (drive)->current_speed;
+        return 0;
+}
+
+static
+int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+
+	if (slot_nr == CDSL_CURRENT) {
+		struct request_sense *sense = &info->sense_data;
+		int stat = cdrom_check_status(drive);
+		if (stat == 0 || sense->sense_key == UNIT_ATTENTION)
+			return CDS_DISC_OK;
+
+		if (sense->sense_key == NOT_READY && sense->asc == 0x04 &&
+		    sense->ascq == 0x04)
+			return CDS_DISC_OK;
+
+		if (sense->sense_key == NOT_READY) {
+			/* ATAPI doesn't have anything that can help
+			   us decide whether the drive is really
+			   emtpy or the tray is just open. irk. */
+			return CDS_TRAY_OPEN;
+		}
+
+		return CDS_DRIVE_NOT_READY;
+	} else {
+		return -EINVAL;
+	}
+}
+
+static
+int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
+				struct cdrom_multisession *ms_info)
+{
+	struct atapi_toc *toc;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+
+	toc = info->toc;
+	ms_info->addr.lba = toc->last_session_lba;
+	ms_info->xa_flag = toc->xa_flag;
+
+	return 0;
+}
+
+static
+int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
+		       struct cdrom_mcn *mcn_info)
+{
+	int stat;
+	char mcnbuf[24];
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+/* get MCN */
+	if ((stat = cdrom_read_subchannel(drive, 2, mcnbuf, sizeof (mcnbuf))))
+		return stat;
+
+	memcpy (mcn_info->medium_catalog_number, mcnbuf+9,
+		sizeof (mcn_info->medium_catalog_number)-1);
+	mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1]
+		= '\0';
+
+	return 0;
+}
+
+
+
+/****************************************************************************
+ * Other driver requests (open, close, check media change).
+ */
+
+static
+int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi,
+				       int slot_nr)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	
+	if (slot_nr == CDSL_CURRENT) {
+		(void) cdrom_check_status(drive);
+		CDROM_STATE_FLAGS (drive)->media_changed = 0;
+		return CDROM_STATE_FLAGS (drive)->media_changed;
+	} else {
+		return -EINVAL;
+	}
+}
+
+
+static
+int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose)
+{
+	return 0;
+}
+
+
+/*
+ * Close down the device.  Invalidate all cached blocks.
+ */
+
+static
+void ide_cdrom_release_real (struct cdrom_device_info *cdi)
+{
+}
+
+
+
+/****************************************************************************
+ * Device initialization.
+ */
+
+static
+struct cdrom_device_ops ide_cdrom_dops = {
+	ide_cdrom_open_real,    /* open */
+	ide_cdrom_release_real, /* release */
+	ide_cdrom_drive_status, /* drive_status */
+	ide_cdrom_check_media_change_real, /* media_changed */
+	ide_cdrom_tray_move,    /* tray_move */
+	ide_cdrom_lock_door,    /* lock_door */
+	ide_cdrom_select_speed, /* select_speed */
+	NULL,			/* select_disc */
+	ide_cdrom_get_last_session, /* get_last_session */
+	ide_cdrom_get_mcn, /* get_mcn */
+	ide_cdrom_reset, /* reset */
+	ide_cdrom_audio_ioctl, /* audio_ioctl */
+	ide_cdrom_dev_ioctl,   /* dev_ioctl */
+	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED
+	| CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
+	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS
+	| CDC_DRIVE_STATUS | CDC_CD_R | CDC_CD_RW | CDC_DVD
+	| CDC_DVD_R| CDC_DVD_RAM | CDC_GENERIC_PACKET, /* capability */
+	0, /* n_minors */
+	ide_cdrom_packet
+};
+
+static int ide_cdrom_register (ide_drive_t *drive, int nslots)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *devinfo = &info->devinfo;
+	int minor = (drive->select.b.unit)<<PARTN_BITS;
+
+	devinfo->dev = MKDEV (HWIF(drive)->major, minor | CD_PART_MASK);
+	devinfo->ops = &ide_cdrom_dops;
+	devinfo->mask = 0;
+	*(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed;
+	*(int *)&devinfo->capacity = nslots;
+	devinfo->handle = (void *) drive;
+	strcpy(devinfo->name, drive->name);
+	
+	/* set capability mask to match the probe. */
+	if (!CDROM_CONFIG_FLAGS (drive)->cd_r)
+		devinfo->mask |= CDC_CD_R;
+	if (!CDROM_CONFIG_FLAGS (drive)->cd_rw)
+		devinfo->mask |= CDC_CD_RW;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd)
+		devinfo->mask |= CDC_DVD;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd_r)
+		devinfo->mask |= CDC_DVD_R;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd_ram)
+		devinfo->mask |= CDC_DVD_RAM;
+	if (!CDROM_CONFIG_FLAGS (drive)->is_changer)
+		devinfo->mask |= CDC_SELECT_DISC;
+	if (!CDROM_CONFIG_FLAGS (drive)->audio_play)
+		devinfo->mask |= CDC_PLAY_AUDIO;
+	if (!CDROM_CONFIG_FLAGS (drive)->close_tray)
+		devinfo->mask |= CDC_CLOSE_TRAY;
+
+	devinfo->de = devfs_register (drive->de, "cd", 2, DEVFS_FL_DEFAULT,
+				      HWIF(drive)->major, minor,
+				      S_IFBLK | S_IRUGO | S_IWUGO, 0, 0,
+				      ide_fops, NULL);
+
+	return register_cdrom (devinfo);
+}
+
+
+static
+int ide_cdrom_probe_capabilities (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+#ifndef __ACER50__
+	int stat, nslots = 1, attempts = 3;
+	struct cdrom_generic_command cgc;
+	struct {
+		char pad[8];
+		struct atapi_capabilities_page cap;
+	} buf;
+#else
+	int nslots = 1;
+	struct cdrom_generic_command cgc;
+	struct get_capabilities_buf buf;
+#endif /* __ACER50__ */
+
+	if (CDROM_CONFIG_FLAGS (drive)->nec260) {
+		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;                       
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;       
+		return nslots;
+	}
+
+	init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
+	/* we have to cheat a little here. the packet will eventually
+	 * be queued with ide_cdrom_packet(), which extracts the
+	 * drive from cdi->handle. Since this device hasn't been
+	 * registered with the Uniform layer yet, it can't do this.
+	 * Same goes for cdi->ops.
+	 */
+	cdi->handle = (ide_drive_t *) drive;
+	cdi->ops = &ide_cdrom_dops;
+#ifndef __ACER50__
+	/* we seem to get stat=0x01,err=0x00 the first time (??) */
+	do {
+		if (attempts-- <= 0)
+			return 0;
+		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+	} while (stat);
+#else
+	if (ide_cdrom_get_capabilities(cdi,&buf))
+		return 0;
+#endif /* __ACER50__ */
+
+	if (buf.cap.lock == 0)
+		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+	if (buf.cap.eject)
+		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;
+	if (buf.cap.cd_r_write)
+		CDROM_CONFIG_FLAGS (drive)->cd_r = 1;
+	if (buf.cap.cd_rw_write)
+		CDROM_CONFIG_FLAGS (drive)->cd_rw = 1;
+	if (buf.cap.test_write)
+		CDROM_CONFIG_FLAGS (drive)->test_write = 1;
+	if (buf.cap.dvd_ram_read || buf.cap.dvd_r_read || buf.cap.dvd_rom)
+		CDROM_CONFIG_FLAGS (drive)->dvd = 1;
+	if (buf.cap.dvd_ram_write)
+		CDROM_CONFIG_FLAGS (drive)->dvd_r = 1;
+	if (buf.cap.dvd_r_write)
+		CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1;
+	if (buf.cap.audio_play)
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
+	if (buf.cap.mechtype == 0)
+		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
+
+#if ! STANDARD_ATAPI
+	if (cdi->sanyo_slot > 0) {
+		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+		nslots = 3;
+	}
+
+	else
+#endif /* not STANDARD_ATAPI */
+	if (buf.cap.mechtype == mechtype_individual_changer ||
+	    buf.cap.mechtype == mechtype_cartridge_changer) {
+		if ((nslots = cdrom_number_of_slots(cdi)) > 1) {
+			CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+			CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1;
+		}
+	}
+
+#ifndef __ACER50__
+	/* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+	if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
+		CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+	} else {
+		CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(ntohs(buf.cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(ntohs(buf.cap.maxspeed) + (176/2)) / 176;
+	}
+#endif /* __ACER50__ */
+
+	/* don't print speed if the drive reported 0.
+	 */
+	printk("%s: ATAPI", drive->name);
+	if (CDROM_CONFIG_FLAGS(drive)->max_speed)
+		printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed);
+	printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM");
+
+	if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram)
+        	printk (" DVD%s%s", 
+        	(CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "", 
+        	(CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : "");
+
+        if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) 
+        	printk (" CD%s%s", 
+        	(CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", 
+        	(CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : "");
+
+        if (CDROM_CONFIG_FLAGS (drive)->is_changer) 
+        	printk (" changer w/%d slots", nslots);
+        else 	
+        	printk (" drive");
+
+	printk (", %dkB Cache", be16_to_cpu(buf.cap.buffer_size));
+
+	if (drive->using_dma) {
+		if ((drive->id->field_valid & 4) &&
+		    (drive->id->hw_config & 0x2000) &&
+		    (HWIF(drive)->udma_four) &&
+		    (drive->id->dma_ultra & (drive->id->dma_ultra >> 11) & 3)) {
+			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
+		} else if ((drive->id->field_valid & 4) &&
+			   (drive->id->dma_ultra & (drive->id->dma_ultra >> 8) & 7)) {
+			printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
+		} else if (drive->id->field_valid & 4) {
+			printk(", (U)DMA");	/* Can be BIOS-enabled! */
+		} else {
+			printk(", DMA");
+		}
+	}
+	printk("\n");
+
+	return nslots;
+}
+
+static void ide_cdrom_add_settings(ide_drive_t *drive)
+{
+	int major = HWIF(drive)->major;
+	int minor = drive->select.b.unit << PARTN_BITS;
+
+	ide_add_setting(drive,	"breada_readahead",	SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
+	ide_add_setting(drive,	"file_readahead",	SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor],	NULL);
+	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
+}
+
+static
+int ide_cdrom_setup (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	int minor = drive->select.b.unit << PARTN_BITS;
+	int nslots;
+
+	set_device_ro(MKDEV(HWIF(drive)->major, minor), 1);
+	set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE);
+
+	drive->special.all	= 0;
+	drive->ready_stat	= 0;
+
+	CDROM_STATE_FLAGS (drive)->media_changed = 1;
+	CDROM_STATE_FLAGS (drive)->toc_valid     = 0;
+	CDROM_STATE_FLAGS (drive)->door_locked   = 0;
+
+#if NO_DOOR_LOCKING
+	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+#else
+	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
+#endif
+
+	if (drive->id != NULL)
+		CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
+			((drive->id->config & 0x0060) == 0x20);
+	else
+		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
+
+	CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
+	CDROM_CONFIG_FLAGS (drive)->cd_r = 0;
+	CDROM_CONFIG_FLAGS (drive)->cd_rw = 0;
+	CDROM_CONFIG_FLAGS (drive)->test_write = 0;
+	CDROM_CONFIG_FLAGS (drive)->dvd = 0;
+	CDROM_CONFIG_FLAGS (drive)->dvd_r = 0;
+	CDROM_CONFIG_FLAGS (drive)->dvd_ram = 0;
+	CDROM_CONFIG_FLAGS (drive)->no_eject = 1;
+	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
+	CDROM_CONFIG_FLAGS (drive)->audio_play = 0;
+	CDROM_CONFIG_FLAGS (drive)->close_tray = 1;
+	
+	/* limit transfer size per interrupt. */
+	CDROM_CONFIG_FLAGS (drive)->limit_nframes = 0;
+	if (drive->id != NULL) {
+		/* a testament to the nice quality of Samsung drives... */
+		if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2430"))
+			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+		else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2432"))
+			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+		/* the 3231 model does not support the SET_CD_SPEED command */
+		else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-3231"))
+			cdi->mask |= CDC_SELECT_SPEED;
+	}
+
+#if ! STANDARD_ATAPI
+	/* by default Sanyo 3 CD changer support is turned off and
+           ATAPI Rev 2.2+ standard support for CD changers is used */
+	cdi->sanyo_slot = 0;
+
+	CDROM_CONFIG_FLAGS (drive)->nec260 = 0;
+	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
+	CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
+	CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 0;
+	CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 0;
+
+	if (drive->id != NULL) {
+		if (strcmp (drive->id->model, "V003S0DS") == 0 &&
+		    drive->id->fw_rev[4] == '1' &&
+		    drive->id->fw_rev[6] <= '2') {
+			/* Vertos 300.
+			   Some versions of this drive like to talk BCD. */
+			CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+		}
+
+		else if (strcmp (drive->id->model, "V006E0DS") == 0 &&
+		    drive->id->fw_rev[4] == '1' &&
+		    drive->id->fw_rev[6] <= '2') {
+			/* Vertos 600 ESD. */
+			CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
+		}
+
+		else if (strcmp (drive->id->model,
+				 "NEC CD-ROM DRIVE:260") == 0 &&
+			 strncmp (drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */
+			/* Old NEC260 (not R).
+			   This drive was released before the 1.2 version
+			   of the spec. */
+			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->nec260         = 1;
+		}
+
+		else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
+			 strncmp (drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */
+			/* Wearnes */
+			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+		}
+
+                /* Sanyo 3 CD changer uses a non-standard command
+                    for CD changing */
+                 else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
+                         (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) ||
+                         (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) {
+                        /* uses CD in slot 0 when value is set to 3 */
+                        cdi->sanyo_slot = 3;
+                }
+
+
+	}
+#endif /* not STANDARD_ATAPI */
+
+	info->toc		= NULL;
+	info->buffer		= NULL;
+	info->sector_buffered	= 0;
+	info->nsectors_buffered	= 0;
+	info->changer_info      = NULL;
+	info->last_block	= 0;
+	info->start_seek	= 0;
+
+	nslots = ide_cdrom_probe_capabilities (drive);
+
+	if (ide_cdrom_register (drive, nslots)) {
+		printk ("%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name);
+		info->devinfo.handle = NULL;
+		return 1;
+	}
+	ide_cdrom_add_settings(drive);
+	return 0;
+}
+
+/* Forwarding functions to generic routines. */
+static
+int ide_cdrom_ioctl (ide_drive_t *drive,
+		     struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	return cdrom_fops.ioctl (inode, file, cmd, arg);
+}
+
+static
+int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	int rc;
+
+	MOD_INC_USE_COUNT;
+	if (info->buffer == NULL)
+		info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL);
+	rc = cdrom_fops.open (ip, fp);
+	if (rc) {
+		drive->usage--;
+		MOD_DEC_USE_COUNT;
+	}
+	return rc;
+}
+
+static
+void ide_cdrom_release (struct inode *inode, struct file *file,
+			ide_drive_t *drive)
+{
+	cdrom_fops.release (inode, file);
+	MOD_DEC_USE_COUNT;
+}
+
+static
+int ide_cdrom_check_media_change (ide_drive_t *drive)
+{
+	return cdrom_fops.check_media_change
+		(MKDEV (HWIF (drive)->major,
+			(drive->select.b.unit)<<PARTN_BITS));
+}
+
+static
+int ide_cdrom_cleanup(ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *devinfo = &info->devinfo;
+
+	if (ide_unregister_subdriver (drive))
+		return 1;
+	if (info->buffer != NULL)
+		kfree(info->buffer);
+	if (info->toc != NULL)
+		kfree(info->toc);
+	if (info->changer_info != NULL)
+		kfree(info->changer_info);
+	if (devinfo->handle == drive && unregister_cdrom (devinfo))
+		printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name);
+	kfree(info);
+	drive->driver_data = NULL;
+	return 0;
+}
+
+static ide_driver_t ide_cdrom_driver = {
+	"ide-cdrom",			/* name */
+	IDECD_VERSION,			/* version */
+	ide_cdrom,			/* media */
+	0,				/* busy */
+	1,				/* supports_dma */
+	1,				/* supports_dsc_overlap */
+	ide_cdrom_cleanup,		/* cleanup */
+	ide_do_rw_cdrom,		/* do_request */
+	NULL,				/* ??? or perhaps cdrom_end_request? */
+	ide_cdrom_ioctl,		/* ioctl */
+	ide_cdrom_open,			/* open */
+	ide_cdrom_release,		/* release */
+	ide_cdrom_check_media_change,	/* media_change */
+	NULL,				/* pre_reset */
+	NULL,				/* capacity */
+	NULL,				/* special */
+	NULL				/* proc */
+};
+
+int ide_cdrom_init (void);
+static ide_module_t ide_cdrom_module = {
+	IDE_DRIVER_MODULE,
+	ide_cdrom_init,
+	&ide_cdrom_driver,
+	NULL
+};
+
+/* options */
+char *ignore = NULL;
+
+#ifdef MODULE
+MODULE_PARM(ignore, "s");
+MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
+
+void __exit ide_cdrom_exit(void)
+{
+	ide_drive_t *drive;
+	int failed = 0;
+
+	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL)
+		if (ide_cdrom_cleanup (drive)) {
+			printk ("%s: cleanup_module() called while still busy\n", drive->name);
+			failed++;
+		}
+	ide_unregister_module (&ide_cdrom_module);
+}
+#endif /* MODULE */
+ 
+int ide_cdrom_init (void)
+{
+	ide_drive_t *drive;
+	struct cdrom_info *info;
+	int failed = 0;
+
+	MOD_INC_USE_COUNT;
+	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
+		/* skip drives that we were told to ignore */
+		if (ignore != NULL) {
+			if (strstr(ignore, drive->name)) {
+				printk("ide-cd: ignoring drive %s\n", drive->name);
+				continue;
+			}
+		}
+		if (drive->scsi) {
+			printk("ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name);
+			continue;
+		}
+		info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
+		if (info == NULL) {
+			printk ("%s: Can't allocate a cdrom structure\n", drive->name);
+			continue;
+		}
+		if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) {
+			printk ("%s: Failed to register the driver with ide.c\n", drive->name);
+			kfree (info);
+			continue;
+		}
+		memset (info, 0, sizeof (struct cdrom_info));
+		drive->driver_data = info;
+		DRIVER(drive)->busy++;
+		if (ide_cdrom_setup (drive)) {
+			DRIVER(drive)->busy--;
+			if (ide_cdrom_cleanup (drive))
+				printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name);
+			continue;
+		}
+		DRIVER(drive)->busy--;
+		failed--;
+	}
+	ide_register_module(&ide_cdrom_module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+module_init(ide_cdrom_init);
+module_exit(ide_cdrom_exit);
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-cd.h linux/drivers/ide/ide-cd.h
--- v2.3.51/linux/drivers/ide/ide-cd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-cd.h	Tue Mar 14 18:38:09 2000
@@ -0,0 +1,736 @@
+/*
+ *  linux/drivers/block/ide_cd.h
+ *
+ *  Copyright (C) 1996-98  Erik Andersen
+ *  Copyright (C) 1998-2000 Jens Axboe
+ */
+#ifndef _IDE_CD_H
+#define _IDE_CD_H
+
+#include <linux/cdrom.h>
+#include <asm/byteorder.h>
+
+/* Turn this on to have the driver print out the meanings of the
+   ATAPI error codes.  This will use up additional kernel-space
+   memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 1
+#endif
+
+
+/* Turning this on will remove code to work around various nonstandard
+   ATAPI implementations.  If you know your drive follows the standard,
+   this will give you a slightly smaller kernel. */
+
+#ifndef STANDARD_ATAPI
+#define STANDARD_ATAPI 0
+#endif
+
+
+/* Turning this on will disable the door-locking functionality.
+   This is apparently needed for supermount. */
+
+#ifndef NO_DOOR_LOCKING
+#define NO_DOOR_LOCKING 0
+#endif
+
+/************************************************************************/
+
+#define SECTOR_SIZE		512
+#define SECTOR_BITS 		9
+#define SECTORS_PER_FRAME	(CD_FRAMESIZE / SECTOR_SIZE)
+#define SECTOR_BUFFER_SIZE	(CD_FRAMESIZE * 32)
+#define SECTORS_BUFFER		(SECTOR_BUFFER_SIZE / SECTOR_SIZE)
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* special command codes for strategy routine. */
+#define PACKET_COMMAND        4315
+#define REQUEST_SENSE_COMMAND 4316
+#define RESET_DRIVE_COMMAND   4317
+
+
+/* Configuration flags.  These describe the capabilities of the drive.
+   They generally do not change after initialization, unless we learn
+   more about the drive from stuff failing. */
+struct ide_cd_config_flags {
+	__u8 drq_interrupt	: 1; /* Device sends an interrupt when ready
+					for a packet command. */
+	__u8 no_doorlock	: 1; /* Drive cannot lock the door. */
+	__u8 no_eject		: 1; /* Drive cannot eject the disc. */
+	__u8 nec260		: 1; /* Drive is a pre-1.2 NEC 260 drive. */
+	__u8 playmsf_as_bcd	: 1; /* PLAYMSF command takes BCD args. */
+	__u8 tocaddr_as_bcd	: 1; /* TOC addresses are in BCD. */
+	__u8 toctracks_as_bcd	: 1; /* TOC track numbers are in BCD. */
+	__u8 subchan_as_bcd	: 1; /* Subchannel info is in BCD. */
+	__u8 is_changer		: 1; /* Drive is a changer. */
+	__u8 cd_r		: 1; /* Drive can write to CD-R media . */
+	__u8 cd_rw		: 1; /* Drive can write to CD-R/W media . */
+	__u8 dvd		: 1; /* Drive is a DVD-ROM */
+	__u8 dvd_r		: 1; /* Drive can write DVD-R */
+	__u8 dvd_ram		: 1; /* Drive can write DVD-RAM */
+	__u8 test_write		: 1; /* Drive can fake writes */
+	__u8 supp_disc_present	: 1; /* Changer can report exact contents
+					of slots. */
+	__u8 limit_nframes	: 1; /* Drive does not provide data in
+					multiples of SECTOR_SIZE when more
+					than one interrupt is needed. */
+	__u8 seeking		: 1; /* Seeking in progress */
+	__u8 audio_play		: 1; /* can do audio related commands */
+	__u8 close_tray		: 1; /* can close the tray */
+	__u8 writing		: 1; /* pseudo write in progress */
+	__u8 reserved		: 3;
+	byte max_speed;		     /* Max speed of the drive */
+};
+#define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags))
+
+ 
+/* State flags.  These give information about the current state of the
+   drive, and will change during normal operation. */
+struct ide_cd_state_flags {
+	__u8 media_changed : 1; /* Driver has noticed a media change. */
+	__u8 toc_valid     : 1; /* Saved TOC information is current. */
+	__u8 door_locked   : 1; /* We think that the drive door is locked. */
+	__u8 writing       : 1; /* the drive is currently writing */
+	__u8 reserved      : 4;
+	byte current_speed;	/* Current speed of the drive */
+};
+
+#define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags))
+
+struct packet_command {
+	char *buffer;
+	int buflen;
+	int stat;
+	struct request_sense *sense_data;
+	unsigned char c[12];
+};
+
+/* Structure of a MSF cdrom address. */
+struct atapi_msf {
+	byte reserved;
+	byte minute;
+	byte second;
+	byte frame;
+};
+
+/* Space to hold the disk TOC. */
+#define MAX_TRACKS 99
+struct atapi_toc_header {
+	unsigned short toc_length;
+	byte first_track;
+	byte last_track;
+};
+
+struct atapi_toc_entry {
+	byte reserved1;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 adr     : 4;
+	__u8 control : 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 control : 4;
+	__u8 adr     : 4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	byte track;
+	byte reserved2;
+	union {
+		unsigned lba;
+		struct atapi_msf msf;
+	} addr;
+};
+
+struct atapi_toc {
+	int    last_session_lba;
+	int    xa_flag;
+	unsigned capacity;
+	struct atapi_toc_header hdr;
+	struct atapi_toc_entry  ent[MAX_TRACKS+1];
+	  /* One extra for the leadout. */
+};
+
+
+/* This structure is annoyingly close to, but not identical with,
+   the cdrom_subchnl structure from cdrom.h. */
+struct atapi_cdrom_subchnl {
+ 	u_char  acdsc_reserved;
+ 	u_char  acdsc_audiostatus;
+ 	u_short acdsc_length;
+	u_char  acdsc_format;
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	u_char  acdsc_ctrl:     4;
+	u_char  acdsc_adr:      4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	u_char  acdsc_adr:	4;
+	u_char  acdsc_ctrl:	4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	u_char  acdsc_trk;
+	u_char  acdsc_ind;
+	union {
+		struct atapi_msf msf;
+		int	lba;
+	} acdsc_absaddr;
+	union {
+		struct atapi_msf msf;
+		int	lba;
+	} acdsc_reladdr;
+};
+
+
+
+/* This should probably go into cdrom.h along with the other
+ * generic stuff now in the Mt. Fuji spec.
+ */
+struct atapi_capabilities_page {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 parameters_saveable : 1;
+	__u8 reserved1           : 1;
+	__u8 page_code           : 6;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 page_code           : 6;
+	__u8 reserved1           : 1;
+	__u8 parameters_saveable : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+	byte     page_length;
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved2           : 2;
+	/* Drive supports reading of DVD-RAM discs */
+	__u8 dvd_ram_read        : 1;
+	/* Drive supports reading of DVD-R discs */
+	__u8 dvd_r_read          : 1;
+	/* Drive supports reading of DVD-ROM discs */
+	__u8 dvd_rom             : 1;
+	/* Drive supports reading CD-R discs with addressing method 2 */
+	__u8 method2             : 1; /* reserved in 1.2 */
+	/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
+	__u8 cd_rw_read		 : 1; /* reserved in 1.2 */
+	/* Drive supports read from CD-R discs (orange book, part II) */
+	__u8 cd_r_read           : 1; /* reserved in 1.2 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	/* Drive supports read from CD-R discs (orange book, part II) */
+	__u8 cd_r_read           : 1; /* reserved in 1.2 */
+	/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
+	__u8 cd_rw_read          : 1; /* reserved in 1.2 */
+	/* Drive supports reading CD-R discs with addressing method 2 */
+	__u8 method2             : 1;
+	/* Drive supports reading of DVD-ROM discs */
+	__u8 dvd_rom             : 1;
+	/* Drive supports reading of DVD-R discs */
+	__u8 dvd_r_read          : 1;
+	/* Drive supports reading of DVD-RAM discs */
+	__u8 dvd_ram_read        : 1;
+	__u8 reserved2		 : 2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved3           : 2;
+	/* Drive can write DVD-RAM discs */
+	__u8 dvd_ram_write       : 1;
+	/* Drive can write DVD-R discs */
+	__u8 dvd_r_write         : 1;
+	__u8 reserved3a          : 1;
+	/* Drive can fake writes */
+	__u8 test_write          : 1;
+	/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
+	__u8 cd_rw_write	 : 1; /* reserved in 1.2 */
+	/* Drive supports write to CD-R discs (orange book, part II) */
+	__u8 cd_r_write          : 1; /* reserved in 1.2 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	/* Drive can write to CD-R discs (orange book, part II) */
+	__u8 cd_r_write          : 1; /* reserved in 1.2 */
+	/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
+	__u8 cd_rw_write	 : 1; /* reserved in 1.2 */
+	/* Drive can fake writes */
+	__u8 test_write          : 1;
+	__u8 reserved3a          : 1;
+	/* Drive can write DVD-R discs */
+	__u8 dvd_r_write         : 1;
+	/* Drive can write DVD-RAM discs */
+	__u8 dvd_ram_write       : 1;
+	__u8 reserved3           : 2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved4           : 1;
+	/* Drive can read multisession discs. */
+	__u8 multisession        : 1;
+	/* Drive can read mode 2, form 2 data. */
+	__u8 mode2_form2         : 1;
+	/* Drive can read mode 2, form 1 (XA) data. */
+	__u8 mode2_form1         : 1;
+	/* Drive supports digital output on port 2. */
+	__u8 digport2            : 1;
+	/* Drive supports digital output on port 1. */
+	__u8 digport1            : 1;
+	/* Drive can deliver a composite audio/video data stream. */
+	__u8 composite           : 1;
+	/* Drive supports audio play operations. */
+	__u8 audio_play          : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	/* Drive supports audio play operations. */
+	__u8 audio_play          : 1;
+	/* Drive can deliver a composite audio/video data stream. */
+	__u8 composite           : 1;
+	/* Drive supports digital output on port 1. */
+	__u8 digport1            : 1;
+	/* Drive supports digital output on port 2. */
+	__u8 digport2            : 1;
+	/* Drive can read mode 2, form 1 (XA) data. */
+	__u8 mode2_form1         : 1;
+	/* Drive can read mode 2, form 2 data. */
+	__u8 mode2_form2         : 1;
+	/* Drive can read multisession discs. */
+	__u8 multisession        : 1;
+	__u8 reserved4           : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved5           : 1;
+	/* Drive can return Media Catalog Number (UPC) info. */
+	__u8 upc                 : 1;
+	/* Drive can return International Standard Recording Code info. */
+	__u8 isrc                : 1;
+	/* Drive supports C2 error pointers. */
+	__u8 c2_pointers         : 1;
+	/* R-W data will be returned deinterleaved and error corrected. */
+	__u8 rw_corr             : 1;
+	/* Subchannel reads can return combined R-W information. */
+	__u8 rw_supported        : 1;
+	/* Drive can continue a read cdda operation from a loss of streaming.*/
+	__u8 cdda_accurate       : 1;
+	/* Drive can read Red Book audio data. */
+	__u8 cdda                : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	/* Drive can read Red Book audio data. */
+	__u8 cdda                : 1;
+	/* Drive can continue a read cdda operation from a loss of streaming.*/
+	__u8 cdda_accurate       : 1;
+	/* Subchannel reads can return combined R-W information. */
+	__u8 rw_supported        : 1;
+	/* R-W data will be returned deinterleaved and error corrected. */
+	__u8 rw_corr             : 1;
+	/* Drive supports C2 error pointers. */
+	__u8 c2_pointers         : 1;
+	/* Drive can return International Standard Recording Code info. */
+	__u8 isrc                : 1;
+	/* Drive can return Media Catalog Number (UPC) info. */
+	__u8 upc                 : 1;
+	__u8 reserved5           : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	/* Drive mechanism types. */
+	mechtype_t mechtype	 : 3;
+	__u8 reserved6           : 1;
+	/* Drive can eject a disc or changer cartridge. */
+	__u8 eject               : 1;
+	/* State of prevent/allow jumper. */
+	__u8 prevent_jumper      : 1;
+	/* Present state of door lock. */
+	__u8 lock_state          : 1;
+	/* Drive can lock the door. */
+	__u8 lock                : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+
+	/* Drive can lock the door. */
+	__u8 lock                : 1;
+	/* Present state of door lock. */
+	__u8 lock_state          : 1;
+	/* State of prevent/allow jumper. */
+	__u8 prevent_jumper      : 1;
+	/* Drive can eject a disc or changer cartridge. */
+	__u8 eject               : 1;
+	__u8 reserved6           : 1;
+	/* Drive mechanism types. */
+	mechtype_t mechtype	 : 3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved7           : 4;
+	/* Drive supports software slot selection. */
+	__u8 sss                 : 1;  /* reserved in 1.2 */
+	/* Changer can report exact contents of slots. */
+	__u8 disc_present        : 1;  /* reserved in 1.2 */
+	/* Audio for each channel can be muted independently. */
+	__u8 separate_mute       : 1;
+	/* Audio level for each channel can be controlled independently. */
+	__u8 separate_volume     : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+
+	/* Audio level for each channel can be controlled independently. */
+	__u8 separate_volume     : 1;
+	/* Audio for each channel can be muted independently. */
+	__u8 separate_mute       : 1;
+	/* Changer can report exact contents of slots. */
+	__u8 disc_present        : 1;  /* reserved in 1.2 */
+	/* Drive supports software slot selection. */
+	__u8 sss                 : 1;  /* reserved in 1.2 */
+	__u8 reserved7           : 4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+	/* Note: the following four fields are returned in big-endian form. */
+	/* Maximum speed (in kB/s). */
+	unsigned short maxspeed;
+	/* Number of discrete volume levels. */
+	unsigned short n_vol_levels;
+	/* Size of cache in drive, in kB. */
+	unsigned short buffer_size;
+	/* Current speed (in kB/s). */
+	unsigned short curspeed;
+
+	/* Truncate the structure here, so we don't have headaches reading
+	   from older drives. */
+};
+
+
+struct atapi_mechstat_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 fault         : 1;
+	__u8 changer_state : 2;
+	__u8 curslot       : 5;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 curslot       : 5;
+	__u8 changer_state : 2;
+	__u8 fault         : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 mech_state    : 3;
+	__u8 door_open     : 1;
+	__u8 reserved1     : 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 reserved1     : 4;
+	__u8 door_open     : 1;
+	__u8 mech_state    : 3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+	byte     curlba[3];
+	byte     nslots;
+	__u8 short slot_tablelen;
+};
+
+
+struct atapi_slot {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 disc_present : 1;
+	__u8 reserved1    : 6;
+	__u8 change       : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 change       : 1;
+	__u8 reserved1    : 6;
+	__u8 disc_present : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+	byte reserved2[3];
+};
+
+struct atapi_changer_info {
+	struct atapi_mechstat_header hdr;
+	struct atapi_slot slots[0];
+};
+
+/* Extra per-device info for cdrom drives. */
+struct cdrom_info {
+
+	/* Buffer for table of contents.  NULL if we haven't allocated
+	   a TOC buffer for this device yet. */
+
+	struct atapi_toc *toc;
+
+	unsigned long	sector_buffered;
+	unsigned long	nsectors_buffered;
+	unsigned char	*buffer;
+
+	/* The result of the last successful request sense command
+	   on this device. */
+	struct request_sense sense_data;
+
+	struct request request_sense_request;
+	struct packet_command request_sense_pc;
+	int dma;
+	unsigned long last_block;
+	unsigned long start_seek;
+	/* Buffer to hold mechanism status and changer slot table. */
+	struct atapi_changer_info *changer_info;
+
+	struct ide_cd_config_flags	config_flags;
+	struct ide_cd_state_flags	state_flags;
+
+        /* Per-device info needed by cdrom.c generic driver. */
+        struct cdrom_device_info devinfo;
+};
+
+/****************************************************************************
+ * Descriptions of ATAPI error codes.
+ */
+
+#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
+
+/* This stuff should be in cdrom.h, since it is now generic... */
+
+/* ATAPI sense keys (from table 140 of ATAPI 2.6) */
+#define NO_SENSE                0x00
+#define RECOVERED_ERROR         0x01
+#define NOT_READY               0x02
+#define MEDIUM_ERROR            0x03
+#define HARDWARE_ERROR          0x04
+#define ILLEGAL_REQUEST         0x05
+#define UNIT_ATTENTION          0x06
+#define DATA_PROTECT            0x07
+#define ABORTED_COMMAND         0x0b
+#define MISCOMPARE              0x0e
+
+ 
+
+/* This stuff should be in cdrom.h, since it is now generic... */
+#if VERBOSE_IDE_CD_ERRORS
+
+ /* The generic packet command opcodes for CD/DVD Logical Units,
+ * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ 
+const struct {
+	unsigned short packet_command;
+	const char * const text;
+} packet_command_texts[] = {
+	{ GPCMD_TEST_UNIT_READY, "Test Unit Ready" },
+	{ GPCMD_REQUEST_SENSE, "Request Sense" },
+	{ GPCMD_FORMAT_UNIT, "Format Unit" },
+	{ GPCMD_INQUIRY, "Inquiry" },
+	{ GPCMD_START_STOP_UNIT, "Start/Stop Unit" },
+	{ GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" },
+	{ GPCMD_READ_FORMAT_CAPACITIES, "Read Format Capacities" },
+	{ GPCMD_READ_CDVD_CAPACITY, "Read Cd/Dvd Capacity" },
+	{ GPCMD_READ_10, "Read 10" },
+	{ GPCMD_WRITE_10, "Write 10" },
+	{ GPCMD_SEEK, "Seek" },
+	{ GPCMD_WRITE_AND_VERIFY_10, "Write and Verify 10" },
+	{ GPCMD_VERIFY_10, "Verify 10" },
+	{ GPCMD_FLUSH_CACHE, "Flush Cache" },
+	{ GPCMD_READ_SUBCHANNEL, "Read Subchannel" },
+	{ GPCMD_READ_TOC_PMA_ATIP, "Read Table of Contents" },
+	{ GPCMD_READ_HEADER, "Read Header" },
+	{ GPCMD_PLAY_AUDIO_10, "Play Audio 10" },
+	{ GPCMD_GET_CONFIGURATION, "Get Configuration" },
+	{ GPCMD_PLAY_AUDIO_MSF, "Play Audio MSF" },
+	{ GPCMD_PLAYAUDIO_TI, "Play Audio TrackIndex" },
+	{ GPCMD_GET_EVENT_STATUS_NOTIFICATION, "Get Event Status Notification" },
+	{ GPCMD_PAUSE_RESUME, "Pause/Resume" },
+	{ GPCMD_STOP_PLAY_SCAN, "Stop Play/Scan" },
+	{ GPCMD_READ_DISC_INFO, "Read Disc Info" },
+	{ GPCMD_READ_TRACK_RZONE_INFO, "Read Track Rzone Info" },
+	{ GPCMD_RESERVE_RZONE_TRACK, "Reserve Rzone Track" },
+	{ GPCMD_SEND_OPC, "Send OPC" },
+	{ GPCMD_MODE_SELECT_10, "Mode Select 10" },
+	{ GPCMD_REPAIR_RZONE_TRACK, "Repair Rzone Track" },
+	{ GPCMD_MODE_SENSE_10, "Mode Sense 10" },
+	{ GPCMD_CLOSE_TRACK, "Close Track" },
+	{ GPCMD_BLANK, "Blank" },
+	{ GPCMD_SEND_EVENT, "Send Event" },
+	{ GPCMD_SEND_KEY, "Send Key" },
+	{ GPCMD_REPORT_KEY, "Report Key" },
+	{ GPCMD_LOAD_UNLOAD, "Load/Unload" },
+	{ GPCMD_SET_READ_AHEAD, "Set Read-ahead" },
+	{ GPCMD_READ_12, "Read 12" },
+	{ GPCMD_GET_PERFORMANCE, "Get Performance" },
+	{ GPCMD_SEND_DVD_STRUCTURE, "Send DVD Structure" },
+	{ GPCMD_READ_DVD_STRUCTURE, "Read DVD Structure" },
+	{ GPCMD_SET_STREAMING, "Set Streaming" },
+	{ GPCMD_READ_CD_MSF, "Read CD MSF" },
+	{ GPCMD_SCAN, "Scan" },
+	{ GPCMD_SET_SPEED, "Set Speed" },
+	{ GPCMD_PLAY_CD, "Play CD" },
+	{ GPCMD_MECHANISM_STATUS, "Mechanism Status" },
+	{ GPCMD_READ_CD, "Read CD" },
+};
+
+
+
+/* From Table 303 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
+const char * const sense_key_texts[16] = {
+	"No sense data",
+	"Recovered error",
+	"Not ready",
+	"Medium error",
+	"Hardware error",
+	"Illegal request",
+	"Unit attention",
+	"Data protect",
+	"(reserved)",
+	"(reserved)",
+	"(reserved)",
+	"Aborted command",
+	"(reserved)",
+	"(reserved)",
+	"Miscompare",
+	"(reserved)",
+};
+
+/* From Table 304 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
+const struct {
+	unsigned long asc_ascq;
+	const char * const text;
+} sense_data_texts[] = {
+	{ 0x000000, "No additional sense information" },
+	{ 0x000011, "Play operation in progress" },
+	{ 0x000012, "Play operation paused" },
+	{ 0x000013, "Play operation successfully completed" },
+	{ 0x000014, "Play operation stopped due to error" },
+	{ 0x000015, "No current audio status to return" },
+	{ 0x010c0a, "Write error - padding blocks added" },
+	{ 0x011700, "Recovered data with no error correction applied" },
+	{ 0x011701, "Recovered data with retries" },
+	{ 0x011702, "Recovered data with positive head offset" },
+	{ 0x011703, "Recovered data with negative head offset" },
+	{ 0x011704, "Recovered data with retries and/or CIRC applied" },
+	{ 0x011705, "Recovered data using previous sector ID" },
+	{ 0x011800, "Recovered data with error correction applied" },
+	{ 0x011801, "Recovered data with error correction and retries applied"},
+	{ 0x011802, "Recovered data - the data was auto-reallocated" },
+	{ 0x011803, "Recovered data with CIRC" },
+	{ 0x011804, "Recovered data with L-EC" },
+	{ 0x015d00, 
+	    "Failure prediction threshold exceeded - Predicted logical unit failure" },
+	{ 0x015d01, 
+	    "Failure prediction threshold exceeded - Predicted media failure" },
+	{ 0x015dff, "Failure prediction threshold exceeded - False" },
+	{ 0x017301, "Power calibration area almost full" },
+	{ 0x020400, "Logical unit not ready - cause not reportable" },
+	/* Following is misspelled in ATAPI 2.6, _and_ in Mt. Fuji */
+	{ 0x020401,
+	  "Logical unit not ready - in progress [sic] of becoming ready" },
+	{ 0x020402, "Logical unit not ready - initializing command required" },
+	{ 0x020403, "Logical unit not ready - manual intervention required" },
+	{ 0x020404, "In process of becoming ready - writing" },
+	{ 0x020600, "No reference position found (media may be upside down)" },
+	{ 0x023000, "Incompatible medium installed" },
+	{ 0x023a00, "Medium not present" },
+	{ 0x025300, "Media load or eject failed" },
+	{ 0x025700, "Unable to recover table of contents" },
+	{ 0x030300, "Peripheral device write fault" },
+	{ 0x030301, "No write current" },
+	{ 0x030302, "Excessive write errors" },
+	{ 0x030c00, "Write error" },
+	{ 0x030c01, "Write error - Recovered with auto reallocation" },
+	{ 0x030c02, "Write error - auto reallocation failed" },
+	{ 0x030c03, "Write error - recommend reassignment" },
+	{ 0x030c04, "Compression check miscompare error" },
+	{ 0x030c05, "Data expansion occurred during compress" },
+	{ 0x030c06, "Block not compressible" },
+	{ 0x030c07, "Write error - recovery needed" },
+	{ 0x030c08, "Write error - recovery failed" },
+	{ 0x030c09, "Write error - loss of streaming" },
+	{ 0x031100, "Unrecovered read error" },
+	{ 0x031106, "CIRC unrecovered error" },
+	{ 0x033101, "Format command failed" },
+	{ 0x033200, "No defect spare location available" },
+	{ 0x033201, "Defect list update failure" },
+	{ 0x035100, "Erase failure" },
+	{ 0x037200, "Session fixation error" },
+	{ 0x037201, "Session fixation error writin lead-in" },
+	{ 0x037202, "Session fixation error writin lead-out" },
+	{ 0x037300, "CD control error" },
+	{ 0x037302, "Power calibration area is full" },
+	{ 0x037303, "Power calibration area error" },
+	{ 0x037304, "Program memory area / RMA update failure" },
+	{ 0x037305, "Program memory area / RMA is full" },
+	{ 0x037306, "Program memory area / RMA is (almost) full" },
+
+	{ 0x040200, "No seek complete" },
+	{ 0x040300, "Write fault" },
+	{ 0x040900, "Track following error" },
+	{ 0x040901, "Tracking servo failure" },
+	{ 0x040902, "Focus servo failure" },
+	{ 0x040903, "Spindle servo failure" },
+	{ 0x041500, "Random positioning error" },
+	{ 0x041501, "Mechanical positioning or changer error" },
+	{ 0x041502, "Positioning error detected by read of medium" },
+	{ 0x043c00, "Mechanical positioning or changer error" },
+	{ 0x044000, "Diagnostic failure on component (ASCQ)" },
+	{ 0x044400, "Internal CD/DVD logical unit failure" },
+	{ 0x04b600, "Media load mechanism failed" },
+	{ 0x051a00, "Parameter list length error" },
+	{ 0x052000, "Invalid command operation code" },
+	{ 0x052c00, "Command sequence error" },
+	{ 0x052100, "Logical block address out of range" },
+	{ 0x052102, "Invalid address for write" },
+	{ 0x052400, "Invalid field in command packet" },
+	{ 0x052600, "Invalid field in parameter list" },
+	{ 0x052601, "Parameter not supported" },
+	{ 0x052602, "Parameter value invalid" },
+	{ 0x052700, "Write protected media" },
+	{ 0x052c00, "Command sequence error" },
+	{ 0x052c03, "Current program area is not empty" },
+	{ 0x052c04, "Current program area is empty" },
+	{ 0x053001, "Cannot read medium - unknown format" },
+	{ 0x053002, "Cannot read medium - incompatible format" },
+	{ 0x053900, "Saving parameters not supported" },
+	{ 0x054e00, "Overlapped commands attempted" },
+	{ 0x055302, "Medium removal prevented" },
+	{ 0x055500, "System resource failure" },
+	{ 0x056300, "End of user area encountered on this track" },
+	{ 0x056400, "Illegal mode for this track or incompatible medium" },
+	{ 0x056f00, "Copy protection key exchange failure - Authentication failure" },
+	{ 0x056f01, "Copy protection key exchange failure - Key not present" },
+	{ 0x056f02, "Copy protection key exchange failure - Key not established" },
+	{ 0x056f03, "Read of scrambled sector without authentication" },
+	{ 0x056f04, "Media region code is mismatched to logical unit" },
+	{ 0x056f05,  "Drive region must be permanent / region reset count error" },
+	{ 0x057203, "Session fixation error - incomplete track in session" },
+	{ 0x057204, "Empty or partially written reserved track" },
+	{ 0x057205, "No more RZONE reservations are allowed" },
+	{ 0x05bf00, "Loss of streaming" },
+	{ 0x062800, "Not ready to ready transition, medium may have changed" },
+	{ 0x062900, "Power on, reset or hardware reset occurred" },
+	{ 0x062a00, "Parameters changed" },
+	{ 0x062a01, "Mode parameters changed" },
+	{ 0x062e00, "Insufficient time for operation" },
+	{ 0x063f00, "Logical unit operating conditions have changed" },
+	{ 0x063f01, "Microcode has been changed" },
+	{ 0x065a00, "Operator request or state change input (unspecified)" },
+	{ 0x065a01, "Operator medium removal request" },
+	{ 0x0bb900, "Play operation aborted" },
+
+	/* Here we use 0xff for the key (not a valid key) to signify
+	 * that these can have _any_ key value associated with them... */
+	{ 0xff0401, "Logical unit is in process of becoming ready" },
+	{ 0xff0400, "Logical unit not ready, cause not reportable" },
+	{ 0xff0402, "Logical unit not ready, initializing command required" },
+	{ 0xff0403, "Logical unit not ready, manual intervention required" },
+	{ 0xff0500, "Logical unit does not respond to selection" },
+	{ 0xff0800, "Logical unit communication failure" },
+	{ 0xff0802, "Logical unit communication parity error" },
+	{ 0xff0801, "Logical unit communication time-out" },
+	{ 0xff2500, "Logical unit not supported" },
+	{ 0xff4c00, "Logical unit failed self-configuration" },
+	{ 0xff3e00, "Logical unit has not self-configured yet" },
+};
+#endif
+
+
+#endif /* _IDE_CD_H */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-cs.c linux/drivers/ide/ide-cs.c
--- v2.3.51/linux/drivers/ide/ide-cs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-cs.c	Thu Mar  2 11:32:37 2000
@@ -0,0 +1,481 @@
+/*======================================================================
+
+    A driver for PCMCIA IDE/ATA disk cards
+
+    ide_cs.c 1.26 1999/11/16 02:10:49
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dhinds@pcmcia.sourceforge.org>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/hdreg.h>
+#include <linux/major.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"ide_cs.c 1.26 1999/11/16 02:10:49 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+static const char ide_major[] = {
+    IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR,
+#ifdef IDE4_MAJOR
+    IDE4_MAJOR, IDE5_MAJOR
+#endif
+};
+
+typedef struct ide_info_t {
+    dev_link_t	link;
+    int		ndev;
+    dev_node_t	node;
+    int		hd;
+} ide_info_t;
+
+static void ide_config(dev_link_t *link);
+static void ide_release(u_long arg);
+static int ide_event(event_t event, int priority,
+		     event_callback_args_t *args);
+
+static dev_info_t dev_info = "ide_cs";
+
+static dev_link_t *ide_attach(void);
+static void ide_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/*======================================================================
+
+    ide_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+======================================================================*/
+
+static dev_link_t *ide_attach(void)
+{
+    ide_info_t *info;
+    dev_link_t *link;
+    client_reg_t client_reg;
+    int i, ret;
+    
+    DEBUG(0, "ide_attach()\n");
+
+    /* Create new ide device */
+    info = kmalloc(sizeof(*info), GFP_KERNEL);
+    if (!info) return NULL;
+    memset(info, 0, sizeof(*info));
+    link = &info->link; link->priv = info;
+
+    link->release.function = &ide_release;
+    link->release.data = (u_long)link;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+    link->io.IOAddrLines = 3;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+    if (irq_list[0] == -1)
+	link->irq.IRQInfo2 = irq_mask;
+    else
+	for (i = 0; i < 4; i++)
+	    link->irq.IRQInfo2 |= 1 << irq_list[i];
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &ide_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = CardServices(RegisterClient, &link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+	cs_error(link->handle, RegisterClient, ret);
+	ide_detach(link);
+	return NULL;
+    }
+    
+    return link;
+} /* ide_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void ide_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+    long flags;
+    int ret;
+
+    DEBUG(0, "ide_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    save_flags(flags);
+    cli();
+    if (link->state & DEV_RELEASE_PENDING) {
+	del_timer(&link->release);
+	link->state &= ~DEV_RELEASE_PENDING;
+    }
+    restore_flags(flags);
+    
+    if (link->state & DEV_CONFIG)
+	ide_release((u_long)link);
+    
+    if (link->handle) {
+	ret = CardServices(DeregisterClient, link->handle);
+	if (ret != CS_SUCCESS)
+	    cs_error(link->handle, DeregisterClient, ret);
+    }
+    
+    /* Unlink, free device structure */
+    *linkp = link->next;
+    kfree(link->priv);
+    
+} /* ide_detach */
+
+/*======================================================================
+
+    ide_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ide device available to the system.
+
+======================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+#define CFG_CHECK(fn, args...) \
+if (CardServices(fn, args) != 0) goto next_entry
+
+void ide_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    ide_info_t *info = link->priv;
+    tuple_t tuple;
+    u_short buf[128];
+    cisparse_t parse;
+    config_info_t conf;
+    cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
+    cistpl_cftable_entry_t dflt = { 0 };
+    int i, pass, last_ret, last_fn, hd, io_base, ctl_base;
+
+    DEBUG(0, "ide_config(0x%p)\n", link);
+    
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+    tuple.Attributes = 0;
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    CS_CHECK(GetTupleData, handle, &tuple);
+    CS_CHECK(ParseTuple, handle, &tuple, &parse);
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    /* Not sure if this is right... look up the current Vcc */
+    CS_CHECK(GetConfigurationInfo, handle, &conf);
+    link->conf.Vcc = conf.Vcc;
+    
+    pass = io_base = ctl_base = 0;
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    tuple.Attributes = 0;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    while (1) {
+	CFG_CHECK(GetTupleData, handle, &tuple);
+	CFG_CHECK(ParseTuple, handle, &tuple, &parse);
+
+	/* Check for matching Vcc, unless we're desperate */
+	if (!pass) {
+	    if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+		if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
+		    goto next_entry;
+	    } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+		if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000)
+		    goto next_entry;
+	    }
+	}
+	
+	if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+	    link->conf.Vpp1 = link->conf.Vpp2 =
+		cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+	else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
+	    link->conf.Vpp1 = link->conf.Vpp2 =
+		dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
+	
+	if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+	    cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+	    link->conf.ConfigIndex = cfg->index;
+	    link->io.BasePort1 = io->win[0].base;
+	    link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+	    if (!(io->flags & CISTPL_IO_16BIT))
+		link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	    if (io->nwin == 2) {
+		link->io.NumPorts1 = 8;
+		link->io.BasePort2 = io->win[1].base;
+		link->io.NumPorts2 = 1;
+		CFG_CHECK(RequestIO, link->handle, &link->io);
+		io_base = link->io.BasePort1;
+		ctl_base = link->io.BasePort2;
+	    } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
+		link->io.NumPorts1 = io->win[0].len;
+		link->io.NumPorts2 = 0;
+		CFG_CHECK(RequestIO, link->handle, &link->io);
+		io_base = link->io.BasePort1;
+		ctl_base = link->io.BasePort1+0x0e;
+	    } else goto next_entry;
+	    /* If we've got this far, we're done */
+	    break;
+	}
+	
+    next_entry:
+	if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
+	if (pass) {
+	    CS_CHECK(GetNextTuple, handle, &tuple);
+	} else if (CardServices(GetNextTuple, handle, &tuple) != 0) {
+	    CS_CHECK(GetFirstTuple, handle, &tuple);
+	    memset(&dflt, 0, sizeof(dflt));
+	    pass++;
+	}
+    }
+    
+    CS_CHECK(RequestIRQ, handle, &link->irq);
+    CS_CHECK(RequestConfiguration, handle, &link->conf);
+
+    /* deal with brain dead IDE resource management */
+    release_region(link->io.BasePort1, link->io.NumPorts1);
+    if (link->io.NumPorts2)
+	release_region(link->io.BasePort2, link->io.NumPorts2);
+
+    /* retry registration in case device is still spinning up */
+    for (i = 0; i < 10; i++) {
+	hd = ide_register(io_base, ctl_base, link->irq.AssignedIRQ);
+	if (hd >= 0) break;
+	if (link->io.NumPorts1 == 0x20) {
+	    hd = ide_register(io_base+0x10, ctl_base+0x10,
+			      link->irq.AssignedIRQ);
+	    if (hd >= 0) {
+		io_base += 0x10; ctl_base += 0x10;
+		break;
+	    }
+	}
+	__set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ/10);
+    }
+    
+    if (hd < 0) {
+	printk(KERN_NOTICE "ide_cs: ide_register() at 0x%3x & 0x%3x"
+	       ", irq %u failed\n", io_base, ctl_base,
+	       link->irq.AssignedIRQ);
+	goto failed;
+    }
+
+    MOD_INC_USE_COUNT;
+    info->ndev = 1;
+    sprintf(info->node.dev_name, "hd%c", 'a'+(hd*2));
+    info->node.major = ide_major[hd];
+    info->node.minor = 0;
+    info->hd = hd;
+    link->dev = &info->node;
+    printk(KERN_INFO "ide_cs: %s: Vcc = %d.%d, Vpp = %d.%d\n",
+	   info->node.dev_name, link->conf.Vcc/10, link->conf.Vcc%10,
+	   link->conf.Vpp1/10, link->conf.Vpp1%10);
+
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+    
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    ide_release((u_long)link);
+
+} /* ide_config */
+
+/*======================================================================
+
+    After a card is removed, ide_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+    
+======================================================================*/
+
+void ide_release(u_long arg)
+{
+    dev_link_t *link = (dev_link_t *)arg;
+    ide_info_t *info = link->priv;
+    
+    DEBUG(0, "ide_release(0x%p)\n", link);
+
+    if (info->ndev) {
+	ide_unregister(info->hd);
+	MOD_DEC_USE_COUNT;
+    }
+    info->ndev = 0;
+    link->dev = NULL;
+    
+    CardServices(ReleaseConfiguration, link->handle);
+    CardServices(ReleaseIO, link->handle, &link->io);
+    CardServices(ReleaseIRQ, link->handle, &link->irq);
+    
+    link->state &= ~DEV_CONFIG;
+
+} /* ide_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the ide drivers from
+    talking to the ports.
+    
+======================================================================*/
+
+int ide_event(event_t event, int priority,
+	      event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+
+    DEBUG(1, "ide_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG) {
+	    link->release.expires = jiffies + HZ/20;
+	    link->state |= DEV_RELEASE_PENDING;
+	    add_timer(&link->release);
+	}
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	ide_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG)
+	    CardServices(ReleaseConfiguration, link->handle);
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (DEV_OK(link))
+	    CardServices(RequestConfiguration, link->handle, &link->conf);
+	break;
+    }
+    return 0;
+} /* ide_event */
+
+/*====================================================================*/
+
+static int __init init_ide_cs(void)
+{
+    servinfo_t serv;
+    DEBUG(0, "%s\n", version);
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+	printk(KERN_NOTICE "ide_cs: Card Services release "
+	       "does not match!\n");
+	return -1;
+    }
+    register_pccard_driver(&dev_info, &ide_attach, &ide_detach);
+    return 0;
+}
+
+static void __exit exit_ide_cs(void)
+{
+    DEBUG(0, "ide_cs: unloading\n");
+    unregister_pccard_driver(&dev_info);
+    while (dev_list != NULL)
+	ide_detach(dev_list);
+}
+
+module_init(init_ide_cs);
+module_exit(exit_ide_cs);
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-disk.c linux/drivers/ide/ide-disk.c
--- v2.3.51/linux/drivers/ide/ide-disk.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-disk.c	Thu Mar  9 17:39:10 2000
@@ -0,0 +1,906 @@
+/*
+ *  linux/drivers/block/ide-disk.c	Version 1.09	April 23, 1999
+ *
+ *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
+ */
+
+/*
+ *  Mostly written by Mark Lord <mlord@pobox.com>
+ *                and  Gadi Oxman <gadio@netvision.net.il>
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
+ *
+ * This is the IDE/ATA disk driver, as evolved from hd.c and ide.c.
+ *
+ * Version 1.00		move disk only code from ide.c to ide-disk.c
+ *			support optional byte-swapping of all data
+ * Version 1.01		fix previous byte-swapping code
+ * Version 1.02		remove ", LBA" from drive identification msgs
+ * Version 1.03		fix display of id->buf_size for big-endian
+ * Version 1.04		add /proc configurable settings and S.M.A.R.T support
+ * Version 1.05		add capacity support for ATA3 >= 8GB
+ * Version 1.06		get boot-up messages to show full cyl count
+ * Version 1.07		disable door-locking if it fails
+ * Version 1.08		fixed CHS/LBA translations for ATA4 > 8GB,
+ *			process of adding new ATA4 compliance.
+ *			fixed problems in allowing fdisk to see
+ *			the entire disk.
+ * Version 1.09		added increment of rq->sector in ide_multwrite
+ *			added UDMA 3/4 reporting
+ */
+
+#define IDEDISK_VERSION	"1.09"
+
+#undef REALLY_SLOW_IO		/* most systems can safely undef this */
+
+#define _IDE_DISK_C		/* Tell linux/hdsmart.h it's really us */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_BLK_DEV_PDC4030
+#define IS_PDC4030_DRIVE (HWIF(drive)->chipset == ide_pdc4030)
+#else
+#define IS_PDC4030_DRIVE (0)	/* auto-NULLs out pdc4030 code */
+#endif
+
+static void idedisk_bswap_data (void *buffer, int wcount)
+{
+	u16 *p = buffer;
+
+	while (wcount--) {
+		*p++ = *p << 8 | *p >> 8;
+		*p++ = *p << 8 | *p >> 8;
+	}
+}
+
+static inline void idedisk_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+	ide_input_data(drive, buffer, wcount);
+	if (drive->bswap)
+		idedisk_bswap_data(buffer, wcount);
+}
+
+static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+	if (drive->bswap) {
+		idedisk_bswap_data(buffer, wcount);
+		ide_output_data(drive, buffer, wcount);
+		idedisk_bswap_data(buffer, wcount);
+	} else
+		ide_output_data(drive, buffer, wcount);
+}
+
+/*
+ * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
+ * value for this drive (from its reported identification information).
+ *
+ * Returns:	1 if lba_capacity looks sensible
+ *		0 otherwise
+ *
+ * It is called only once for each drive.
+ */
+static int lba_capacity_is_ok (struct hd_driveid *id)
+{
+	unsigned long lba_sects, chs_sects, head, tail;
+
+	/*
+	 * The ATA spec tells large drives to return
+	 * C/H/S = 16383/16/63 independent of their size.
+	 * Some drives can be jumpered to use 15 heads instead of 16.
+	 * Some drives can be jumpered to use 4092 cyls instead of 16383.
+	 */
+	if ((id->cyls == 16383
+	     || (id->cyls == 4092 && id->cur_cyls == 16383)) &&
+	    id->sectors == 63 &&
+	    (id->heads == 15 || id->heads == 16) &&
+	    id->lba_capacity >= 16383*63*id->heads)
+		return 1;
+
+	lba_sects   = id->lba_capacity;
+	chs_sects   = id->cyls * id->heads * id->sectors;
+
+	/* perform a rough sanity check on lba_sects:  within 10% is OK */
+	if ((lba_sects - chs_sects) < chs_sects/10)
+		return 1;
+
+	/* some drives have the word order reversed */
+	head = ((lba_sects >> 16) & 0xffff);
+	tail = (lba_sects & 0xffff);
+	lba_sects = (head | (tail << 16));
+	if ((lba_sects - chs_sects) < chs_sects/10) {
+		id->lba_capacity = lba_sects;
+		return 1;	/* lba_capacity is (now) good */
+	}
+
+	return 0;	/* lba_capacity value may be bad */
+}
+
+/*
+ * read_intr() is the handler for disk read/multread interrupts
+ */
+static ide_startstop_t read_intr (ide_drive_t *drive)
+{
+	byte stat;
+	int i;
+	unsigned int msect, nsect;
+	struct request *rq;
+#if 0
+	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+		return ide_error(drive, "read_intr", stat);
+	}
+#else	/* new way for dealing with premature shared PCI interrupts */
+	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+		if (stat & (ERR_STAT|DRQ_STAT)) {
+			return ide_error(drive, "read_intr", stat);
+		}
+		/* no data yet, so wait for another interrupt */
+		ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
+		return ide_started;
+	}
+#endif
+	msect = drive->mult_count;
+	
+read_next:
+	rq = HWGROUP(drive)->rq;
+	if (msect) {
+		if ((nsect = rq->current_nr_sectors) > msect)
+			nsect = msect;
+		msect -= nsect;
+	} else
+		nsect = 1;
+	idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+	printk("%s:  read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
+		drive->name, rq->sector, rq->sector+nsect-1,
+		(unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#endif
+	rq->sector += nsect;
+	rq->buffer += nsect<<9;
+	rq->errors = 0;
+	i = (rq->nr_sectors -= nsect);
+	if (((long)(rq->current_nr_sectors -= nsect)) <= 0)
+		ide_end_request(1, HWGROUP(drive));
+	if (i > 0) {
+		if (msect)
+			goto read_next;
+		ide_set_handler (drive, &read_intr, WAIT_CMD, NULL);
+                return ide_started;
+	}
+        return ide_stopped;
+}
+
+/*
+ * write_intr() is the handler for disk write interrupts
+ */
+static ide_startstop_t write_intr (ide_drive_t *drive)
+{
+	byte stat;
+	int i;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct request *rq = hwgroup->rq;
+
+	if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+		printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat);
+        } else {
+#ifdef DEBUG
+		printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
+			drive->name, rq->sector, (unsigned long) rq->buffer,
+			rq->nr_sectors-1);
+#endif
+		if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
+			rq->sector++;
+			rq->buffer += 512;
+			rq->errors = 0;
+			i = --rq->nr_sectors;
+			--rq->current_nr_sectors;
+			if (((long)rq->current_nr_sectors) <= 0)
+				ide_end_request(1, hwgroup);
+			if (i > 0) {
+				idedisk_output_data (drive, rq->buffer, SECTOR_WORDS);
+				ide_set_handler (drive, &write_intr, WAIT_CMD, NULL);
+                                return ide_started;
+			}
+                        return ide_stopped;
+		}
+		return ide_stopped;	/* the original code did this here (?) */
+	}
+	return ide_error(drive, "write_intr", stat);
+}
+
+/*
+ * ide_multwrite() transfers a block of up to mcount sectors of data
+ * to a drive as part of a disk multiple-sector write operation.
+ *
+ * Returns 0 if successful;  returns 1 if request had to be aborted due to corrupted buffer list.
+ */
+int ide_multwrite (ide_drive_t *drive, unsigned int mcount)
+{
+ 	ide_hwgroup_t	*hwgroup= HWGROUP(drive);
+
+	/*
+	 *	This may look a bit odd, but remember wrq is a copy of the
+	 *	request not the original. The pointers are real however so the
+	 *	bh's are not copies. Remember that or bad stuff will happen
+	 *
+	 *	At the point we are called the drive has asked us for the
+	 *	data, and its our job to feed it, walking across bh boundaries
+	 *	if need be.
+	 */
+
+ 	struct request	*rq = &hwgroup->wrq;
+
+  	do {
+ 		unsigned long flags;
+  		unsigned int nsect = rq->current_nr_sectors;
+		if (nsect > mcount)
+			nsect = mcount;
+		mcount -= nsect;
+
+		idedisk_output_data(drive, rq->buffer, nsect<<7);
+#ifdef DEBUG
+		printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
+			drive->name, rq->sector, (unsigned long) rq->buffer,
+			nsect, rq->nr_sectors - nsect);
+#endif
+		spin_lock_irqsave(&io_request_lock, flags);	/* Is this really necessary? */
+#ifdef CONFIG_BLK_DEV_PDC4030
+		rq->sector += nsect;
+#endif
+		if (((long)(rq->nr_sectors -= nsect)) <= 0) {
+#ifdef DEBUG
+			printk("%s: multwrite: count=%d, current=%ld\n",
+				drive->name, nsect, rq->nr_sectors);
+#endif
+			spin_unlock_irqrestore(&io_request_lock, flags);
+			break;
+		}
+		if ((rq->current_nr_sectors -= nsect) == 0) {
+			if ((rq->bh = rq->bh->b_reqnext) != NULL) {
+				rq->current_nr_sectors = rq->bh->b_size>>9;
+				rq->buffer             = rq->bh->b_data;
+			} else {
+				spin_unlock_irqrestore(&io_request_lock, flags);
+				printk("%s: buffer list corrupted (%ld, %ld, %d)\n",
+					drive->name, rq->current_nr_sectors,
+					rq->nr_sectors, nsect);
+				ide_end_request(0, hwgroup);
+				return 1;
+			}
+		} else {
+			/* Fix the pointer.. we ate data */
+			rq->buffer += nsect << 9;
+		}
+                spin_unlock_irqrestore(&io_request_lock, flags);
+	} while (mcount);
+        return 0;
+}
+
+/*
+ * multwrite_intr() is the handler for disk multwrite interrupts
+ */
+static ide_startstop_t multwrite_intr (ide_drive_t *drive)
+{
+	byte stat;
+	int i;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct request *rq = &hwgroup->wrq;
+
+	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+		if (stat & DRQ_STAT) {
+			/*
+			 *	The drive wants data. Remember rq is the copy
+			 *	of the request
+			 */
+			if (rq->nr_sectors) {
+				if (ide_multwrite(drive, drive->mult_count))
+					return ide_stopped;
+				ide_set_handler (drive, &multwrite_intr, WAIT_CMD, NULL);
+				return ide_started;
+			}
+		} else {
+			/*
+			 *	If the copy has all the blocks completed then
+			 *	we can end the original request.
+			 */
+			if (!rq->nr_sectors) {	/* all done? */
+				rq = hwgroup->rq;
+				for (i = rq->nr_sectors; i > 0;){
+					i -= rq->current_nr_sectors;
+					ide_end_request(1, hwgroup);
+				}
+				return ide_stopped;
+			}
+		}
+		return ide_stopped;	/* the original code did this here (?) */
+	}
+	return ide_error(drive, "multwrite_intr", stat);
+}
+
+/*
+ * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
+ */
+static ide_startstop_t set_multmode_intr (ide_drive_t *drive)
+{
+	byte stat;
+
+	if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) {
+		drive->mult_count = drive->mult_req;
+	} else {
+		drive->mult_req = drive->mult_count = 0;
+		drive->special.b.recalibrate = 1;
+		(void) ide_dump_status(drive, "set_multmode", stat);
+	}
+	return ide_stopped;
+}
+
+/*
+ * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
+ */
+static ide_startstop_t set_geometry_intr (ide_drive_t *drive)
+{
+	byte stat;
+
+	if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT))
+		return ide_stopped;
+
+	if (stat & (ERR_STAT|DRQ_STAT))
+		return ide_error(drive, "set_geometry_intr", stat);
+
+	ide_set_handler(drive, &set_geometry_intr, WAIT_CMD, NULL);
+	return ide_started;	
+}
+
+/*
+ * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
+ */
+static ide_startstop_t recal_intr (ide_drive_t *drive)
+{
+	byte stat = GET_STAT();
+
+	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+		return ide_error(drive, "recal_intr", stat);
+	return ide_stopped;
+}
+
+/*
+ * do_rw_disk() issues READ and WRITE commands to a disk,
+ * using LBA if supported, or CHS otherwise, to address sectors.
+ * It also takes care of issuing special DRIVE_CMDs.
+ */
+static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	if (IDE_CONTROL_REG)
+		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+	OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
+#ifdef CONFIG_BLK_DEV_PDC4030
+	if (drive->select.b.lba || IS_PDC4030_DRIVE) {
+#else /* !CONFIG_BLK_DEV_PDC4030 */
+	if (drive->select.b.lba) {
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+#ifdef DEBUG
+		printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
+			drive->name, (rq->cmd==READ)?"read":"writ",
+			block, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+		OUT_BYTE(block,IDE_SECTOR_REG);
+		OUT_BYTE(block>>=8,IDE_LCYL_REG);
+		OUT_BYTE(block>>=8,IDE_HCYL_REG);
+		OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
+	} else {
+		unsigned int sect,head,cyl,track;
+		track = block / drive->sect;
+		sect  = block % drive->sect + 1;
+		OUT_BYTE(sect,IDE_SECTOR_REG);
+		head  = track % drive->head;
+		cyl   = track / drive->head;
+		OUT_BYTE(cyl,IDE_LCYL_REG);
+		OUT_BYTE(cyl>>8,IDE_HCYL_REG);
+		OUT_BYTE(head|drive->select.all,IDE_SELECT_REG);
+#ifdef DEBUG
+		printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
+			drive->name, (rq->cmd==READ)?"read":"writ", cyl,
+			head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+	}
+#ifdef CONFIG_BLK_DEV_PDC4030
+	if (IS_PDC4030_DRIVE) {
+		extern ide_startstop_t do_pdc4030_io(ide_drive_t *, struct request *);
+		return do_pdc4030_io (drive, rq);
+	}
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+	if (rq->cmd == READ) {
+#ifdef CONFIG_BLK_DEV_IDEDMA
+		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
+			return ide_started;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+		ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
+		OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG);
+		return ide_started;
+	}
+	if (rq->cmd == WRITE) {
+		ide_startstop_t startstop;
+#ifdef CONFIG_BLK_DEV_IDEDMA
+		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
+			return ide_started;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+		OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG);
+		if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+			printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name,
+				drive->mult_count ? "MULTWRITE" : "WRITE");
+			return startstop;
+		}
+		if (!drive->unmask)
+			__cli();	/* local CPU only */
+		if (drive->mult_count) {
+			ide_hwgroup_t *hwgroup = HWGROUP(drive);
+			/*
+			 * Ugh.. this part looks ugly because we MUST set up
+			 * the interrupt handler before outputting the first block
+			 * of data to be written.  If we hit an error (corrupted buffer list)
+			 * in ide_multwrite(), then we need to remove the handler/timer
+			 * before returning.  Fortunately, this NEVER happens (right?).
+			 *
+			 * Except when you get an error it seems...
+			 */
+			hwgroup->wrq = *rq; /* scratchpad */
+			ide_set_handler (drive, &multwrite_intr, WAIT_CMD, NULL);
+			if (ide_multwrite(drive, drive->mult_count)) {
+				unsigned long flags;
+				spin_lock_irqsave(&io_request_lock, flags);
+				hwgroup->handler = NULL;
+				del_timer(&hwgroup->timer);
+				spin_unlock_irqrestore(&io_request_lock, flags);
+				return ide_stopped;
+			}
+		} else {
+			ide_set_handler (drive, &write_intr, WAIT_CMD, NULL);
+			idedisk_output_data(drive, rq->buffer, SECTOR_WORDS);
+		}
+		return ide_started;
+	}
+	printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd);
+	ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
+}
+
+static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_INC_USE_COUNT;
+	if (drive->removable && drive->usage == 1) {
+		check_disk_change(inode->i_rdev);
+		/*
+		 * Ignore the return code from door_lock,
+		 * since the open() has already succeeded,
+		 * and the door_lock is irrelevant at this point.
+		 */
+		if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORLOCK, 0, 0, 0, NULL))
+			drive->doorlocking = 0;
+	}
+	return 0;
+}
+
+static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	if (drive->removable && !drive->usage) {
+		invalidate_buffers(inode->i_rdev);
+		if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORUNLOCK, 0, 0, 0, NULL))
+			drive->doorlocking = 0;
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+static int idedisk_media_change (ide_drive_t *drive)
+{
+	return drive->removable;	/* if removable, always assume it was changed */
+}
+
+/*
+ * Compute drive->capacity, the full capacity of the drive
+ * Called with drive->id != NULL.
+ */
+static void init_idedisk_capacity (ide_drive_t  *drive)
+{
+	struct hd_driveid *id = drive->id;
+	unsigned long capacity = drive->cyl * drive->head * drive->sect;
+
+	drive->select.b.lba = 0;
+
+	/* Determine capacity, and use LBA if the drive properly supports it */
+	if ((id->capability & 2) && lba_capacity_is_ok(id)) {
+		capacity = id->lba_capacity;
+		drive->cyl = capacity / (drive->head * drive->sect);
+		drive->select.b.lba = 1;
+	}
+	drive->capacity = capacity;
+}
+
+static unsigned long idedisk_capacity (ide_drive_t  *drive)
+{
+	return (drive->capacity - drive->sect0);
+}
+
+static ide_startstop_t idedisk_special (ide_drive_t *drive)
+{
+	special_t *s = &drive->special;
+
+	if (s->b.set_geometry) {
+		s->b.set_geometry = 0;
+		OUT_BYTE(drive->sect,IDE_SECTOR_REG);
+		OUT_BYTE(drive->cyl,IDE_LCYL_REG);
+		OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
+		OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
+		if (!IS_PDC4030_DRIVE)
+			ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
+	} else if (s->b.recalibrate) {
+		s->b.recalibrate = 0;
+		if (!IS_PDC4030_DRIVE)
+			ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
+	} else if (s->b.set_multmode) {
+		s->b.set_multmode = 0;
+		if (drive->id && drive->mult_req > drive->id->max_multsect)
+			drive->mult_req = drive->id->max_multsect;
+		if (!IS_PDC4030_DRIVE)
+			ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
+	} else if (s->all) {
+		int special = s->all;
+		s->all = 0;
+		printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
+		return ide_stopped;
+	}
+	return IS_PDC4030_DRIVE ? ide_stopped : ide_started;
+}
+
+static void idedisk_pre_reset (ide_drive_t *drive)
+{
+	drive->special.all = 0;
+	drive->special.b.set_geometry = 1;
+	drive->special.b.recalibrate  = 1;
+	if (OK_TO_RESET_CONTROLLER)
+		drive->mult_count = 0;
+	if (!drive->keep_settings && !drive->using_dma)
+		drive->mult_req = 0;
+	if (drive->mult_req != drive->mult_count)
+		drive->special.b.set_multmode = 1;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static int smart_enable(ide_drive_t *drive)
+{
+	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL);
+}
+
+static int get_smart_values(ide_drive_t *drive, byte *buf)
+{
+	(void) smart_enable(drive);
+	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_VALUES, 1, buf);
+}
+
+static int get_smart_thresholds(ide_drive_t *drive, byte *buf)
+{
+	(void) smart_enable(drive);
+	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_THRESHOLDS, 1, buf);
+}
+
+static int proc_idedisk_read_cache
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	char		*out = page;
+	int		len;
+
+	if (drive->id)
+		len = sprintf(out,"%i\n", drive->id->buf_size / 2);
+	else
+		len = sprintf(out,"(none)\n");
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_idedisk_read_smart_thresholds
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *)data;
+	int		len = 0, i = 0;
+
+	if (!get_smart_thresholds(drive, page)) {
+		unsigned short *val = ((unsigned short *)page) + 2;
+		char *out = ((char *)val) + (SECTOR_WORDS * 4);
+		page = out;
+		do {
+			out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+			val += 1;
+		} while (i < (SECTOR_WORDS * 2));
+		len = out - page;
+	}
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_idedisk_read_smart_values
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *)data;
+	int		len = 0, i = 0;
+
+	if (!get_smart_values(drive, page)) {
+		unsigned short *val = ((unsigned short *)page) + 2;
+		char *out = ((char *)val) + (SECTOR_WORDS * 4);
+		page = out;
+		do {
+			out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+			val += 1;
+		} while (i < (SECTOR_WORDS * 2));
+		len = out - page;
+	}
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t idedisk_proc[] = {
+	{ "cache",		S_IFREG|S_IRUGO,	proc_idedisk_read_cache,		NULL },
+	{ "geometry",		S_IFREG|S_IRUGO,	proc_ide_read_geometry,			NULL },
+	{ "smart_values",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_values,		NULL },
+	{ "smart_thresholds",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_thresholds,	NULL },
+	{ NULL, 0, NULL, NULL }
+};
+
+#else
+
+#define	idedisk_proc	NULL
+
+#endif	/* CONFIG_PROC_FS */
+
+static int set_multcount(ide_drive_t *drive, int arg)
+{
+	struct request rq;
+
+	if (drive->special.b.set_multmode)
+		return -EBUSY;
+	ide_init_drive_cmd (&rq);
+	drive->mult_req = arg;
+	drive->special.b.set_multmode = 1;
+	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
+	return (drive->mult_count == arg) ? 0 : -EIO;
+}
+
+static int set_nowerr(ide_drive_t *drive, int arg)
+{
+	unsigned long flags;
+
+	if (ide_spin_wait_hwgroup(drive, &flags))
+		return -EBUSY;
+	drive->nowerr = arg;
+	drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
+	spin_unlock_irqrestore(&io_request_lock, flags);
+	return 0;
+}
+
+static void idedisk_add_settings(ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	int major = HWIF(drive)->major;
+	int minor = drive->select.b.unit << PARTN_BITS;
+
+	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	65535,				1,	1,	&drive->bios_cyl,		NULL);
+	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
+	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
+	ide_add_setting(drive,	"bswap",		SETTING_READ,					-1,			-1,			TYPE_BYTE,	0,	1,				1,	1,	&drive->bswap,			NULL);
+	ide_add_setting(drive,	"multcount",		id ? SETTING_RW : SETTING_READ,			HDIO_GET_MULTCOUNT,	HDIO_SET_MULTCOUNT,	TYPE_BYTE,	0,	id ? id->max_multsect : 0,	1,	2,	&drive->mult_count,		set_multcount);
+	ide_add_setting(drive,	"nowerr",		SETTING_RW,					HDIO_GET_NOWERR,	HDIO_SET_NOWERR,	TYPE_BYTE,	0,	1,				1,	1,	&drive->nowerr,			set_nowerr);
+	ide_add_setting(drive,	"breada_readahead",	SETTING_RW,					BLKRAGET,		BLKRASET,		TYPE_INT,	0,	255,				1,	2,	&read_ahead[major],		NULL);
+	ide_add_setting(drive,	"file_readahead",	SETTING_RW,					BLKFRAGET,		BLKFRASET,		TYPE_INTA,	0,	INT_MAX,			1,	1024,	&max_readahead[major][minor],	NULL);
+	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW,					BLKSECTGET,		BLKSECTSET,		TYPE_INTA,	1,	255,				1,	2,	&max_sectors[major][minor],	NULL);
+
+}
+
+/*
+ *	IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idedisk_driver = {
+	"ide-disk",		/* name */
+	IDEDISK_VERSION,	/* version */
+	ide_disk,		/* media */
+	0,			/* busy */
+	1,			/* supports_dma */
+	0,			/* supports_dsc_overlap */
+	NULL,			/* cleanup */
+	do_rw_disk,		/* do_request */
+	NULL,			/* end_request */
+	NULL,			/* ioctl */
+	idedisk_open,		/* open */
+	idedisk_release,	/* release */
+	idedisk_media_change,	/* media_change */
+	idedisk_pre_reset,	/* pre_reset */
+	idedisk_capacity,	/* capacity */
+	idedisk_special,	/* special */
+	idedisk_proc		/* proc */
+};
+
+int idedisk_init (void);
+static ide_module_t idedisk_module = {
+	IDE_DRIVER_MODULE,
+	idedisk_init,
+	&idedisk_driver,
+	NULL
+};
+
+static int idedisk_cleanup (ide_drive_t *drive)
+{
+	return ide_unregister_subdriver(drive);
+}
+
+static void idedisk_setup (ide_drive_t *drive)
+{
+	int i;
+	
+	struct hd_driveid *id = drive->id;
+	unsigned long capacity;
+	
+	idedisk_add_settings(drive);
+
+	if (id == NULL)
+		return;
+
+	/*
+	 * CompactFlash cards and their brethern look just like hard drives
+	 * to us, but they are removable and don't have a doorlock mechanism.
+	 */
+	if (drive->removable && !drive_is_flashcard(drive)) {
+		/*
+		 * Removable disks (eg. SYQUEST); ignore 'WD' drives 
+		 */
+		if (id->model[0] != 'W' || id->model[1] != 'D') {
+			drive->doorlocking = 1;
+		}
+	}
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		ide_hwif_t *hwif = HWIF(drive);
+
+		if (drive != &hwif->drives[i]) continue;
+		hwif->gd->de_arr[i] = drive->de;
+		if (drive->removable)
+			hwif->gd->flags[i] |= GENHD_FL_REMOVABLE;
+		break;
+	}
+
+	/* Extract geometry if we did not already have one for the drive */
+	if (!drive->cyl || !drive->head || !drive->sect) {
+		drive->cyl     = drive->bios_cyl  = id->cyls;
+		drive->head    = drive->bios_head = id->heads;
+		drive->sect    = drive->bios_sect = id->sectors;
+	}
+
+	/* Handle logical geometry translation by the drive */
+	if ((id->field_valid & 1) && id->cur_cyls &&
+	    id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
+		drive->cyl  = id->cur_cyls;
+		drive->head = id->cur_heads;
+		drive->sect = id->cur_sectors;
+	}
+
+	/* Use physical geometry if what we have still makes no sense */
+	if (drive->head > 16 && id->heads && id->heads <= 16) {
+		drive->cyl  = id->cyls;
+		drive->head = id->heads;
+		drive->sect = id->sectors;
+	}
+
+	/* calculate drive capacity, and select LBA if possible */
+	init_idedisk_capacity (drive);
+
+	/*
+	 * if possible, give fdisk access to more of the drive,
+	 * by correcting bios_cyls:
+	 */
+	capacity = idedisk_capacity (drive);
+	if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) &&
+	    (!drive->forced_geom) && drive->bios_sect && drive->bios_head)
+		drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
+
+#if 0	/* done instead for entire identify block in arch/ide.h stuff */
+	/* fix byte-ordering of buffer size field */
+	id->buf_size = le16_to_cpu(id->buf_size);
+#endif
+	printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
+			drive->name, id->model,
+			capacity/2048L, id->buf_size/2,
+			drive->bios_cyl, drive->bios_head, drive->bios_sect);
+
+	if (drive->using_dma) {
+		if ((id->field_valid & 4) && (id->hw_config & 0x2000) &&
+		    (HWIF(drive)->udma_four) &&
+		    (id->dma_ultra & (id->dma_ultra >> 11) & 3)) {
+			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
+		} else if ((id->field_valid & 4) &&
+			   (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+			printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
+		} else if (id->field_valid & 4) {
+			printk(", (U)DMA");	/* Can be BIOS-enabled! */
+		} else {
+			printk(", DMA");
+		}
+	}
+	printk("\n");
+
+	drive->mult_count = 0;
+	if (id->max_multsect) {
+#ifdef CONFIG_IDEDISK_MULTI_MODE
+		id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
+		id->multsect_valid = id->multsect ? 1 : 0;
+		drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT;
+		drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
+#else	/* original, pre IDE-NFG, per request of AC */
+		drive->mult_req = INITIAL_MULT_COUNT;
+		if (drive->mult_req > id->max_multsect)
+			drive->mult_req = id->max_multsect;
+		if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
+			drive->special.b.set_multmode = 1;
+#endif
+	}
+	drive->no_io_32bit = id->dword_io ? 1 : 0;
+}
+
+int idedisk_init (void)
+{
+	ide_drive_t *drive;
+	int failed = 0;
+	
+	MOD_INC_USE_COUNT;
+	while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) {
+		if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) {
+			printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name);
+			continue;
+		}
+		idedisk_setup(drive);
+		if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
+			printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head);
+			(void) idedisk_cleanup(drive);
+			continue;
+		}
+		failed--;
+	}
+	ide_register_module(&idedisk_module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return idedisk_init();
+}
+
+void cleanup_module (void)
+{
+	ide_drive_t *drive;
+	int failed = 0;
+
+	while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, &idedisk_driver, failed)) != NULL) {
+		if (idedisk_cleanup (drive)) {
+			printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name);
+			failed++;
+		}
+		/* We must remove proc entries defined in this module.
+		   Otherwise we oops while accessing these entries */
+		if (drive->proc)
+			ide_remove_proc_entries(drive->proc, idedisk_proc);
+	}
+	ide_unregister_module(&idedisk_module);
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-dma.c linux/drivers/ide/ide-dma.c
--- v2.3.51/linux/drivers/ide/ide-dma.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-dma.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,580 @@
+/*
+ *  linux/drivers/block/ide-dma.c		Version 4.09	April 23, 1999
+ *
+ *  Copyright (c) 1999  Andre Hedrick
+ *  May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ *  Special Thanks to Mark for his Six years of work.
+ *
+ *  Copyright (c) 1995-1998  Mark Lord
+ *  May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for the bus-master IDE DMA functions
+ * of various PCI chipsets, including the Intel PIIX (i82371FB for
+ * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and 
+ * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset)
+ * ("PIIX" stands for "PCI ISA IDE Xcellerator").
+ *
+ * Pretty much the same code works for other IDE PCI bus-mastering chipsets.
+ *
+ * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
+ *
+ * By default, DMA support is prepared for use, but is currently enabled only
+ * for drives which already have DMA enabled (UltraDMA or mode 2 multi/single),
+ * or which are recognized as "good" (see table below).  Drives with only mode0
+ * or mode1 (multi/single) DMA should also work with this chipset/driver
+ * (eg. MC2112A) but are not enabled by default.
+ *
+ * Use "hdparm -i" to view modes supported by a given drive.
+ *
+ * The hdparm-3.5 (or later) utility can be used for manually enabling/disabling
+ * DMA support, but must be (re-)compiled against this kernel version or later.
+ *
+ * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
+ * If problems arise, ide.c will disable DMA operation after a few retries.
+ * This error recovery mechanism works and has been extremely well exercised.
+ *
+ * IDE drives, depending on their vintage, may support several different modes
+ * of DMA operation.  The boot-time modes are indicated with a "*" in
+ * the "hdparm -i" listing, and can be changed with *knowledgeable* use of
+ * the "hdparm -X" feature.  There is seldom a need to do this, as drives
+ * normally power-up with their "best" PIO/DMA modes enabled.
+ *
+ * Testing has been done with a rather extensive number of drives,
+ * with Quantum & Western Digital models generally outperforming the pack,
+ * and Fujitsu & Conner (and some Seagate which are really Conner) drives
+ * showing more lackluster throughput.
+ *
+ * Keep an eye on /var/adm/messages for "DMA disabled" messages.
+ *
+ * Some people have reported trouble with Intel Zappa motherboards.
+ * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
+ * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
+ * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
+ *
+ * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for
+ * fixing the problem with the BIOS on some Acer motherboards.
+ *
+ * Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
+ * "TX" chipset compatibility and for providing patches for the "TX" chipset.
+ *
+ * Thanks to Christian Brunner <chb@muc.de> for taking a good first crack
+ * at generic DMA -- his patches were referred to when preparing this code.
+ *
+ * Most importantly, thanks to Robert Bringman <rob@mars.trion.com>
+ * for supplying a Promise UDMA board & WD UDMA drive for this work!
+ *
+ * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
+ *
+ * ACARD ATP850UF Chipset "Modified SCSI Class" with other names
+ *       AEC6210 U/UF
+ *       SIIG's UltraIDE Pro CN-2449
+ * TTI   HPT343 Chipset "Modified SCSI Class" but reports as an
+ *       unknown storage device.
+ * NEW	 check_drive_lists(ide_drive_t *drive, int good_bad)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc);
+
+#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
+
+struct drive_list_entry {
+	char * id_model;
+	char * id_firmware;
+};
+
+struct drive_list_entry drive_whitelist [] = {
+
+	{ "Micropolis 2112A"	,       "ALL"		},
+	{ "CONNER CTMA 4000"	,       "ALL"		},
+	{ "CONNER CTT8000-A"	,       "ALL"		},
+	{ "ST34342A"		,	"ALL"		},
+	{ 0			,	0		}
+};
+
+struct drive_list_entry drive_blacklist [] = {
+
+	{ "WDC AC11000H"	,	"ALL"		},
+	{ "WDC AC22100H"	,	"ALL"		},
+	{ "WDC AC32500H"	,	"ALL"		},
+	{ "WDC AC33100H"	,	"ALL"		},
+	{ "WDC AC31600H"	,	"ALL"		},
+	{ "WDC AC32100H"	,	"24.09P07"	},
+	{ "WDC AC23200L"	,	"21.10N21"	},
+	{ 0			,	0		}
+
+};
+
+int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
+{
+	for ( ; drive_table->id_model ; drive_table++)
+		if ((!strcmp(drive_table->id_model, id->model)) &&
+		    ((!strstr(drive_table->id_firmware, id->fw_rev)) ||
+		     (!strcmp(drive_table->id_firmware, "ALL"))))
+			return 1;
+	return 0;
+}
+
+#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+
+/*
+ * good_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which do not support mode2 DMA but which are
+ * known to work fine with this interface under Linux.
+ */
+const char *good_dma_drives[] = {"Micropolis 2112A",
+				 "CONNER CTMA 4000",
+				 "CONNER CTT8000-A",
+				 "ST34342A",	/* for Sun Ultra */
+				 NULL};
+
+/*
+ * bad_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which supposedly support (U)DMA but which are
+ * known to corrupt data with this interface under Linux.
+ *
+ * This is an empirical list. Its generated from bug reports. That means
+ * while it reflects actual problem distributions it doesn't answer whether
+ * the drive or the controller, or cabling, or software, or some combination
+ * thereof is the fault. If you don't happen to agree with the kernel's 
+ * opinion of your drive - use hdparm to turn DMA on.
+ */
+const char *bad_dma_drives[] = {"WDC AC11000H",
+				"WDC AC22100H",
+				"WDC AC32100H",
+				"WDC AC32500H",
+				"WDC AC33100H",
+				"WDC AC31600H",
+ 				NULL};
+
+#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+
+/*
+ * Our Physical Region Descriptor (PRD) table should be large enough
+ * to handle the biggest I/O request we are likely to see.  Since requests
+ * can have no more than 256 sectors, and since the typical blocksize is
+ * two or more sectors, we could get by with a limit of 128 entries here for
+ * the usual worst case.  Most requests seem to include some contiguous blocks,
+ * further reducing the number of table entries required.
+ *
+ * The driver reverts to PIO mode for individual requests that exceed
+ * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
+ * 100% of all crazy scenarios here is not necessary.
+ *
+ * As it turns out though, we must allocate a full 4KB page for this,
+ * so the two PRD tables (ide0 & ide1) will each get half of that,
+ * allowing each to have about 256 entries (8 bytes each) from this.
+ */
+#define PRD_BYTES	8
+#define PRD_ENTRIES	(PAGE_SIZE / (2 * PRD_BYTES))
+
+/*
+ * dma_intr() is the handler for disk read/write DMA interrupts
+ */
+ide_startstop_t ide_dma_intr (ide_drive_t *drive)
+{
+	int i;
+	byte stat, dma_stat;
+
+	dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive);
+	stat = GET_STAT();			/* get drive status */
+	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+		if (!dma_stat) {
+			struct request *rq = HWGROUP(drive)->rq;
+			rq = HWGROUP(drive)->rq;
+			for (i = rq->nr_sectors; i > 0;) {
+				i -= rq->current_nr_sectors;
+				ide_end_request(1, HWGROUP(drive));
+			}
+			return ide_stopped;
+		}
+		printk("%s: dma_intr: bad DMA status\n", drive->name);
+	}
+	return ide_error(drive, "dma_intr", stat);
+}
+
+static int ide_build_sglist (ide_hwif_t *hwif, struct request *rq)
+{
+	struct buffer_head *bh;
+	struct scatterlist *sg = hwif->sg_table;
+	int nents = 0;
+
+	if (rq->cmd == READ)
+		hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
+	else
+		hwif->sg_dma_direction = PCI_DMA_TODEVICE;
+	bh = rq->bh;
+	do {
+		unsigned char *virt_addr = bh->b_data;
+		unsigned int size = bh->b_size;
+
+		while ((bh = bh->b_reqnext) != NULL) {
+			if ((virt_addr + size) != (unsigned char *) bh->b_data)
+				break;
+			size += bh->b_size;
+		}
+		memset(&sg[nents], 0, sizeof(*sg));
+		sg[nents].address = virt_addr;
+		sg[nents].length = size;
+		nents++;
+	} while (bh != NULL);
+
+	return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction);
+}
+
+/*
+ * ide_build_dmatable() prepares a dma request.
+ * Returns 0 if all went okay, returns 1 otherwise.
+ * May also be invoked from trm290.c
+ */
+int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func)
+{
+	unsigned int *table = HWIF(drive)->dmatable_cpu;
+#ifdef CONFIG_BLK_DEV_TRM290
+	unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290);
+#else
+	const int is_trm290_chipset = 0;
+#endif
+	unsigned int count = 0;
+	int i;
+	struct scatterlist *sg;
+
+	HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq);
+
+	sg = HWIF(drive)->sg_table;
+	while (i && sg_dma_len(sg)) {
+		u32 cur_addr;
+		u32 cur_len;
+
+		cur_addr = sg_dma_address(sg);
+		cur_len = sg_dma_len(sg);
+
+		while (cur_len) {
+			if (++count >= PRD_ENTRIES) {
+				printk("%s: DMA table too small\n", drive->name);
+				pci_unmap_sg(HWIF(drive)->pci_dev,
+					     HWIF(drive)->sg_table,
+					     HWIF(drive)->sg_nents,
+					     HWIF(drive)->sg_dma_direction);
+				return 0; /* revert to PIO for this request */
+			} else {
+				u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
+
+				if (bcount > cur_len)
+					bcount = cur_len;
+				*table++ = cpu_to_le32(cur_addr);
+				xcount = bcount & 0xffff;
+				if (is_trm290_chipset)
+					xcount = ((xcount >> 2) - 1) << 16;
+				*table++ = cpu_to_le32(xcount);
+				cur_addr += bcount;
+				cur_len -= bcount;
+			}
+		}
+
+		sg++;
+		i--;
+	}
+
+	if (!count)
+		printk("%s: empty DMA table?\n", drive->name);
+	else if (!is_trm290_chipset)
+		*--table |= cpu_to_le32(0x80000000);
+
+	return count;
+}
+
+/* Teardown mappings after DMA has completed.  */
+void ide_destroy_dmatable (ide_drive_t *drive)
+{
+	struct pci_dev *dev = HWIF(drive)->pci_dev;
+	struct scatterlist *sg = HWIF(drive)->sg_table;
+	int nents = HWIF(drive)->sg_nents;
+
+	pci_unmap_sg(dev, sg, nents, HWIF(drive)->sg_dma_direction);
+}
+
+/*
+ *  For both Blacklisted and Whitelisted drives.
+ *  This is setup to be called as an extern for future support
+ *  to other special driver code.
+ */
+int check_drive_lists (ide_drive_t *drive, int good_bad)
+{
+	struct hd_driveid *id = drive->id;
+
+#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
+	if (good_bad) {
+		return in_drive_list(id, drive_whitelist);
+	} else {
+		int blacklist = in_drive_list(id, drive_blacklist);
+		if (blacklist)
+			printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
+		return(blacklist);
+	}
+#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+	const char **list;
+
+	if (good_bad) {
+		/* Consult the list of known "good" drives */
+		list = good_dma_drives;
+		while (*list) {
+			if (!strcmp(*list++,id->model))
+				return 1;
+		}
+	} else {
+		/* Consult the list of known "bad" drives */
+		list = bad_dma_drives;
+		while (*list) {
+			if (!strcmp(*list++,id->model)) {
+				printk("%s: Disabling (U)DMA for %s\n",
+					drive->name, id->model);
+				return 1;
+			}
+		}
+	}
+#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+	return 0;
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_hwif_t *hwif = HWIF(drive);
+
+	if (id && (id->capability & 1) && hwif->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive))
+			return hwif->dmaproc(ide_dma_off, drive);
+
+		/* Enable DMA on any drive that has UltraDMA (mode 3/4) enabled */
+		if ((id->field_valid & 4) && (hwif->udma_four) && (id->hw_config & 0x2000))
+			if ((id->dma_ultra & (id->dma_ultra >> 11) & 3))
+				return hwif->dmaproc(ide_dma_on, drive);
+		/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
+		if (id->field_valid & 4)	/* UltraDMA */
+			if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
+				return hwif->dmaproc(ide_dma_on, drive);
+		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
+		if (id->field_valid & 2)	/* regular DMA */
+			if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
+				return hwif->dmaproc(ide_dma_on, drive);
+		/* Consult the list of known "good" drives */
+		if (ide_dmaproc(ide_dma_good_drive, drive))
+			return hwif->dmaproc(ide_dma_on, drive);
+	}
+	return hwif->dmaproc(ide_dma_off_quietly, drive);
+}
+
+/*
+ * ide_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA.  All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * For ATAPI devices, we just prepare for DMA and return. The caller should
+ * then issue the packet command to the drive and call us again with
+ * ide_dma_begin afterwards.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case
+ * the caller should revert to PIO for the current request.
+ * May also be invoked from trm290.c
+ */
+int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned long dma_base = hwif->dma_base;
+	byte unit = (drive->select.b.unit & 0x01);
+	unsigned int count, reading = 0;
+	byte dma_stat;
+
+	switch (func) {
+		case ide_dma_off:
+			printk("%s: DMA disabled\n", drive->name);
+		case ide_dma_off_quietly:
+			outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
+		case ide_dma_on:
+			drive->using_dma = (func == ide_dma_on);
+			if (drive->using_dma)
+				outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+			return 0;
+		case ide_dma_check:
+			return config_drive_for_dma (drive);
+		case ide_dma_read:
+			reading = 1 << 3;
+		case ide_dma_write:
+			if (!(count = ide_build_dmatable(drive, func)))
+				return 1;	/* try PIO instead of DMA */
+			outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
+			outb(reading, dma_base);			/* specify r/w */
+			outb(inb(dma_base+2)|6, dma_base+2);		/* clear INTR & ERROR flags */
+			drive->waiting_for_dma = 1;
+			if (drive->media != ide_disk)
+				return 0;
+			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);	/* issue cmd to drive */
+			OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+		case ide_dma_begin:
+			/* Note that this is done *after* the cmd has
+			 * been issued to the drive, as per the BM-IDE spec.
+			 * The Promise Ultra33 doesn't work correctly when
+			 * we do this part before issuing the drive cmd.
+			 */
+			outb(inb(dma_base)|1, dma_base);		/* start DMA */
+			return 0;
+		case ide_dma_end: /* returns 1 on error, 0 otherwise */
+			drive->waiting_for_dma = 0;
+			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+			dma_stat = inb(dma_base+2);		/* get DMA status */
+			outb(dma_stat|6, dma_base+2);	/* clear the INTR & ERROR bits */
+			ide_destroy_dmatable(drive);	/* purge DMA mappings */
+			return (dma_stat & 7) != 4;	/* verify good DMA status */
+		case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
+			dma_stat = inb(dma_base+2);
+			return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
+		case ide_dma_bad_drive:
+		case ide_dma_good_drive:
+			return check_drive_lists(drive, (func == ide_dma_good_drive));
+		case ide_dma_lostirq:
+		case ide_dma_timeout:
+			printk("ide_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func),  func);
+			return 1;
+		default:
+			printk("ide_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func);
+			return 1;
+	}
+}
+
+/*
+ * Needed for allowing full modular support of ide-driver
+ */
+int ide_release_dma (ide_hwif_t *hwif)
+{
+	if (hwif->dmatable_cpu) {
+		pci_free_consistent(hwif->pci_dev,
+				    PRD_ENTRIES * PRD_BYTES,
+				    hwif->dmatable_cpu,
+				    hwif->dmatable_dma);
+		hwif->dmatable_cpu = NULL;
+	}
+	if (hwif->sg_table) {
+		kfree(hwif->sg_table);
+		hwif->sg_table = NULL;
+	}
+	if ((hwif->dma_extra) && (hwif->channel == 0))
+		release_region((hwif->dma_base + 16), hwif->dma_extra);
+	release_region(hwif->dma_base, 8);
+	return 1;
+}
+
+/*
+ *	This can be called for a dynamically installed interface. Don't __init it
+ */
+ 
+void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports)
+{
+	printk("    %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1);
+	if (check_region(dma_base, num_ports)) {
+		printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
+		return;
+	}
+	request_region(dma_base, num_ports, hwif->name);
+	hwif->dma_base = dma_base;
+	hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
+						  PRD_ENTRIES * PRD_BYTES,
+						  &hwif->dmatable_dma);
+	if (hwif->dmatable_cpu == NULL)
+		goto dma_alloc_failure;
+
+	hwif->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
+				 GFP_KERNEL);
+	if (hwif->sg_table == NULL) {
+		pci_free_consistent(hwif->pci_dev, PRD_ENTRIES * PRD_BYTES,
+				    hwif->dmatable_cpu, hwif->dmatable_dma);
+		goto dma_alloc_failure;
+	}
+
+	hwif->dmaproc = &ide_dmaproc;
+
+	if (hwif->chipset != ide_trm290) {
+		byte dma_stat = inb(dma_base+2);
+		printk(", BIOS settings: %s:%s, %s:%s",
+		       hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
+		       hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
+	}
+	printk("\n");
+	return;
+
+dma_alloc_failure:
+	printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n");
+}
+
+/*
+ * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space:
+ */
+unsigned long __init ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name)
+{
+	unsigned long	dma_base = 0;
+	struct pci_dev	*dev = hwif->pci_dev;
+
+	if (hwif->mate && hwif->mate->dma_base) {
+		dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
+	} else {
+		dma_base = dev->resource[4].start;
+		if (!dma_base || dma_base == PCI_BASE_ADDRESS_IO_MASK) {
+			printk("%s: dma_base is invalid (0x%04lx)\n", name, dma_base);
+			dma_base = 0;
+		}
+	}
+	if (dma_base) {
+		if (extra) /* PDC20246, PDC20262, HPT343, & HPT366 */
+			request_region(dma_base+16, extra, name);
+		dma_base += hwif->channel ? 8 : 0;
+		hwif->dma_extra = extra;
+
+		switch(dev->device) {
+			case PCI_DEVICE_ID_AL_M5219:
+			case PCI_DEVICE_ID_AMD_VIPER_7409:
+			case PCI_DEVICE_ID_CMD_643:
+				outb(inb(dma_base+2) & 0x60, dma_base+2);
+				if (inb(dma_base+2) & 0x80) {
+					printk("%s: simplex device: DMA forced\n", name);
+				}
+				break;
+			default:
+				/*
+				 * If the device claims "simplex" DMA,
+				 * this means only one of the two interfaces
+				 * can be trusted with DMA at any point in time.
+				 * So we should enable DMA only on one of the
+				 * two interfaces.
+				 */
+				if ((inb(dma_base+2) & 0x80)) {	/* simplex device? */
+					if ((!hwif->drives[0].present && !hwif->drives[1].present) ||
+					    (hwif->mate && hwif->mate->dma_base)) {
+						printk("%s: simplex device:  DMA disabled\n", name);
+						dma_base = 0;
+					}
+				}
+		}
+	}
+	return dma_base;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-features.c linux/drivers/ide/ide-features.c
--- v2.3.51/linux/drivers/ide/ide-features.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-features.c	Sat Feb 26 20:32:13 2000
@@ -0,0 +1,312 @@
+/*
+ * linux/drivers/block/ide-features.c	Version 0.03	Feb. 10, 2000
+ *
+ *  Copyright (C) 1999-2000	Linus Torvalds & authors (see below)
+ *  
+ *  Copyright (C) 1999-2000	Andre Hedrick <andre@suse.com>
+ *
+ *  Extracts if ide.c to address the evolving transfer rate code for
+ *  the SETFEATURES_XFER callouts.  Various parts of any given function
+ *  are credited to previous ATA-IDE maintainers.
+ *
+ *  May be copied or modified under the terms of the GNU General Public License
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/blkpg.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#define SETFEATURES_CONTROL_REG		(0)	/* some arch's may need */
+
+/*
+ * A Verbose noise maker for debugging on the attempted transfer rates.
+ */
+char *ide_xfer_verbose (byte xfer_rate)
+{
+	switch(xfer_rate) {
+		case XFER_UDMA_7:	return("UDMA 7");
+		case XFER_UDMA_6:	return("UDMA 6");
+		case XFER_UDMA_5:	return("UDMA 5");
+		case XFER_UDMA_4:	return("UDMA 4");
+		case XFER_UDMA_3:	return("UDMA 3");
+		case XFER_UDMA_2:	return("UDMA 2");
+		case XFER_UDMA_1:	return("UDMA 1");
+		case XFER_UDMA_0:	return("UDMA 0");
+		case XFER_MW_DMA_2:	return("MW DMA 2");
+		case XFER_MW_DMA_1:	return("MW DMA 1");
+		case XFER_MW_DMA_0:	return("MW DMA 0");
+		case XFER_SW_DMA_2:	return("SW DMA 2");
+		case XFER_SW_DMA_1:	return("SW DMA 1");
+		case XFER_SW_DMA_0:	return("SW DMA 0");
+		case XFER_PIO_4:	return("PIO 4");
+		case XFER_PIO_3:	return("PIO 3");
+		case XFER_PIO_2:	return("PIO 2");
+		case XFER_PIO_1:	return("PIO 1");
+		case XFER_PIO_0:	return("PIO 0");
+		case XFER_PIO_SLOW:	return("PIO SLOW");
+		default:		return("XFER ERROR");
+	}
+}
+
+/*
+ *
+ */
+char *ide_media_verbose (ide_drive_t *drive)
+{
+	switch (drive->media) {
+		case ide_scsi:		return("scsi   ");
+		case ide_disk:		return("disk   ");
+		case ide_optical:	return("optical");
+		case ide_cdrom:		return("cdrom  ");
+		case ide_tape:		return("tape   ");
+		case ide_floppy:	return("floppy ");
+		default:		return("???????");
+	}
+}
+
+/*
+ * A Verbose noise maker for debugging on the attempted dmaing calls.
+ */
+char *ide_dmafunc_verbose (ide_dma_action_t dmafunc)
+{
+	switch (dmafunc) {
+		case ide_dma_read:		return("ide_dma_read");
+		case ide_dma_write:		return("ide_dma_write");
+		case ide_dma_begin:		return("ide_dma_begin");
+		case ide_dma_end:		return("ide_dma_end:");
+		case ide_dma_check:		return("ide_dma_check");
+		case ide_dma_on:		return("ide_dma_on");
+		case ide_dma_off:		return("ide_dma_off");
+		case ide_dma_off_quietly:	return("ide_dma_off_quietly");
+		case ide_dma_test_irq:		return("ide_dma_test_irq");
+		case ide_dma_bad_drive:		return("ide_dma_bad_drive");
+		case ide_dma_good_drive:	return("ide_dma_good_drive");
+		case ide_dma_lostirq:		return("ide_dma_lostirq");
+		case ide_dma_timeout:		return("ide_dma_timeout");
+		default:			return("unknown");
+	}
+}
+
+/*
+ * Update the 
+ */
+int ide_driveid_update (ide_drive_t *drive)
+{
+	/*
+	 * Re-read drive->id for possible DMA mode
+	 * change (copied from ide-probe.c)
+	 */
+	struct hd_driveid *id;
+	unsigned long timeout, irqs, flags;
+
+	probe_irq_off(probe_irq_on());
+	irqs = probe_irq_on();
+	if (IDE_CONTROL_REG)
+		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+	ide_delay_50ms();
+	OUT_BYTE(WIN_IDENTIFY, IDE_COMMAND_REG);
+	timeout = jiffies + WAIT_WORSTCASE;
+	do {
+		if (0 < (signed long)(jiffies - timeout)) {
+			if (irqs)
+				(void) probe_irq_off(irqs);
+			return 0;	/* drive timed-out */
+		}
+		ide_delay_50ms();	/* give drive a breather */
+	} while (IN_BYTE(IDE_ALTSTATUS_REG) & BUSY_STAT);
+	ide_delay_50ms();	/* wait for IRQ and DRQ_STAT */
+	if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT))
+		return 0;
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only; some systems need this */
+	id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC);
+	ide_input_data(drive, id, SECTOR_WORDS);
+	(void) GET_STAT();	/* clear drive IRQ */
+	ide__sti();		/* local CPU only */
+	__restore_flags(flags);	/* local CPU only */
+	ide_fix_driveid(id);
+	if (id && id->cyls) {
+		drive->id->dma_ultra = id->dma_ultra;
+		drive->id->dma_mword = id->dma_mword;
+		drive->id->dma_1word = id->dma_1word;
+		/* anything more ? */
+#ifdef DEBUG
+		printk("%s: dma_ultra=%04X, dma_mword=%04X, dma_1word=%04X\n",
+			drive->name, id->dma_ultra, id->dma_mword, id->dma_1word);
+#endif
+		kfree(id);
+	}
+	return 1;
+}
+
+/*
+ * Verify that we are doing an approved SETFEATURES_XFER with respect
+ * to the hardware being able to support request.  Since some hardware
+ * can improperly report capabilties, we check to see if the host adapter
+ * in combination with the device (usually a disk) properly detect
+ * and acknowledge each end of the ribbon.
+ */
+int ide_ata66_check (ide_drive_t *drive, int cmd, int nsect, int feature)
+{
+	if ((cmd == WIN_SETFEATURES) &&
+	    (nsect > XFER_UDMA_2) &&
+	    (feature == SETFEATURES_XFER)) {
+		if (!HWIF(drive)->udma_four) {
+			printk("%s: Speed warnings UDMA 3/4 is not functional.\n", HWIF(drive)->name);
+			return 1;
+		}
+		if ((drive->id->hw_config & 0x2000) == 0) {
+			printk("%s: Speed warnings UDMA 3/4 is not functional.\n", drive->name);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Backside of HDIO_DRIVE_CMD call of SETFEATURES_XFER.
+ * 1 : Safe to update drive->id DMA registers.
+ * 0 : OOPs not allowed.
+ */
+int set_transfer (ide_drive_t *drive, int cmd, int nsect, int feature)
+{
+	struct hd_driveid *id = drive->id;
+
+	if ((cmd == WIN_SETFEATURES) &&
+	    (nsect >= XFER_SW_DMA_0) &&
+	    (feature == SETFEATURES_XFER) &&
+	    (id->dma_ultra || id->dma_mword || id->dma_1word))
+		return 1;
+	return 0;
+}
+
+/*
+ * Similar to ide_wait_stat(), except it never calls ide_error internally.
+ * This is a kludge to handle the new ide_config_drive_speed() function,
+ * and should not otherwise be used anywhere.  Eventually, the tuneproc's
+ * should be updated to return ide_startstop_t, in which case we can get
+ * rid of this abomination again.  :)   -ml
+ *
+ * It is gone..........
+ *
+ * const char *msg == consider adding for verbose errors.
+ */
+int ide_config_drive_speed (ide_drive_t *drive, byte speed)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	int	i, error = 1;
+	byte unit = (drive->select.b.unit & 0x01);
+	byte stat;
+
+	/*
+	 * Don't use ide_wait_cmd here - it will
+	 * attempt to set_geometry and recalibrate,
+	 * but for some reason these don't work at
+	 * this point (lost interrupt).
+	 */
+        /*
+         * Select the drive, and issue the SETFEATURES command
+         */
+	disable_irq(hwif->irq);	/* disable_irq_nosync ?? */
+	udelay(1);
+	SELECT_DRIVE(HWIF(drive), drive);
+	udelay(1);
+	if (IDE_CONTROL_REG)
+		OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
+	OUT_BYTE(speed, IDE_NSECTOR_REG);
+	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
+	OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
+	if ((IDE_CONTROL_REG) && (SETFEATURES_CONTROL_REG))
+		OUT_BYTE(drive->ctl, IDE_CONTROL_REG);
+	udelay(1);
+	/*
+	 * Wait for drive to become non-BUSY
+	 */
+	if ((stat = GET_STAT()) & BUSY_STAT) {
+		unsigned long flags, timeout;
+		__save_flags(flags);	/* local CPU only */
+		ide__sti();		/* local CPU only -- for jiffies */
+		timeout = jiffies + WAIT_CMD;
+		while ((stat = GET_STAT()) & BUSY_STAT) {
+			if (0 < (signed long)(jiffies - timeout))
+				break;
+		}
+		__restore_flags(flags); /* local CPU only */
+	}
+
+	/*
+	 * Allow status to settle, then read it again.
+	 * A few rare drives vastly violate the 400ns spec here,
+	 * so we'll wait up to 10usec for a "good" status
+	 * rather than expensively fail things immediately.
+	 * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
+	 */
+	for (i = 0; i < 10; i++) {
+		udelay(1);
+		if (OK_STAT((stat = GET_STAT()), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) {
+			error = 0;
+			break;
+		}
+	}
+
+	enable_irq(hwif->irq);
+
+	if (error) {
+		(void) ide_dump_status(drive, "set_drive_speed_status", stat);
+		return error;
+	}
+
+	drive->id->dma_ultra &= ~0xFF00;
+	drive->id->dma_mword &= ~0x0F00;
+	drive->id->dma_1word &= ~0x0F00;
+
+	if (speed > XFER_PIO_4) {
+		outb(inb(hwif->dma_base+2)|(1<<(5+unit)), hwif->dma_base+2);
+	} else {
+		outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
+	}
+
+	switch(speed) {
+		case XFER_UDMA_7:   drive->id->dma_ultra |= 0x8080; break;
+		case XFER_UDMA_6:   drive->id->dma_ultra |= 0x4040; break;
+		case XFER_UDMA_5:   drive->id->dma_ultra |= 0x2020; break;
+		case XFER_UDMA_4:   drive->id->dma_ultra |= 0x1010; break;
+		case XFER_UDMA_3:   drive->id->dma_ultra |= 0x0808; break;
+		case XFER_UDMA_2:   drive->id->dma_ultra |= 0x0404; break;
+		case XFER_UDMA_1:   drive->id->dma_ultra |= 0x0202; break;
+		case XFER_UDMA_0:   drive->id->dma_ultra |= 0x0101; break;
+		case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break;
+		case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break;
+		case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break;
+		case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break;
+		case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break;
+		case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break;
+		default: break;
+	}
+	return error;
+}
+
+EXPORT_SYMBOL(ide_driveid_update);
+EXPORT_SYMBOL(ide_ata66_check);
+EXPORT_SYMBOL(set_transfer);
+EXPORT_SYMBOL(ide_config_drive_speed);
+
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-floppy.c linux/drivers/ide/ide-floppy.c
--- v2.3.51/linux/drivers/ide/ide-floppy.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-floppy.c	Tue Mar 14 18:08:06 2000
@@ -0,0 +1,1680 @@
+/*
+ * linux/drivers/block/ide-floppy.c	Version 0.9	Jul   4, 1999
+ *
+ * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * IDE ATAPI floppy driver.
+ *
+ * The driver currently doesn't have any fancy features, just the bare
+ * minimum read/write support.
+ *
+ * Many thanks to Lode Leroy <Lode.Leroy@www.ibase.be>, who tested so many
+ * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive.
+ *
+ * Ver 0.1   Oct 17 96   Initial test version, mostly based on ide-tape.c.
+ * Ver 0.2   Oct 31 96   Minor changes.
+ * Ver 0.3   Dec  2 96   Fixed error recovery bug.
+ * Ver 0.4   Jan 26 97   Add support for the HDIO_GETGEO ioctl.
+ * Ver 0.5   Feb 21 97   Add partitions support.
+ *                       Use the minimum of the LBA and CHS capacities.
+ *                       Avoid hwgroup->rq == NULL on the last irq.
+ *                       Fix potential null dereferencing with DEBUG_LOG.
+ * Ver 0.8   Dec  7 97   Increase irq timeout from 10 to 50 seconds.
+ *                       Add media write-protect detection.
+ *                       Issue START command only if TEST UNIT READY fails.
+ *                       Add work-around for IOMEGA ZIP revision 21.D.
+ *                       Remove idefloppy_get_capabilities().
+ * Ver 0.9   Jul  4 99   Fix a bug which might have caused the number of
+ *                        bytes requested on each interrupt to be zero.
+ *                        Thanks to <shanos@es.co.nz> for pointing this out.
+ */
+
+#define IDEFLOPPY_VERSION "0.9"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/cdrom.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+/*
+ *	The following are used to debug the driver.
+ */
+#define IDEFLOPPY_DEBUG_LOG		0
+#define IDEFLOPPY_DEBUG_INFO		0
+#define IDEFLOPPY_DEBUG_BUGS		1
+
+/*
+ *	Some drives require a longer irq timeout.
+ */
+#define IDEFLOPPY_WAIT_CMD		(5 * WAIT_CMD)
+
+/*
+ *	After each failed packet command we issue a request sense command
+ *	and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
+ */
+#define IDEFLOPPY_MAX_PC_RETRIES	3
+
+/*
+ *	With each packet command, we allocate a buffer of
+ *	IDEFLOPPY_PC_BUFFER_SIZE bytes.
+ */
+#define IDEFLOPPY_PC_BUFFER_SIZE	256
+
+/*
+ *	In various places in the driver, we need to allocate storage
+ *	for packet commands and requests, which will remain valid while
+ *	we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDEFLOPPY_PC_STACK		(10 + IDEFLOPPY_MAX_PC_RETRIES)
+
+/*
+ *	Our view of a packet command.
+ */
+typedef struct idefloppy_packet_command_s {
+	u8 c[12];				/* Actual packet bytes */
+	int retries;				/* On each retry, we increment retries */
+	int error;				/* Error code */
+	int request_transfer;			/* Bytes to transfer */
+	int actually_transferred;		/* Bytes actually transferred */
+	int buffer_size;			/* Size of our data buffer */
+	char *b_data;				/* Pointer which runs on the buffers */
+	int b_count;				/* Missing/Available data on the current buffer */
+	struct request *rq;			/* The corresponding request */
+	byte *buffer;				/* Data buffer */
+	byte *current_position;			/* Pointer into the above buffer */
+	void (*callback) (ide_drive_t *);	/* Called when this packet command is completed */
+	byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE];	/* Temporary buffer */
+	unsigned int flags;			/* Status/Action bit flags */
+} idefloppy_pc_t;
+
+/*
+ *	Packet command flag bits.
+ */
+#define	PC_ABORT			0	/* Set when an error is considered normal - We won't retry */
+#define PC_DMA_RECOMMENDED		2	/* 1 when we prefer to use DMA if possible */
+#define	PC_DMA_IN_PROGRESS		3	/* 1 while DMA in progress */
+#define	PC_DMA_ERROR			4	/* 1 when encountered problem during DMA */
+#define	PC_WRITING			5	/* Data direction */
+
+/*
+ *	Removable Block Access Capabilities Page
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned	page_code	:6;	/* Page code - Should be 0x1b */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	ps		:1;	/* Should be 0 */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	ps		:1;	/* Should be 0 */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	page_code	:6;	/* Page code - Should be 0x1b */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	u8		page_length;		/* Page Length - Should be 0xa */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned	reserved2	:6;
+	unsigned	srfp		:1;	/* Supports reporting progress of format */
+	unsigned	sflp		:1;	/* System floppy type device */
+	unsigned	tlun		:3;	/* Total logical units supported by the device */
+	unsigned	reserved3	:3;
+	unsigned	sml		:1;	/* Single / Multiple lun supported */
+	unsigned	ncd		:1;	/* Non cd optical device */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	sflp		:1;	/* System floppy type device */
+	unsigned	srfp		:1;	/* Supports reporting progress of format */
+	unsigned	reserved2	:6;
+	unsigned	ncd		:1;	/* Non cd optical device */
+	unsigned	sml		:1;	/* Single / Multiple lun supported */
+	unsigned	reserved3	:3;
+	unsigned	tlun		:3;	/* Total logical units supported by the device */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	u8		reserved[8];
+} idefloppy_capabilities_page_t;
+
+/*
+ *	Flexible disk page.
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned	page_code	:6;	/* Page code - Should be 0x5 */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	ps		:1;	/* The device is capable of saving the page */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	ps		:1;	/* The device is capable of saving the page */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	page_code	:6;	/* Page code - Should be 0x5 */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	u8		page_length;		/* Page Length - Should be 0x1e */
+	u16		transfer_rate;		/* In kilobits per second */
+	u8		heads, sectors;		/* Number of heads, Number of sectors per track */
+	u16		sector_size;		/* Byes per sector */
+	u16		cyls;			/* Number of cylinders */
+	u8		reserved10[10];
+	u8		motor_delay;		/* Motor off delay */
+	u8		reserved21[7];
+	u16		rpm;			/* Rotations per minute */
+	u8		reserved30[2];
+} idefloppy_flexible_disk_page_t;
+ 
+/*
+ *	Format capacity
+ */
+typedef struct {
+	u8		reserved[3];
+	u8		length;			/* Length of the following descriptors in bytes */
+} idefloppy_capacity_header_t;
+
+typedef struct {
+	u32		blocks;			/* Number of blocks */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned	dc		:2;	/* Descriptor Code */
+	unsigned	reserved	:6;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	reserved	:6;
+	unsigned	dc		:2;	/* Descriptor Code */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	u8		length_msb;		/* Block Length (MSB)*/
+	u16		length;			/* Block Length */
+} idefloppy_capacity_descriptor_t;
+
+#define CAPACITY_INVALID	0x00
+#define CAPACITY_UNFORMATTED	0x01
+#define CAPACITY_CURRENT	0x02
+#define CAPACITY_NO_CARTRIDGE	0x03
+
+/*
+ *	Most of our global data which we need to save even as we leave the
+ *	driver due to an interrupt or a timer event is stored in a variable
+ *	of type idefloppy_floppy_t, defined below.
+ */
+typedef struct {
+	ide_drive_t *drive;
+
+	idefloppy_pc_t *pc;			/* Current packet command */
+	idefloppy_pc_t *failed_pc; 		/* Last failed packet command */
+	idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */
+	int pc_stack_index;			/* Next free packet command storage space */
+	struct request rq_stack[IDEFLOPPY_PC_STACK];
+	int rq_stack_index;			/* We implement a circular array */
+
+	/*
+	 *	Last error information
+	 */
+	byte sense_key, asc, ascq;
+
+	/*
+	 *	Device information
+	 */
+	int blocks, block_size, bs_factor;			/* Current format */
+	idefloppy_capacity_descriptor_t capacity;		/* Last format capacity */
+	idefloppy_flexible_disk_page_t flexible_disk_page;	/* Copy of the flexible disk page */
+	int wp;							/* Write protect */
+
+	unsigned int flags;			/* Status/Action flags */
+} idefloppy_floppy_t;
+
+/*
+ *	Floppy flag bits values.
+ */
+#define IDEFLOPPY_DRQ_INTERRUPT		0	/* DRQ interrupt device */
+#define IDEFLOPPY_MEDIA_CHANGED		1	/* Media may have changed */
+#define IDEFLOPPY_USE_READ12		2	/* Use READ12/WRITE12 or READ10/WRITE10 */
+
+/*
+ *	ATAPI floppy drive packet commands
+ */
+#define IDEFLOPPY_FORMAT_UNIT_CMD	0x04
+#define IDEFLOPPY_INQUIRY_CMD		0x12
+#define IDEFLOPPY_MODE_SELECT_CMD	0x55
+#define IDEFLOPPY_MODE_SENSE_CMD	0x5a
+#define IDEFLOPPY_READ10_CMD		0x28
+#define IDEFLOPPY_READ12_CMD		0xa8
+#define IDEFLOPPY_READ_CAPACITY_CMD	0x23
+#define IDEFLOPPY_REQUEST_SENSE_CMD	0x03
+#define IDEFLOPPY_PREVENT_REMOVAL_CMD	0x1e
+#define IDEFLOPPY_SEEK_CMD		0x2b
+#define IDEFLOPPY_START_STOP_CMD	0x1b
+#define IDEFLOPPY_TEST_UNIT_READY_CMD	0x00
+#define IDEFLOPPY_VERIFY_CMD		0x2f
+#define IDEFLOPPY_WRITE10_CMD		0x2a
+#define IDEFLOPPY_WRITE12_CMD		0xaa
+#define IDEFLOPPY_WRITE_VERIFY_CMD	0x2e
+
+/*
+ *	Defines for the mode sense command
+ */
+#define MODE_SENSE_CURRENT		0x00
+#define MODE_SENSE_CHANGEABLE		0x01
+#define MODE_SENSE_DEFAULT		0x02 
+#define MODE_SENSE_SAVED		0x03
+
+/*
+ *	Special requests for our block device strategy routine.
+ */
+#define	IDEFLOPPY_FIRST_RQ		90
+
+/*
+ * 	IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define	IDEFLOPPY_PC_RQ			90
+
+#define IDEFLOPPY_LAST_RQ		90
+
+/*
+ *	A macro which can be used to check if a given request command
+ *	originated in the driver or in the buffer cache layer.
+ */
+#define IDEFLOPPY_RQ_CMD(cmd) 		((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
+
+/*
+ *	Error codes which are returned in rq->errors to the higher part
+ *	of the driver.
+ */
+#define	IDEFLOPPY_ERROR_GENERAL		101
+
+/*
+ *	The ATAPI Status Register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+		unsigned check		:1;	/* Error occurred */
+		unsigned idx		:1;	/* Reserved */
+		unsigned corr		:1;	/* Correctable error occurred */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned dsc		:1;	/* Media access command finished */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
+		unsigned bsy		:1;	/* The device has access to the command block */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned bsy		:1;	/* The device has access to the command block */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned dsc		:1;	/* Media access command finished */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned corr		:1;	/* Correctable error occurred */
+		unsigned idx		:1;	/* Reserved */
+		unsigned check		:1;	/* Error occurred */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	} b;
+} idefloppy_status_reg_t;
+
+/*
+ *	The ATAPI error register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+		unsigned ili		:1;	/* Illegal Length Indication */
+		unsigned eom		:1;	/* End Of Media Detected */
+		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
+		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
+		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
+		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
+		unsigned eom		:1;	/* End Of Media Detected */
+		unsigned ili		:1;	/* Illegal Length Indication */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	} b;
+} idefloppy_error_reg_t;
+
+/*
+ *	ATAPI Feature Register
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+		unsigned dma		:1;	/* Using DMA or PIO */
+		unsigned reserved321	:3;	/* Reserved */
+		unsigned reserved654	:3;	/* Reserved (Tag Type) */
+		unsigned reserved7	:1;	/* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned reserved7	:1;	/* Reserved */
+		unsigned reserved654	:3;	/* Reserved (Tag Type) */
+		unsigned reserved321	:3;	/* Reserved */
+		unsigned dma		:1;	/* Using DMA or PIO */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	} b;
+} idefloppy_feature_reg_t;
+
+/*
+ *	ATAPI Byte Count Register.
+ */
+typedef union {
+	unsigned all			:16;
+	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+		unsigned low		:8;	/* LSB */
+		unsigned high		:8;	/* MSB */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned high		:8;	/* MSB */
+		unsigned low		:8;	/* LSB */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	} b;
+} idefloppy_bcount_reg_t;
+
+/*
+ *	ATAPI Interrupt Reason Register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned reserved	:6;	/* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned reserved	:6;	/* Reserved */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	} b;
+} idefloppy_ireason_reg_t;
+
+/*
+ *	ATAPI floppy Drive Select Register
+ */
+typedef union {	
+	unsigned all			:8;
+	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+		unsigned sam_lun	:3;	/* Logical unit number */
+		unsigned reserved3	:1;	/* Reserved */
+		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
+		unsigned one5		:1;	/* Should be set to 1 */
+		unsigned reserved6	:1;	/* Reserved */
+		unsigned one7		:1;	/* Should be set to 1 */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned one7		:1;	/* Should be set to 1 */
+		unsigned reserved6	:1;	/* Reserved */
+		unsigned one5		:1;	/* Should be set to 1 */
+		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
+		unsigned reserved3	:1;	/* Reserved */
+		unsigned sam_lun	:3;	/* Logical unit number */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	} b;
+} idefloppy_drivesel_reg_t;
+
+/*
+ *	ATAPI Device Control Register
+ */
+typedef union {			
+	unsigned all			:8;
+	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+		unsigned zero0		:1;	/* Should be set to zero */
+		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
+		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+		unsigned one3		:1;	/* Should be set to 1 */
+		unsigned reserved4567	:4;	/* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned reserved4567	:4;	/* Reserved */
+		unsigned one3		:1;	/* Should be set to 1 */
+		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
+		unsigned zero0		:1;	/* Should be set to zero */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	} b;
+} idefloppy_control_reg_t;
+
+/*
+ *	The following is used to format the general configuration word of
+ *	the ATAPI IDENTIFY DEVICE command.
+ */
+struct idefloppy_id_gcw {	
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned packet_size		:2;	/* Packet Size */
+	unsigned reserved234		:3;	/* Reserved */
+	unsigned drq_type		:2;	/* Command packet DRQ type */
+	unsigned removable		:1;	/* Removable media */
+	unsigned device_type		:5;	/* Device type */
+	unsigned reserved13		:1;	/* Reserved */
+	unsigned protocol		:2;	/* Protocol type */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned protocol		:2;	/* Protocol type */
+	unsigned reserved13		:1;	/* Reserved */
+	unsigned device_type		:5;	/* Device type */
+	unsigned removable		:1;	/* Removable media */
+	unsigned drq_type		:2;	/* Command packet DRQ type */
+	unsigned reserved234		:3;	/* Reserved */
+	unsigned packet_size		:2;	/* Packet Size */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+};
+
+/*
+ *	INQUIRY packet command - Data Format
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned	device_type	:5;	/* Peripheral Device Type */
+	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned	reserved1_6t0	:7;	/* Reserved */
+	unsigned	rmb		:1;	/* Removable Medium Bit */
+	unsigned	ansi_version	:3;	/* ANSI Version */
+	unsigned	ecma_version	:3;	/* ECMA Version */
+	unsigned	iso_version	:2;	/* ISO Version */
+	unsigned	response_format :4;	/* Response Data Format */
+	unsigned	reserved3_45	:2;	/* Reserved */
+	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned	reserved3_7	:1;	/* AENC - Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned	device_type	:5;	/* Peripheral Device Type */
+	unsigned	rmb		:1;	/* Removable Medium Bit */
+	unsigned	reserved1_6t0	:7;	/* Reserved */
+	unsigned	iso_version	:2;	/* ISO Version */
+	unsigned	ecma_version	:3;	/* ECMA Version */
+	unsigned	ansi_version	:3;	/* ANSI Version */
+	unsigned	reserved3_7	:1;	/* AENC - Reserved */
+	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned	reserved3_45	:2;	/* Reserved */
+	unsigned	response_format :4;	/* Response Data Format */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	u8		additional_length;	/* Additional Length (total_length-4) */
+	u8		rsv5, rsv6, rsv7;	/* Reserved */
+	u8		vendor_id[8];		/* Vendor Identification */
+	u8		product_id[16];		/* Product Identification */
+	u8		revision_level[4];	/* Revision Level */
+	u8		vendor_specific[20];	/* Vendor Specific - Optional */
+	u8		reserved56t95[40];	/* Reserved - Optional */
+						/* Additional information may be returned */
+} idefloppy_inquiry_result_t;
+
+/*
+ *	REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned	error_code	:7;	/* Current error (0x70) */
+	unsigned	valid		:1;	/* The information field conforms to SFF-8070i */
+	u8		reserved1	:8;	/* Reserved */
+	unsigned	sense_key	:4;	/* Sense Key */
+	unsigned	reserved2_4	:1;	/* Reserved */
+	unsigned	ili		:1;	/* Incorrect Length Indicator */
+	unsigned	reserved2_67	:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	valid		:1;	/* The information field conforms to SFF-8070i */
+	unsigned	error_code	:7;	/* Current error (0x70) */
+	u8		reserved1	:8;	/* Reserved */
+	unsigned	reserved2_67	:2;
+	unsigned	ili		:1;	/* Incorrect Length Indicator */
+	unsigned	reserved2_4	:1;	/* Reserved */
+	unsigned	sense_key	:4;	/* Sense Key */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	u32		information __attribute__ ((packed));
+	u8		asl;			/* Additional sense length (n-7) */
+	u32		command_specific;	/* Additional command specific information */
+	u8		asc;			/* Additional Sense Code */
+	u8		ascq;			/* Additional Sense Code Qualifier */
+	u8		replaceable_unit_code;	/* Field Replaceable Unit Code */
+	u8		reserved[3];
+	u8		pad[2];			/* Padding to 20 bytes */
+} idefloppy_request_sense_result_t;
+
+/*
+ *	Pages of the SELECT SENSE / MODE SENSE packet commands.
+ */
+#define	IDEFLOPPY_CAPABILITIES_PAGE	0x1b
+#define IDEFLOPPY_FLEXIBLE_DISK_PAGE	0x05
+
+/*
+ *	Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+	u16		mode_data_length;	/* Length of the following data transfer */
+	u8		medium_type;		/* Medium Type */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned	reserved3	:7;
+	unsigned	wp		:1;	/* Write protect */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	wp		:1;	/* Write protect */
+	unsigned	reserved3	:7;
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+	u8		reserved[4];
+} idefloppy_mode_parameter_header_t;
+
+#define IDEFLOPPY_MIN(a,b)	((a)<(b) ? (a):(b))
+#define	IDEFLOPPY_MAX(a,b)	((a)>(b) ? (a):(b))
+
+/*
+ *	Too bad. The drive wants to send us data which we are not ready to accept.
+ *	Just throw it away.
+ */
+static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		IN_BYTE (IDE_DATA_REG);
+}
+
+#if IDEFLOPPY_DEBUG_BUGS
+static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		OUT_BYTE (0, IDE_DATA_REG);
+}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+/*
+ *	idefloppy_end_request is used to finish servicing a request.
+ *
+ *	For read/write requests, we will call ide_end_request to pass to the
+ *	next buffer.
+ */
+static void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive = hwgroup->drive;
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	struct request *rq = hwgroup->rq;
+	int error;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_end_request\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	switch (uptodate) {
+		case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
+		case 1: error = 0; break;
+		default: error = uptodate;
+	}
+	if (error)
+		floppy->failed_pc = NULL;
+	/* Why does this happen? */
+	if (!rq)
+		return;
+	if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
+		ide_end_request (uptodate, hwgroup);
+		return;
+	}
+	rq->errors = error;
+	ide_end_drive_cmd (drive, 0, 0);
+}
+
+static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+	int count;
+
+	while (bcount) {
+		if (pc->b_count == bh->b_size) {
+			rq->sector += rq->current_nr_sectors;
+			rq->nr_sectors -= rq->current_nr_sectors;
+			idefloppy_end_request (1, HWGROUP(drive));
+			if ((bh = rq->bh) != NULL)
+				pc->b_count = 0;
+		}
+		if (bh == NULL) {
+			printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
+			idefloppy_discard_data (drive, bcount);
+			return;
+		}
+		count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount);
+		atapi_input_bytes (drive, bh->b_data + pc->b_count, count);
+		bcount -= count; pc->b_count += count;
+	}
+}
+
+static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+	int count;
+	
+	while (bcount) {
+		if (!pc->b_count) {
+			rq->sector += rq->current_nr_sectors;
+			rq->nr_sectors -= rq->current_nr_sectors;
+			idefloppy_end_request (1, HWGROUP(drive));
+			if ((bh = rq->bh) != NULL) {
+				pc->b_data = bh->b_data;
+				pc->b_count = bh->b_size;
+			}
+		}
+		if (bh == NULL) {
+			printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
+			idefloppy_write_zeros (drive, bcount);
+			return;
+		}
+		count = IDEFLOPPY_MIN (pc->b_count, bcount);
+		atapi_output_bytes (drive, pc->b_data, count);
+		bcount -= count; pc->b_data += count; pc->b_count -= count;
+	}
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+
+	while ((bh = rq->bh) != NULL)
+		idefloppy_end_request (1, HWGROUP(drive));
+}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+/*
+ *	idefloppy_queue_pc_head generates a new packet command request in front
+ *	of the request queue, before the current request, so that it will be
+ *	processed immediately, on the next pass through the driver.
+ */
+static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq)
+{
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->cmd = IDEFLOPPY_PC_RQ;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+
+	if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK)
+		floppy->pc_stack_index=0;
+	return (&floppy->pc_stack[floppy->pc_stack_index++]);
+}
+
+static struct request *idefloppy_next_rq_storage (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+
+	if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK)
+		floppy->rq_stack_index=0;
+	return (&floppy->rq_stack[floppy->rq_stack_index++]);
+}
+
+/*
+ *	idefloppy_analyze_error is called on each failed packet command retry
+ *	to analyze the request sense.
+ */
+static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+
+	floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq;
+#if IDEFLOPPY_DEBUG_LOG
+	if (floppy->failed_pc)
+		printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
+	else
+		printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+}
+
+static void idefloppy_request_sense_callback (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+	if (!floppy->pc->error) {
+		idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer);
+		idefloppy_end_request (1,HWGROUP (drive));
+	} else {
+		printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
+		idefloppy_end_request (0,HWGROUP (drive));
+	}
+}
+
+/*
+ *	General packet command callback function.
+ */
+static void idefloppy_pc_callback (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive));
+}
+
+/*
+ *	idefloppy_init_pc initializes a packet command.
+ */
+static void idefloppy_init_pc (idefloppy_pc_t *pc)
+{
+	memset (pc->c, 0, 12);
+	pc->retries = 0;
+	pc->flags = 0;
+	pc->request_transfer = 0;
+	pc->buffer = pc->pc_buffer;
+	pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
+	pc->b_data = NULL;
+	pc->callback = &idefloppy_pc_callback;
+}
+
+static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
+{
+	idefloppy_init_pc (pc);	
+	pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD;
+	pc->c[4] = 255;
+	pc->request_transfer = 18;
+	pc->callback = &idefloppy_request_sense_callback;
+}
+
+/*
+ *	idefloppy_retry_pc is called when an error was detected during the
+ *	last packet command. We queue a request sense packet command in
+ *	the head of the request list.
+ */
+static void idefloppy_retry_pc (ide_drive_t *drive)
+{
+	idefloppy_pc_t *pc;
+	struct request *rq;
+	idefloppy_error_reg_t error;
+
+	error.all = IN_BYTE (IDE_ERROR_REG);
+	pc = idefloppy_next_pc_storage (drive);
+	rq = idefloppy_next_rq_storage (drive);
+	idefloppy_create_request_sense_cmd (pc);
+	idefloppy_queue_pc_head (drive, pc, rq);
+}
+
+/*
+ *	idefloppy_pc_intr is the usual interrupt handler which will be called
+ *	during a packet command.
+ */
+static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	idefloppy_status_reg_t status;
+	idefloppy_bcount_reg_t bcount;
+	idefloppy_ireason_reg_t ireason;
+	idefloppy_pc_t *pc=floppy->pc;
+	struct request *rq = pc->rq;
+	unsigned int temp;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */	
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+		if (HWIF(drive)->dmaproc(ide_dma_end, drive)) {
+			set_bit (PC_DMA_ERROR, &pc->flags);
+		} else {
+			pc->actually_transferred=pc->request_transfer;
+			idefloppy_update_buffers (drive, pc);
+		}
+#if IDEFLOPPY_DEBUG_LOG
+		printk (KERN_INFO "ide-floppy: DMA finished\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+	status.all = GET_STAT();					/* Clear the interrupt */
+
+	if (!status.b.drq) {						/* No more interrupts */
+#if IDEFLOPPY_DEBUG_LOG
+		printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+		clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+		ide__sti();	/* local CPU only */
+
+		if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) {	/* Error detected */
+#if IDEFLOPPY_DEBUG_LOG
+			printk (KERN_INFO "ide-floppy: %s: I/O error\n",drive->name);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+			rq->errors++;
+			if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+				printk (KERN_ERR "ide-floppy: I/O error in request sense command\n");
+				return ide_do_reset (drive);
+			}
+			idefloppy_retry_pc (drive);				/* Retry operation */
+			return ide_stopped; /* queued, but not started */
+		}
+		pc->error = 0;
+		if (floppy->failed_pc == pc)
+			floppy->failed_pc=NULL;
+		pc->callback(drive);			/* Command finished - Call the callback function */
+		return ide_stopped;
+	}
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+		printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
+		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+		return ide_do_reset (drive);
+	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
+	bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);			/* on this interrupt */
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+	if (ireason.b.cod) {
+		printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
+		return ide_do_reset (drive);
+	}
+	if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {	/* Hopefully, we will never get here */
+		printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+		printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write");
+		return ide_do_reset (drive);
+	}
+	if (!test_bit (PC_WRITING, &pc->flags)) {			/* Reading - Check that we have enough space */
+		temp = pc->actually_transferred + bcount.all;
+		if ( temp > pc->request_transfer) {
+			if (temp > pc->buffer_size) {
+				printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
+				idefloppy_discard_data (drive,bcount.all);
+				ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD, NULL);
+				return ide_started;
+			}
+#if IDEFLOPPY_DEBUG_LOG
+			printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+		}
+	}
+	if (test_bit (PC_WRITING, &pc->flags)) {
+		if (pc->buffer != NULL)
+			atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+		else
+			idefloppy_output_buffers (drive, pc, bcount.all);
+	} else {
+		if (pc->buffer != NULL)
+			atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+		else
+			idefloppy_input_buffers (drive, pc, bcount.all);
+	}
+	pc->actually_transferred+=bcount.all;				/* Update the current position */
+	pc->current_position+=bcount.all;
+
+	ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD, NULL);		/* And set the interrupt handler again */
+	return ide_started;
+}
+
+static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive)
+{
+	ide_startstop_t startstop;
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	idefloppy_ireason_reg_t ireason;
+
+	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+		printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return startstop;
+	}
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+	if (!ireason.b.cod || ireason.b.io) {
+		printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n");
+		return ide_do_reset (drive);
+	}
+	ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL);	/* Set the interrupt routine */
+	atapi_output_bytes (drive, floppy->pc->c, 12);		/* Send the actual packet */
+	return ide_started;
+}
+
+/*
+ *	Issue a packet command
+ */
+static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	idefloppy_bcount_reg_t bcount;
+	int dma_ok = 0;
+
+#if IDEFLOPPY_DEBUG_BUGS
+	if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+		printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n");
+	}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+	if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD)
+		floppy->failed_pc=pc;
+	floppy->pc=pc;							/* Set the current packet command */
+
+	if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+		/*
+		 *	We will "abort" retrying a packet command in case
+		 *	a legitimate error code was received.
+		 */
+		if (!test_bit (PC_ABORT, &pc->flags)) {
+			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+				drive->name, pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+			pc->error = IDEFLOPPY_ERROR_GENERAL;		/* Giving up */
+		}
+		floppy->failed_pc=NULL;
+		pc->callback(drive);
+		return ide_stopped;
+	}
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Retry number - %d\n",pc->retries);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	pc->retries++;
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->current_position=pc->buffer;
+	bcount.all = IDE_MIN(pc->request_transfer, 63 * 1024);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
+		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+	}
+	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+	if (IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);			/* Use PIO/DMA */
+	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (dma_ok) {							/* Begin DMA, if necessary */
+		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+	if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
+		ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD, NULL);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+		return ide_started;
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+		return idefloppy_transfer_pc (drive);
+	}
+}
+
+static void idefloppy_rw_callback (ide_drive_t *drive)
+{
+#if IDEFLOPPY_DEBUG_LOG	
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_end_request(1, HWGROUP(drive));
+	return;
+}
+
+static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent)
+{
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD;
+	pc->c[4] = prevent;
+}
+
+static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc)
+{
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD;
+	pc->c[7] = 255;
+	pc->c[8] = 255;
+	pc->request_transfer = 255;
+}
+
+/*
+ *	A mode sense command is used to "sense" floppy parameters.
+ */
+static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type)
+{
+	unsigned short length = sizeof (idefloppy_mode_parameter_header_t);
+	
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD;
+	pc->c[1] = 0;
+	pc->c[2] = page_code + (type << 6);
+
+	switch (page_code) {
+		case IDEFLOPPY_CAPABILITIES_PAGE:
+			length += 12;
+			break;
+		case IDEFLOPPY_FLEXIBLE_DISK_PAGE:
+			length += 32;
+			break;
+		default:
+			printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n");
+	}
+	put_unaligned (htons (length), (unsigned short *) &pc->c[7]);
+	pc->request_transfer = length;
+}
+
+static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start)
+{
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_START_STOP_CMD;
+	pc->c[4] = start;
+}
+
+static void idefloppy_create_test_unit_ready_cmd(idefloppy_pc_t *pc)
+{
+	idefloppy_init_pc(pc);
+	pc->c[0] = IDEFLOPPY_TEST_UNIT_READY_CMD;
+}
+
+static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
+{
+	int block = sector / floppy->bs_factor;
+	int blocks = rq->nr_sectors / floppy->bs_factor;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
+		2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_init_pc (pc);
+	if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) {
+		pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD;
+		put_unaligned (htonl (blocks), (unsigned int *) &pc->c[6]);
+	} else {
+		pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD;
+		put_unaligned (htons (blocks), (unsigned short *) &pc->c[7]);
+	}
+	put_unaligned (htonl (block), (unsigned int *) &pc->c[2]);
+	pc->callback = &idefloppy_rw_callback;
+	pc->rq = rq;
+	pc->b_data = rq->buffer;
+	pc->b_count = rq->cmd == READ ? 0 : rq->bh->b_size;
+	if (rq->cmd == WRITE)
+		set_bit (PC_WRITING, &pc->flags);
+	pc->buffer = NULL;
+	pc->request_transfer = pc->buffer_size = blocks * floppy->block_size;
+	set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+}
+
+/*
+ *	idefloppy_do_request is our request handling function.	
+ */
+static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	idefloppy_pc_t *pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	if (rq->errors >= ERROR_MAX) {
+		if (floppy->failed_pc != NULL)
+			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+				drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+		else
+			printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name);
+		idefloppy_end_request (0, HWGROUP(drive));
+		return ide_stopped;
+	}
+	switch (rq->cmd) {
+		case READ:
+		case WRITE:
+			if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) {
+				printk ("%s: unsupported r/w request size\n", drive->name);
+				idefloppy_end_request (0, HWGROUP(drive));
+				return ide_stopped;
+			}
+			pc = idefloppy_next_pc_storage (drive);
+			idefloppy_create_rw_cmd (floppy, pc, rq, block);
+			break;
+		case IDEFLOPPY_PC_RQ:
+			pc = (idefloppy_pc_t *) rq->buffer;
+			break;
+		default:
+			printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd);
+			idefloppy_end_request (0,HWGROUP (drive));
+			return ide_stopped;
+	}
+	pc->rq = rq;
+	return idefloppy_issue_pc (drive, pc);
+}
+
+/*
+ *	idefloppy_queue_pc_tail adds a special packet command request to the
+ *	tail of the request queue, and waits for it to be serviced.
+ */
+static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc)
+{
+	struct request rq;
+
+	ide_init_drive_cmd (&rq);
+	rq.buffer = (char *) pc;
+	rq.cmd = IDEFLOPPY_PC_RQ;
+	return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *	Look at the flexible disk page parameters. We will ignore the CHS
+ *	capacity parameters and use the LBA parameters instead.
+ */
+static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	idefloppy_pc_t pc;
+	idefloppy_mode_parameter_header_t *header;
+	idefloppy_flexible_disk_page_t *page;
+	int capacity, lba_capacity;
+
+	idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT);
+	if (idefloppy_queue_pc_tail (drive,&pc)) {
+		printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n");
+		return 1;
+	}
+	header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+	floppy->wp = header->wp;
+	page = (idefloppy_flexible_disk_page_t *) (header + 1);
+
+	page->transfer_rate = ntohs (page->transfer_rate);
+	page->sector_size = ntohs (page->sector_size);
+	page->cyls = ntohs (page->cyls);
+	page->rpm = ntohs (page->rpm);
+	capacity = page->cyls * page->heads * page->sectors * page->sector_size;
+	if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t)))
+		printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n",
+			drive->name, capacity / 1024, page->cyls, page->heads, page->sectors,
+			page->transfer_rate / 8, page->sector_size, page->rpm);
+
+	floppy->flexible_disk_page = *page;
+	drive->bios_cyl = page->cyls;
+	drive->bios_head = page->heads;
+	drive->bios_sect = page->sectors;
+	lba_capacity = floppy->blocks * floppy->block_size;
+	if (capacity < lba_capacity) {
+		printk (KERN_NOTICE "%s: The disk reports a capacity of %d bytes, "
+			"but the drive only handles %d\n",
+			drive->name, lba_capacity, capacity);
+		floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
+	}
+	return 0;
+}
+
+/*
+ *	Determine if a media is present in the floppy drive, and if so,
+ *	its LBA capacity.
+ */
+static int idefloppy_get_capacity (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	idefloppy_pc_t pc;
+	idefloppy_capacity_header_t *header;
+	idefloppy_capacity_descriptor_t *descriptor;
+	int i, descriptors, rc = 1, blocks, length;
+	
+	drive->bios_cyl = 0;
+	drive->bios_head = drive->bios_sect = 0;
+	floppy->blocks = floppy->bs_factor = 0;
+	drive->part[0].nr_sects = 0;
+
+	idefloppy_create_read_capacity_cmd (&pc);
+	if (idefloppy_queue_pc_tail (drive, &pc)) {
+		printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n");
+		return 1;
+	}
+	header = (idefloppy_capacity_header_t *) pc.buffer;
+	descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t);
+	descriptor = (idefloppy_capacity_descriptor_t *) (header + 1);
+	for (i = 0; i < descriptors; i++, descriptor++) {
+		blocks = descriptor->blocks = ntohl (descriptor->blocks);
+		length = descriptor->length = ntohs (descriptor->length);
+		if (!i && descriptor->dc == CAPACITY_CURRENT) {
+			if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t))) {
+				printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size, %s \n",
+					drive->name, blocks * length / 1024, blocks, length,
+					drive->using_dma ? ", DMA":"");
+			}
+			floppy->capacity = *descriptor;
+			if (!length || length % 512)
+				printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length);
+			else {
+				floppy->blocks = blocks;
+				floppy->block_size = length;
+				if ((floppy->bs_factor = length / 512) != 1)
+					printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name);
+				rc = 0;
+			}
+		}
+#if IDEFLOPPY_DEBUG_INFO
+		if (!i) printk (KERN_INFO "Descriptor 0 Code: %d\n", descriptor->dc);
+		printk (KERN_INFO "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length);
+#endif /* IDEFLOPPY_DEBUG_INFO */
+	}
+	(void) idefloppy_get_flexible_disk_page (drive);
+	drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor;
+	return rc;
+}
+
+/*
+ *	Our special ide-floppy ioctl's.
+ *
+ *	Currently there aren't any ioctl's.
+ */
+static int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+				 unsigned int cmd, unsigned long arg)
+{
+	idefloppy_pc_t pc;
+
+	if (cmd == CDROMEJECT) {
+		if (drive->usage > 1)
+			return -EBUSY;
+		idefloppy_create_prevent_cmd (&pc, 0);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		idefloppy_create_start_stop_cmd (&pc, 2);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		return 0;
+	}
+ 	return -EIO;
+}
+
+/*
+ *	Our open/release functions
+ */
+static int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	idefloppy_pc_t pc;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_open\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	MOD_INC_USE_COUNT;
+	if (drive->usage == 1) {
+		idefloppy_create_test_unit_ready_cmd(&pc);
+		if (idefloppy_queue_pc_tail(drive, &pc)) {
+			idefloppy_create_start_stop_cmd (&pc, 1);
+			(void) idefloppy_queue_pc_tail (drive, &pc);
+		}
+		if (idefloppy_get_capacity (drive)) {
+			drive->usage--;
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
+		if (floppy->wp && (filp->f_mode & 2)) {
+			drive->usage--;
+			MOD_DEC_USE_COUNT;
+			return -EROFS;
+		}		
+		set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+		idefloppy_create_prevent_cmd (&pc, 1);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		check_disk_change(inode->i_rdev);
+	}
+	return 0;
+}
+
+static void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	idefloppy_pc_t pc;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_release\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	if (!drive->usage) {
+		invalidate_buffers (inode->i_rdev);
+		idefloppy_create_prevent_cmd (&pc, 0);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ *	Check media change. Use a simple algorithm for now.
+ */
+static int idefloppy_media_change (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	
+	return test_and_clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+}
+
+/*
+ *	Return the current floppy capacity to ide.c.
+ */
+static unsigned long idefloppy_capacity (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	unsigned long capacity = floppy->blocks * floppy->bs_factor;
+
+	return capacity;
+}
+
+/*
+ *	idefloppy_identify_device checks if we can support a drive,
+ *	based on the ATAPI IDENTIFY command results.
+ */
+static int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+	struct idefloppy_id_gcw gcw;
+#if IDEFLOPPY_DEBUG_INFO
+	unsigned short mask,i;
+	char buffer[80];
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+	*((unsigned short *) &gcw) = id->config;
+
+#ifdef CONFIG_PPC
+	/* kludge for Apple PowerBook internal zip */
+	if ((gcw.device_type == 5) && !strstr(id->model, "CD-ROM")
+	    && strstr(id->model, "ZIP"))
+		gcw.device_type = 0;			
+#endif
+
+#if IDEFLOPPY_DEBUG_INFO
+	printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n");
+	switch (gcw.protocol) {
+		case 0: case 1: sprintf (buffer, "ATA");break;
+		case 2:	sprintf (buffer, "ATAPI");break;
+		case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break;
+	}
+	printk (KERN_INFO "Protocol Type: %s\n", buffer);
+	switch (gcw.device_type) {
+		case 0: sprintf (buffer, "Direct-access Device");break;
+		case 1: sprintf (buffer, "Streaming Tape Device");break;
+		case 2: case 3: case 4: sprintf (buffer, "Reserved");break;
+		case 5: sprintf (buffer, "CD-ROM Device");break;
+		case 6: sprintf (buffer, "Reserved");
+		case 7: sprintf (buffer, "Optical memory Device");break;
+		case 0x1f: sprintf (buffer, "Unknown or no Device type");break;
+		default: sprintf (buffer, "Reserved");
+	}
+	printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer);
+	printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No");	
+	switch (gcw.drq_type) {
+		case 0: sprintf (buffer, "Microprocessor DRQ");break;
+		case 1: sprintf (buffer, "Interrupt DRQ");break;
+		case 2: sprintf (buffer, "Accelerated DRQ");break;
+		case 3: sprintf (buffer, "Reserved");break;
+	}
+	printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer);
+	switch (gcw.packet_size) {
+		case 0: sprintf (buffer, "12 bytes");break;
+		case 1: sprintf (buffer, "16 bytes");break;
+		default: sprintf (buffer, "Reserved");break;
+	}
+	printk (KERN_INFO "Command Packet Size: %s\n", buffer);
+	printk (KERN_INFO "Model: %.40s\n",id->model);
+	printk (KERN_INFO "Firmware Revision: %.8s\n",id->fw_rev);
+	printk (KERN_INFO "Serial Number: %.20s\n",id->serial_no);
+	printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512);
+	printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+	printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+	printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+	printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+	printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+	printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO);
+	printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA);
+	printk (KERN_INFO "Single Word DMA supported modes:\n");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_1word & mask)
+			printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : "");
+	}
+	printk (KERN_INFO "Multi Word DMA supported modes:\n");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_mword & mask)
+			printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : "");
+	}
+	if (id->field_valid & 0x0002) {
+		printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+		if (id->eide_dma_min == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_dma_min);
+		printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer);
+		if (id->eide_dma_time == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_dma_time);
+		printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer);
+		if (id->eide_pio == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_pio);
+		printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer);
+		if (id->eide_pio_iordy == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_pio_iordy);
+		printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer);
+	} else
+		printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+	if (gcw.protocol != 2)
+		printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n");
+	else if (gcw.device_type != 0)
+		printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n");
+	else if (!gcw.removable)
+		printk (KERN_ERR "ide-floppy: The removable flag is not set\n");
+	else if (gcw.drq_type == 3) {
+		printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type);
+	} else if (gcw.packet_size != 0) {
+		printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n");
+	} else
+		return 1;
+	return 0;
+}
+
+static void idefloppy_add_settings(ide_drive_t *drive)
+{
+	int major = HWIF(drive)->major;
+	int minor = drive->select.b.unit << PARTN_BITS;
+
+	ide_add_setting(drive,	"bios_cyl",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	1023,				1,	1,	&drive->bios_cyl,		NULL);
+	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
+	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
+	ide_add_setting(drive,	"breada_readahead",	SETTING_RW,					BLKRAGET,		BLKRASET,		TYPE_INT,	0,	255,				1,	2,	&read_ahead[major],		NULL);
+	ide_add_setting(drive,	"file_readahead",	SETTING_RW,					BLKFRAGET,		BLKFRASET,		TYPE_INTA,	0,	INT_MAX,			1,	1024,	&max_readahead[major][minor],	NULL);
+	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW,					BLKSECTGET,		BLKSECTSET,		TYPE_INTA,	1,	255,				1,	2,	&max_sectors[major][minor],	NULL);
+
+}
+
+/*
+ *	Driver initialization.
+ */
+static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy)
+{
+	struct idefloppy_id_gcw gcw;
+	int major = HWIF(drive)->major, i;
+	int minor = drive->select.b.unit << PARTN_BITS;
+
+	*((unsigned short *) &gcw) = drive->id->config;
+	drive->driver_data = floppy;
+	drive->ready_stat = 0;
+	memset (floppy, 0, sizeof (idefloppy_floppy_t));
+	floppy->drive = drive;
+	floppy->pc = floppy->pc_stack;
+	if (gcw.drq_type == 1)
+		set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
+	/*
+	 *	We used to check revisions here. At this point however
+	 *	I'm giving up. Just assume they are all broken, its easier.
+	 *
+	 *	The actual reason for the workarounds was likely
+	 *	a driver bug after all rather than a firmware bug,
+	 *	and the workaround below used to hide it. It should
+	 *	be fixed as of version 1.9, but to be on the safe side
+	 *	we'll leave the limitation below for the 2.2.x tree.
+	 */
+
+	if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0)
+	{
+		for (i = 0; i < 1 << PARTN_BITS; i++)
+			max_sectors[major][minor + i] = 64;
+	}
+
+	(void) idefloppy_get_capacity (drive);
+	idefloppy_add_settings(drive);
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		ide_hwif_t *hwif = HWIF(drive);
+
+		if (drive != &hwif->drives[i]) continue;
+		hwif->gd->de_arr[i] = drive->de;
+		if (drive->removable)
+			hwif->gd->flags[i] |= GENHD_FL_REMOVABLE;
+		break;
+	}
+}
+
+static int idefloppy_cleanup (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->driver_data;
+
+	if (ide_unregister_subdriver (drive))
+		return 1;
+	drive->driver_data = NULL;
+	kfree (floppy);
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static ide_proc_entry_t idefloppy_proc[] = {
+	{ "geometry",	S_IFREG|S_IRUGO,	proc_ide_read_geometry,	NULL },
+	{ NULL, 0, NULL, NULL }
+};
+
+#else
+
+#define	idefloppy_proc	NULL
+
+#endif	/* CONFIG_PROC_FS */
+
+/*
+ *	IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idefloppy_driver = {
+	"ide-floppy",		/* name */
+	IDEFLOPPY_VERSION,	/* version */
+	ide_floppy,		/* media */
+	0,			/* busy */
+	1,			/* supports_dma */
+	0,			/* supports_dsc_overlap */
+	idefloppy_cleanup,	/* cleanup */
+	idefloppy_do_request,	/* do_request */
+	idefloppy_end_request,	/* end_request */
+	idefloppy_ioctl,	/* ioctl */
+	idefloppy_open,		/* open */
+	idefloppy_release,	/* release */
+	idefloppy_media_change,	/* media_change */
+	NULL,			/* pre_reset */
+	idefloppy_capacity,	/* capacity */
+	NULL,			/* special */
+	idefloppy_proc		/* proc */
+};
+
+int idefloppy_init (void);
+static ide_module_t idefloppy_module = {
+	IDE_DRIVER_MODULE,
+	idefloppy_init,
+	&idefloppy_driver,
+	NULL
+};
+
+/*
+ *	idefloppy_init will register the driver for each floppy.
+ */
+int idefloppy_init (void)
+{
+	ide_drive_t *drive;
+	idefloppy_floppy_t *floppy;
+	int failed = 0;
+
+	MOD_INC_USE_COUNT;
+	while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) {
+		if (!idefloppy_identify_device (drive, drive->id)) {
+			printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
+			continue;
+		}
+		if (drive->scsi) {
+			printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name);
+			continue;
+		}
+		if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) {
+			printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name);
+			continue;
+		}
+		if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) {
+			printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name);
+			kfree (floppy);
+			continue;
+		}
+		idefloppy_setup (drive, floppy);
+		failed--;
+	}
+	ide_register_module(&idefloppy_module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return idefloppy_init ();
+}
+
+void cleanup_module (void)
+{
+	ide_drive_t *drive;
+	int failed = 0;
+
+	while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, &idefloppy_driver, failed)) != NULL) {
+		if (idefloppy_cleanup (drive)) {
+			printk ("%s: cleanup_module() called while still busy\n", drive->name);
+			failed++;
+		}
+		/* We must remove proc entries defined in this module.
+		   Otherwise we oops while accessing these entries */
+		if (drive->proc)
+			ide_remove_proc_entries(drive->proc, idefloppy_proc);
+	}
+	ide_unregister_module(&idefloppy_module);
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-geometry.c linux/drivers/ide/ide-geometry.c
--- v2.3.51/linux/drivers/ide/ide-geometry.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-geometry.c	Thu Mar  9 17:39:10 2000
@@ -0,0 +1,214 @@
+/*
+ * linux/drivers/block/ide-geometry.c
+ */
+#include <linux/config.h>
+
+#ifdef CONFIG_BLK_DEV_IDE
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+extern ide_drive_t * get_info_ptr(kdev_t);
+extern unsigned long current_capacity (ide_drive_t *);
+
+/*
+ * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
+ * controller that is BIOS compatible with ST-506, and thus showing up in our
+ * BIOS table, but not register compatible, and therefore not present in CMOS.
+ *
+ * Furthermore, we will assume that our ST-506 drives <if any> are the primary
+ * drives in the system -- the ones reflected as drive 1 or 2.  The first
+ * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
+ * nibble.  This will be either a 4 bit drive type or 0xf indicating use byte
+ * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.  A non-zero value
+ * means we have an AT controller hard disk for that drive.
+ *
+ * Of course, there is no guarantee that either drive is actually on the
+ * "primary" IDE interface, but we don't bother trying to sort that out here.
+ * If a drive is not actually on the primary interface, then these parameters
+ * will be ignored.  This results in the user having to supply the logical
+ * drive geometry as a boot parameter for each drive not on the primary i/f.
+ */
+/*
+ * The only "perfect" way to handle this would be to modify the setup.[cS] code
+ * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
+ * for us during initialization.  I have the necessary docs -- any takers?  -ml
+ */
+/*
+ * I did this, but it doesnt work - there is no reasonable way to find the
+ * correspondence between the BIOS numbering of the disks and the Linux
+ * numbering. -aeb
+ *
+ * The code below is bad. One of the problems is that drives 1 and 2
+ * may be SCSI disks (even when IDE disks are present), so that
+ * the geometry we read here from BIOS is attributed to the wrong disks.
+ * Consequently, also the "drive->present = 1" below is a mistake.
+ *
+ * Eventually the entire routine below should be removed.
+ */
+void probe_cmos_for_drives (ide_hwif_t *hwif)
+{
+#ifdef __i386__
+	extern struct drive_info_struct drive_info;
+	byte cmos_disks, *BIOS = (byte *) &drive_info;
+	int unit;
+
+#ifdef CONFIG_BLK_DEV_PDC4030
+	if (hwif->chipset == ide_pdc4030 && hwif->channel != 0)
+		return;
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+	outb_p(0x12,0x70);		/* specify CMOS address 0x12 */
+	cmos_disks = inb_p(0x71);	/* read the data from 0x12 */
+	/* Extract drive geometry from CMOS+BIOS if not already setup */
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		ide_drive_t *drive = &hwif->drives[unit];
+
+		if ((cmos_disks & (0xf0 >> (unit*4)))
+		   && !drive->present && !drive->nobios) {
+			unsigned short cyl = *(unsigned short *)BIOS;
+			unsigned char head = *(BIOS+2);
+			unsigned char sect = *(BIOS+14);
+			if (cyl > 0 && head > 0 && sect > 0 && sect < 64) {
+				drive->cyl   = drive->bios_cyl  = cyl;
+				drive->head  = drive->bios_head = head;
+				drive->sect  = drive->bios_sect = sect;
+				drive->ctl   = *(BIOS+8);
+			} else {
+				printk("hd%d: C/H/S=%d/%d/%d from BIOS ignored\n", unit, cyl, head, sect);
+			}
+		}
+
+		BIOS += 16;
+	}
+#endif
+}
+
+
+/*
+ * If heads is nonzero: find a translation with this many heads and S=63.
+ * Otherwise: find out how OnTrack Disk Manager would translate the disk.
+ */
+static void
+ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s) {
+	static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
+	const byte *headp = dm_head_vals;
+	unsigned long total;
+
+	/*
+	 * The specs say: take geometry as obtained from Identify,
+	 * compute total capacity C*H*S from that, and truncate to
+	 * 1024*255*63. Now take S=63, H the first in the sequence
+	 * 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total.
+	 * [Please tell aeb@cwi.nl in case this computes a
+	 * geometry different from what OnTrack uses.]
+	 */
+	total = DRIVER(drive)->capacity(drive);
+
+	*s = 63;
+
+	if (heads) {
+		*h = heads;
+		*c = total / (63 * heads);
+		return;
+	}
+
+	while (63 * headp[0] * 1024 < total && headp[1] != 0)
+		 headp++;
+	*h = headp[0];
+	*c = total / (63 * headp[0]);
+}
+
+/*
+ * This routine is called from the partition-table code in pt/msdos.c.
+ * It has two tasks:
+ * (i) to handle Ontrack DiskManager by offsetting everything by 63 sectors,
+ *  or to handle EZdrive by remapping sector 0 to sector 1.
+ * (ii) to invent a translated geometry.
+ * Part (i) is suppressed if the user specifies the "noremap" option
+ * on the command line.
+ * Part (ii) is suppressed if the user specifies an explicit geometry.
+ *
+ * The ptheads parameter is either 0 or tells about the number of
+ * heads shown by the end of the first nonempty partition.
+ * If this is either 16, 32, 64, 128, 240 or 255 we'll believe it.
+ *
+ * The xparm parameter has the following meaning:
+ *	 0 = convert to CHS with fewer than 1024 cyls
+ *	     using the same method as Ontrack DiskManager.
+ *	 1 = same as "0", plus offset everything by 63 sectors.
+ *	-1 = similar to "0", plus redirect sector 0 to sector 1.
+ *	 2 = convert to a CHS geometry with "ptheads" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not 
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
+ */
+int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg)
+{
+	ide_drive_t *drive;
+	const char *msg1 = "";
+	int heads = 0;
+	int c, h, s;
+	int transl = 1;		/* try translation */
+	int ret = 0;
+
+	drive = get_info_ptr(i_rdev);
+	if (!drive)
+		return 0;
+
+	/* remap? */
+	if (drive->remap_0_to_1 != 2) {
+		if (xparm == 1) {		/* DM */
+			drive->sect0 = 63;
+			msg1 = " [remap +63]";
+			ret = 1;
+		} else if (xparm == -1) {	/* EZ-Drive */
+			if (drive->remap_0_to_1 == 0) {
+				drive->remap_0_to_1 = 1;
+				msg1 = " [remap 0->1]";
+				ret = 1;
+			}
+		}
+	}
+
+	/* There used to be code here that assigned drive->id->CHS
+	   to drive->CHS and that to drive->bios_CHS. However,
+	   some disks have id->C/H/S = 4092/16/63 but are larger than 2.1 GB.
+	   In such cases that code was wrong.  Moreover,
+	   there seems to be no reason to do any of these things. */
+
+	/* translate? */
+	if (drive->forced_geom)
+		transl = 0;
+
+	/* does ptheads look reasonable? */
+	if (ptheads == 32 || ptheads == 64 || ptheads == 128 ||
+	    ptheads == 240 || ptheads == 255)
+		heads = ptheads;
+
+	if (xparm == 2) {
+		if (!heads ||
+		   (drive->bios_head >= heads && drive->bios_sect == 63))
+			transl = 0;
+	}
+	if (xparm == -1) {
+		if (drive->bios_head > 16)
+			transl = 0;     /* we already have a translation */
+	}
+
+	if (transl) {
+		ontrack(drive, heads, &c, &h, &s);
+		drive->bios_cyl = c;
+		drive->bios_head = h;
+		drive->bios_sect = s;
+		ret = 1;
+	}
+
+	drive->part[0].nr_sects = current_capacity(drive);
+
+	if (ret)
+		printk("%s%s [%d/%d/%d]", msg, msg1,
+		       drive->bios_cyl, drive->bios_head, drive->bios_sect);
+	return ret;
+}
+#endif /* CONFIG_BLK_DEV_IDE */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-pci.c linux/drivers/ide/ide-pci.c
--- v2.3.51/linux/drivers/ide/ide-pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-pci.c	Mon Feb 28 07:20:23 2000
@@ -0,0 +1,728 @@
+/*
+ *  linux/drivers/block/ide-pci.c		Version 1.04	July 27, 1999
+ *
+ *  Copyright (c) 1998-1999  Andre Hedrick
+ *
+ *  Copyright (c) 1995-1998  Mark Lord
+ *  May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ *  This module provides support for automatic detection and
+ *  configuration of all PCI IDE interfaces present in a system.  
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define DEVID_PIIXa	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371FB_0})
+#define DEVID_PIIXb	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371FB_1})
+#define DEVID_PIIX3	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371SB_1})
+#define DEVID_PIIX4	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371AB})
+#define DEVID_PIIX4E	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AB_1})
+#define DEVID_PIIX4U	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801AA_1})
+#define DEVID_PIIX4U2	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82372FB_1})
+#define DEVID_VIA_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C561})
+#define DEVID_VP_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C586_1})
+#define DEVID_PDC20246	((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246})
+#define DEVID_PDC20262	((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262})
+#define DEVID_RZ1000	((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH,  PCI_DEVICE_ID_PCTECH_RZ1000})
+#define DEVID_RZ1001	((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH,  PCI_DEVICE_ID_PCTECH_RZ1001})
+#define DEVID_SAMURAI	((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH,  PCI_DEVICE_ID_PCTECH_SAMURAI_IDE})
+#define DEVID_CMD640	((ide_pci_devid_t){PCI_VENDOR_ID_CMD,     PCI_DEVICE_ID_CMD_640})
+#define DEVID_CMD643	((ide_pci_devid_t){PCI_VENDOR_ID_CMD,     PCI_DEVICE_ID_CMD_643})
+#define DEVID_CMD646	((ide_pci_devid_t){PCI_VENDOR_ID_CMD,     PCI_DEVICE_ID_CMD_646})
+#define DEVID_CMD648	((ide_pci_devid_t){PCI_VENDOR_ID_CMD,     PCI_DEVICE_ID_CMD_648})
+#define DEVID_SIS5513	((ide_pci_devid_t){PCI_VENDOR_ID_SI,      PCI_DEVICE_ID_SI_5513})
+#define DEVID_OPTI621	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    PCI_DEVICE_ID_OPTI_82C621})
+#define DEVID_OPTI621V	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    PCI_DEVICE_ID_OPTI_82C558})
+#define DEVID_OPTI621X	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    PCI_DEVICE_ID_OPTI_82C825})
+#define DEVID_TRM290	((ide_pci_devid_t){PCI_VENDOR_ID_TEKRAM,  PCI_DEVICE_ID_TEKRAM_DC290})
+#define DEVID_NS87410	((ide_pci_devid_t){PCI_VENDOR_ID_NS,      PCI_DEVICE_ID_NS_87410})
+#define DEVID_NS87415	((ide_pci_devid_t){PCI_VENDOR_ID_NS,      PCI_DEVICE_ID_NS_87415})
+#define DEVID_HT6565	((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK,  PCI_DEVICE_ID_HOLTEK_6565})
+#define DEVID_AEC6210	((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP,   PCI_DEVICE_ID_ARTOP_ATP850UF})
+#define DEVID_W82C105	((ide_pci_devid_t){PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105})
+#define DEVID_UM8673F	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8673F})
+#define DEVID_UM8886A	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886A})
+#define DEVID_UM8886BF	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886BF})
+#define DEVID_HPT34X	((ide_pci_devid_t){PCI_VENDOR_ID_TTI,     PCI_DEVICE_ID_TTI_HPT343})
+#define DEVID_HPT366	((ide_pci_devid_t){PCI_VENDOR_ID_TTI,     PCI_DEVICE_ID_TTI_HPT366})
+#define DEVID_ALI15X3	((ide_pci_devid_t){PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M5229})
+#define DEVID_CY82C693	((ide_pci_devid_t){PCI_VENDOR_ID_CONTAQ,  PCI_DEVICE_ID_CONTAQ_82C693})
+#define DEVID_HINT	((ide_pci_devid_t){0x3388,                0x8013})
+#define DEVID_CS5530	((ide_pci_devid_t){PCI_VENDOR_ID_CYRIX,   PCI_DEVICE_ID_CYRIX_5530_IDE})
+#define DEVID_AMD7409	((ide_pci_devid_t){PCI_VENDOR_ID_AMD,     PCI_DEVICE_ID_AMD_VIPER_7409})
+
+#define	IDE_IGNORE	((void *)-1)
+
+#ifdef CONFIG_BLK_DEV_AEC6210
+extern unsigned int pci_init_aec6210(struct pci_dev *, const char *);
+extern void ide_init_aec6210(ide_hwif_t *);
+extern void ide_dmacapable_aec6210(ide_hwif_t *, unsigned long);
+#define PCI_AEC6210	&pci_init_aec6210
+#define INIT_AEC6210	&ide_init_aec6210
+#define DMA_AEC6210	&ide_dmacapable_aec6210
+#else
+#define PCI_AEC6210	NULL
+#define INIT_AEC6210	NULL
+#define DMA_AEC6210	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_ALI15X3
+extern unsigned int pci_init_ali15x3(struct pci_dev *, const char *);
+extern unsigned int ata66_ali15x3(ide_hwif_t *);
+extern void ide_init_ali15x3(ide_hwif_t *);
+extern void ide_dmacapable_ali15x3(ide_hwif_t *, unsigned long);
+#define PCI_ALI15X3	&pci_init_ali15x3
+#define ATA66_ALI15X3	&ata66_ali15x3
+#define INIT_ALI15X3	&ide_init_ali15x3
+#define DMA_ALI15X3	&ide_dmacapable_ali15x3
+#else
+#define PCI_ALI15X3	NULL
+#define ATA66_ALI15X3	NULL
+#define INIT_ALI15X3	NULL
+#define DMA_ALI15X3	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_AMD7409
+extern unsigned int pci_init_amd7409(struct pci_dev *, const char *);
+extern unsigned int ata66_amd7409(ide_hwif_t *);
+extern void ide_init_amd7409(ide_hwif_t *);
+extern void ide_dmacapable_amd7409(ide_hwif_t *, unsigned long);
+#define PCI_AMD7409	&pci_init_amd7409
+#define ATA66_AMD7409	&ata66_amd7409
+#define INIT_AMD7409	&ide_init_amd7409
+#define DMA_AMD7409	&ide_dmacapable_amd7409
+#else
+#define PCI_AMD7409	NULL
+#define ATA66_AMD7409	NULL
+#define INIT_AMD7409	NULL
+#define DMA_AMD7409	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_CMD64X
+extern unsigned int pci_init_cmd64x(struct pci_dev *, const char *);
+extern unsigned int ata66_cmd64x(ide_hwif_t *);
+extern void ide_init_cmd64x(ide_hwif_t *);
+extern void ide_dmacapable_cmd64x(ide_hwif_t *, unsigned long);
+#define PCI_CMD64X	&pci_init_cmd64x
+#define ATA66_CMD64X	&ata66_cmd64x
+#define INIT_CMD64X	&ide_init_cmd64x
+#else
+#define PCI_CMD64X	NULL
+#define ATA66_CMD64X	NULL
+#ifdef __sparc_v9__
+#define INIT_CMD64X	IDE_IGNORE
+#else
+#define INIT_CMD64X	NULL
+#endif
+#endif
+
+#ifdef CONFIG_BLK_DEV_CY82C693
+extern unsigned int pci_init_cy82c693(struct pci_dev *, const char *);
+extern void ide_init_cy82c693(ide_hwif_t *);
+#define PCI_CY82C693	&pci_init_cy82c693
+#define INIT_CY82C693	&ide_init_cy82c693
+#else
+#define PCI_CY82C693	NULL
+#define INIT_CY82C693	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_CS5530
+extern unsigned int pci_init_cs5530(struct pci_dev *, const char *);
+extern void ide_init_cs5530(ide_hwif_t *);
+#define PCI_CS5530	&pci_init_cs5530
+#define INIT_CS5530	&ide_init_cs5530
+#else
+#define PCI_CS5530	NULL
+#define INIT_CS5530	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_HPT34X
+extern unsigned int pci_init_hpt34x(struct pci_dev *, const char *);
+extern void ide_init_hpt34x(ide_hwif_t *);
+#define PCI_HPT34X	&pci_init_hpt34x
+#define INIT_HPT34X	&ide_init_hpt34x
+#else
+#define PCI_HPT34X	NULL
+#define INIT_HPT34X	IDE_IGNORE
+#endif
+
+#ifdef CONFIG_BLK_DEV_HPT366
+extern byte hpt363_shared_irq;
+extern byte hpt363_shared_pin;
+extern unsigned int pci_init_hpt366(struct pci_dev *, const char *);
+extern unsigned int ata66_hpt366(ide_hwif_t *);
+extern void ide_init_hpt366(ide_hwif_t *);
+extern void ide_dmacapable_hpt366(ide_hwif_t *, unsigned long);
+#define PCI_HPT366	&pci_init_hpt366
+#define ATA66_HPT366	&ata66_hpt366
+#define INIT_HPT366	&ide_init_hpt366
+#define DMA_HPT366	&ide_dmacapable_hpt366
+#else
+static byte hpt363_shared_irq = 0;
+static byte hpt363_shared_pin = 0;
+#define PCI_HPT366	NULL
+#define ATA66_HPT366	NULL
+#define INIT_HPT366	NULL
+#define DMA_HPT366	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_NS87415
+extern void ide_init_ns87415(ide_hwif_t *);
+#define INIT_NS87415	&ide_init_ns87415
+#else
+#define INIT_NS87415	IDE_IGNORE
+#endif
+
+#ifdef CONFIG_BLK_DEV_OPTI621
+extern void ide_init_opti621(ide_hwif_t *);
+#define INIT_OPTI621	&ide_init_opti621
+#else
+#define INIT_OPTI621	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_PDC202XX
+extern unsigned int pci_init_pdc202xx(struct pci_dev *, const char *);
+extern unsigned int ata66_pdc202xx(ide_hwif_t *);
+extern void ide_init_pdc202xx(ide_hwif_t *);
+#define PCI_PDC202XX	&pci_init_pdc202xx
+#define ATA66_PDC202XX	&ata66_pdc202xx
+#define INIT_PDC202XX	&ide_init_pdc202xx
+#else
+#define PCI_PDC202XX	NULL
+#define ATA66_PDC202XX	NULL
+#define INIT_PDC202XX	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_PIIX
+extern unsigned int pci_init_piix(struct pci_dev *, const char *);
+extern unsigned int ata66_piix(ide_hwif_t *);
+extern void ide_init_piix(ide_hwif_t *);
+#define PCI_PIIX	&pci_init_piix
+#define ATA66_PIIX	&ata66_piix
+#define INIT_PIIX	&ide_init_piix
+#else
+#define PCI_PIIX	NULL
+#define ATA66_PIIX	NULL
+#define INIT_PIIX	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_RZ1000
+extern void ide_init_rz1000(ide_hwif_t *);
+#define INIT_RZ1000	&ide_init_rz1000
+#else
+#define INIT_RZ1000	IDE_IGNORE
+#endif
+
+#define INIT_SAMURAI	NULL
+
+#ifdef CONFIG_BLK_DEV_SIS5513
+extern unsigned int pci_init_sis5513(struct pci_dev *, const char *);
+extern unsigned int ata66_sis5513(ide_hwif_t *);
+extern void ide_init_sis5513(ide_hwif_t *);
+#define PCI_SIS5513	&pci_init_sis5513
+#define ATA66_SIS5513	&ata66_sis5513
+#define INIT_SIS5513	&ide_init_sis5513
+#else
+#define PCI_SIS5513	NULL
+#define ATA66_SIS5513	NULL
+#define INIT_SIS5513	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_SL82C105
+extern void ide_init_sl82c105(ide_hwif_t *);
+extern void ide_dmacapable_sl82c105(ide_hwif_t *, unsigned long);
+#define INIT_W82C105	&ide_init_sl82c105
+#define DMA_W82C105	&ide_dmacapable_sl82c105
+#else
+#define INIT_W82C105	IDE_IGNORE
+#define DMA_W82C105	NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_TRM290
+extern void ide_init_trm290(ide_hwif_t *);
+#define INIT_TRM290	&ide_init_trm290
+#else
+#define INIT_TRM290	IDE_IGNORE
+#endif
+
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+extern unsigned int pci_init_via82cxxx(struct pci_dev *, const char *);
+extern unsigned int ata66_via82cxxx(ide_hwif_t *);
+extern void ide_init_via82cxxx(ide_hwif_t *);
+extern void ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long);
+#define PCI_VIA82CXXX	&pci_init_via82cxxx
+#define ATA66_VIA82CXXX	&ata66_via82cxxx
+#define INIT_VIA82CXXX	&ide_init_via82cxxx
+#define DMA_VIA82CXXX	&ide_dmacapable_via82cxxx
+#else
+#define PCI_VIA82CXXX	NULL
+#define ATA66_VIA82CXXX	NULL
+#define INIT_VIA82CXXX	NULL
+#define DMA_VIA82CXXX	NULL
+#endif
+
+typedef struct ide_pci_enablebit_s {
+	byte	reg;	/* byte pci reg holding the enable-bit */
+	byte	mask;	/* mask to isolate the enable-bit */
+	byte	val;	/* value of masked reg when "enabled" */
+} ide_pci_enablebit_t;
+
+typedef struct ide_pci_device_s {
+	ide_pci_devid_t		devid;
+	const char		*name;
+	unsigned int		(*init_chipset)(struct pci_dev *dev, const char *name);
+	unsigned int		(*ata66_check)(ide_hwif_t *hwif);
+	void 			(*init_hwif)(ide_hwif_t *hwif);
+	void			(*dma_init)(ide_hwif_t *hwif, unsigned long dmabase);
+	ide_pci_enablebit_t	enablebits[2];
+	byte			bootable;
+	unsigned int		extra;
+} ide_pci_device_t;
+
+static ide_pci_device_t ide_pci_chipsets[] __initdata = {
+	{DEVID_PIIXa,	"PIIX",		NULL,		NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
+	{DEVID_PIIXb,	"PIIX",		NULL,		NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
+	{DEVID_PIIX3,	"PIIX3",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
+	{DEVID_PIIX4,	"PIIX4",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	ON_BOARD,	0 },
+	{DEVID_PIIX4E,	"PIIX4",	PCI_PIIX,	NULL,		INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
+	{DEVID_PIIX4U,	"PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
+	{DEVID_PIIX4U2,	"PIIX4",	PCI_PIIX,	ATA66_PIIX,	INIT_PIIX,	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}},	ON_BOARD,	0 },
+	{DEVID_VIA_IDE,	"VIA_IDE",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_VP_IDE,	"VP_IDE",	PCI_VIA82CXXX,	ATA66_VIA82CXXX,INIT_VIA82CXXX,	DMA_VIA82CXXX,	{{0x40,0x02,0x02}, {0x40,0x01,0x01}}, 	ON_BOARD,	0 },
+	{DEVID_PDC20246,"PDC20246",	PCI_PDC202XX,	NULL,		INIT_PDC202XX,	NULL,		{{0x50,0x02,0x02}, {0x50,0x04,0x04}}, 	OFF_BOARD,	16 },
+	{DEVID_PDC20262,"PDC20262",	PCI_PDC202XX,	ATA66_PDC202XX,	INIT_PDC202XX,	NULL,		{{0x50,0x02,0x02}, {0x50,0x04,0x04}},	OFF_BOARD,	48 },
+	{DEVID_RZ1000,	"RZ1000",	NULL,		NULL,		INIT_RZ1000,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_RZ1001,	"RZ1001",	NULL,		NULL,		INIT_RZ1000,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_SAMURAI,	"SAMURAI",	NULL,		NULL,		INIT_SAMURAI,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_CMD640,	"CMD640",	NULL,		NULL,		IDE_IGNORE,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_NS87410,	"NS87410",	NULL,		NULL,		NULL,		NULL,		{{0x43,0x08,0x08}, {0x47,0x08,0x08}}, 	ON_BOARD,	0 },
+	{DEVID_SIS5513,	"SIS5513",	PCI_SIS5513,	ATA66_SIS5513,	INIT_SIS5513,	NULL,		{{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, 	ON_BOARD,	0 },
+	{DEVID_CMD643,	"CMD643",	PCI_CMD64X,	NULL,		INIT_CMD64X,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_CMD646,	"CMD646",	PCI_CMD64X,	NULL,		INIT_CMD64X,	NULL,		{{0x00,0x00,0x00}, {0x51,0x80,0x80}}, 	ON_BOARD,	0 },
+	{DEVID_CMD648,	"CMD648",	PCI_CMD64X,	ATA66_CMD64X,	INIT_CMD64X,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_HT6565,	"HT6565",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_OPTI621,	"OPTI621",	NULL,		NULL,		INIT_OPTI621,	NULL,		{{0x45,0x80,0x00}, {0x40,0x08,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_OPTI621X,"OPTI621X",	NULL,		NULL,		INIT_OPTI621,	NULL,		{{0x45,0x80,0x00}, {0x40,0x08,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_TRM290,	"TRM290",	NULL,		NULL,		INIT_TRM290,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_NS87415,	"NS87415",	NULL,		NULL,		INIT_NS87415,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_AEC6210,	"AEC6210",	PCI_AEC6210,	NULL,		INIT_AEC6210,	DMA_AEC6210,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	OFF_BOARD,	0 },
+	{DEVID_W82C105,	"W82C105",	NULL,		NULL,		INIT_W82C105,	DMA_W82C105,	{{0x40,0x01,0x01}, {0x40,0x10,0x10}}, 	ON_BOARD,	0 },
+	{DEVID_UM8673F,	"UM8673F",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_UM8886A,	"UM8886A",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_UM8886BF,"UM8886BF",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_HPT34X,	"HPT34X",	PCI_HPT34X,	NULL,		INIT_HPT34X,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	NEVER_BOARD,	16 },
+	{DEVID_HPT366,	"HPT366",	PCI_HPT366,	ATA66_HPT366,	INIT_HPT366,	DMA_HPT366,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	OFF_BOARD,	240 },
+	{DEVID_ALI15X3,	"ALI15X3",	PCI_ALI15X3,	ATA66_ALI15X3,	INIT_ALI15X3,	DMA_ALI15X3,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_CY82C693,"CY82C693",	PCI_CY82C693,	NULL,		INIT_CY82C693,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_HINT,	"HINT_IDE",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_CS5530,	"CS5530",	PCI_CS5530,	NULL,		INIT_CS5530,	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
+	{DEVID_AMD7409,	"AMD7409",	PCI_AMD7409,	ATA66_AMD7409,	INIT_AMD7409,	DMA_AMD7409,	{{0x40,0x01,0x01}, {0x40,0x02,0x02}},	ON_BOARD,	0 },
+	{IDE_PCI_DEVID_NULL, "PCI_IDE",	NULL,		NULL,		NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 }};
+
+/*
+ * This allows offboard ide-pci cards the enable a BIOS, verify interrupt
+ * settings of split-mirror pci-config space, place chipset into init-mode,
+ * and/or preserve an interrupt if the card is not native ide support.
+ */
+static unsigned int __init ide_special_settings (struct pci_dev *dev, const char *name)
+{
+	switch(dev->device) {
+		case PCI_DEVICE_ID_TTI_HPT366:
+		case PCI_DEVICE_ID_PROMISE_20246:
+		case PCI_DEVICE_ID_PROMISE_20262:
+		case PCI_DEVICE_ID_ARTOP_ATP850UF:
+			return dev->irq;
+		default:
+			break;
+	}
+	return 0;
+}
+
+/*
+ * Match a PCI IDE port against an entry in ide_hwifs[],
+ * based on io_base port if possible.
+ */
+static ide_hwif_t __init *ide_match_hwif (unsigned long io_base, byte bootable, const char *name)
+{
+	int h;
+	ide_hwif_t *hwif;
+
+	/*
+	 * Look for a hwif with matching io_base specified using
+	 * parameters to ide_setup().
+	 */
+	for (h = 0; h < MAX_HWIFS; ++h) {
+		hwif = &ide_hwifs[h];
+		if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
+			if (hwif->chipset == ide_generic)
+				return hwif; /* a perfect match */
+		}
+	}
+	/*
+	 * Look for a hwif with matching io_base default value.
+	 * If chipset is "ide_unknown", then claim that hwif slot.
+	 * Otherwise, some other chipset has already claimed it..  :(
+	 */
+	for (h = 0; h < MAX_HWIFS; ++h) {
+		hwif = &ide_hwifs[h];
+		if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
+			if (hwif->chipset == ide_unknown)
+				return hwif; /* match */
+			printk("%s: port 0x%04lx already claimed by %s\n", name, io_base, hwif->name);
+			return NULL;	/* already claimed */
+		}
+	}
+	/*
+	 * Okay, there is no hwif matching our io_base,
+	 * so we'll just claim an unassigned slot.
+	 * Give preference to claiming other slots before claiming ide0/ide1,
+	 * just in case there's another interface yet-to-be-scanned
+	 * which uses ports 1f0/170 (the ide0/ide1 defaults).
+	 *
+	 * Unless there is a bootable card that does not use the standard
+	 * ports 1f0/170 (the ide0/ide1 defaults). The (bootable) flag.
+	 */
+	if (bootable) {
+		for (h = 0; h < MAX_HWIFS; ++h) {
+			hwif = &ide_hwifs[h];
+			if (hwif->chipset == ide_unknown)
+				return hwif;	/* pick an unused entry */
+		}
+	} else {
+		for (h = 2; h < MAX_HWIFS; ++h) {
+			hwif = ide_hwifs + h;
+			if (hwif->chipset == ide_unknown)
+				return hwif;	/* pick an unused entry */
+		}
+	}
+	for (h = 0; h < 2; ++h) {
+		hwif = ide_hwifs + h;
+		if (hwif->chipset == ide_unknown)
+			return hwif;	/* pick an unused entry */
+	}
+	printk("%s: too many IDE interfaces, no room in table\n", name);
+	return NULL;
+}
+
+static int __init ide_setup_pci_baseregs (struct pci_dev *dev, const char *name)
+{
+	byte reg, progif = 0;
+
+	/*
+	 * Place both IDE interfaces into PCI "native" mode:
+	 */
+	if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) {
+		if ((progif & 0xa) != 0xa) {
+			printk("%s: device not capable of full native PCI mode\n", name);
+			return 1;
+		}
+		printk("%s: placing both ports into native PCI mode\n", name);
+		(void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5);
+		if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) {
+			printk("%s: rewrite of PROGIF failed, wanted 0x%04x, got 0x%04x\n", name, progif|5, progif);
+			return 1;
+		}
+	}
+	/*
+	 * Setup base registers for IDE command/control spaces for each interface:
+	 */
+	for (reg = 0; reg < 4; reg++) {
+		struct resource *res = dev->resource + reg;
+		if (!(res->flags & PCI_BASE_ADDRESS_SPACE_IO))
+			continue;
+		if (!res->start) {
+			printk("%s: Missing I/O address #%d\n", name, reg);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * ide_setup_pci_device() looks at the primary/secondary interfaces
+ * on a PCI IDE device and, if they are enabled, prepares the IDE driver
+ * for use with them.  This generic code works for most PCI chipsets.
+ *
+ * One thing that is not standardized is the location of the
+ * primary/secondary interface "enable/disable" bits.  For chipsets that
+ * we "know" about, this information is in the ide_pci_device_t struct;
+ * for all other chipsets, we just assume both interfaces are enabled.
+ */
+static void __init ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d)
+{
+	unsigned int port, at_least_one_hwif_enabled = 0, autodma = 0, pciirq = 0;
+	unsigned short pcicmd = 0, tried_config = 0;
+	byte tmp = 0;
+	ide_hwif_t *hwif, *mate = NULL;
+
+#ifdef CONFIG_IDEDMA_AUTO
+	autodma = 1;
+#endif
+check_if_enabled:
+	if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) {
+		printk("%s: error accessing PCI regs\n", d->name);
+		return;
+	}
+	if (!(pcicmd & PCI_COMMAND_IO)) {	/* is device disabled? */
+		/*
+		 * PnP BIOS was *supposed* to have set this device up for us,
+		 * but we can do it ourselves, so long as the BIOS has assigned an IRQ
+		 *  (or possibly the device is using a "legacy header" for IRQs).
+		 * Maybe the user deliberately *disabled* the device,
+		 * but we'll eventually ignore it again if no drives respond.
+		 */
+		if (tried_config++
+		 || ide_setup_pci_baseregs(dev, d->name)
+		 || pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_IO)) {
+			printk("%s: device disabled (BIOS)\n", d->name);
+			return;
+		}
+		autodma = 0;	/* default DMA off if we had to configure it here */
+		goto check_if_enabled;
+	}
+	if (tried_config)
+		printk("%s: device enabled (Linux)\n", d->name);
+	/*
+	 * Can we trust the reported IRQ?
+	 */
+	pciirq = dev->irq;
+	if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) {
+		printk("%s: not 100%% native mode: will probe irqs later\n", d->name);
+		/*
+		 * This allows offboard ide-pci cards the enable a BIOS,
+		 * verify interrupt settings of split-mirror pci-config
+		 * space, place chipset into init-mode, and/or preserve
+		 * an interrupt if the card is not native ide support.
+		 */
+		pciirq = (d->init_chipset) ? d->init_chipset(dev, d->name) : ide_special_settings(dev, d->name);
+	} else if (tried_config) {
+		printk("%s: will probe irqs later\n", d->name);
+		pciirq = 0;
+	} else if (!pciirq) {
+		printk("%s: bad irq (%d): will probe later\n", d->name, pciirq);
+		pciirq = 0;
+	} else {
+		if (d->init_chipset)
+			(void) d->init_chipset(dev, d->name);
+#ifdef __sparc__
+		printk("%s: 100%% native mode on irq %s\n",
+		       d->name, __irq_itoa(pciirq));
+#else
+		printk("%s: 100%% native mode on irq %d\n", d->name, pciirq);
+#endif
+	}
+	if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X)) {
+		/* see comments in hpt34x.c on why..... */
+		d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD;
+	}
+	/*
+	 * Set up the IDE ports
+	 */
+	for (port = 0; port <= 1; ++port) {
+		unsigned long base = 0, ctl = 0;
+		ide_pci_enablebit_t *e = &(d->enablebits[port]);
+		if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val))
+			continue;	/* port not enabled */
+		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) && (port))
+			return;
+		if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE || (dev->class & (port ? 4 : 1)) != 0) {
+			ctl  = dev->resource[(2*port)+1].start;
+			base = dev->resource[2*port].start;
+			if (!(ctl & PCI_BASE_ADDRESS_IO_MASK) ||
+			    !(base & PCI_BASE_ADDRESS_IO_MASK)) {
+				printk("%s: IO baseregs (BIOS) are reported as MEM, report to <andre@suse.com>.\n", d->name);
+#if 0
+				/* FIXME! This really should check that it really gets the IO/MEM part right! */
+				continue;
+#endif
+			}
+		}
+		if ((ctl && !base) || (base && !ctl)) {
+			printk("%s: inconsistent baseregs (BIOS) for port %d, skipping\n", d->name, port);
+			continue;
+		}
+		if (!ctl)
+			ctl = port ? 0x374 : 0x3f4;	/* use default value */
+		if (!base)
+			base = port ? 0x170 : 0x1f0;	/* use default value */
+		if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL)
+			continue;	/* no room in ide_hwifs[] */
+		if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
+			ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL);
+			memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
+			hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
+		}
+		hwif->chipset = ide_pci;
+		hwif->pci_dev = dev;
+		hwif->pci_devid = d->devid;
+		hwif->channel = port;
+		if (!hwif->irq)
+			hwif->irq = pciirq;
+		if (mate) {
+			hwif->mate = mate;
+			mate->mate = hwif;
+			if (IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210)) {
+				hwif->serialized = 1;
+				mate->serialized = 1;
+			}
+		}
+		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886BF) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8673F)) {
+			hwif->irq = hwif->channel ? 15 : 14;
+			goto bypass_umc_dma;
+		}
+		if (hwif->udma_four) {
+			printk("%s: ATA-66 forced bit set (WARNING)!!\n", d->name);
+		} else {
+			hwif->udma_four = (d->ata66_check) ? d->ata66_check(hwif) : 0;
+		}
+#ifdef CONFIG_BLK_DEV_IDEDMA
+		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X))
+			autodma = 0;
+		if (autodma)
+			hwif->autodma = 1;
+		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) ||
+		    ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) {
+			unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name);
+			if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) {
+				/*
+ 	 			 * Set up BM-DMA capability (PnP BIOS should have done this)
+ 	 			 */
+				hwif->autodma = 0;	/* default DMA off if we had to configure it here */
+				(void) pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER);
+				if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || !(pcicmd & PCI_COMMAND_MASTER)) {
+					printk("%s: %s error updating PCICMD\n", hwif->name, d->name);
+					dma_base = 0;
+				}
+			}
+			if (dma_base) {
+				if (d->dma_init) {
+					d->dma_init(hwif, dma_base);
+				} else {
+					ide_setup_dma(hwif, dma_base, 8);
+				}
+			} else {
+				printk("%s: %s Bus-Master DMA disabled (BIOS)\n", hwif->name, d->name);
+			}
+		}
+#endif	/* CONFIG_BLK_DEV_IDEDMA */
+bypass_umc_dma:
+		if (d->init_hwif)  /* Call chipset-specific routine for each enabled hwif */
+			d->init_hwif(hwif);
+		mate = hwif;
+		at_least_one_hwif_enabled = 1;
+	}
+	if (!at_least_one_hwif_enabled)
+		printk("%s: neither IDE port enabled (BIOS)\n", d->name);
+}
+
+static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d)
+{
+	struct pci_dev *dev2 = NULL, *findev;
+	ide_pci_device_t *d2;
+	unsigned char pin1 = 0, pin2 = 0;
+
+	if (PCI_FUNC(dev->devfn) & 1)
+		return;
+	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1);
+	pci_for_each_dev(findev) {
+		if ((findev->vendor == dev->vendor) &&
+		    (findev->device == dev->device) &&
+		    ((findev->devfn - dev->devfn) == 1) &&
+		    (PCI_FUNC(findev->devfn) & 1)) {
+			dev2 = findev;
+			pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2);
+			hpt363_shared_pin = (pin1 != pin2) ? 1 : 0;
+			hpt363_shared_irq = (dev->irq == dev2->irq) ? 1 : 0;
+			if (hpt363_shared_pin && hpt363_shared_irq) {
+				d->bootable = ON_BOARD;
+				printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", d->name, pin1, pin2);
+			}
+			break;
+		}
+	}
+	printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+	ide_setup_pci_device(dev, d);
+	if (!dev2)
+		return;
+	d2 = d;
+	printk("%s: IDE controller on PCI bus %02x dev %02x\n", d2->name, dev2->bus->number, dev2->devfn);
+	if (hpt363_shared_pin && !hpt363_shared_irq) {
+		printk("%s: IDE controller run unsupported mode three!!!\n", d2->name);
+#ifndef CONFIG_HPT366_MODE3
+		printk("%s: IDE controller report to <andre@suse.com>\n", d->name);
+		return;
+#else /* CONFIG_HPT366_MODE3 */
+		printk("%s: OVERRIDE IDE controller not advisable this mode!!!\n", d2->name);
+#endif /* CONFIG_HPT366_MODE3 */
+	}
+	ide_setup_pci_device(dev2, d2);
+}
+
+/*
+ * ide_scan_pcibus() gets invoked at boot time from ide.c.
+ * It finds all PCI IDE controllers and calls ide_setup_pci_device for them.
+ */
+void __init ide_scan_pcidev (struct pci_dev *dev)
+{
+	ide_pci_devid_t		devid;
+	ide_pci_device_t	*d;
+
+	devid.vid = dev->vendor;
+	devid.did = dev->device;
+	for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d);
+	if (d->init_hwif == IDE_IGNORE)
+		printk("%s: ignored by ide_scan_pci_device() (uses own driver)\n", d->name);
+	else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_OPTI621V) && !(PCI_FUNC(dev->devfn) & 1))
+		return;
+	else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) && (!(PCI_FUNC(dev->devfn) & 1) || !((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)))
+		return;	/* CY82C693 is more than only a IDE controller */
+	else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) && !(PCI_FUNC(dev->devfn) & 1))
+		return;	/* UM8886A/BF pair */
+	else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366))
+		hpt366_device_order_fixup(dev, d);
+	else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+		if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL))
+			printk("%s: unknown IDE controller on PCI bus %02x device %02x, VID=%04x, DID=%04x\n",
+			       d->name, dev->bus->number, dev->devfn, devid.vid, devid.did);
+		else
+			printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+		ide_setup_pci_device(dev, d);
+	}
+}
+
+void __init ide_scan_pcibus (int scan_direction)
+{
+	struct pci_dev *dev;
+
+	if (!scan_direction) {
+		pci_for_each_dev(dev) {
+			ide_scan_pcidev(dev);
+		}
+	} else {
+		pci_for_each_dev_reverse(dev) {
+			ide_scan_pcidev(dev);
+		}
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-pmac.c linux/drivers/ide/ide-pmac.c
--- v2.3.51/linux/drivers/ide/ide-pmac.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-pmac.c	Thu Mar  2 22:46:07 2000
@@ -0,0 +1,969 @@
+/*
+ * Support for IDE interfaces on PowerMacs.
+ * These IDE interfaces are memory-mapped and have a DBDMA channel
+ * for doing DMA.
+ *
+ *  Copyright (C) 1998 Paul Mackerras.
+ *
+ *  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.
+ *
+ * Some code taken from drivers/block/ide-dma.c:
+ *
+ *  Copyright (c) 1995-1998  Mark Lord
+ *
+ * BenH: I began adding more complete timing setup code, mostly because DMA
+ *       won't work on new machines unless timings are setup correctly. This
+ *       code was mainly stolen from Cmd646 driver and should be completed to
+ *       include real timing calc. instead of hard coded values. The format of
+ *       the timing register can be found in Darwin's source code, except for
+ *       Keylargo ATA-4 controller.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/dbdma.h>
+#include <asm/ide.h>
+#include <asm/mediabay.h>
+#include <asm/feature.h>
+#ifdef CONFIG_PMAC_PBOOK
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <asm/irq.h>
+#endif
+#include "ide_modes.h"
+
+#undef IDE_PMAC_DEBUG
+
+#define IDE_SYSCLK_NS	30
+
+struct pmac_ide_hwif {
+	ide_ioreg_t			regbase;
+	int				irq;
+	int				kind;
+	struct device_node*		node;
+	u32				timings[2];
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+	volatile struct dbdma_regs*	dma_regs;
+	struct dbdma_cmd*		dma_table;
+#endif
+	
+} pmac_ide[MAX_HWIFS];
+
+static int pmac_ide_count;
+
+enum {
+	controller_ohare,	/* OHare based */
+	controller_heathrow,	/* Heathrow/Paddington */
+	controller_kl_ata3,	/* KeyLargo ATA-3 */
+	controller_kl_ata4	/* KeyLargo ATA-4 */
+};
+
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+
+typedef struct {
+	int	accessTime;
+	int	cycleTime;
+} pmac_ide_timing;
+
+/* Multiword DMA timings */
+static pmac_ide_timing mdma_timings[] =
+{
+    { 215,    480 },	/* Mode 0 */
+    {  80,    150 },	/*      1 */
+    {  70,    120 }	/*      2 */
+};
+
+/* Ultra DMA timings (for use when I know how to calculate them */
+static pmac_ide_timing udma_timings[] =
+{
+    {   0,    114 },	/* Mode 0 */
+    {   0,     73 },	/*      1 */
+    {   0,     54 },	/*      2 */
+    {   0,     39 },	/*      3 */
+    {   0,     25 }	/*      4 */
+};
+
+#define MAX_DCMDS	256	/* allow up to 256 DBDMA commands per xfer */
+
+static void pmac_ide_setup_dma(struct device_node *np, int ix);
+static int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive);
+static int pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr);
+static void pmac_ide_tuneproc(ide_drive_t *drive, byte pio);
+static void pmac_ide_selectproc(ide_drive_t *drive);
+
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+#ifdef CONFIG_PMAC_PBOOK
+static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when);
+struct pmu_sleep_notifier idepmac_sleep_notifier = {
+	idepmac_notify_sleep, SLEEP_LEVEL_BLOCK,
+};
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+pmac_ide_find(ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	ide_ioreg_t base;
+	int i;
+	
+	for (i=0; i<pmac_ide_count; i++) {
+		base = pmac_ide[i].regbase;
+		if (base && base == hwif->io_ports[0])
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * N.B. this can't be an initfunc, because the media-bay task can
+ * call ide_[un]register at any time.
+ */
+void pmac_ide_init_hwif_ports(hw_regs_t *hw,
+			      ide_ioreg_t data_port, ide_ioreg_t ctrl_port,
+			      int *irq)
+{
+	int i, ix;
+
+	if (data_port == 0)
+		return;
+
+	for (ix = 0; ix < MAX_HWIFS; ++ix)
+		if (data_port == pmac_ide[ix].regbase)
+			break;
+
+	if (ix >= MAX_HWIFS) {
+		/* Probably a PCI interface... */
+		for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i)
+			hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET;
+		/* XXX is this right? */
+		hw->io_ports[IDE_CONTROL_OFFSET] = 0;
+		if (irq != 0)
+			*irq = 0;
+		return;
+	}
+
+	/* we check only for -EINVAL meaning that we have found a matching
+	   bay but with the wrong device type */ 
+	i = check_media_bay_by_base(data_port, MB_CD);
+	if (i == -EINVAL) {
+		hw->io_ports[IDE_DATA_OFFSET] = 0;
+		return;
+	}
+
+	for (i = 0; i < 8; ++i)
+		hw->io_ports[i] = data_port + i * 0x10;
+	hw->io_ports[8] = data_port + 0x160;
+
+	if (irq != NULL)
+		*irq = pmac_ide[ix].irq;
+
+	ide_hwifs[ix].tuneproc = pmac_ide_tuneproc;
+	ide_hwifs[ix].selectproc = pmac_ide_selectproc;
+	if (pmac_ide[ix].dma_regs && pmac_ide[ix].dma_table) {
+		ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc;
+#ifdef CONFIG_PMAC_IDEDMA_AUTO
+		ide_hwifs[ix].autodma = 1;
+#endif
+	}
+}
+
+#if 0
+/* This one could be later extended to handle CMD IDE and be used by some kind
+ * of /proc interface. I want to be able to get the devicetree path of a block
+ * device for yaboot configuration
+ */
+struct device_node*
+pmac_ide_get_devnode(ide_drive_t *drive)
+{
+	int i = pmac_ide_find(drive);
+	if (i < 0)
+		return NULL;
+	return pmac_ide[i].node;
+}
+#endif
+
+/* Setup timings for the selected drive (master/slave). I still need to verify if this
+ * is enough, I beleive selectproc will be called whenever an IDE command is started,
+ * but... */
+static void
+pmac_ide_selectproc(ide_drive_t *drive)
+{
+	int i = pmac_ide_find(drive);
+	if (i < 0)
+		return;
+			
+	if (drive->select.all & 0x10)
+		out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[1]);
+	else
+		out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[0]);
+}
+
+/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */
+#define SYSCLK_TICKS(t)		(((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)
+
+static void
+pmac_ide_tuneproc(ide_drive_t *drive, byte pio)
+{
+	ide_pio_data_t d;
+	int i;
+	u32 *timings;
+	int accessTicks, recTicks;
+	
+	i = pmac_ide_find(drive);
+	if (i < 0)
+		return;
+		
+	/* The "ata-4" IDE controller of UMA machines is a bit different.
+	 * We don't do anything for PIO modes until we know how to do the
+	 * calculation.
+	 */
+	if (pmac_ide[i].kind == controller_kl_ata4)
+		return;
+		
+	pio = ide_get_best_pio_mode(drive, pio, 4, &d);
+	accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time);
+	if (accessTicks < 4)
+		accessTicks = 4;
+	recTicks = SYSCLK_TICKS(d.cycle_time) - accessTicks - 4;
+	if (recTicks < 1)
+		recTicks = 1;
+	if (drive->select.all & 0x10)
+		timings = &pmac_ide[i].timings[1];
+	else
+		timings = &pmac_ide[i].timings[0];
+	
+	*timings = ((*timings) & 0xFFFFFF800) | accessTicks | (recTicks << 5);	
+#ifdef IDE_PMAC_DEBUG
+	printk("ide_pmac: Set PIO timing for mode %d, reg: 0x%08x\n",
+		pio,  *timings);
+#endif	
+		
+	if (drive->select.all == IN_BYTE(IDE_SELECT_REG))
+		pmac_ide_selectproc(drive);
+}
+
+ide_ioreg_t
+pmac_ide_get_base(int index)
+{
+	return pmac_ide[index].regbase;
+}
+
+static int ide_majors[] = { 3, 22, 33, 34, 56, 57 };
+
+kdev_t __init
+pmac_find_ide_boot(char *bootdevice, int n)
+{
+	int i;
+	
+	/*
+	 * Look through the list of IDE interfaces for this one.
+	 */
+	for (i = 0; i < pmac_ide_count; ++i) {
+		char *name;
+		if (!pmac_ide[i].node || !pmac_ide[i].node->full_name)
+			continue;
+		name = pmac_ide[i].node->full_name;
+		if (memcmp(name, bootdevice, n) == 0 && name[n] == 0) {
+			/* XXX should cope with the 2nd drive as well... */
+			return MKDEV(ide_majors[i], 0);
+		}
+	}
+
+	return 0;
+}
+
+void __init
+pmac_ide_probe(void)
+{
+	struct device_node *np;
+	int i;
+	struct device_node *atas;
+	struct device_node *p, **pp, *removables, **rp;
+	unsigned long base;
+	int irq;
+	ide_hwif_t *hwif;
+
+	if (_machine != _MACH_Pmac)
+		return;
+	pp = &atas;
+	rp = &removables;
+	p = find_devices("ATA");
+	if (p == NULL)
+		p = find_devices("IDE");
+	if (p == NULL)
+		p = find_type_devices("ide");
+	if (p == NULL)
+		p = find_type_devices("ata");
+	/* Move removable devices such as the media-bay CDROM
+	   on the PB3400 to the end of the list. */
+	for (; p != NULL; p = p->next) {
+		if (p->parent && p->parent->type
+		    && strcasecmp(p->parent->type, "media-bay") == 0) {
+			*rp = p;
+			rp = &p->next;
+		} else {
+			*pp = p;
+			pp = &p->next;
+		}
+	}
+	*rp = NULL;
+	*pp = removables;
+
+	for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) {
+		struct device_node *tp;
+
+		/*
+		 * If this node is not under a mac-io or dbdma node,
+		 * leave it to the generic PCI driver.
+		 */
+		for (tp = np->parent; tp != 0; tp = tp->parent)
+			if (tp->type && (strcmp(tp->type, "mac-io") == 0
+					 || strcmp(tp->type, "dbdma") == 0))
+				break;
+		if (tp == 0)
+			continue;
+
+		if (np->n_addrs == 0) {
+			printk(KERN_WARNING "ide: no address for device %s\n",
+			       np->full_name);
+			continue;
+		}
+
+		/*
+		 * If this slot is taken (e.g. by ide-pci.c) try the next one.
+		 */
+		while (i < MAX_HWIFS
+		       && ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0)
+			++i;
+		if (i >= MAX_HWIFS)
+			break;
+
+		base = (unsigned long) ioremap(np->addrs[0].address, 0x200) - _IO_BASE;
+
+		/* XXX This is bogus. Should be fixed in the registry by checking
+		   the kind of host interrupt controller, a bit like gatwick
+		   fixes in irq.c
+		 */
+		if (np->n_intrs == 0) {
+			printk("ide: no intrs for device %s, using 13\n",
+			       np->full_name);
+			irq = 13;
+		} else {
+			irq = np->intrs[0].line;
+		}
+		pmac_ide[i].regbase = base;
+		pmac_ide[i].irq = irq;
+		pmac_ide[i].node = np;
+		if (device_is_compatible(np, "keylargo-ata")) {
+			if (strcmp(np->name, "ata-4") == 0)
+				pmac_ide[i].kind = controller_kl_ata4;
+			else
+				pmac_ide[i].kind = controller_kl_ata3;
+		} else if (device_is_compatible(np, "heathrow-ata"))
+			pmac_ide[i].kind = controller_heathrow;
+		else
+			pmac_ide[i].kind = controller_ohare;
+
+		if (np->parent && np->parent->name
+		    && strcasecmp(np->parent->name, "media-bay") == 0) {
+			media_bay_set_ide_infos(np->parent,base,irq,i);
+		} else if (pmac_ide[i].kind == controller_ohare) {
+			/* The code below is having trouble on some ohare machines
+			 * (timing related ?). Until I can put my hand on one of these
+			 * units, I keep the old way
+			 */
+			 feature_set(np, FEATURE_IDE0_enable);
+		} else {
+			/* This is necessary to enable IDE when net-booting */
+			int *bidp = (int *)get_property(np, "AAPL,bus-id", NULL);
+			int bid = bidp ? *bidp : 0;
+			printk("pmac_ide: enabling IDE bus ID %d\n", bid);
+			switch(bid) {
+			    case 0:
+				feature_set(np, FEATURE_IDE0_reset);
+				feature_set(np, FEATURE_IOBUS_enable);
+				mdelay(10);
+ 				feature_set(np, FEATURE_IDE0_enable);
+				mdelay(10);
+				feature_clear(np, FEATURE_IDE0_reset);
+				break;
+			    case 1:
+				feature_set(np, FEATURE_Mediabay_IDE_reset);
+				mdelay(10);
+ 				feature_set(np, FEATURE_Mediabay_IDE_enable);
+				mdelay(10);
+				feature_clear(np, FEATURE_Mediabay_IDE_reset);
+				break;
+			    case 2:
+			    	/* This one exists only for KL, I don't know about any
+			    	   enable bit */
+				feature_set(np, FEATURE_IDE2_reset);
+				mdelay(10);
+				feature_clear(np, FEATURE_IDE2_reset);
+				break;
+			}
+			mdelay(1000);
+		}
+
+		hwif = &ide_hwifs[i];
+		pmac_ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->irq);
+		memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
+		hwif->chipset = ide_pmac;
+		hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+		if (np->n_addrs >= 2) {
+			/* has a DBDMA controller channel */
+			pmac_ide_setup_dma(np, i);
+		}
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+		++i;
+	}
+	pmac_ide_count = i;
+
+#ifdef CONFIG_PMAC_PBOOK
+	pmu_register_sleep_notifier(&idepmac_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+
+static void __init 
+pmac_ide_setup_dma(struct device_node *np, int ix)
+{
+	pmac_ide[ix].dma_regs =
+		(volatile struct dbdma_regs*)ioremap(np->addrs[1].address, 0x200);
+
+	/*
+	 * Allocate space for the DBDMA commands.
+	 * The +2 is +1 for the stop command and +1 to allow for
+	 * aligning the start address to a multiple of 16 bytes.
+	 */
+	pmac_ide[ix].dma_table = (struct dbdma_cmd*)
+	       kmalloc((MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), GFP_KERNEL);
+	if (pmac_ide[ix].dma_table == 0) {
+		printk(KERN_ERR "%s: unable to allocate DMA command list\n",
+		       ide_hwifs[ix].name);
+		return;
+	}
+
+	ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc;
+#ifdef CONFIG_PMAC_IDEDMA_AUTO
+	ide_hwifs[ix].autodma = 1;
+#endif
+}
+
+/*
+ * pmac_ide_build_dmatable builds the DBDMA command list
+ * for a transfer and sets the DBDMA channel to point to it.
+ */
+static int
+pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr)
+{
+	struct dbdma_cmd *table, *tstart;
+	int count = 0;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct buffer_head *bh = rq->bh;
+	unsigned int size, addr;
+	volatile struct dbdma_regs *dma = pmac_ide[ix].dma_regs;
+
+	table = tstart = (struct dbdma_cmd *) DBDMA_ALIGN(pmac_ide[ix].dma_table);
+	out_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
+	while (in_le32(&dma->status) & RUN)
+		udelay(1);
+
+	do {
+		/*
+		 * Determine addr and size of next buffer area.  We assume that
+		 * individual virtual buffers are always composed linearly in
+		 * physical memory.  For example, we assume that any 8kB buffer
+		 * is always composed of two adjacent physical 4kB pages rather
+		 * than two possibly non-adjacent physical 4kB pages.
+		 */
+		if (bh == NULL) {  /* paging requests have (rq->bh == NULL) */
+			addr = virt_to_bus(rq->buffer);
+			size = rq->nr_sectors << 9;
+		} else {
+			/* group sequential buffers into one large buffer */
+			addr = virt_to_bus(bh->b_data);
+			size = bh->b_size;
+			while ((bh = bh->b_reqnext) != NULL) {
+				if ((addr + size) != virt_to_bus(bh->b_data))
+					break;
+				size += bh->b_size;
+			}
+		}
+
+		/*
+		 * Fill in the next DBDMA command block.
+		 * Note that one DBDMA command can transfer
+		 * at most 65535 bytes.
+		 */
+		while (size) {
+			unsigned int tc = (size < 0xfe00)? size: 0xfe00;
+
+			if (++count >= MAX_DCMDS) {
+				printk("%s: DMA table too small\n",
+				       drive->name);
+				return 0; /* revert to PIO for this request */
+			}
+			st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE);
+			st_le16(&table->req_count, tc);
+			st_le32(&table->phy_addr, addr);
+			table->cmd_dep = 0;
+			table->xfer_status = 0;
+			table->res_count = 0;
+			addr += tc;
+			size -= tc;
+			++table;
+		}
+	} while (bh != NULL);
+
+	/* convert the last command to an input/output last command */
+	if (count)
+		st_le16(&table[-1].command, wr? OUTPUT_LAST: INPUT_LAST);
+	else
+		printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name);
+
+	/* add the stop command to the end of the list */
+	memset(table, 0, sizeof(struct dbdma_cmd));
+	out_le16(&table->command, DBDMA_STOP);
+
+	out_le32(&dma->cmdptr, virt_to_bus(tstart));
+	return 1;
+}
+
+
+/* This is fun.  -DaveM */
+#define IDE_SETXFER		0x03
+#define IDE_SETFEATURE		0xef
+#define IDE_DMA2_ENABLE		0x22
+#define IDE_DMA1_ENABLE		0x21
+#define IDE_DMA0_ENABLE		0x20
+#define IDE_UDMA4_ENABLE	0x44
+#define IDE_UDMA3_ENABLE	0x43
+#define IDE_UDMA2_ENABLE	0x42
+#define IDE_UDMA1_ENABLE	0x41
+#define IDE_UDMA0_ENABLE	0x40
+
+static __inline__ unsigned char
+dma_bits_to_command(unsigned char bits)
+{
+	if(bits & 0x04)
+		return IDE_DMA2_ENABLE;
+	if(bits & 0x02)
+		return IDE_DMA1_ENABLE;
+	return IDE_DMA0_ENABLE;
+}
+
+static __inline__ unsigned char
+udma_bits_to_command(unsigned char bits)
+{
+	if(bits & 0x10)
+		return IDE_UDMA4_ENABLE;
+	if(bits & 0x08)
+		return IDE_UDMA3_ENABLE;
+	if(bits & 0x04)
+		return IDE_UDMA2_ENABLE;
+	if(bits & 0x02)
+		return IDE_UDMA1_ENABLE;
+	if(bits & 0x01)
+		return IDE_UDMA0_ENABLE;
+	return 0;
+}
+
+static __inline__ int
+wait_for_ready(ide_drive_t *drive)
+{
+	/* Timeout bumped for some powerbooks */
+	int timeout = 2000;
+	byte stat;
+
+	while(--timeout) {
+		stat = GET_STAT();
+		if(!(stat & BUSY_STAT)) {
+			if (drive->ready_stat == 0)
+				break;
+			else if((stat & drive->ready_stat) || (stat & ERR_STAT))
+				break;
+		}
+		mdelay(1);
+	}
+	if((stat & ERR_STAT) || timeout <= 0) {
+		if (stat & ERR_STAT) {
+			printk("ide_pmace: wait_for_ready, error status: %x\n", stat);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+static int
+pmac_ide_do_setfeature(ide_drive_t *drive, byte command)
+{
+	unsigned long flags;
+	byte old_select;
+	int result = 1;
+
+	save_flags(flags);
+	cli();
+	old_select = IN_BYTE(IDE_SELECT_REG);
+	OUT_BYTE(drive->select.all, IDE_SELECT_REG);
+	udelay(10);
+	OUT_BYTE(IDE_SETXFER, IDE_FEATURE_REG);
+	OUT_BYTE(command, IDE_NSECTOR_REG);
+	if(wait_for_ready(drive)) {
+		printk("pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n");
+		goto out;
+	}
+	OUT_BYTE(IDE_SETFEATURE, IDE_COMMAND_REG);
+	result = wait_for_ready(drive);
+	if (result)
+		printk("pmac_ide_do_setfeature disk not ready after SET_FEATURE !\n");
+out:
+	OUT_BYTE(old_select, IDE_SELECT_REG);
+	restore_flags(flags);
+	
+	return result;
+}
+
+static int
+pmac_ide_mdma_enable(ide_drive_t *drive, int idx)
+{
+	byte bits = drive->id->dma_mword & 0x07;
+	byte feature = dma_bits_to_command(bits);
+	u32 *timings;
+	int cycleTime, accessTime;
+	int accessTicks, recTicks;
+	struct hd_driveid *id = drive->id;
+	
+	/* For now, we don't know these values */
+	if (pmac_ide[idx].kind == controller_kl_ata4 && feature != IDE_DMA2_ENABLE)
+		return 0;
+	if (pmac_ide[idx].kind != controller_kl_ata4 && feature == IDE_DMA0_ENABLE)
+		return 0;
+				
+	/* Set feature on drive */
+    	printk("%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf);
+	if (pmac_ide_do_setfeature(drive, feature)) {
+	    	printk("%s: Failed !\n", drive->name);
+	    	return 0;
+	}
+	
+	/* which drive is it ? */
+	if (drive->select.all & 0x10)
+		timings = &pmac_ide[idx].timings[1];
+	else
+		timings = &pmac_ide[idx].timings[0];
+
+	/* Calculate accesstime and cycle time */
+	cycleTime = mdma_timings[feature & 0xf].cycleTime;
+	accessTime = mdma_timings[feature & 0xf].accessTime;
+	if ((id->field_valid & 2) && (id->eide_dma_time))
+		cycleTime = id->eide_dma_time;
+	if ((pmac_ide[idx].kind == controller_ohare) && (cycleTime < 150))
+		cycleTime = 150;
+
+	/* For ata-4 controller, we don't know the calculation */
+	if (pmac_ide[idx].kind == controller_kl_ata4) {
+		*timings = 0x00019465;	/* MDMA2 */
+	} else {
+		int halfTick = 0;
+		int origAccessTime = accessTime;
+		int origCycleTime = cycleTime;
+		
+		accessTicks = SYSCLK_TICKS(accessTime);
+		if (accessTicks < 1)
+			accessTicks = 1;
+		accessTime = accessTicks * IDE_SYSCLK_NS;
+		recTicks = SYSCLK_TICKS(cycleTime - accessTime) - 1;
+		if (recTicks < 1)
+			recTicks = 1;
+		cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS;
+
+		if ((accessTicks > 1) &&
+			((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
+			((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) {
+            			halfTick    = 1;
+				accessTicks--;
+		}
+		*timings = ((*timings) & 0x7FF) |
+			(accessTicks | (recTicks << 5) | (halfTick << 10)) << 11;
+	}
+#ifdef IDE_PMAC_DEBUG
+	printk("ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n",
+		feature & 0xf, *timings);
+#endif	
+	return 1;
+}
+
+static int
+pmac_ide_udma_enable(ide_drive_t *drive, int idx)
+{
+	byte bits = drive->id->dma_ultra & 0x1f;
+	byte feature = udma_bits_to_command(bits);
+	u32 timings;
+	
+	/* We support only those values */
+	if (feature != IDE_UDMA4_ENABLE && feature != IDE_UDMA2_ENABLE)
+		return 0;
+		
+	/* Set feature on drive */
+    	printk("%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf);
+	if (pmac_ide_do_setfeature(drive, feature)) {
+		printk("%s: Failed !\n", drive->name);
+		return 0;
+	}
+
+	/* Put this channel into UDMA mode.
+	 * This value is set by MacOS on the iBook for U/DMA2
+	 */
+	switch(feature) {	
+		case IDE_UDMA4_ENABLE:
+			timings = 0x0cd00065;
+			break;
+		case IDE_UDMA2_ENABLE:
+			timings = 0x11100065;
+			break;
+	}
+	
+	if (drive->select.all & 0x10)
+		pmac_ide[idx].timings[1] = timings;
+	else
+		pmac_ide[idx].timings[0] = timings;
+	
+	return 1;
+}
+
+static int
+pmac_ide_dma_onoff(ide_drive_t *drive, int enable)
+{
+	int ata4, udma, idx;
+	struct hd_driveid *id = drive->id;
+
+	drive->using_dma = 0;
+	
+	idx = pmac_ide_find(drive);
+	if (idx < 0)
+		return 0;
+		
+	if (drive->media == ide_floppy)
+		enable = 0;
+	if (((id->capability & 1) == 0) && !check_drive_lists(drive, GOOD_DMA_DRIVE))
+		enable = 0;
+	if (check_drive_lists(drive, BAD_DMA_DRIVE))
+		enable = 0;
+
+	udma = 0;
+	ata4 = (pmac_ide[idx].kind == controller_kl_ata4);
+			
+	if(enable) {
+		if (ata4 && (drive->media == ide_disk) &&
+		    (id->field_valid & 0x0004) && (id->dma_ultra & 0x17)) {
+			/* UltraDMA modes. */
+			drive->using_dma = pmac_ide_udma_enable(drive, idx);
+		}
+		if (!drive->using_dma && (id->dma_mword & 0x0007)) {
+			/* Normal MultiWord DMA modes. */
+			drive->using_dma = pmac_ide_mdma_enable(drive, idx);
+		}
+		/* Without this, strange things will happen on Keylargo-based
+		 * machines
+		 */
+		OUT_BYTE(0, IDE_CONTROL_REG);
+		if (drive->select.all == IN_BYTE(IDE_SELECT_REG))
+			pmac_ide_selectproc(drive);
+	}
+	return 0;
+}
+
+int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	int ix, dstat;
+	volatile struct dbdma_regs *dma;
+
+	/* Can we stuff a pointer to our intf structure in config_data
+	 * or select_data in hwif ?
+	 */
+	ix = pmac_ide_find(drive);
+	if (ix < 0)
+		return 0;		
+	dma = pmac_ide[ix].dma_regs;
+
+	switch (func) {
+	case ide_dma_on:
+	case ide_dma_off:
+	case ide_dma_off_quietly:
+		pmac_ide_dma_onoff(drive, (func == ide_dma_on));
+		break;
+	case ide_dma_check:
+		if (hwif->autodma)
+			pmac_ide_dma_onoff(drive, 1);
+		break;
+	case ide_dma_read:
+	case ide_dma_write:
+		if (!pmac_ide_build_dmatable(drive, ix, func==ide_dma_write))
+			return 1;
+		drive->waiting_for_dma = 1;
+		if (drive->media != ide_disk)
+			return 0;
+		ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
+		OUT_BYTE(func==ide_dma_write? WIN_WRITEDMA: WIN_READDMA,
+			 IDE_COMMAND_REG);
+	case ide_dma_begin:
+		out_le32(&dma->control, (RUN << 16) | RUN);
+		break;
+	case ide_dma_end:
+		drive->waiting_for_dma = 0;
+		dstat = in_le32(&dma->status);
+		out_le32(&dma->control, ((RUN|WAKE|DEAD) << 16));
+		/* verify good dma status */
+		return (dstat & (RUN|DEAD|ACTIVE)) != RUN;
+	case ide_dma_test_irq:
+		return (in_le32(&dma->status) & (RUN|ACTIVE)) == RUN;
+	default:
+		printk(KERN_ERR "pmac_ide_dmaproc: bad func %d\n", func);
+	}
+	return 0;
+}
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+#ifdef CONFIG_PMAC_PBOOK
+static void idepmac_sleep_disk(int i, unsigned long base)
+{
+	struct device_node* np = pmac_ide[i].node;
+	int j;
+
+	/* FIXME: We only handle the master IDE */
+	if (ide_hwifs[i].drives[0].media == ide_disk) {
+		/* Spin down the drive */
+		outb(0xa0, base+0x60);
+		outb(0x0, base+0x30);
+		outb(0x0, base+0x20);
+		outb(0x0, base+0x40);
+		outb(0x0, base+0x50);
+		outb(0xe0, base+0x70);
+		outb(0x2, base+0x160);   
+		for (j = 0; j < 10; j++) {
+			int status;
+			mdelay(100);
+			status = inb(base+0x70);
+			if (!(status & BUSY_STAT) && (status & DRQ_STAT))
+				break;
+		}
+	}
+	feature_set(np, FEATURE_IDE0_reset);
+	feature_clear(np, FEATURE_IOBUS_enable);
+	feature_clear(np, FEATURE_IDE0_enable);
+	pmac_ide[i].timings[0] = 0;
+	pmac_ide[i].timings[1] = 0;
+}
+
+static void idepmac_wake_disk(int i, unsigned long base)
+{
+	struct device_node* np = pmac_ide[i].node;
+	int j;
+
+	/* Revive IDE disk and controller */
+	feature_set(np, FEATURE_IOBUS_enable);
+	mdelay(10);
+	feature_set(np, FEATURE_IDE0_enable);
+	mdelay(10);
+	feature_clear(np, FEATURE_IDE0_reset);
+	mdelay(100);
+
+	/* Reset timings */
+	pmac_ide_selectproc(&ide_hwifs[i].drives[0]);
+	mdelay(10);
+
+	/* Wait up to 10 seconds (enough for recent drives) */
+	for (j = 0; j < 100; j++) {
+		int status;
+		mdelay(100);
+		status = inb(base + 0x70);
+		if (!(status & BUSY_STAT))
+			break;
+	}
+}
+
+/* Here we handle media bay devices */
+static void
+idepmac_wake_bay(int i, unsigned long base)
+{
+	int timeout;
+
+	/* Reset timings */
+	pmac_ide_selectproc(&ide_hwifs[i].drives[0]);
+	mdelay(10);
+
+	timeout = 10000;
+	while ((inb(base + 0x70) & BUSY_STAT) && timeout) {
+		mdelay(1);
+		--timeout;
+	}
+}
+
+/* Note: We support only master drives for now. This will have to be
+ * improved if we want to handle sleep on the iMacDV where the CD-ROM
+ * is a slave
+ */
+static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when)
+{
+	int i, ret;
+	unsigned long base;
+
+	switch (when) {
+	case PBOOK_SLEEP_REQUEST:
+		break;
+	case PBOOK_SLEEP_REJECT:
+		break;
+	case PBOOK_SLEEP_NOW:
+		for (i = 0; i < pmac_ide_count; ++i) {
+			if ((base = pmac_ide[i].regbase) == 0)
+				continue;
+			/* Disable irq during sleep */
+			disable_irq(pmac_ide[i].irq);
+			ret = check_media_bay_by_base(base, MB_CD);
+			if (ret == -ENODEV)
+				/* not media bay - put the disk to sleep */
+				idepmac_sleep_disk(i, base);
+		}
+		break;
+	case PBOOK_WAKE:
+		for (i = 0; i < pmac_ide_count; ++i) {
+			ide_hwif_t *hwif;
+			if ((base = pmac_ide[i].regbase) == 0)
+				continue;
+			hwif = &ide_hwifs[i];
+		        /* We don't handle media bay devices this way */
+			ret = check_media_bay_by_base(base, MB_CD);
+			if (ret == -ENODEV)
+				idepmac_wake_disk(i, base);
+			else if (ret == 0)
+				idepmac_wake_bay(i, base);
+			enable_irq(pmac_ide[i].irq);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+			if (hwif->drives[0].present && hwif->drives[0].using_dma)
+				pmac_ide_dma_onoff(&hwif->drives[0], 1);
+#endif				
+		}
+		break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+#endif /* CONFIG_PMAC_PBOOK */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-pnp.c linux/drivers/ide/ide-pnp.c
--- v2.3.51/linux/drivers/ide/ide-pnp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-pnp.c	Thu Mar  9 23:31:43 2000
@@ -0,0 +1,158 @@
+/*
+ * linux/drivers/block/ide-pnp.c
+ *
+ * This file provides autodetection for ISA PnP IDE interfaces.
+ * It was tested with "ESS ES1868 Plug and Play AudioDrive" IDE interface.
+ *
+ * Copyright (C) 2000 Andrey Panin <pazke@orbita.don.sitek.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ */
+
+#include <linux/ide.h>
+#include <linux/isapnp.h>
+
+#ifndef PREPARE_FUNC
+#define PREPARE_FUNC(dev)  (dev->prepare)
+#define ACTIVATE_FUNC(dev)  (dev->activate)
+#define DEACTIVATE_FUNC(dev)  (dev->deactivate)
+#endif
+
+#define DEV_IO(dev, index) (dev->resource[index].start)
+#define DEV_IRQ(dev, index) (dev->irq_resource[index].start)
+
+#define DEV_NAME(dev) (dev->bus->name ? dev->bus->name : "ISA PnP")
+
+#define GENERIC_HD_DATA		0
+#define GENERIC_HD_ERROR	1
+#define GENERIC_HD_NSECTOR	2
+#define GENERIC_HD_SECTOR	3
+#define GENERIC_HD_LCYL		4
+#define GENERIC_HD_HCYL		5
+#define GENERIC_HD_SELECT	6
+#define GENERIC_HD_STATUS	7
+
+static int generic_ide_offsets[IDE_NR_PORTS] __initdata = {
+	GENERIC_HD_DATA, GENERIC_HD_ERROR, GENERIC_HD_NSECTOR, 
+	GENERIC_HD_SECTOR, GENERIC_HD_LCYL, GENERIC_HD_HCYL,
+	GENERIC_HD_SELECT, GENERIC_HD_STATUS, -1, -1
+};
+
+/* ISA PnP device table entry */
+struct pnp_dev_t {
+	unsigned int vendor, device;
+	int (*init_fn)(struct pci_dev *dev, int enable);
+};
+
+/* Generic initialisation function for ISA PnP IDE interface */
+static int __init pnpide_generic_init(struct pci_dev *dev, int enable)
+{
+	hw_regs_t hw;
+	int index;
+
+	if (!enable)
+		return 0;
+
+	if (!(DEV_IO(dev, 0) && DEV_IO(dev, 1) && DEV_IRQ(dev, 0)))
+		return 1;
+
+	ide_setup_ports(&hw, (ide_ioreg_t) DEV_IO(dev, 0),
+			generic_ide_offsets, (ide_ioreg_t) DEV_IO(dev, 1),
+			0, NULL, DEV_IRQ(dev, 0));
+
+	index = ide_register_hw(&hw, NULL);
+
+	if (index != -1) {
+	    	printk("ide%d: %s IDE interface\n", index, DEV_NAME(dev));
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Add your devices here :)) */
+struct pnp_dev_t idepnp_devices[] __initdata = {
+	/* Generic ESDI/IDE/ATA compatible hard disk controller
 */
+	{	ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0600),
+		pnpide_generic_init },
+	{	0 }
+};
+
+#ifdef MODULE
+#define NR_PNP_DEVICES 8
+struct pnp_dev_inst {
+	struct pci_dev *dev;
+	struct pnp_dev_t *dev_type;
+};
+static struct pnp_dev_inst devices[NR_PNP_DEVICES];
+static int pnp_ide_dev_idx = 0;
+#endif
+
+/*
+ * Probe for ISA PnP IDE interfaces.
+ */
+void pnpide_init(int enable)
+{
+	struct pci_dev *dev = NULL;
+	struct pnp_dev_t *dev_type;
+
+	if (!isapnp_present())
+		return;
+
+#ifdef MODULE
+	/* Module unload, deactivate all registered devices. */
+	if (!enable) {
+		int i;
+		for (i = 0; i < pnp_ide_dev_idx; i++) {
+			devices[i].dev_type->init_fn(dev, 0);
+
+			if (DEACTIVATE_FUNC(devices[i].dev))
+				DEACTIVATE_FUNC(devices[i].dev)(devices[i].dev);
+		}
+		return;
+	}
+#endif
+	for (dev_type = idepnp_devices; dev_type->vendor; dev_type++) {
+		while ((dev = isapnp_find_dev(NULL, dev_type->vendor,
+			dev_type->device, dev))) {
+
+			if (dev->active)
+				continue;
+
+       			if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) {
+				printk("ide: %s prepare failed\n", DEV_NAME(dev));
+				continue;
+			}
+
+			if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) {
+				printk("ide: %s activate failed\n", DEV_NAME(dev));
+				continue;
+			}
+
+			/* Call device initialization function */
+			if (dev_type->init_fn(dev, 1)) {
+				if (DEACTIVATE_FUNC(dev))
+					DEACTIVATE_FUNC(dev)(dev);
+			} else {
+#ifdef MODULE
+				/*
+				 * Register device in the array to
+				 * deactivate it on a module unload.
+				 */
+				if (pnp_ide_dev_idx >= NR_PNP_DEVICES)
+					return;
+				devices[pnp_ide_dev_idx].dev = dev;
+				devices[pnp_ide_dev_idx].dev_type = dev_type;
+				pnp_ide_dev_idx++;
+#endif
+			}
+		}
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-probe.c linux/drivers/ide/ide-probe.c
--- v2.3.51/linux/drivers/ide/ide-probe.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-probe.c	Wed Mar  8 11:40:25 2000
@@ -0,0 +1,929 @@
+/*
+ *  linux/drivers/block/ide-probe.c	Version 1.05	July 3, 1999
+ *
+ *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
+ */
+
+/*
+ *  Mostly written by Mark Lord <mlord@pobox.com>
+ *                and Gadi Oxman <gadio@netvision.net.il>
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
+ *
+ * This is the IDE probe module, as evolved from hd.c and ide.c.
+ *
+ * Version 1.00		move drive probing code from ide.c to ide-probe.c
+ * Version 1.01		fix compilation problem for m68k
+ * Version 1.02		increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot
+ *			 by Andrea Arcangeli
+ * Version 1.03		fix for (hwif->chipset == ide_4drives)
+ * Version 1.04		fixed buggy treatments of known flash memory cards
+ *
+ * Version 1.05		fix for (hwif->chipset == ide_pdc4030)
+ *			added ide6/7/8/9
+ *			allowed for secondary flash card to be detectable
+ *			 with new flag : drive->ata_flash : 1;
+ */
+
+#undef REALLY_SLOW_IO		/* most systems can safely undef this */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+static inline void do_identify (ide_drive_t *drive, byte cmd)
+{
+	int bswap = 1;
+	struct hd_driveid *id;
+
+	id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_ATOMIC);	/* called with interrupts disabled! */
+	ide_input_data(drive, id, SECTOR_WORDS);		/* read 512 bytes of id info */
+	ide__sti();	/* local CPU only */
+	ide_fix_driveid(id);
+	if (!drive->forced_lun)
+		drive->last_lun = id->last_lun & 0x7;
+#if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA)
+	/*
+	 * EATA SCSI controllers do a hardware ATA emulation:
+	 * Ignore them if there is a driver for them available.
+	 */
+	if ((id->model[0] == 'P' && id->model[1] == 'M')
+	 || (id->model[0] == 'S' && id->model[1] == 'K')) {
+		printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
+		drive->present = 0;
+		return;
+	}
+#endif /* CONFIG_SCSI_EATA_DMA || CONFIG_SCSI_EATA_PIO */
+
+	/*
+	 *  WIN_IDENTIFY returns little-endian info,
+	 *  WIN_PIDENTIFY *usually* returns little-endian info.
+	 */
+	if (cmd == WIN_PIDENTIFY) {
+		if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
+		 || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
+		 || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
+			bswap ^= 1;	/* Vertos drives may still be weird */
+	}
+	ide_fixstring (id->model,     sizeof(id->model),     bswap);
+	ide_fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
+	ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
+
+	if (strstr(id->model, "E X A B Y T E N E S T"))
+		return;
+
+	id->model[sizeof(id->model)-1] = '\0';	/* we depend on this a lot! */
+	printk("%s: %s, ", drive->name, id->model);
+	drive->present = 1;
+
+	/*
+	 * Check for an ATAPI device
+	 */
+	if (cmd == WIN_PIDENTIFY) {
+		byte type = (id->config >> 8) & 0x1f;
+		printk("ATAPI ");
+#ifdef CONFIG_BLK_DEV_PDC4030
+		if (HWIF(drive)->channel == 1 && HWIF(drive)->chipset == ide_pdc4030) {
+			printk(" -- not supported on 2nd Promise port\n");
+			drive->present = 0;
+			return;
+		}
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+		switch (type) {
+			case ide_floppy:
+				if (!strstr(id->model, "CD-ROM")) {
+					if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
+						printk("cdrom or floppy?, assuming ");
+					if (drive->media != ide_cdrom) {
+						printk ("FLOPPY");
+						break;
+					}
+				}
+				type = ide_cdrom;	/* Early cdrom models used zero */
+			case ide_cdrom:
+				drive->removable = 1;
+#ifdef CONFIG_PPC
+				/* kludge for Apple PowerBook internal zip */
+				if (!strstr(id->model, "CD-ROM") && strstr(id->model, "ZIP")) {
+					printk ("FLOPPY");
+					type = ide_floppy;
+					break;
+				}
+#endif
+				printk ("CDROM");
+				break;
+			case ide_tape:
+				printk ("TAPE");
+				break;
+			case ide_optical:
+				printk ("OPTICAL");
+				drive->removable = 1;
+				break;
+			default:
+				printk("UNKNOWN (type %d)", type);
+				break;
+		}
+		printk (" drive\n");
+		drive->media = type;
+		return;
+	}
+
+	/*
+	 * Not an ATAPI device: looks like a "regular" hard disk
+	 */
+	if (id->config & (1<<7))
+		drive->removable = 1;
+	/*
+	 * Prevent long system lockup probing later for non-existant
+	 * slave drive if the hwif is actually a flash memory card of some variety:
+	 */
+	if (drive_is_flashcard(drive)) {
+		ide_drive_t *mate = &HWIF(drive)->drives[1^drive->select.b.unit];
+		if (!mate->ata_flash) {
+			mate->present = 0;
+			mate->noprobe = 1;
+		}
+	}
+	drive->media = ide_disk;
+	printk("ATA DISK drive\n");
+	return;
+}
+
+/*
+ * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
+ * and waits for a response.  It also monitors irqs while this is
+ * happening, in hope of automatically determining which one is
+ * being used by the interface.
+ *
+ * Returns:	0  device was identified
+ *		1  device timed-out (no response to identify request)
+ *		2  device aborted the command (refused to identify itself)
+ */
+static int try_to_identify (ide_drive_t *drive, byte cmd)
+{
+	int rc;
+	ide_ioreg_t hd_status;
+	unsigned long timeout;
+	unsigned long irqs = 0;
+	byte s, a;
+
+	if (IDE_CONTROL_REG) {
+		if (!HWIF(drive)->irq) {		/* already got an IRQ? */
+			probe_irq_off(probe_irq_on());	/* clear dangling irqs */
+			irqs = probe_irq_on();		/* start monitoring irqs */
+			OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* enable device irq */
+		}
+
+		ide_delay_50ms();				/* take a deep breath */
+		a = IN_BYTE(IDE_ALTSTATUS_REG);
+		s = IN_BYTE(IDE_STATUS_REG);
+		if ((a ^ s) & ~INDEX_STAT) {
+			printk("%s: probing with STATUS(0x%02x) instead of ALTSTATUS(0x%02x)\n", drive->name, s, a);
+			hd_status = IDE_STATUS_REG;	/* ancient Seagate drives, broken interfaces */
+		} else {
+			hd_status = IDE_ALTSTATUS_REG;	/* use non-intrusive polling */
+		}
+	} else {
+		ide_delay_50ms();
+		hd_status = IDE_STATUS_REG;
+	}
+
+#if CONFIG_BLK_DEV_PDC4030
+	if (HWIF(drive)->chipset == ide_pdc4030) {
+		/* DC4030 hosted drives need their own identify... */
+		extern int pdc4030_identify(ide_drive_t *);
+		if (pdc4030_identify(drive)) {
+			if (irqs)
+				(void) probe_irq_off(irqs);
+			return 1;
+		}
+	} else
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+		OUT_BYTE(cmd,IDE_COMMAND_REG);		/* ask drive for ID */
+	timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
+	timeout += jiffies;
+	do {
+		if (0 < (signed long)(jiffies - timeout)) {
+			if (irqs)
+				(void) probe_irq_off(irqs);
+			return 1;	/* drive timed-out */
+		}
+		ide_delay_50ms();		/* give drive a breather */
+	} while (IN_BYTE(hd_status) & BUSY_STAT);
+
+	ide_delay_50ms();		/* wait for IRQ and DRQ_STAT */
+	if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
+		unsigned long flags;
+		__save_flags(flags);	/* local CPU only */
+		__cli();		/* local CPU only; some systems need this */
+		do_identify(drive, cmd); /* drive returned ID */
+		rc = 0;			/* drive responded with ID */
+		(void) GET_STAT();	/* clear drive IRQ */
+		__restore_flags(flags);	/* local CPU only */
+	} else
+		rc = 2;			/* drive refused ID */
+	if (IDE_CONTROL_REG && !HWIF(drive)->irq) {
+		irqs = probe_irq_off(irqs);	/* get our irq number */
+		if (irqs > 0) {
+			HWIF(drive)->irq = irqs; /* save it for later */
+			irqs = probe_irq_on();
+			OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
+			udelay(5);
+			(void) probe_irq_off(irqs);
+			(void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */
+			(void) GET_STAT();	/* clear drive IRQ */
+
+		} else {	/* Mmmm.. multiple IRQs.. don't know which was ours */
+			printk("%s: IRQ probe failed (%ld)\n", drive->name, irqs);
+#ifdef CONFIG_BLK_DEV_CMD640
+#ifdef CMD640_DUMP_REGS
+			if (HWIF(drive)->chipset == ide_cmd640) {
+				printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
+				CMD640_DUMP_REGS;
+			}
+#endif /* CMD640_DUMP_REGS */
+#endif /* CONFIG_BLK_DEV_CMD640 */
+		}
+	}
+	return rc;
+}
+
+/*
+ * do_probe() has the difficult job of finding a drive if it exists,
+ * without getting hung up if it doesn't exist, without trampling on
+ * ethernet cards, and without leaving any IRQs dangling to haunt us later.
+ *
+ * If a drive is "known" to exist (from CMOS or kernel parameters),
+ * but does not respond right away, the probe will "hang in there"
+ * for the maximum wait time (about 30 seconds), otherwise it will
+ * exit much more quickly.
+ *
+ * Returns:	0  device was identified
+ *		1  device timed-out (no response to identify request)
+ *		2  device aborted the command (refused to identify itself)
+ *		3  bad status from device (possible for ATAPI drives)
+ *		4  probe was not attempted because failure was obvious
+ */
+static int do_probe (ide_drive_t *drive, byte cmd)
+{
+	int rc;
+	ide_hwif_t *hwif = HWIF(drive);
+	if (drive->present) {	/* avoid waiting for inappropriate probes */
+		if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
+			return 4;
+	}
+#ifdef DEBUG
+	printk("probing for %s: present=%d, media=%d, probetype=%s\n",
+		drive->name, drive->present, drive->media,
+		(cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
+#endif
+	ide_delay_50ms();	/* needed for some systems (e.g. crw9624 as drive0 with disk as slave) */
+	SELECT_DRIVE(hwif,drive);
+	ide_delay_50ms();
+	if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
+		if (drive->select.b.unit != 0) {
+			SELECT_DRIVE(hwif,&hwif->drives[0]);	/* exit with drive0 selected */
+			ide_delay_50ms();		/* allow BUSY_STAT to assert & clear */
+		}
+		return 3;    /* no i/f present: mmm.. this should be a 4 -ml */
+	}
+
+	if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
+	 || drive->present || cmd == WIN_PIDENTIFY)
+	{
+		if ((rc = try_to_identify(drive,cmd)))   /* send cmd and wait */
+			rc = try_to_identify(drive,cmd); /* failed: try again */
+		if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) {
+			unsigned long timeout;
+			printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT());
+			ide_delay_50ms();
+			OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+			ide_delay_50ms();
+			OUT_BYTE(WIN_SRST, IDE_COMMAND_REG);
+			timeout = jiffies;
+			while ((GET_STAT() & BUSY_STAT) && time_before(jiffies, timeout + WAIT_WORSTCASE))
+				ide_delay_50ms();
+			rc = try_to_identify(drive, cmd);
+		}
+		if (rc == 1)
+			printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
+		(void) GET_STAT();		/* ensure drive irq is clear */
+	} else {
+		rc = 3;				/* not present or maybe ATAPI */
+	}
+	if (drive->select.b.unit != 0) {
+		SELECT_DRIVE(hwif,&hwif->drives[0]);	/* exit with drive0 selected */
+		ide_delay_50ms();
+		(void) GET_STAT();		/* ensure drive irq is clear */
+	}
+	return rc;
+}
+
+/*
+ *
+ */
+static void enable_nest (ide_drive_t *drive)
+{
+	unsigned long timeout;
+
+	printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model);
+	SELECT_DRIVE(HWIF(drive), drive);
+	ide_delay_50ms();
+	OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG);
+	timeout = jiffies + WAIT_WORSTCASE;
+	do {
+		if (jiffies > timeout) {
+			printk("failed (timeout)\n");
+			return;
+		}
+		ide_delay_50ms();
+	} while (GET_STAT() & BUSY_STAT);
+	ide_delay_50ms();
+	if (!OK_STAT(GET_STAT(), 0, BAD_STAT))
+		printk("failed (status = 0x%02x)\n", GET_STAT());
+	else
+		printk("success\n");
+	if (do_probe(drive, WIN_IDENTIFY) >= 2) {	/* if !(success||timed-out) */
+		(void) do_probe(drive, WIN_PIDENTIFY);	/* look for ATAPI device */
+	}
+}
+
+/*
+ * probe_for_drive() tests for existence of a given drive using do_probe().
+ *
+ * Returns:	0  no device was found
+ *		1  device was found (note: drive->present might still be 0)
+ */
+static inline byte probe_for_drive (ide_drive_t *drive)
+{
+	if (drive->noprobe)			/* skip probing? */
+		return drive->present;
+	if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+		(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+	}
+	if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T"))
+		enable_nest(drive);
+	if (!drive->present)
+		return 0;			/* drive not found */
+	if (drive->id == NULL) {		/* identification failed? */
+		if (drive->media == ide_disk) {
+			printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
+			 drive->name, drive->cyl, drive->head, drive->sect);
+		} else if (drive->media == ide_cdrom) {
+			printk("%s: ATAPI cdrom (?)\n", drive->name);
+		} else {
+			drive->present = 0;	/* nuke it */
+		}
+	}
+	return 1;	/* drive was found */
+}
+
+/*
+ * Calculate the region that this interface occupies,
+ * handling interfaces where the registers may not be
+ * ordered sanely.  We deal with the CONTROL register
+ * separately.
+ */
+static int hwif_check_regions (ide_hwif_t *hwif)
+{
+	int region_errors = 0;
+
+	hwif->straight8 = 0;
+	region_errors  = ide_check_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
+	region_errors += ide_check_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
+	region_errors += ide_check_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
+	region_errors += ide_check_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1);
+	region_errors += ide_check_region(hwif->io_ports[IDE_LCYL_OFFSET], 1);
+	region_errors += ide_check_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
+	region_errors += ide_check_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
+	region_errors += ide_check_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
+
+	if (hwif->io_ports[IDE_CONTROL_OFFSET])
+		region_errors += ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
+
+	if (hwif->io_ports[IDE_IRQ_OFFSET])
+		region_errors += ide_check_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
+
+	/*
+	 * If any errors are return, we drop the hwif interface.
+	 */
+	return(region_errors);
+}
+
+static void hwif_register (ide_hwif_t *hwif)
+{
+	if ((hwif->io_ports[IDE_DATA_OFFSET] | 7) ==
+	    (hwif->io_ports[IDE_STATUS_OFFSET])) {
+		ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name);
+		hwif->straight8 = 1;
+		goto jump_straight8;
+	}
+
+	if (hwif->io_ports[IDE_DATA_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 1, hwif->name);
+	if (hwif->io_ports[IDE_ERROR_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_ERROR_OFFSET], 1, hwif->name);
+	if (hwif->io_ports[IDE_NSECTOR_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1, hwif->name);
+	if (hwif->io_ports[IDE_SECTOR_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1, hwif->name);
+	if (hwif->io_ports[IDE_LCYL_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_LCYL_OFFSET], 1, hwif->name);
+	if (hwif->io_ports[IDE_HCYL_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_HCYL_OFFSET], 1, hwif->name);
+	if (hwif->io_ports[IDE_SELECT_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_SELECT_OFFSET], 1, hwif->name);
+	if (hwif->io_ports[IDE_STATUS_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_STATUS_OFFSET], 1, hwif->name);
+
+jump_straight8:
+	if (hwif->io_ports[IDE_CONTROL_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
+	if (hwif->io_ports[IDE_IRQ_OFFSET])
+		ide_request_region(hwif->io_ports[IDE_IRQ_OFFSET], 1, hwif->name);
+}
+
+/*
+ * This routine only knows how to look for drive units 0 and 1
+ * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
+ */
+static void probe_hwif (ide_hwif_t *hwif)
+{
+	unsigned int unit;
+	unsigned long flags;
+
+	if (hwif->noprobe)
+		return;
+	if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) {
+		extern void probe_cmos_for_drives(ide_hwif_t *);
+
+		probe_cmos_for_drives (hwif);
+	}
+
+	if ((hwif->chipset != ide_4drives || !hwif->mate->present) &&
+#if CONFIG_BLK_DEV_PDC4030
+	    (hwif->chipset != ide_pdc4030 || hwif->channel == 0) &&
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+	    (hwif_check_regions(hwif))) {
+		int msgout = 0;
+		for (unit = 0; unit < MAX_DRIVES; ++unit) {
+			ide_drive_t *drive = &hwif->drives[unit];
+			if (drive->present) {
+				drive->present = 0;
+				printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
+				msgout = 1;
+			}
+		}
+		if (!msgout)
+			printk("%s: ports already in use, skipping probe\n", hwif->name);
+		return;	
+	}
+
+	__save_flags(flags);	/* local CPU only */
+	__sti();		/* local CPU only; needed for jiffies and irq probing */
+	/*
+	 * Second drive should only exist if first drive was found,
+	 * but a lot of cdrom drives are configured as single slaves.
+	 */
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		ide_drive_t *drive = &hwif->drives[unit];
+		(void) probe_for_drive (drive);
+		if (drive->present && !hwif->present) {
+			hwif->present = 1;
+			if (hwif->chipset != ide_4drives || !hwif->mate->present) {
+				hwif_register(hwif);
+			}
+		}
+	}
+	if (hwif->io_ports[IDE_CONTROL_OFFSET] && hwif->reset) {
+		unsigned long timeout = jiffies + WAIT_WORSTCASE;
+		byte stat;
+
+		printk("%s: reset\n", hwif->name);
+		OUT_BYTE(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
+		udelay(10);
+		OUT_BYTE(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
+		do {
+			ide_delay_50ms();
+			stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+		} while ((stat & BUSY_STAT) && 0 < (signed long)(timeout - jiffies));
+
+	}
+	__restore_flags(flags);	/* local CPU only */
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		ide_drive_t *drive = &hwif->drives[unit];
+		if (drive->present) {
+			ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+			if (tuneproc != NULL && drive->autotune == 1)
+				tuneproc(drive, 255);	/* auto-tune PIO mode */
+		}
+	}
+}
+
+#if MAX_HWIFS > 1
+/*
+ * save_match() is used to simplify logic in init_irq() below.
+ *
+ * A loophole here is that we may not know about a particular
+ * hwif's irq until after that hwif is actually probed/initialized..
+ * This could be a problem for the case where an hwif is on a
+ * dual interface that requires serialization (eg. cmd640) and another
+ * hwif using one of the same irqs is initialized beforehand.
+ *
+ * This routine detects and reports such situations, but does not fix them.
+ */
+static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
+{
+	ide_hwif_t *m = *match;
+
+	if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
+		if (!new->hwgroup)
+			return;
+		printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name);
+	}
+	if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
+		*match = new;
+}
+#endif /* MAX_HWIFS > 1 */
+
+/*
+ * This routine sets up the irq for an ide interface, and creates a new
+ * hwgroup for the irq/hwif if none was previously assigned.
+ *
+ * Much of the code is for correctly detecting/handling irq sharing
+ * and irq serialization situations.  This is somewhat complex because
+ * it handles static as well as dynamic (PCMCIA) IDE interfaces.
+ *
+ * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
+ * interrupts completely disabled.  This can be bad for interrupt latency,
+ * but anything else has led to problems on some machines.  We re-enable
+ * interrupts as much as we can safely do in most places.
+ */
+static int init_irq (ide_hwif_t *hwif)
+{
+	unsigned long flags;
+	unsigned int index;
+	ide_hwgroup_t *hwgroup;
+	ide_hwif_t *match = NULL;
+
+	save_flags(flags);	/* all CPUs */
+	cli();			/* all CPUs */
+
+	hwif->hwgroup = NULL;
+#if MAX_HWIFS > 1
+	/*
+	 * Group up with any other hwifs that share our irq(s).
+	 */
+	for (index = 0; index < MAX_HWIFS; index++) {
+		ide_hwif_t *h = &ide_hwifs[index];
+		if (h->hwgroup) {  /* scan only initialized hwif's */
+			if (hwif->irq == h->irq) {
+				hwif->sharing_irq = h->sharing_irq = 1;
+				if (hwif->chipset != ide_pci || h->chipset != ide_pci) {
+					save_match(hwif, h, &match);
+				}
+			}
+			if (hwif->serialized) {
+				if (hwif->mate && hwif->mate->irq == h->irq)
+					save_match(hwif, h, &match);
+			}
+			if (h->serialized) {
+				if (h->mate && hwif->irq == h->mate->irq)
+					save_match(hwif, h, &match);
+			}
+		}
+	}
+#endif /* MAX_HWIFS > 1 */
+	/*
+	 * If we are still without a hwgroup, then form a new one
+	 */
+	if (match) {
+		hwgroup = match->hwgroup;
+	} else {
+		hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
+		memset(hwgroup, 0, sizeof(ide_hwgroup_t));
+		hwgroup->hwif     = hwif->next = hwif;
+		hwgroup->rq       = NULL;
+		hwgroup->handler  = NULL;
+		hwgroup->drive    = NULL;
+		hwgroup->busy     = 0;
+		init_timer(&hwgroup->timer);
+		hwgroup->timer.function = &ide_timer_expiry;
+		hwgroup->timer.data = (unsigned long) hwgroup;
+	}
+
+	/*
+	 * Allocate the irq, if not already obtained for another hwif
+	 */
+	if (!match || match->irq != hwif->irq) {
+#ifdef CONFIG_IDEPCI_SHARE_IRQ
+		int sa = (hwif->chipset == ide_pci) ? SA_SHIRQ : SA_INTERRUPT;
+#else /* !CONFIG_IDEPCI_SHARE_IRQ */
+		int sa = (hwif->chipset == ide_pci) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT;
+#endif /* CONFIG_IDEPCI_SHARE_IRQ */
+		if (ide_request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwgroup)) {
+			if (!match)
+				kfree(hwgroup);
+			restore_flags(flags);	/* all CPUs */
+			return 1;
+		}
+	}
+
+	/*
+	 * Everything is okay, so link us into the hwgroup
+	 */
+	hwif->hwgroup = hwgroup;
+	hwif->next = hwgroup->hwif->next;
+	hwgroup->hwif->next = hwif;
+
+	for (index = 0; index < MAX_DRIVES; ++index) {
+		ide_drive_t *drive = &hwif->drives[index];
+		if (!drive->present)
+			continue;
+		if (!hwgroup->drive)
+			hwgroup->drive = drive;
+		drive->next = hwgroup->drive->next;
+		hwgroup->drive->next = drive;
+	}
+	if (!hwgroup->hwif) {
+		hwgroup->hwif = HWIF(hwgroup->drive);
+#ifdef DEBUG
+		printk("%s : Adding missed hwif to hwgroup!!\n", hwif->name);
+#endif
+	}
+	restore_flags(flags);	/* all CPUs; safe now that hwif->hwgroup is set up */
+
+#if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__)
+	printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
+		hwif->io_ports[IDE_DATA_OFFSET],
+		hwif->io_ports[IDE_DATA_OFFSET]+7,
+		hwif->io_ports[IDE_CONTROL_OFFSET], hwif->irq);
+#elif defined(__sparc__)
+	printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %s", hwif->name,
+		hwif->io_ports[IDE_DATA_OFFSET],
+		hwif->io_ports[IDE_DATA_OFFSET]+7,
+		hwif->io_ports[IDE_CONTROL_OFFSET], __irq_itoa(hwif->irq));
+#else
+	printk("%s at %p on irq 0x%08x", hwif->name,
+		hwif->io_ports[IDE_DATA_OFFSET], hwif->irq);
+#endif /* __mc68000__ && CONFIG_APUS */
+	if (match)
+		printk(" (%sed with %s)",
+			hwif->sharing_irq ? "shar" : "serializ", match->name);
+	printk("\n");
+	return 0;
+}
+
+/*
+ * init_gendisk() (as opposed to ide_geninit) is called for each major device,
+ * after probing for drives, to allocate partition tables and other data
+ * structures needed for the routines in genhd.c.  ide_geninit() gets called
+ * somewhat later, during the partition check.
+ */
+static void init_gendisk (ide_hwif_t *hwif)
+{
+	struct gendisk *gd, **gdp;
+	unsigned int unit, units, minors;
+	int *bs, *max_sect, *max_ra;
+	extern devfs_handle_t ide_devfs_handle;
+
+	/* figure out maximum drive number on the interface */
+	for (units = MAX_DRIVES; units > 0; --units) {
+		if (hwif->drives[units-1].present)
+			break;
+	}
+	minors    = units * (1<<PARTN_BITS);
+	gd        = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
+	gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
+	gd->part  = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
+	bs        = kmalloc (minors*sizeof(int), GFP_KERNEL);
+	max_sect  = kmalloc (minors*sizeof(int), GFP_KERNEL);
+	max_ra    = kmalloc (minors*sizeof(int), GFP_KERNEL);
+
+	memset(gd->part, 0, minors * sizeof(struct hd_struct));
+
+	/* cdroms and msdos f/s are examples of non-1024 blocksizes */
+	blksize_size[hwif->major] = bs;
+	max_sectors[hwif->major] = max_sect;
+	max_readahead[hwif->major] = max_ra;
+	for (unit = 0; unit < minors; ++unit) {
+		*bs++ = BLOCK_SIZE;
+#ifdef CONFIG_BLK_DEV_PDC4030
+		*max_sect++ = ((hwif->chipset == ide_pdc4030) ? 127 : MAX_SECTORS);
+#else
+		*max_sect++ = MAX_SECTORS;
+#endif
+		*max_ra++ = MAX_READAHEAD;
+	}
+
+	for (unit = 0; unit < units; ++unit)
+		hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
+
+	gd->major	= hwif->major;		/* our major device number */
+	gd->major_name	= IDE_MAJOR_NAME;	/* treated special in genhd.c */
+	gd->minor_shift	= PARTN_BITS;		/* num bits for partitions */
+	gd->max_p	= 1<<PARTN_BITS;	/* 1 + max partitions / drive */
+	gd->nr_real	= units;		/* current num real drives */
+	gd->real_devices= hwif;			/* ptr to internal data */
+	gd->next	= NULL;			/* linked list of major devs */
+	gd->fops        = ide_fops;             /* file operations */
+	gd->de_arr	= kmalloc (sizeof *gd->de_arr * units, GFP_KERNEL);
+	gd->flags	= kmalloc (sizeof *gd->flags * units, GFP_KERNEL);
+	if (gd->de_arr)
+		memset (gd->de_arr, 0, sizeof *gd->de_arr * units);
+	if (gd->flags)
+		memset (gd->flags, 0, sizeof *gd->flags * units);
+
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
+	hwif->gd = *gdp = gd;			/* link onto tail of list */
+
+	for (unit = 0; unit < units; ++unit) {
+		if (hwif->drives[unit].present) {
+			char name[64];
+
+			ide_add_generic_settings(hwif->drives + unit);
+			sprintf (name, "host%d/bus%d/target%d/lun%d",
+				 (hwif->channel && hwif->mate) ? hwif->mate->index : hwif->index,
+				 hwif->channel, unit, 0);
+			hwif->drives[unit].de =
+				devfs_mk_dir (ide_devfs_handle, name, 0, NULL);
+		}
+	}
+}
+
+static int hwif_init (ide_hwif_t *hwif)
+{
+	ide_drive_t *drive;
+	void (*rfn)(request_queue_t *);
+	
+	if (!hwif->present)
+		return 0;
+	if (!hwif->irq) {
+		if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET])))
+		{
+			printk("%s: DISABLED, NO IRQ\n", hwif->name);
+			return (hwif->present = 0);
+		}
+	}
+#ifdef CONFIG_BLK_DEV_HD
+	if (hwif->irq == HD_IRQ && hwif->io_ports[IDE_DATA_OFFSET] != HD_DATA) {
+		printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
+		return (hwif->present = 0);
+	}
+#endif /* CONFIG_BLK_DEV_HD */
+	
+	hwif->present = 0; /* we set it back to 1 if all is ok below */
+	switch (hwif->major) {
+	case IDE0_MAJOR: rfn = &do_ide0_request; break;
+#if MAX_HWIFS > 1
+	case IDE1_MAJOR: rfn = &do_ide1_request; break;
+#endif
+#if MAX_HWIFS > 2
+	case IDE2_MAJOR: rfn = &do_ide2_request; break;
+#endif
+#if MAX_HWIFS > 3
+	case IDE3_MAJOR: rfn = &do_ide3_request; break;
+#endif
+#if MAX_HWIFS > 4
+	case IDE4_MAJOR: rfn = &do_ide4_request; break;
+#endif
+#if MAX_HWIFS > 5
+	case IDE5_MAJOR: rfn = &do_ide5_request; break;
+#endif
+#if MAX_HWIFS > 6
+	case IDE6_MAJOR: rfn = &do_ide6_request; break;
+#endif
+#if MAX_HWIFS > 7
+	case IDE7_MAJOR: rfn = &do_ide7_request; break;
+#endif
+#if MAX_HWIFS > 8
+	case IDE8_MAJOR: rfn = &do_ide8_request; break;
+#endif
+#if MAX_HWIFS > 9
+	case IDE9_MAJOR: rfn = &do_ide9_request; break;
+#endif
+	default:
+		printk("%s: request_fn NOT DEFINED\n", hwif->name);
+		return (hwif->present = 0);
+	}
+	if (devfs_register_blkdev (hwif->major, hwif->name, ide_fops)) {
+		printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
+		return (hwif->present = 0);
+	}
+	
+	if (init_irq(hwif)) {
+		int i = hwif->irq;
+		/*
+		 *	It failed to initialise. Find the default IRQ for 
+		 *	this port and try that.
+		 */
+		if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) {
+			printk("%s: Disabled unable to get IRQ %d.\n", hwif->name, i);
+			(void) unregister_blkdev (hwif->major, hwif->name);
+			return (hwif->present = 0);
+		}
+		if (init_irq(hwif)) {
+			printk("%s: probed IRQ %d and default IRQ %d failed.\n",
+				hwif->name, i, hwif->irq);
+			(void) unregister_blkdev (hwif->major, hwif->name);
+			return (hwif->present = 0);
+		}
+		printk("%s: probed IRQ %d failed, using default.\n",
+			hwif->name, hwif->irq);
+	}
+	
+	init_gendisk(hwif);
+	blk_dev[hwif->major].data = hwif;
+	blk_dev[hwif->major].queue = ide_get_queue;
+	read_ahead[hwif->major] = 8;	/* (4kB) */
+	hwif->present = 1;	/* success */
+
+	/*
+	 * FIXME(eric) - This needs to be tested.  I *think* that this
+	 * is correct.   Also, I believe that there is no longer any
+	 * reason to have multiple functions (do_ide[0-7]_request)
+	 * functions - the queuedata field could be used to indicate
+	 * the correct hardware group - either this, or we could add
+	 * a new field to request_queue_t to hold this information.
+	 */
+	drive = &hwif->drives[0];
+	blk_init_queue(&drive->queue, rfn);
+
+	drive = &hwif->drives[1];
+	blk_init_queue(&drive->queue, rfn);
+
+#if (DEBUG_SPINLOCK > 0)
+{
+	static int done = 0;
+	if (!done++)
+		printk("io_request_lock is %p\n", &io_request_lock);    /* FIXME */
+}
+#endif
+	return hwif->present;
+}
+
+int ideprobe_init (void);
+static ide_module_t ideprobe_module = {
+	IDE_PROBE_MODULE,
+	ideprobe_init,
+	NULL
+};
+
+int ideprobe_init (void)
+{
+	unsigned int index;
+	int probe[MAX_HWIFS];
+	
+	MOD_INC_USE_COUNT;
+	memset(probe, 0, MAX_HWIFS * sizeof(int));
+	for (index = 0; index < MAX_HWIFS; ++index)
+		probe[index] = !ide_hwifs[index].present;
+
+	/*
+	 * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
+	 */
+	for (index = 0; index < MAX_HWIFS; ++index)
+		if (probe[index])
+			probe_hwif(&ide_hwifs[index]);
+	for (index = 0; index < MAX_HWIFS; ++index)
+		if (probe[index])
+			hwif_init(&ide_hwifs[index]);
+	if (!ide_probe)
+		ide_probe = &ideprobe_module;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	unsigned int index;
+	
+	for (index = 0; index < MAX_HWIFS; ++index)
+		ide_unregister(index);
+	ideprobe_init();
+	create_proc_ide_interfaces();
+	return 0;
+}
+
+void cleanup_module (void)
+{
+	ide_probe = NULL;
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-proc.c linux/drivers/ide/ide-proc.c
--- v2.3.51/linux/drivers/ide/ide-proc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-proc.c	Thu Mar  9 17:39:10 2000
@@ -0,0 +1,904 @@
+/*
+ *  linux/drivers/block/ide-proc.c	Version 1.03	January  2, 1998
+ *
+ *  Copyright (C) 1997-1998	Mark Lord
+ */
+
+/*
+ * This is the /proc/ide/ filesystem implementation.
+ *
+ * The major reason this exists is to provide sufficient access
+ * to driver and config data, such that user-mode programs can
+ * be developed to handle chipset tuning for most PCI interfaces.
+ * This should provide better utilities, and less kernel bloat.
+ *
+ * The entire pci config space for a PCI interface chipset can be
+ * retrieved by just reading it.  e.g.    "cat /proc/ide3/config"
+ *
+ * To modify registers *safely*, do something like:
+ *   echo "P40:88" >/proc/ide/ide3/config
+ * That expression writes 0x88 to pci config register 0x40
+ * on the chip which controls ide3.  Multiple tuples can be issued,
+ * and the writes will be completed as an atomic set:
+ *   echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
+ *
+ * All numbers must be specified using pairs of ascii hex digits.
+ * It is important to note that these writes will be performed
+ * after waiting for the IDE controller (both interfaces)
+ * to be completely idle, to ensure no corruption of I/O in progress.
+ *
+ * Non-PCI registers can also be written, using "R" in place of "P"
+ * in the above examples.  The size of the port transfer is determined
+ * by the number of pairs of hex digits given for the data.  If a two
+ * digit value is given, the write will be a byte operation; if four
+ * digits are used, the write will be performed as a 16-bit operation;
+ * and if eight digits are specified, a 32-bit "dword" write will be
+ * performed.  Odd numbers of digits are not permitted.
+ *
+ * If there is an error *anywhere* in the string of registers/data
+ * then *none* of the writes will be performed.
+ *
+ * Drive/Driver settings can be retrieved by reading the drive's
+ * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
+ * To write a new value "val" into a specific setting "name", use:
+ *   echo "name:val" >/proc/ide/ide0/hda/settings
+ *
+ * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
+ * smart_thresholds, capabilities]" will issue an IDENTIFY /
+ * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
+ * SENSE CAPABILITIES command to /dev/hda, and then dump out the
+ * returned data as 256 16-bit words.  The "hdparm" utility will
+ * be updated someday soon to use this mechanism.
+ *
+ * Feel free to develop and distribute fancy GUI configuration
+ * utilities for your favorite PCI chipsets.  I'll be working on
+ * one for the Promise 20246 someday soon.  -ml
+ *
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/ctype.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifdef CONFIG_BLK_DEV_AEC6210
+extern byte aec6210_proc;
+int (*aec6210_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_AEC6210 */
+#ifdef CONFIG_BLK_DEV_ALI15X3
+extern byte ali_proc;
+int (*ali_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_ALI15X3 */
+#ifdef CONFIG_BLK_DEV_AMD7409
+extern byte amd7409_proc;
+int (*amd7409_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_AMD7409 */
+#ifdef CONFIG_BLK_DEV_CMD64X
+extern byte cmd64x_proc;
+int (*cmd64x_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_CMD64X */
+#ifdef CONFIG_BLK_DEV_CS5530
+extern byte cs5530_proc;
+int (*cs5530_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_CS5530 */
+#ifdef CONFIG_BLK_DEV_HPT34X
+extern byte hpt34x_proc;
+int (*hpt34x_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_HPT34X */
+#ifdef CONFIG_BLK_DEV_HPT366
+extern byte hpt366_proc;
+int (*hpt366_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_HPT366 */
+#ifdef CONFIG_BLK_DEV_PDC202XX
+extern byte pdc202xx_proc;
+int (*pdc202xx_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_PDC202XX */
+#ifdef CONFIG_BLK_DEV_PIIX
+extern byte piix_proc;
+int (*piix_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_PIIX */
+#ifdef CONFIG_BLK_DEV_SIS5513
+extern byte sis_proc;
+int (*sis_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_SIS5513 */
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+extern byte via_proc;
+int (*via_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+
+static int ide_getxdigit(char c)
+{
+	int digit;
+	if (isdigit(c))
+		digit = c - '0';
+	else if (isxdigit(c))
+		digit = tolower(c) - 'a' + 10;
+	else
+		digit = -1;
+	return digit;
+}
+
+static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
+{
+	char errbuf[16];
+	int i;
+	if (len >= sizeof(errbuf))
+		len = sizeof(errbuf) - 1;
+	for (i = 0; i < len; ++i) {
+		char c = data[i];
+		if (!c || c == '\n')
+			c = '\0';
+		else if (iscntrl(c))
+			c = '?';
+		errbuf[i] = c;
+	}
+	errbuf[i] = '\0';
+	printk("proc_ide: error: %s: '%s'\n", msg, errbuf);
+	return -EINVAL;
+}
+
+static struct proc_dir_entry * proc_ide_root = NULL;
+
+static int proc_ide_write_config
+	(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+	ide_hwif_t	*hwif = (ide_hwif_t *)data;
+	int		for_real = 0;
+	unsigned long	startn = 0, n, flags;
+	const char	*start = NULL, *msg = NULL;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	/*
+	 * Skip over leading whitespace
+	 */
+	while (count && isspace(*buffer)) {
+		--count;
+		++buffer;
+	}
+	/*
+	 * Do one full pass to verify all parameters,
+	 * then do another to actually write the regs.
+	 */
+	save_flags(flags);	/* all CPUs */
+	do {
+		const char *p;
+		if (for_real) {
+			unsigned long timeout = jiffies + (3 * HZ);
+			ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
+			ide_hwgroup_t *mategroup = NULL;
+			if (hwif->mate && hwif->mate->hwgroup)
+				mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
+			cli();	/* all CPUs; ensure all writes are done together */
+			while (mygroup->busy || (mategroup && mategroup->busy)) {
+				sti();	/* all CPUs */
+				if (0 < (signed long)(jiffies - timeout)) {
+					printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name);
+					restore_flags(flags);	/* all CPUs */
+					return -EBUSY;
+				}
+				cli();	/* all CPUs */
+			}
+		}
+		p = buffer;
+		n = count;
+		while (n > 0) {
+			int d, digits;
+			unsigned int reg = 0, val = 0, is_pci;
+			start = p;
+			startn = n--;
+			switch (*p++) {
+				case 'R':	is_pci = 0;
+						break;
+				case 'P':	is_pci = 1;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+						if (hwif->pci_dev && !IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
+							break;
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
+						msg = "not a PCI device";
+						goto parse_error;
+				default:	msg = "expected 'R' or 'P'";
+						goto parse_error;
+			}
+			digits = 0;
+			while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+				reg = (reg << 4) | d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (!digits || (digits > 4) || (is_pci && reg > 0xff)) {
+				msg = "bad/missing register number";
+				goto parse_error;
+			}
+			if (n-- == 0 || *p++ != ':') {
+				msg = "missing ':'";
+				goto parse_error;
+			}
+			digits = 0;
+			while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+				val = (val << 4) | d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (digits != 2 && digits != 4 && digits != 8) {
+				msg = "bad data, 2/4/8 digits required";
+				goto parse_error;
+			}
+			if (n > 0 && !isspace(*p)) {
+				msg = "expected whitespace after data";
+				goto parse_error;
+			}
+			while (n > 0 && isspace(*p)) {
+				--n;
+				++p;
+			}
+#ifdef CONFIG_BLK_DEV_IDEPCI
+			if (is_pci && (reg & ((digits >> 1) - 1))) {
+				msg = "misaligned access";
+				goto parse_error;
+			}
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
+			if (for_real) {
+#if 0
+				printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? "PCI" : "non-PCI", reg, val, digits);
+#endif
+				if (is_pci) {
+#ifdef CONFIG_BLK_DEV_IDEPCI
+					int rc = 0;
+					struct pci_dev *dev = hwif->pci_dev;
+					switch (digits) {
+						case 2:	msg = "byte";
+							rc = pci_write_config_byte(dev, reg, val);
+							break;
+						case 4:	msg = "word";
+							rc = pci_write_config_word(dev, reg, val);
+							break;
+						case 8:	msg = "dword";
+							rc = pci_write_config_dword(dev, reg, val);
+							break;
+					}
+					if (rc) {
+						restore_flags(flags);	/* all CPUs */
+						printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n",
+							msg, dev->bus->number, dev->devfn, reg, val);
+						printk("proc_ide_write_config: error %d\n", rc);
+						return -EIO;
+					}
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
+				} else {	/* not pci */
+#if !defined(__mc68000__) && !defined(CONFIG_APUS)
+
+/*
+ * Geert Uytterhoeven
+ *
+ * unless you can explain me what it really does.
+ * On m68k, we don't have outw() and outl() yet,
+ * and I need a good reason to implement it.
+ * 
+ * BTW, IMHO the main remaining portability problem with the IDE driver 
+ * is that it mixes IO (ioport) and MMIO (iomem) access on different platforms.
+ * 
+ * I think all accesses should be done using
+ * 
+ *     ide_in[bwl](ide_device_instance, offset)
+ *     ide_out[bwl](ide_device_instance, value, offset)
+ * 
+ * so the architecture specific code can #define ide_{in,out}[bwl] to the
+ * appropriate function.
+ * 
+ */
+					switch (digits) {
+						case 2:	outb(val, reg);
+							break;
+						case 4:	outw(val, reg);
+							break;
+						case 8:	outl(val, reg);
+							break;
+					}
+#endif /* !__mc68000__ && !CONFIG_APUS */
+				}
+			}
+		}
+	} while (!for_real++);
+	restore_flags(flags);	/* all CPUs */
+	return count;
+parse_error:
+	restore_flags(flags);	/* all CPUs */
+	printk("parse error\n");
+	return xx_xx_parse_error(start, startn, msg);
+}
+
+static int proc_ide_read_config
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	char		*out = page;
+	int		len;
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+	ide_hwif_t	*hwif = (ide_hwif_t *)data;
+	struct pci_dev	*dev = hwif->pci_dev;
+	if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL) && dev && dev->bus) {
+		int reg = 0;
+
+		out += sprintf(out, "pci bus %02x device %02x vid %04x did %04x channel %d\n",
+			dev->bus->number, dev->devfn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
+		do {
+			byte val;
+			int rc = pci_read_config_byte(dev, reg, &val);
+			if (rc) {
+				printk("proc_ide_read_config: error %d reading bus %02x dev %02x reg 0x%02x\n",
+					rc, dev->bus->number, dev->devfn, reg);
+				out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
+			} else
+				out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n');
+		} while (reg < 0x100);
+	} else
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
+		out += sprintf(out, "(none)\n");
+	len = out - page;
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+
+static int ide_getdigit(char c)
+{
+	int digit;
+	if (isdigit(c))
+		digit = c - '0';
+	else
+		digit = -1;
+	return digit;
+}
+
+static int proc_ide_read_drivers
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	char		*out = page;
+	int		len;
+	ide_module_t	*p = ide_modules;
+	ide_driver_t	*driver;
+
+	while (p) {
+		driver = (ide_driver_t *) p->info;
+		if (driver)
+			out += sprintf(out, "%s version %s\n", driver->name, driver->version);
+		p = p->next;
+	}
+	len = out - page;
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_imodel
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_hwif_t	*hwif = (ide_hwif_t *) data;
+	int		len;
+	const char	*name;
+
+	switch (hwif->chipset) {
+		case ide_unknown:	name = "(none)";	break;
+		case ide_generic:	name = "generic";	break;
+		case ide_pci:		name = "pci";		break;
+		case ide_cmd640:	name = "cmd640";	break;
+		case ide_dtc2278:	name = "dtc2278";	break;
+		case ide_ali14xx:	name = "ali14xx";	break;
+		case ide_qd6580:	name = "qd6580";	break;
+		case ide_umc8672:	name = "umc8672";	break;
+		case ide_ht6560b:	name = "ht6560b";	break;
+		case ide_pdc4030:	name = "pdc4030";	break;
+		case ide_rz1000:	name = "rz1000";	break;
+		case ide_trm290:	name = "trm290";	break;
+		case ide_cmd646:	name = "cmd646";	break;
+		case ide_cy82c693:	name = "cy82c693";	break;
+		case ide_4drives:	name = "4drives";	break;
+		case ide_pmac:		name = "mac-io";	break;
+		default:		name = "(unknown)";	break;
+	}
+	len = sprintf(page, "%s\n", name);
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_mate
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_hwif_t	*hwif = (ide_hwif_t *) data;
+	int		len;
+
+	if (hwif && hwif->mate && hwif->mate->present)
+		len = sprintf(page, "%s\n", hwif->mate->name);
+	else
+		len = sprintf(page, "(none)\n");
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_channel
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_hwif_t	*hwif = (ide_hwif_t *) data;
+	int		len;
+
+	page[0] = hwif->channel ? '1' : '0';
+	page[1] = '\n';
+	len = 2;
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_get_identify(ide_drive_t *drive, byte *buf)
+{
+	return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf);
+}
+
+static int proc_ide_read_identify
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *)data;
+	int		len = 0, i = 0;
+
+	if (drive && !proc_ide_get_identify(drive, page)) {
+		unsigned short *val = ((unsigned short *)page) + 2;
+		char *out = ((char *)val) + (SECTOR_WORDS * 4);
+		page = out;
+		do {
+			out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+			val += 1;
+		} while (i < (SECTOR_WORDS * 2));
+		len = out - page;
+	}
+	else
+		len = sprintf(page, "\n");
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_settings
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	ide_settings_t	*setting = (ide_settings_t *) drive->settings;
+	char		*out = page;
+	int		len, rc, mul_factor, div_factor;
+
+	out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
+	out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
+	while(setting) {
+		mul_factor = setting->mul_factor;
+		div_factor = setting->div_factor;
+		out += sprintf(out, "%-24s", setting->name);
+		if ((rc = ide_read_setting(drive, setting)) >= 0)
+			out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
+		else
+			out += sprintf(out, "%-16s", "write-only");
+		out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
+		if (setting->rw & SETTING_READ)
+			out += sprintf(out, "r");
+		if (setting->rw & SETTING_WRITE)
+			out += sprintf(out, "w");
+		out += sprintf(out, "\n");
+		setting = setting->next;
+	}
+	len = out - page;
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+#define MAX_LEN	30
+
+static int proc_ide_write_settings
+	(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	char		name[MAX_LEN + 1];
+	int		for_real = 0, len;
+	unsigned long	n;
+	const char	*start = NULL;
+	ide_settings_t	*setting;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	/*
+	 * Skip over leading whitespace
+	 */
+	while (count && isspace(*buffer)) {
+		--count;
+		++buffer;
+	}
+	/*
+	 * Do one full pass to verify all parameters,
+	 * then do another to actually write the new settings.
+	 */
+	do {
+		const char *p;
+		p = buffer;
+		n = count;
+		while (n > 0) {
+			int d, digits;
+			unsigned int val = 0;
+			start = p;
+
+			while (n > 0 && *p != ':') {
+				--n;
+				p++;
+			}
+			if (*p != ':')
+				goto parse_error;
+			len = IDE_MIN(p - start, MAX_LEN);
+			strncpy(name, start, IDE_MIN(len, MAX_LEN));
+			name[len] = 0;
+
+			if (n > 0) {
+				--n;
+				p++;
+			} else
+				goto parse_error;
+			
+			digits = 0;
+			while (n > 0 && (d = ide_getdigit(*p)) >= 0) {
+				val = (val * 10) + d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (n > 0 && !isspace(*p))
+				goto parse_error;
+			while (n > 0 && isspace(*p)) {
+				--n;
+				++p;
+			}
+			setting = ide_find_setting_by_name(drive, name);
+			if (!setting)
+				goto parse_error;
+
+			if (for_real)
+				ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
+		}
+	} while (!for_real++);
+	return count;
+parse_error:
+	printk("proc_ide_write_settings(): parse error\n");
+	return -EINVAL;
+}
+
+int proc_ide_read_capacity
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	ide_driver_t    *driver = (ide_driver_t *) drive->driver;
+	int		len;
+
+	if (!driver)
+		len = sprintf(page, "(none)\n");
+        else
+		len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+int proc_ide_read_geometry
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	char		*out = page;
+	int		len;
+
+	out += sprintf(out,"physical     %d/%d/%d\n", drive->cyl, drive->head, drive->sect);
+	out += sprintf(out,"logical      %d/%d/%d\n", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+	len = out - page;
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_dmodel
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	struct hd_driveid *id = drive->id;
+	int		len;
+
+	len = sprintf(page, "%.40s\n", (id && id->model[0]) ? (char *)id->model : "(none)");
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_driver
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	ide_driver_t	*driver = (ide_driver_t *) drive->driver;
+	int		len;
+
+	if (!driver)
+		len = sprintf(page, "(none)\n");
+	else
+		len = sprintf(page, "%s version %s\n", driver->name, driver->version);
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_write_driver
+	(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (ide_replace_subdriver(drive, buffer))
+		return -EINVAL;
+	return count;
+}
+
+static int proc_ide_read_media
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	const char	*media;
+	int		len;
+
+	switch (drive->media) {
+		case ide_disk:	media = "disk\n";
+				break;
+		case ide_cdrom:	media = "cdrom\n";
+				break;
+		case ide_tape:	media = "tape\n";
+				break;
+		case ide_floppy:media = "floppy\n";
+				break;
+		default:	media = "UNKNOWN\n";
+				break;
+	}
+	strcpy(page,media);
+	len = strlen(media);
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t generic_drive_entries[] = {
+	{ "driver",	S_IFREG|S_IRUGO,	proc_ide_read_driver,	proc_ide_write_driver },
+	{ "identify",	S_IFREG|S_IRUSR,	proc_ide_read_identify,	NULL },
+	{ "media",	S_IFREG|S_IRUGO,	proc_ide_read_media,	NULL },
+	{ "model",	S_IFREG|S_IRUGO,	proc_ide_read_dmodel,	NULL },
+	{ "settings",	S_IFREG|S_IRUSR|S_IWUSR,proc_ide_read_settings,	proc_ide_write_settings },
+	{ NULL,	0, NULL, NULL }
+};
+
+void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
+{
+	struct proc_dir_entry *ent;
+
+	if (!dir || !p)
+		return;
+	while (p->name != NULL) {
+		ent = create_proc_entry(p->name, p->mode, dir);
+		if (!ent) return;
+		ent->nlink = 1;
+		ent->data = data;
+		ent->read_proc = p->read_proc;
+		ent->write_proc = p->write_proc;
+		p++;
+	}
+}
+
+void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
+{
+	if (!dir || !p)
+		return;
+	while (p->name != NULL) {
+		remove_proc_entry(p->name, dir);
+		p++;
+	}
+}
+
+static void create_proc_ide_drives(ide_hwif_t *hwif)
+{
+	int	d;
+	struct proc_dir_entry *ent;
+	struct proc_dir_entry *parent = hwif->proc;
+	char name[64];
+
+	for (d = 0; d < MAX_DRIVES; d++) {
+		ide_drive_t *drive = &hwif->drives[d];
+		ide_driver_t *driver = drive->driver;
+
+		if (!drive->present)
+			continue;
+		if (drive->proc)
+			continue;
+
+		drive->proc = proc_mkdir(drive->name, parent);
+		if (drive->proc) {
+			ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
+			if (driver) {
+				ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
+				ide_add_proc_entries(drive->proc, driver->proc, drive);
+			}
+		}
+		sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name);
+		ent = proc_symlink(drive->name, proc_ide_root, name);
+		if (!ent) return;
+	}
+}
+
+void destroy_proc_ide_drives(ide_hwif_t *hwif)
+{
+	int	d;
+
+	for (d = 0; d < MAX_DRIVES; d++) {
+		ide_drive_t *drive = &hwif->drives[d];
+		ide_driver_t *driver = drive->driver;
+
+		if (!drive->proc)
+			continue;
+		if (driver)
+			ide_remove_proc_entries(drive->proc, driver->proc);
+		ide_remove_proc_entries(drive->proc, generic_drive_entries);
+		remove_proc_entry(drive->name, proc_ide_root);
+		remove_proc_entry(drive->name, hwif->proc);
+		drive->proc = NULL;
+	}
+}
+
+static ide_proc_entry_t hwif_entries[] = {
+	{ "channel",	S_IFREG|S_IRUGO,	proc_ide_read_channel,	NULL },
+	{ "config",	S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config,	proc_ide_write_config },
+	{ "mate",	S_IFREG|S_IRUGO,	proc_ide_read_mate,	NULL },
+	{ "model",	S_IFREG|S_IRUGO,	proc_ide_read_imodel,	NULL },
+	{ NULL,	0, NULL, NULL }
+};
+
+void create_proc_ide_interfaces(void)
+{
+	int	h;
+
+	for (h = 0; h < MAX_HWIFS; h++) {
+		ide_hwif_t *hwif = &ide_hwifs[h];
+
+		if (!hwif->present)
+			continue;
+		if (!hwif->proc) {
+			hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
+			if (!hwif->proc)
+				return;
+			ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
+		}
+		create_proc_ide_drives(hwif);
+	}
+}
+
+static void destroy_proc_ide_interfaces(void)
+{
+	int	h;
+
+	for (h = 0; h < MAX_HWIFS; h++) {
+		ide_hwif_t *hwif = &ide_hwifs[h];
+		int exist = (hwif->proc != NULL);
+#if 0
+		if (!hwif->present)
+			continue;
+#endif
+		if (exist) {
+			destroy_proc_ide_drives(hwif);
+			ide_remove_proc_entries(hwif->proc, hwif_entries);
+			remove_proc_entry(hwif->name, proc_ide_root);
+			hwif->proc = NULL;
+		} else
+			continue;
+	}
+}
+
+void proc_ide_create(void)
+{
+	proc_ide_root = proc_mkdir("ide", 0);
+	if (!proc_ide_root) return;
+
+	create_proc_ide_interfaces();
+
+	create_proc_read_entry("drivers", 0, proc_ide_root,
+				proc_ide_read_drivers, NULL);
+
+#ifdef CONFIG_BLK_DEV_AEC6210
+	if ((aec6210_display_info) && (aec6210_proc))
+		create_proc_info_entry("aec6210", 0, proc_ide_root, aec6210_display_info);
+#endif /* CONFIG_BLK_DEV_AEC6210 */
+#ifdef CONFIG_BLK_DEV_ALI15X3
+	if ((ali_display_info) && (ali_proc))
+		create_proc_info_entry("ali", 0, proc_ide_root, ali_display_info);
+#endif /* CONFIG_BLK_DEV_ALI15X3 */
+#ifdef CONFIG_BLK_DEV_AMD7409
+	if ((amd7409_display_info) && (amd7409_proc))
+		create_proc_info_entry("amd7409", 0, proc_ide_root, amd7409_display_info);
+#endif /* CONFIG_BLK_DEV_AMD7409 */
+#ifdef CONFIG_BLK_DEV_CMD64X
+	if ((cmd64x_display_info) && (cmd64x_proc))
+		create_proc_info_entry("cmd64x", 0, proc_ide_root, cmd64x_display_info);
+#endif /* CONFIG_BLK_DEV_CMD64X */
+#ifdef CONFIG_BLK_DEV_CS5530
+	if ((cs5530_display_info) && (cs5530_proc))
+		create_proc_info_entry("cs5530", 0, proc_ide_root, cs5530_display_info);
+#endif /* CONFIG_BLK_DEV_CS5530 */
+#ifdef CONFIG_BLK_DEV_HPT34X
+	if ((hpt34x_display_info) && (hpt34x_proc))
+		create_proc_info_entry("hpt34x", 0, proc_ide_root, hpt34x_display_info);
+#endif /* CONFIG_BLK_DEV_HPT34X */
+#ifdef CONFIG_BLK_DEV_HPT366
+	if ((hpt366_display_info) && (hpt366_proc))
+		create_proc_info_entry("hpt366", 0, proc_ide_root, hpt366_display_info);
+#endif /* CONFIG_BLK_DEV_HPT366 */
+#ifdef CONFIG_BLK_DEV_PDC202XX
+	if ((pdc202xx_display_info) && (pdc202xx_proc))
+		create_proc_info_entry("pdc202xx", 0, proc_ide_root, pdc202xx_display_info);
+#endif /* CONFIG_BLK_DEV_PDC202XX */
+#ifdef CONFIG_BLK_DEV_PIIX
+	if ((piix_display_info) && (piix_proc))
+		create_proc_info_entry("piix", 0, proc_ide_root, piix_display_info);
+#endif /* CONFIG_BLK_DEV_PIIX */
+#ifdef CONFIG_BLK_DEV_SIS5513
+	if ((sis_display_info) && (sis_proc))
+		create_proc_info_entry("sis", 0, proc_ide_root, sis_display_info);
+#endif /* CONFIG_BLK_DEV_SIS5513 */
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+	if ((via_display_info) && (via_proc))
+		create_proc_info_entry("via", 0, proc_ide_root, via_display_info);
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+}
+
+void proc_ide_destroy(void)
+{
+	/*
+	 * Mmmm.. does this free up all resources,
+	 * or do we need to do a more proper cleanup here ??
+	 */
+#ifdef CONFIG_BLK_DEV_AEC6210
+	if ((aec6210_display_info) && (aec6210_proc))
+		remove_proc_entry("ide/aec6210",0);
+#endif /* CONFIG_BLK_DEV_AEC6210 */
+#ifdef CONFIG_BLK_DEV_ALI15X3
+	if ((ali_display_info) && (ali_proc))
+		remove_proc_entry("ide/ali",0);
+#endif /* CONFIG_BLK_DEV_ALI15X3 */
+#ifdef CONFIG_BLK_DEV_AMD7409
+	if ((amd7409_display_info) && (amd7409_proc))
+		remove_proc_entry("ide/amd7409",0);
+#endif /* CONFIG_BLK_DEV_AMD7409 */
+#ifdef CONFIG_BLK_DEV_CMD64X
+	if ((cmd64x_display_info) && (cmd64x_proc))
+		remove_proc_entry("ide/cmd64x",0);
+#endif /* CONFIG_BLK_DEV_CMD64X */
+#ifdef CONFIG_BLK_DEV_CS5530
+	if ((cs5530_display_info) && (cs5530_proc))
+		remove_proc_entry("ide/cs5530",0);
+#endif /* CONFIG_BLK_DEV_CS5530 */
+#ifdef CONFIG_BLK_DEV_HPT34X
+	if ((hpt34x_display_info) && (hpt34x_proc))
+		remove_proc_entry("ide/hpt34x",0);
+#endif /* CONFIG_BLK_DEV_HPT34X */
+#ifdef CONFIG_BLK_DEV_HPT366
+	if ((hpt366_display_info) && (hpt366_proc))
+		remove_proc_entry("ide/hpt366",0);
+#endif /* CONFIG_BLK_DEV_HPT366 */
+#ifdef CONFIG_BLK_DEV_PDC202XX
+	if ((pdc202xx_display_info) && (pdc202xx_proc))
+		remove_proc_entry("ide/pdc202xx",0);
+#endif /* CONFIG_BLK_DEV_PDC202XX */
+#ifdef CONFIG_BLK_DEV_PIIX
+	if ((piix_display_info) && (piix_proc))
+		remove_proc_entry("ide/piix",0);
+#endif /* CONFIG_BLK_DEV_PIIX */
+#ifdef CONFIG_BLK_DEV_SIS5513
+	if ((sis_display_info) && (sis_proc))
+		remove_proc_entry("ide/sis", 0);
+#endif /* CONFIG_BLK_DEV_SIS5513 */
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+	if ((via_display_info) && (via_proc))
+		remove_proc_entry("ide/via",0);
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+
+	remove_proc_entry("ide/drivers", 0);
+	destroy_proc_ide_interfaces();
+	remove_proc_entry("ide", 0);
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-tape.c linux/drivers/ide/ide-tape.c
--- v2.3.51/linux/drivers/ide/ide-tape.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide-tape.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,6031 @@
+/*
+ * linux/drivers/block/ide-tape.c		Version 1.16f	Dec  15, 1999
+ *
+ * Copyright (C) 1995 - 1999 Gadi Oxman <gadio@netvision.net.il>
+ *
+ * This driver was constructed as a student project in the software laboratory
+ * of the faculty of electrical engineering in the Technion - Israel's
+ * Institute Of Technology, with the guide of Avner Lottem and Dr. Ilana David.
+ *
+ * It is hereby placed under the terms of the GNU general public license.
+ * (See linux/COPYING).
+ */
+ 
+/*
+ * IDE ATAPI streaming tape driver.
+ *
+ * This driver is a part of the Linux ide driver and works in co-operation
+ * with linux/drivers/block/ide.c.
+ *
+ * The driver, in co-operation with ide.c, basically traverses the 
+ * request-list for the block device interface. The character device
+ * interface, on the other hand, creates new requests, adds them
+ * to the request-list of the block device, and waits for their completion.
+ *
+ * Pipelined operation mode is now supported on both reads and writes.
+ *
+ * The block device major and minor numbers are determined from the
+ * tape's relative position in the ide interfaces, as explained in ide.c.
+ *
+ * The character device interface consists of the following devices:
+ *
+ * ht0		major 37, minor 0	first  IDE tape, rewind on close.
+ * ht1		major 37, minor 1	second IDE tape, rewind on close.
+ * ...
+ * nht0		major 37, minor 128	first  IDE tape, no rewind on close.
+ * nht1		major 37, minor 129	second IDE tape, no rewind on close.
+ * ...
+ *
+ * Run linux/scripts/MAKEDEV.ide to create the above entries.
+ *
+ * The general magnetic tape commands compatible interface, as defined by
+ * include/linux/mtio.h, is accessible through the character device.
+ *
+ * General ide driver configuration options, such as the interrupt-unmask
+ * flag, can be configured by issuing an ioctl to the block device interface,
+ * as any other ide device.
+ *
+ * Our own ide-tape ioctl's can be issued to either the block device or
+ * the character device interface.
+ *
+ * Maximal throughput with minimal bus load will usually be achieved in the
+ * following scenario:
+ *
+ *	1.	ide-tape is operating in the pipelined operation mode.
+ *	2.	No buffering is performed by the user backup program.
+ *
+ * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ * 
+ * Ver 0.1   Nov  1 95   Pre-working code :-)
+ * Ver 0.2   Nov 23 95   A short backup (few megabytes) and restore procedure
+ *                        was successful ! (Using tar cvf ... on the block
+ *                        device interface).
+ *                       A longer backup resulted in major swapping, bad
+ *                        overall Linux performance and eventually failed as
+ *                        we received non serial read-ahead requests from the
+ *                        buffer cache.
+ * Ver 0.3   Nov 28 95   Long backups are now possible, thanks to the
+ *                        character device interface. Linux's responsiveness
+ *                        and performance doesn't seem to be much affected
+ *                        from the background backup procedure.
+ *                       Some general mtio.h magnetic tape operations are
+ *                        now supported by our character device. As a result,
+ *                        popular tape utilities are starting to work with
+ *                        ide tapes :-)
+ *                       The following configurations were tested:
+ *                       	1. An IDE ATAPI TAPE shares the same interface
+ *                       	   and irq with an IDE ATAPI CDROM.
+ *                        	2. An IDE ATAPI TAPE shares the same interface
+ *                          	   and irq with a normal IDE disk.
+ *                        Both configurations seemed to work just fine !
+ *                        However, to be on the safe side, it is meanwhile
+ *                        recommended to give the IDE TAPE its own interface
+ *                        and irq.
+ *                       The one thing which needs to be done here is to
+ *                        add a "request postpone" feature to ide.c,
+ *                        so that we won't have to wait for the tape to finish
+ *                        performing a long media access (DSC) request (such
+ *                        as a rewind) before we can access the other device
+ *                        on the same interface. This effect doesn't disturb
+ *                        normal operation most of the time because read/write
+ *                        requests are relatively fast, and once we are
+ *                        performing one tape r/w request, a lot of requests
+ *                        from the other device can be queued and ide.c will
+ *			  service all of them after this single tape request.
+ * Ver 1.0   Dec 11 95   Integrated into Linux 1.3.46 development tree.
+ *                       On each read / write request, we now ask the drive
+ *                        if we can transfer a constant number of bytes
+ *                        (a parameter of the drive) only to its buffers,
+ *                        without causing actual media access. If we can't,
+ *                        we just wait until we can by polling the DSC bit.
+ *                        This ensures that while we are not transferring
+ *                        more bytes than the constant referred to above, the
+ *                        interrupt latency will not become too high and
+ *                        we won't cause an interrupt timeout, as happened
+ *                        occasionally in the previous version.
+ *                       While polling for DSC, the current request is
+ *                        postponed and ide.c is free to handle requests from
+ *                        the other device. This is handled transparently to
+ *                        ide.c. The hwgroup locking method which was used
+ *                        in the previous version was removed.
+ *                       Use of new general features which are provided by
+ *                        ide.c for use with atapi devices.
+ *                        (Programming done by Mark Lord)
+ *                       Few potential bug fixes (Again, suggested by Mark)
+ *                       Single character device data transfers are now
+ *                        not limited in size, as they were before.
+ *                       We are asking the tape about its recommended
+ *                        transfer unit and send a larger data transfer
+ *                        as several transfers of the above size.
+ *                        For best results, use an integral number of this
+ *                        basic unit (which is shown during driver
+ *                        initialization). I will soon add an ioctl to get
+ *                        this important parameter.
+ *                       Our data transfer buffer is allocated on startup,
+ *                        rather than before each data transfer. This should
+ *                        ensure that we will indeed have a data buffer.
+ * Ver 1.1   Dec 14 95   Fixed random problems which occurred when the tape
+ *                        shared an interface with another device.
+ *                        (poll_for_dsc was a complete mess).
+ *                       Removed some old (non-active) code which had
+ *                        to do with supporting buffer cache originated
+ *                        requests.
+ *                       The block device interface can now be opened, so
+ *                        that general ide driver features like the unmask
+ *                        interrupts flag can be selected with an ioctl.
+ *                        This is the only use of the block device interface.
+ *                       New fast pipelined operation mode (currently only on
+ *                        writes). When using the pipelined mode, the
+ *                        throughput can potentially reach the maximum
+ *                        tape supported throughput, regardless of the
+ *                        user backup program. On my tape drive, it sometimes
+ *                        boosted performance by a factor of 2. Pipelined
+ *                        mode is enabled by default, but since it has a few
+ *                        downfalls as well, you may want to disable it.
+ *                        A short explanation of the pipelined operation mode
+ *                        is available below.
+ * Ver 1.2   Jan  1 96   Eliminated pipelined mode race condition.
+ *                       Added pipeline read mode. As a result, restores
+ *                        are now as fast as backups.
+ *                       Optimized shared interface behavior. The new behavior
+ *                        typically results in better IDE bus efficiency and
+ *                        higher tape throughput.
+ *                       Pre-calculation of the expected read/write request
+ *                        service time, based on the tape's parameters. In
+ *                        the pipelined operation mode, this allows us to
+ *                        adjust our polling frequency to a much lower value,
+ *                        and thus to dramatically reduce our load on Linux,
+ *                        without any decrease in performance.
+ *                       Implemented additional mtio.h operations.
+ *                       The recommended user block size is returned by
+ *                        the MTIOCGET ioctl.
+ *                       Additional minor changes.
+ * Ver 1.3   Feb  9 96   Fixed pipelined read mode bug which prevented the
+ *                        use of some block sizes during a restore procedure.
+ *                       The character device interface will now present a
+ *                        continuous view of the media - any mix of block sizes
+ *                        during a backup/restore procedure is supported. The
+ *                        driver will buffer the requests internally and
+ *                        convert them to the tape's recommended transfer
+ *                        unit, making performance almost independent of the
+ *                        chosen user block size.
+ *                       Some improvements in error recovery.
+ *                       By cooperating with ide-dma.c, bus mastering DMA can
+ *                        now sometimes be used with IDE tape drives as well.
+ *                        Bus mastering DMA has the potential to dramatically
+ *                        reduce the CPU's overhead when accessing the device,
+ *                        and can be enabled by using hdparm -d1 on the tape's
+ *                        block device interface. For more info, read the
+ *                        comments in ide-dma.c.
+ * Ver 1.4   Mar 13 96   Fixed serialize support.
+ * Ver 1.5   Apr 12 96   Fixed shared interface operation, broken in 1.3.85.
+ *                       Fixed pipelined read mode inefficiency.
+ *                       Fixed nasty null dereferencing bug.
+ * Ver 1.6   Aug 16 96   Fixed FPU usage in the driver.
+ *                       Fixed end of media bug.
+ * Ver 1.7   Sep 10 96   Minor changes for the CONNER CTT8000-A model.
+ * Ver 1.8   Sep 26 96   Attempt to find a better balance between good
+ *                        interactive response and high system throughput.
+ * Ver 1.9   Nov  5 96   Automatically cross encountered filemarks rather
+ *                        than requiring an explicit FSF command.
+ *                       Abort pending requests at end of media.
+ *                       MTTELL was sometimes returning incorrect results.
+ *                       Return the real block size in the MTIOCGET ioctl.
+ *                       Some error recovery bug fixes.
+ * Ver 1.10  Nov  5 96   Major reorganization.
+ *                       Reduced CPU overhead a bit by eliminating internal
+ *                        bounce buffers.
+ *                       Added module support.
+ *                       Added multiple tape drives support.
+ *                       Added partition support.
+ *                       Rewrote DSC handling.
+ *                       Some portability fixes.
+ *                       Removed ide-tape.h.
+ *                       Additional minor changes.
+ * Ver 1.11  Dec  2 96   Bug fix in previous DSC timeout handling.
+ *                       Use ide_stall_queue() for DSC overlap.
+ *                       Use the maximum speed rather than the current speed
+ *                        to compute the request service time.
+ * Ver 1.12  Dec  7 97   Fix random memory overwriting and/or last block data
+ *                        corruption, which could occur if the total number
+ *                        of bytes written to the tape was not an integral
+ *                        number of tape blocks.
+ *                       Add support for INTERRUPT DRQ devices.
+ * Ver 1.13  Jan  2 98   Add "speed == 0" work-around for HP COLORADO 5GB
+ * Ver 1.14  Dec 30 98   Partial fixes for the Sony/AIWA tape drives.
+ *                       Replace cli()/sti() with hwgroup spinlocks.
+ * Ver 1.15  Mar 25 99   Fix SMP race condition by replacing hwgroup
+ *                        spinlock with private per-tape spinlock.
+ * Ver 1.16  Sep  1 99   Add OnStream tape support.
+ *                       Abort read pipeline on EOD.
+ *                       Wait for the tape to become ready in case it returns
+ *                        "in the process of becoming ready" on open().
+ *                       Fix zero padding of the last written block in
+ *                        case the tape block size is larger than PAGE_SIZE.
+ *                       Decrease the default disconnection time to tn.
+ * Ver 1.16e Oct  3 99   Minor fixes.
+ * Ver 1.16e1 Oct 13 99  Patches by Arnold Niessen,
+ *                          niessen@iae.nl / arnold.niessen@philips.com
+ *                   GO-1)  Undefined code in idetape_read_position
+ *				according to Gadi's email
+ *                   AJN-1) Minor fix asc == 11 should be asc == 0x11
+ *                               in idetape_issue_packet_command (did effect
+ *                               debugging output only)
+ *                   AJN-2) Added more debugging output, and
+ *                              added ide-tape: where missing. I would also
+ *				like to add tape->name where possible
+ *                   AJN-3) Added different debug_level's 
+ *                              via /proc/ide/hdc/settings
+ * 				"debug_level" determines amount of debugging output;
+ * 				can be changed using /proc/ide/hdx/settings
+ * 				0 : almost no debugging output
+ * 				1 : 0+output errors only
+ * 				2 : 1+output all sensekey/asc
+ * 				3 : 2+follow all chrdev related procedures
+ * 				4 : 3+follow all procedures
+ * 				5 : 4+include pc_stack rq_stack info
+ * 				6 : 5+USE_COUNT updates
+ *                   AJN-4) Fixed timeout for retension in idetape_queue_pc_tail
+ *				from 5 to 10 minutes
+ *                   AJN-5) Changed maximum number of blocks to skip when
+ *                              reading tapes with multiple consecutive write
+ *                              errors from 100 to 1000 in idetape_get_logical_blk
+ *                   Proposed changes to code:
+ *                   1) output "logical_blk_num" via /proc
+ *                   2) output "current_operation" via /proc
+ *                   3) Either solve or document the fact that `mt rewind' is
+ *                      required after reading from /dev/nhtx to be
+ *			able to rmmod the idetape module;
+ *			Also, sometimes an application finishes but the
+ *			device remains `busy' for some time. Same cause ?
+ *                   Proposed changes to release-notes:
+ *		     4) write a simple `quickstart' section in the
+ *                      release notes; I volunteer if you don't want to
+ * 		     5) include a pointer to video4linux in the doc
+ *                      to stimulate video applications
+ *                   6) release notes lines 331 and 362: explain what happens
+ *			if the application data rate is higher than 1100 KB/s; 
+ *			similar approach to lower-than-500 kB/s ?
+ *		     7) 6.6 Comparison; wouldn't it be better to allow different 
+ *			strategies for read and write ?
+ *			Wouldn't it be better to control the tape buffer
+ *			contents instead of the bandwidth ?
+ *		     8) line 536: replace will by would (if I understand
+ *			this section correctly, a hypothetical and unwanted situation
+ *			 is being described)
+ * Ver 1.16f Dec 15 99   Change place of the secondary OnStream header frames.
+ *
+ *
+ * Here are some words from the first releases of hd.c, which are quoted
+ * in ide.c and apply here as well:
+ *
+ * | Special care is recommended.  Have Fun!
+ *
+ */
+
+/*
+ * An overview of the pipelined operation mode.
+ *
+ * In the pipelined write mode, we will usually just add requests to our
+ * pipeline and return immediately, before we even start to service them. The
+ * user program will then have enough time to prepare the next request while
+ * we are still busy servicing previous requests. In the pipelined read mode,
+ * the situation is similar - we add read-ahead requests into the pipeline,
+ * before the user even requested them.
+ *
+ * The pipeline can be viewed as a "safety net" which will be activated when
+ * the system load is high and prevents the user backup program from keeping up
+ * with the current tape speed. At this point, the pipeline will get
+ * shorter and shorter but the tape will still be streaming at the same speed.
+ * Assuming we have enough pipeline stages, the system load will hopefully
+ * decrease before the pipeline is completely empty, and the backup program
+ * will be able to "catch up" and refill the pipeline again.
+ * 
+ * When using the pipelined mode, it would be best to disable any type of
+ * buffering done by the user program, as ide-tape already provides all the
+ * benefits in the kernel, where it can be done in a more efficient way.
+ * As we will usually not block the user program on a request, the most
+ * efficient user code will then be a simple read-write-read-... cycle.
+ * Any additional logic will usually just slow down the backup process.
+ *
+ * Using the pipelined mode, I get a constant over 400 KBps throughput,
+ * which seems to be the maximum throughput supported by my tape.
+ *
+ * However, there are some downfalls:
+ *
+ *	1.	We use memory (for data buffers) in proportional to the number
+ *		of pipeline stages (each stage is about 26 KB with my tape).
+ *	2.	In the pipelined write mode, we cheat and postpone error codes
+ *		to the user task. In read mode, the actual tape position
+ *		will be a bit further than the last requested block.
+ *
+ * Concerning (1):
+ *
+ *	1.	We allocate stages dynamically only when we need them. When
+ *		we don't need them, we don't consume additional memory. In
+ *		case we can't allocate stages, we just manage without them
+ *		(at the expense of decreased throughput) so when Linux is
+ *		tight in memory, we will not pose additional difficulties.
+ *
+ *	2.	The maximum number of stages (which is, in fact, the maximum
+ *		amount of memory) which we allocate is limited by the compile
+ *		time parameter IDETAPE_MAX_PIPELINE_STAGES.
+ *
+ *	3.	The maximum number of stages is a controlled parameter - We
+ *		don't start from the user defined maximum number of stages
+ *		but from the lower IDETAPE_MIN_PIPELINE_STAGES (again, we
+ *		will not even allocate this amount of stages if the user
+ *		program can't handle the speed). We then implement a feedback
+ *		loop which checks if the pipeline is empty, and if it is, we
+ *		increase the maximum number of stages as necessary until we
+ *		reach the optimum value which just manages to keep the tape
+ *		busy with minimum allocated memory or until we reach
+ *		IDETAPE_MAX_PIPELINE_STAGES.
+ *
+ * Concerning (2):
+ *
+ *	In pipelined write mode, ide-tape can not return accurate error codes
+ *	to the user program since we usually just add the request to the
+ *      pipeline without waiting for it to be serviced. In case an error
+ *      occurs, I will report it on the next user request.
+ *
+ *	In the pipelined read mode, subsequent read requests or forward
+ *	filemark spacing will perform correctly, as we preserve all blocks
+ *	and filemarks which we encountered during our excess read-ahead.
+ * 
+ *	For accurate tape positioning and error reporting, disabling
+ *	pipelined mode might be the best option.
+ *
+ * You can enable/disable/tune the pipelined operation mode by adjusting
+ * the compile time parameters below.
+ */
+
+/*
+ *	Possible improvements.
+ *
+ *	1.	Support for the ATAPI overlap protocol.
+ *
+ *		In order to maximize bus throughput, we currently use the DSC
+ *		overlap method which enables ide.c to service requests from the
+ *		other device while the tape is busy executing a command. The
+ *		DSC overlap method involves polling the tape's status register
+ *		for the DSC bit, and servicing the other device while the tape
+ *		isn't ready.
+ *
+ *		In the current QIC development standard (December 1995),
+ *		it is recommended that new tape drives will *in addition* 
+ *		implement the ATAPI overlap protocol, which is used for the
+ *		same purpose - efficient use of the IDE bus, but is interrupt
+ *		driven and thus has much less CPU overhead.
+ *
+ *		ATAPI overlap is likely to be supported in most new ATAPI
+ *		devices, including new ATAPI cdroms, and thus provides us
+ *		a method by which we can achieve higher throughput when
+ *		sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
+ */
+
+#define IDETAPE_VERSION "1.16f"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+
+#define NO_LONGER_REQUIRED	(1)
+
+/*
+ *	OnStream support
+ */
+#define ONSTREAM_DEBUG		(0)
+#define OS_CONFIG_PARTITION	(0xff)
+#define OS_DATA_PARTITION	(0)
+#define OS_PARTITION_VERSION	(1)
+
+/*
+ * partition
+ */
+typedef struct os_partition_s {
+	__u8	partition_num;
+	__u8	par_desc_ver;
+	__u16	wrt_pass_cntr;
+	__u32	first_frame_addr;
+	__u32	last_frame_addr;
+	__u32	eod_frame_addr;
+} os_partition_t;
+
+/*
+ * DAT entry
+ */
+typedef struct os_dat_entry_s {
+	__u32	blk_sz;
+	__u16	blk_cnt;
+	__u8	flags;
+	__u8	reserved;
+} os_dat_entry_t;
+
+/*
+ * DAT
+ */
+#define OS_DAT_FLAGS_DATA	(0xc)
+#define OS_DAT_FLAGS_MARK	(0x1)
+
+typedef struct os_dat_s {
+	__u8		dat_sz;
+	__u8		reserved1;
+	__u8		entry_cnt;
+	__u8		reserved3;
+	os_dat_entry_t	dat_list[16];
+} os_dat_t;
+
+/*
+ * Frame types
+ */
+#define OS_FRAME_TYPE_FILL	(0)
+#define OS_FRAME_TYPE_EOD	(1 << 0)
+#define OS_FRAME_TYPE_MARKER	(1 << 1)
+#define OS_FRAME_TYPE_HEADER	(1 << 3)
+#define OS_FRAME_TYPE_DATA	(1 << 7)
+
+/*
+ * AUX
+ */
+typedef struct os_aux_s {
+	__u32		format_id;		/* hardware compability AUX is based on */
+	char		application_sig[4];	/* driver used to write this media */
+	__u32		hdwr;			/* reserved */
+	__u32		update_frame_cntr;	/* for configuration frame */
+	__u8		frame_type;
+	__u8		frame_type_reserved;
+	__u8		reserved_18_19[2];
+	os_partition_t	partition;
+	__u8		reserved_36_43[8];
+	__u32		frame_seq_num;
+	__u32		logical_blk_num_high;
+	__u32		logical_blk_num;
+	os_dat_t	dat;
+	__u8		reserved188_191[4];
+	__u32		filemark_cnt;
+	__u32		phys_fm;
+	__u32		last_mark_addr;
+	__u8		reserved204_223[20];
+
+	/*
+	 * __u8		app_specific[32];
+	 *
+	 * Linux specific fields:
+	 */
+	 __u32		next_mark_addr;		/* when known, points to next marker */
+	 __u8		linux_specific[28];
+
+	__u8		reserved_256_511[256];
+} os_aux_t;
+
+typedef struct os_header_s {
+	char		ident_str[8];
+	__u8		major_rev;
+	__u8		minor_rev;
+	__u8		reserved10_15[6];
+	__u8		par_num;
+	__u8		reserved1_3[3];
+	os_partition_t	partition;
+} os_header_t;
+
+/*
+ * OnStream ADRL frame
+ */
+#define OS_FRAME_SIZE	(32 * 1024 + 512)
+#define OS_DATA_SIZE	(32 * 1024)
+#define OS_AUX_SIZE	(512)
+
+#include <linux/mtio.h>
+
+/**************************** Tunable parameters *****************************/
+
+
+/*
+ *	Pipelined mode parameters.
+ *
+ *	We try to use the minimum number of stages which is enough to
+ *	keep the tape constantly streaming. To accomplish that, we implement
+ *	a feedback loop around the maximum number of stages:
+ *
+ *	We start from MIN maximum stages (we will not even use MIN stages
+ *      if we don't need them), increment it by RATE*(MAX-MIN)
+ *	whenever we sense that the pipeline is empty, until we reach
+ *	the optimum value or until we reach MAX.
+ *
+ *	Setting the following parameter to 0 will disable the pipelined mode.
+ */
+#define IDETAPE_MIN_PIPELINE_STAGES	200
+#define IDETAPE_MAX_PIPELINE_STAGES	400
+#define IDETAPE_INCREASE_STAGES_RATE	 20
+
+/*
+ *	The following are used to debug the driver:
+ *
+ *	Setting IDETAPE_DEBUG_INFO to 1 will report device capabilities.
+ *	Setting IDETAPE_DEBUG_LOG to 1 will log driver flow control.
+ *	Setting IDETAPE_DEBUG_BUGS to 1 will enable self-sanity checks in
+ *	some places.
+ *
+ *	Setting them to 0 will restore normal operation mode:
+ *
+ *		1.	Disable logging normal successful operations.
+ *		2.	Disable self-sanity checks.
+ *		3.	Errors will still be logged, of course.
+ *
+ *	All the #if DEBUG code will be removed some day, when the driver
+ *	is verified to be stable enough. This will make it much more
+ *	esthetic.
+ */
+#define IDETAPE_DEBUG_INFO		0
+#define IDETAPE_DEBUG_LOG		1
+#define IDETAPE_DEBUG_LOG_VERBOSE	0
+#define IDETAPE_DEBUG_BUGS		1
+
+/*
+ *	After each failed packet command we issue a request sense command
+ *	and retry the packet command IDETAPE_MAX_PC_RETRIES times.
+ *
+ *	Setting IDETAPE_MAX_PC_RETRIES to 0 will disable retries.
+ */
+#define IDETAPE_MAX_PC_RETRIES		3
+
+/*
+ *	With each packet command, we allocate a buffer of
+ *	IDETAPE_PC_BUFFER_SIZE bytes. This is used for several packet
+ *	commands (Not for READ/WRITE commands).
+ */
+#define IDETAPE_PC_BUFFER_SIZE		256
+
+/*
+ *	In various places in the driver, we need to allocate storage
+ *	for packet commands and requests, which will remain valid while
+ *	we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDETAPE_PC_STACK		(10 + IDETAPE_MAX_PC_RETRIES)
+
+/*
+ *	Some tape drives require a long irq timeout
+ */
+#define IDETAPE_WAIT_CMD		(60*HZ)
+
+/*
+ *	The following parameter is used to select the point in the internal
+ *	tape fifo in which we will start to refill the buffer. Decreasing
+ *	the following parameter will improve the system's latency and
+ *	interactive response, while using a high value might improve sytem
+ *	throughput.
+ */
+#define IDETAPE_FIFO_THRESHOLD 		2
+
+/*
+ *	DSC polling parameters.
+ *
+ *	Polling for DSC (a single bit in the status register) is a very
+ *	important function in ide-tape. There are two cases in which we
+ *	poll for DSC:
+ *
+ *	1.	Before a read/write packet command, to ensure that we
+ *		can transfer data from/to the tape's data buffers, without
+ *		causing an actual media access. In case the tape is not
+ *		ready yet, we take out our request from the device
+ *		request queue, so that ide.c will service requests from
+ *		the other device on the same interface meanwhile.
+ *
+ *	2.	After the successful initialization of a "media access
+ *		packet command", which is a command which can take a long
+ *		time to complete (it can be several seconds or even an hour).
+ *
+ *		Again, we postpone our request in the middle to free the bus
+ *		for the other device. The polling frequency here should be
+ *		lower than the read/write frequency since those media access
+ *		commands are slow. We start from a "fast" frequency -
+ *		IDETAPE_DSC_MA_FAST (one second), and if we don't receive DSC
+ *		after IDETAPE_DSC_MA_THRESHOLD (5 minutes), we switch it to a
+ *		lower frequency - IDETAPE_DSC_MA_SLOW (1 minute).
+ *
+ *	We also set a timeout for the timer, in case something goes wrong.
+ *	The timeout should be longer then the maximum execution time of a
+ *	tape operation.
+ */
+ 
+/*
+ *	DSC timings.
+ */
+#define IDETAPE_DSC_RW_MIN		5*HZ/100	/* 50 msec */
+#define IDETAPE_DSC_RW_MAX		40*HZ/100	/* 400 msec */
+#define IDETAPE_DSC_RW_TIMEOUT		2*60*HZ		/* 2 minutes */
+#define IDETAPE_DSC_MA_FAST		2*HZ		/* 2 seconds */
+#define IDETAPE_DSC_MA_THRESHOLD	5*60*HZ		/* 5 minutes */
+#define IDETAPE_DSC_MA_SLOW		30*HZ		/* 30 seconds */
+#define IDETAPE_DSC_MA_TIMEOUT		2*60*60*HZ	/* 2 hours */
+
+/*************************** End of tunable parameters ***********************/
+
+/*
+ *	Debugging/Performance analysis
+ *
+ *	I/O trace support
+ */
+#define USE_IOTRACE	0
+#if USE_IOTRACE
+#include <linux/io_trace.h>
+#define IO_IDETAPE_FIFO	500
+#endif
+
+/*
+ *	Read/Write error simulation
+ */
+#define SIMULATE_ERRORS			0
+
+/*
+ *	For general magnetic tape device compatibility.
+ */
+typedef enum {
+	idetape_direction_none,
+	idetape_direction_read,
+	idetape_direction_write
+} idetape_chrdev_direction_t;
+
+/*
+ *	Our view of a packet command.
+ */
+typedef struct idetape_packet_command_s {
+	u8 c[12];				/* Actual packet bytes */
+	int retries;				/* On each retry, we increment retries */
+	int error;				/* Error code */
+	int request_transfer;			/* Bytes to transfer */
+	int actually_transferred;		/* Bytes actually transferred */
+	int buffer_size;			/* Size of our data buffer */
+	struct buffer_head *bh;
+	char *b_data;
+	int b_count;
+	byte *buffer;				/* Data buffer */
+	byte *current_position;			/* Pointer into the above buffer */
+	ide_startstop_t (*callback) (ide_drive_t *);	/* Called when this packet command is completed */
+	byte pc_buffer[IDETAPE_PC_BUFFER_SIZE];	/* Temporary buffer */
+	unsigned int flags;			/* Status/Action bit flags */
+} idetape_pc_t;
+
+/*
+ *	Packet command flag bits.
+ */
+#define	PC_ABORT			0	/* Set when an error is considered normal - We won't retry */
+#define PC_WAIT_FOR_DSC			1	/* 1 When polling for DSC on a media access command */
+#define PC_DMA_RECOMMENDED		2	/* 1 when we prefer to use DMA if possible */
+#define	PC_DMA_IN_PROGRESS		3	/* 1 while DMA in progress */
+#define	PC_DMA_ERROR			4	/* 1 when encountered problem during DMA */
+#define	PC_WRITING			5	/* Data direction */
+
+/*
+ *	Capabilities and Mechanical Status Page
+ */
+typedef struct {
+	unsigned	page_code	:6;	/* Page code - Should be 0x2a */
+	__u8		reserved0_6	:1;
+	__u8		ps		:1;	/* parameters saveable */
+	__u8		page_length;		/* Page Length - Should be 0x12 */
+	__u8		reserved2, reserved3;
+	unsigned	ro		:1;	/* Read Only Mode */
+	unsigned	reserved4_1234	:4;
+	unsigned	sprev		:1;	/* Supports SPACE in the reverse direction */
+	unsigned	reserved4_67	:2;
+	unsigned	reserved5_012	:3;
+	unsigned	efmt		:1;	/* Supports ERASE command initiated formatting */
+	unsigned	reserved5_4	:1;
+	unsigned	qfa		:1;	/* Supports the QFA two partition formats */
+	unsigned	reserved5_67	:2;
+	unsigned	lock		:1;	/* Supports locking the volume */
+	unsigned	locked		:1;	/* The volume is locked */
+	unsigned	prevent		:1;	/* The device defaults in the prevent state after power up */	
+	unsigned	eject		:1;	/* The device can eject the volume */
+	__u8		disconnect	:1;	/* The device can break request > ctl */	
+	__u8		reserved6_5	:1;
+	unsigned	ecc		:1;	/* Supports error correction */
+	unsigned	cmprs		:1;	/* Supports data compression */
+	unsigned	reserved7_0	:1;
+	unsigned	blk512		:1;	/* Supports 512 bytes block size */
+	unsigned	blk1024		:1;	/* Supports 1024 bytes block size */
+	unsigned	reserved7_3_6	:4;
+	unsigned	blk32768	:1;	/* slowb - the device restricts the byte count for PIO */
+						/* transfers for slow buffer memory ??? */
+						/* Also 32768 block size in some cases */
+	__u16		max_speed;		/* Maximum speed supported in KBps */
+	__u8		reserved10, reserved11;
+	__u16		ctl;			/* Continuous Transfer Limit in blocks */
+	__u16		speed;			/* Current Speed, in KBps */
+	__u16		buffer_size;		/* Buffer Size, in 512 bytes */
+	__u8		reserved18, reserved19;
+} idetape_capabilities_page_t;
+
+/*
+ *	Block Size Page
+ */
+typedef struct {
+	unsigned	page_code	:6;	/* Page code - Should be 0x30 */
+	unsigned	reserved1_6	:1;
+	unsigned	ps		:1;
+	__u8		page_length;		/* Page Length - Should be 2 */
+	__u8		reserved2;
+	unsigned	play32		:1;
+	unsigned	play32_5	:1;
+	unsigned	reserved2_23	:2;
+	unsigned	record32	:1;
+	unsigned	record32_5	:1;
+	unsigned	reserved2_6	:1;
+	unsigned	one		:1;
+} idetape_block_size_page_t;
+
+/*
+ *	A pipeline stage.
+ */
+typedef struct idetape_stage_s {
+	struct request rq;			/* The corresponding request */
+	struct buffer_head *bh;			/* The data buffers */
+	struct idetape_stage_s *next;		/* Pointer to the next stage */
+	os_aux_t *aux;				/* OnStream aux ptr */
+} idetape_stage_t;
+
+/*
+ *	REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+	unsigned	error_code	:7;	/* Current of deferred errors */
+	unsigned	valid		:1;	/* The information field conforms to QIC-157C */
+	__u8		reserved1	:8;	/* Segment Number - Reserved */
+	unsigned	sense_key	:4;	/* Sense Key */
+	unsigned	reserved2_4	:1;	/* Reserved */
+	unsigned	ili		:1;	/* Incorrect Length Indicator */
+	unsigned	eom		:1;	/* End Of Medium */
+	unsigned	filemark 	:1;	/* Filemark */
+	__u32		information __attribute__ ((packed));
+	__u8		asl;			/* Additional sense length (n-7) */
+	__u32		command_specific;	/* Additional command specific information */
+	__u8		asc;			/* Additional Sense Code */
+	__u8		ascq;			/* Additional Sense Code Qualifier */
+	__u8		replaceable_unit_code;	/* Field Replaceable Unit Code */
+	unsigned	sk_specific1 	:7;	/* Sense Key Specific */
+	unsigned	sksv		:1;	/* Sense Key Specific information is valid */
+	__u8		sk_specific2;		/* Sense Key Specific */
+	__u8		sk_specific3;		/* Sense Key Specific */
+	__u8		pad[2];			/* Padding to 20 bytes */
+} idetape_request_sense_result_t;
+
+
+/*
+ *	Most of our global data which we need to save even as we leave the
+ *	driver due to an interrupt or a timer event is stored in a variable
+ *	of type idetape_tape_t, defined below.
+ */
+typedef struct {
+	ide_drive_t *drive;
+	devfs_handle_t de_r, de_n;
+
+	/*
+	 *	Since a typical character device operation requires more
+	 *	than one packet command, we provide here enough memory
+	 *	for the maximum of interconnected packet commands.
+	 *	The packet commands are stored in the circular array pc_stack.
+	 *	pc_stack_index points to the last used entry, and warps around
+	 *	to the start when we get to the last array entry.
+	 *
+	 *	pc points to the current processed packet command.
+	 *
+	 *	failed_pc points to the last failed packet command, or contains
+	 *	NULL if we do not need to retry any packet command. This is
+	 *	required since an additional packet command is needed before the
+	 *	retry, to get detailed information on what went wrong.
+	 */
+	idetape_pc_t *pc;			/* Current packet command */
+	idetape_pc_t *failed_pc; 		/* Last failed packet command */
+	idetape_pc_t pc_stack[IDETAPE_PC_STACK];/* Packet command stack */
+	int pc_stack_index;			/* Next free packet command storage space */
+	struct request rq_stack[IDETAPE_PC_STACK];
+	int rq_stack_index;			/* We implement a circular array */
+
+	/*
+	 *	DSC polling variables.
+	 *
+	 *	While polling for DSC we use postponed_rq to postpone the
+	 *	current request so that ide.c will be able to service
+	 *	pending requests on the other device. Note that at most
+	 *	we will have only one DSC (usually data transfer) request
+	 *	in the device request queue. Additional requests can be
+	 *	queued in our internal pipeline, but they will be visible
+	 *	to ide.c only one at a time.
+	 */
+	struct request *postponed_rq;
+	unsigned long dsc_polling_start;	/* The time in which we started polling for DSC */
+	struct timer_list dsc_timer;		/* Timer used to poll for dsc */
+	unsigned long best_dsc_rw_frequency;	/* Read/Write dsc polling frequency */
+	unsigned long dsc_polling_frequency;	/* The current polling frequency */
+	unsigned long dsc_timeout;		/* Maximum waiting time */
+
+	/*
+	 *	Read position information
+	 */
+	byte partition;
+	unsigned int first_frame_position;		/* Current block */
+	unsigned int last_frame_position;
+	unsigned int blocks_in_buffer;
+
+	/*
+	 *	Last error information
+	 */
+	byte sense_key, asc, ascq;
+
+	/*
+	 *	Character device operation
+	 */
+	unsigned int minor;
+	char name[4];					/* device name */
+	idetape_chrdev_direction_t chrdev_direction;	/* Current character device data transfer direction */
+
+	/*
+	 *	Device information
+	 */
+	unsigned short tape_block_size;			/* Usually 512 or 1024 bytes */
+	int user_bs_factor;
+	idetape_capabilities_page_t capabilities;	/* Copy of the tape's Capabilities and Mechanical Page */
+
+	/*
+	 *	Active data transfer request parameters.
+	 *
+	 *	At most, there is only one ide-tape originated data transfer
+	 *	request in the device request queue. This allows ide.c to
+	 *	easily service requests from the other device when we
+	 *	postpone our active request. In the pipelined operation
+	 *	mode, we use our internal pipeline structure to hold
+	 *	more data requests.
+	 *
+	 *	The data buffer size is chosen based on the tape's
+	 *	recommendation.
+	 */
+	struct request *active_data_request;	/* Pointer to the request which is waiting in the device request queue */
+	int stage_size;				/* Data buffer size (chosen based on the tape's recommendation */
+	idetape_stage_t *merge_stage;
+	int merge_stage_size;
+	struct buffer_head *bh;
+	char *b_data;
+	int b_count;
+	
+	/*
+	 *	Pipeline parameters.
+	 *
+	 *	To accomplish non-pipelined mode, we simply set the following
+	 *	variables to zero (or NULL, where appropriate).
+	 */
+	int nr_stages;				/* Number of currently used stages */
+	int nr_pending_stages;			/* Number of pending stages */
+	int max_stages, min_pipeline, max_pipeline; /* We will not allocate more than this number of stages */
+	idetape_stage_t *first_stage;		/* The first stage which will be removed from the pipeline */
+	idetape_stage_t *active_stage;		/* The currently active stage */
+	idetape_stage_t *next_stage;		/* Will be serviced after the currently active request */
+	idetape_stage_t *last_stage;		/* New requests will be added to the pipeline here */
+	idetape_stage_t *cache_stage;		/* Optional free stage which we can use */
+	int pages_per_stage;
+	int excess_bh_size;			/* Wasted space in each stage */
+
+	unsigned int flags;			/* Status/Action flags */
+	spinlock_t spinlock;			/* protects the ide-tape queue */
+
+	/*
+	 * Measures average tape speed
+	 */
+	unsigned long avg_time;
+	int avg_size;
+	int avg_speed;
+
+	idetape_request_sense_result_t sense;	/* last sense information */
+
+	char vendor_id[10];
+	char product_id[18];
+	char firmware_revision[6];
+	int firmware_revision_num;
+
+	int door_locked;			/* the door is currently locked */
+
+	/*
+	 * OnStream flags
+	 */
+	int onstream;				/* the tape is an OnStream tape */
+	int raw;				/* OnStream raw access (32.5KB block size) */
+	int cur_frames;				/* current number of frames in internal buffer */
+	int max_frames;				/* max number of frames in internal buffer */
+	int logical_blk_num;			/* logical block number */
+	__u16 wrt_pass_cntr;			/* write pass counter */
+	__u32 update_frame_cntr;		/* update frame counter */
+	struct semaphore *sem;
+	int onstream_write_error;		/* write error recovery active */
+	int header_ok;				/* header frame verified ok */
+	int linux_media;			/* reading linux-specifc media */
+	int linux_media_version;
+	char application_sig[5];		/* application signature */
+	int filemark_cnt;
+	int first_mark_addr;
+	int last_mark_addr;
+	int eod_frame_addr;
+	unsigned long cmd_start_time;
+	unsigned long max_cmd_time;
+
+	/*
+	 * Optimize the number of "buffer filling"
+	 * mode sense commands.
+	 */
+	unsigned long last_buffer_fill;		/* last time in which we issued fill cmd */
+	int req_buffer_fill;			/* buffer fill command requested */
+	int writes_since_buffer_fill;
+	int reads_since_buffer_fill;
+
+	/*
+	 * Limit the number of times a request can
+	 * be postponed, to avoid an infinite postpone
+	 * deadlock.
+	 */
+	int postpone_cnt;			/* request postpone count limit */
+
+	/*
+	 * Measures number of frames:
+	 *
+	 * 1. written/read to/from the driver pipeline (pipeline_head).
+	 * 2. written/read to/from the tape buffers (buffer_head).
+	 * 3. written/read by the tape to/from the media (tape_head).
+	 */
+	int pipeline_head;
+	int buffer_head;
+	int tape_head;
+	int last_tape_head;
+
+	/*
+	 * Speed control at the tape buffers input/output
+	 */
+	unsigned long insert_time;
+	int insert_size;
+	int insert_speed;
+	int max_insert_speed;
+	int measure_insert_time;
+
+	/*
+	 * Measure tape still time, in milliseconds
+	 */
+	unsigned long tape_still_time_begin;
+	int tape_still_time;
+
+	/*
+	 * Speed regulation negative feedback loop
+	 */
+	int speed_control;
+	int pipeline_head_speed, controlled_pipeline_head_speed, uncontrolled_pipeline_head_speed;
+	int controlled_last_pipeline_head, uncontrolled_last_pipeline_head;
+	unsigned long uncontrolled_pipeline_head_time, controlled_pipeline_head_time;
+	int controlled_previous_pipeline_head, uncontrolled_previous_pipeline_head;
+	unsigned long controlled_previous_head_time, uncontrolled_previous_head_time;
+	int restart_speed_control_req;
+
+        /*
+         * Debug_level determines amount of debugging output;
+         * can be changed using /proc/ide/hdx/settings
+         * 0 : almost no debugging output
+         * 1 : 0+output errors only
+         * 2 : 1+output all sensekey/asc
+         * 3 : 2+follow all chrdev related procedures
+         * 4 : 3+follow all procedures
+         * 5 : 4+include pc_stack rq_stack info
+         * 6 : 5+USE_COUNT updates
+         */
+         int debug_level; 
+} idetape_tape_t;
+
+/*
+ *	Tape door status
+ */
+#define DOOR_UNLOCKED			0
+#define DOOR_LOCKED			1
+#define DOOR_EXPLICITLY_LOCKED		2
+
+/*
+ *	Tape flag bits values.
+ */
+#define IDETAPE_IGNORE_DSC		0
+#define IDETAPE_ADDRESS_VALID		1	/* 0 When the tape position is unknown */
+#define IDETAPE_BUSY			2	/* Device already opened */
+#define IDETAPE_PIPELINE_ERROR		3	/* Error detected in a pipeline stage */
+#define IDETAPE_DETECT_BS		4	/* Attempt to auto-detect the current user block size */
+#define IDETAPE_FILEMARK		5	/* Currently on a filemark */
+#define IDETAPE_DRQ_INTERRUPT		6	/* DRQ interrupt device */
+#define IDETAPE_READ_ERROR		7
+#define IDETAPE_PIPELINE_ACTIVE		8	/* pipeline active */
+
+/*
+ *	Supported ATAPI tape drives packet commands
+ */
+#define IDETAPE_TEST_UNIT_READY_CMD	0x00
+#define IDETAPE_REWIND_CMD		0x01
+#define IDETAPE_REQUEST_SENSE_CMD	0x03
+#define IDETAPE_READ_CMD		0x08
+#define IDETAPE_WRITE_CMD		0x0a
+#define IDETAPE_WRITE_FILEMARK_CMD	0x10
+#define IDETAPE_SPACE_CMD		0x11
+#define IDETAPE_INQUIRY_CMD		0x12
+#define IDETAPE_ERASE_CMD		0x19
+#define IDETAPE_MODE_SENSE_CMD		0x1a
+#define IDETAPE_MODE_SELECT_CMD		0x15
+#define IDETAPE_LOAD_UNLOAD_CMD		0x1b
+#define IDETAPE_PREVENT_CMD		0x1e
+#define IDETAPE_LOCATE_CMD		0x2b
+#define IDETAPE_READ_POSITION_CMD	0x34
+#define IDETAPE_READ_BUFFER_CMD		0x3c
+#define IDETAPE_SET_SPEED_CMD		0xbb
+
+/*
+ *	Some defines for the READ BUFFER command
+ */
+#define IDETAPE_RETRIEVE_FAULTY_BLOCK	6
+
+/*
+ *	Some defines for the SPACE command
+ */
+#define IDETAPE_SPACE_OVER_FILEMARK	1
+#define IDETAPE_SPACE_TO_EOD		3
+
+/*
+ *	Some defines for the LOAD UNLOAD command
+ */
+#define IDETAPE_LU_LOAD_MASK		1
+#define IDETAPE_LU_RETENSION_MASK	2
+#define IDETAPE_LU_EOT_MASK		4
+
+/*
+ *	Special requests for our block device strategy routine.
+ *
+ *	In order to service a character device command, we add special
+ *	requests to the tail of our block device request queue and wait
+ *	for their completion.
+ *
+ */
+#define IDETAPE_FIRST_RQ		90
+
+/*
+ * 	IDETAPE_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define IDETAPE_PC_RQ1			90
+#define IDETAPE_PC_RQ2			91
+
+/*
+ *	IDETAPE_READ_RQ and IDETAPE_WRITE_RQ are used by our
+ *	character device interface to request read/write operations from
+ *	our block device interface.
+ */
+#define IDETAPE_READ_RQ			92
+#define IDETAPE_WRITE_RQ		93
+#define IDETAPE_ABORTED_WRITE_RQ	94
+#define IDETAPE_ABORTED_READ_RQ		95
+#define IDETAPE_READ_BUFFER_RQ		96
+
+#define IDETAPE_LAST_RQ			96
+
+/*
+ *	A macro which can be used to check if a we support a given
+ *	request command.
+ */
+#define IDETAPE_RQ_CMD(cmd) 		((cmd >= IDETAPE_FIRST_RQ) && (cmd <= IDETAPE_LAST_RQ))
+
+/*
+ *	Error codes which are returned in rq->errors to the higher part
+ *	of the driver.
+ */
+#define	IDETAPE_ERROR_GENERAL		101
+#define	IDETAPE_ERROR_FILEMARK		102
+#define	IDETAPE_ERROR_EOD		103
+
+/*
+ *	The ATAPI Status Register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned check		:1;	/* Error occurred */
+		unsigned idx		:1;	/* Reserved */
+		unsigned corr		:1;	/* Correctable error occurred */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned dsc		:1;	/* Buffer availability / Media access command finished */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
+		unsigned bsy		:1;	/* The device has access to the command block */
+	} b;
+} idetape_status_reg_t;
+
+/*
+ *	The ATAPI error register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned ili		:1;	/* Illegal Length Indication */
+		unsigned eom		:1;	/* End Of Media Detected */
+		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
+		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
+		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+	} b;
+} idetape_error_reg_t;
+
+/*
+ *	ATAPI Feature Register
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned dma		:1;	/* Using DMA of PIO */
+		unsigned reserved321	:3;	/* Reserved */
+		unsigned reserved654	:3;	/* Reserved (Tag Type) */
+		unsigned reserved7	:1;	/* Reserved */
+	} b;
+} idetape_feature_reg_t;
+
+/*
+ *	ATAPI Byte Count Register.
+ */
+typedef union {
+	unsigned all			:16;
+	struct {
+		unsigned low		:8;	/* LSB */
+		unsigned high		:8;	/* MSB */
+	} b;
+} idetape_bcount_reg_t;
+
+/*
+ *	ATAPI Interrupt Reason Register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned reserved	:6;	/* Reserved */
+	} b;
+} idetape_ireason_reg_t;
+
+/*
+ *	ATAPI Drive Select Register
+ */
+typedef union {	
+	unsigned all			:8;
+	struct {
+		unsigned sam_lun	:4;	/* Should be zero with ATAPI (not used) */
+		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
+		unsigned one5		:1;	/* Should be set to 1 */
+		unsigned reserved6	:1;	/* Reserved */
+		unsigned one7		:1;	/* Should be set to 1 */
+	} b;
+} idetape_drivesel_reg_t;
+
+/*
+ *	ATAPI Device Control Register
+ */
+typedef union {			
+	unsigned all			:8;
+	struct {
+		unsigned zero0		:1;	/* Should be set to zero */
+		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
+		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+		unsigned one3		:1;	/* Should be set to 1 */
+		unsigned reserved4567	:4;	/* Reserved */
+	} b;
+} idetape_control_reg_t;
+
+/*
+ *	idetape_chrdev_t provides the link between out character device
+ *	interface and our block device interface and the corresponding
+ *	ide_drive_t structure.
+ */
+typedef struct {
+	ide_drive_t *drive;
+} idetape_chrdev_t;
+
+/*
+ *	The following is used to format the general configuration word of
+ *	the ATAPI IDENTIFY DEVICE command.
+ */
+struct idetape_id_gcw {	
+	unsigned packet_size		:2;	/* Packet Size */
+	unsigned reserved234		:3;	/* Reserved */
+	unsigned drq_type		:2;	/* Command packet DRQ type */
+	unsigned removable		:1;	/* Removable media */
+	unsigned device_type		:5;	/* Device type */
+	unsigned reserved13		:1;	/* Reserved */
+	unsigned protocol		:2;	/* Protocol type */
+};
+
+/*
+ *	INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+typedef struct {
+	unsigned	device_type	:5;	/* Peripheral Device Type */
+	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned	reserved1_6t0	:7;	/* Reserved */
+	unsigned	rmb		:1;	/* Removable Medium Bit */
+	unsigned	ansi_version	:3;	/* ANSI Version */
+	unsigned	ecma_version	:3;	/* ECMA Version */
+	unsigned	iso_version	:2;	/* ISO Version */
+	unsigned	response_format :4;	/* Response Data Format */
+	unsigned	reserved3_45	:2;	/* Reserved */
+	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned	reserved3_7	:1;	/* AENC - Reserved */
+	__u8		additional_length;	/* Additional Length (total_length-4) */
+	__u8		rsv5, rsv6, rsv7;	/* Reserved */
+	__u8		vendor_id[8];		/* Vendor Identification */
+	__u8		product_id[16];		/* Product Identification */
+	__u8		revision_level[4];	/* Revision Level */
+	__u8		vendor_specific[20];	/* Vendor Specific - Optional */
+	__u8		reserved56t95[40];	/* Reserved - Optional */
+						/* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ *	READ POSITION packet command - Data Format (From Table 6-57)
+ */
+typedef struct {
+	unsigned	reserved0_10	:2;	/* Reserved */
+	unsigned	bpu		:1;	/* Block Position Unknown */	
+	unsigned	reserved0_543	:3;	/* Reserved */
+	unsigned	eop		:1;	/* End Of Partition */
+	unsigned	bop		:1;	/* Beginning Of Partition */
+	u8		partition;		/* Partition Number */
+	u8		reserved2, reserved3;	/* Reserved */
+	u32		first_block;		/* First Block Location */
+	u32		last_block;		/* Last Block Location (Optional) */
+	u8		reserved12;		/* Reserved */
+	u8		blocks_in_buffer[3];	/* Blocks In Buffer - (Optional) */
+	u32		bytes_in_buffer;	/* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ *	Follows structures which are related to the SELECT SENSE / MODE SENSE
+ *	packet commands. Those packet commands are still not supported
+ *	by ide-tape.
+ */
+#define	IDETAPE_CAPABILITIES_PAGE	0x2a
+#define IDETAPE_BLOCK_SIZE_PAGE		0x30
+
+/*
+ *	Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+	__u8	mode_data_length;	/* Length of the following data transfer */
+	__u8	medium_type;		/* Medium Type */
+	__u8	dsp;			/* Device Specific Parameter */
+	__u8	bdl;			/* Block Descriptor Length */
+#if 0
+	/* data transfer page */
+	__u8	page_code	:6;
+	__u8	reserved0_6	:1;
+	__u8	ps		:1;	/* parameters saveable */
+	__u8	page_length;		/* page Length == 0x02 */
+	__u8	reserved2;
+	__u8	read32k		:1;	/* 32k blk size (data only) */
+	__u8	read32k5	:1;	/* 32.5k blk size (data&AUX) */
+	__u8	reserved3_23	:2;
+	__u8	write32k	:1;	/* 32k blk size (data only) */
+	__u8	write32k5	:1;	/* 32.5k blk size (data&AUX) */
+	__u8	reserved3_6	:1;
+	__u8	streaming	:1;	/* streaming mode enable */
+#endif
+} idetape_mode_parameter_header_t;
+
+/*
+ *	Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ *	Support for block descriptors is optional.
+ */
+typedef struct {
+	__u8		density_code;		/* Medium density code */
+	__u8		blocks[3];		/* Number of blocks */
+	__u8		reserved4;		/* Reserved */
+	__u8		length[3];		/* Block Length */
+} idetape_parameter_block_descriptor_t;
+
+/*
+ *	The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+	unsigned	page_code	:6;	/* Page Code - Should be 0xf */
+	unsigned	reserved0	:1;	/* Reserved */
+	unsigned	ps		:1;
+	__u8		page_length;		/* Page Length - Should be 14 */
+	unsigned	reserved2	:6;	/* Reserved */
+	unsigned	dcc		:1;	/* Data Compression Capable */
+	unsigned	dce		:1;	/* Data Compression Enable */
+	unsigned	reserved3	:5;	/* Reserved */
+	unsigned	red		:2;	/* Report Exception on Decompression */
+	unsigned	dde		:1;	/* Data Decompression Enable */
+	__u32		ca;			/* Compression Algorithm */
+	__u32		da;			/* Decompression Algorithm */
+	__u8		reserved[4];		/* Reserved */
+} idetape_data_compression_page_t;
+
+/*
+ *	The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+	unsigned	page_code	:6;	/* Page Code - Should be 0x11 */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	ps		:1;
+	__u8		page_length;		/* Page Length - Should be 6 */
+	__u8		map;			/* Maximum Additional Partitions - Should be 0 */
+	__u8		apd;			/* Additional Partitions Defined - Should be 0 */
+	unsigned	reserved4_012	:3;	/* Reserved */
+	unsigned	psum		:2;	/* Should be 0 */
+	unsigned	idp		:1;	/* Should be 0 */
+	unsigned	sdp		:1;	/* Should be 0 */
+	unsigned	fdp		:1;	/* Fixed Data Partitions */
+	__u8		mfr;			/* Medium Format Recognition */
+	__u8		reserved[2];		/* Reserved */
+} idetape_medium_partition_page_t;
+
+/*
+ *	Run time configurable parameters.
+ */
+typedef struct {
+	int	dsc_rw_frequency;
+	int	dsc_media_access_frequency;
+	int	nr_stages;
+} idetape_config_t;
+
+/*
+ *	The variables below are used for the character device interface.
+ *	Additional state variables are defined in our ide_drive_t structure.
+ */
+static idetape_chrdev_t idetape_chrdevs[MAX_HWIFS * MAX_DRIVES];
+static int idetape_chrdev_present = 0;
+
+#if IDETAPE_DEBUG_LOG_VERBOSE
+
+/*
+ * DO NOT REMOVE, BUILDING A VERBOSE DEBUG SCHEME FOR ATAPI
+ */
+
+char *idetape_sense_key_verbose (byte idetape_sense_key)
+{
+	switch (idetape_sense_key) {
+		default: {
+			char buf[22];
+			sprintf(buf, "IDETAPE_SENSE (0x%02x)", idetape_sense_key);
+			return(buf);
+		}
+
+	}
+}
+
+char *idetape_command_key_verbose (byte idetape_command_key)
+{
+	switch (idetape_command_key) {
+		case IDETAPE_TEST_UNIT_READY_CMD:	return("TEST_UNIT_READY_CMD");
+		case IDETAPE_REWIND_CMD:		return("REWIND_CMD");
+		case IDETAPE_REQUEST_SENSE_CMD:		return("REQUEST_SENSE_CMD");
+		case IDETAPE_READ_CMD:			return("READ_CMD");
+		case IDETAPE_WRITE_CMD:			return("WRITE_CMD");
+		case IDETAPE_WRITE_FILEMARK_CMD:	return("WRITE_FILEMARK_CMD");
+		case IDETAPE_SPACE_CMD:			return("SPACE_CMD");
+		case IDETAPE_INQUIRY_CMD:		return("INQUIRY_CMD");
+		case IDETAPE_ERASE_CMD:			return("ERASE_CMD")
+		case IDETAPE_MODE_SENSE_CMD:		return("MODE_SENSE_CMD");
+		case IDETAPE_MODE_SELECT_CMD:		return("MODE_SELECT_CMD");
+		case IDETAPE_LOAD_UNLOAD_CMD:		return("LOAD_UNLOAD_CMD");
+		case IDETAPE_PREVENT_CMD:		return("PREVENT_CMD");
+		case IDETAPE_LOCATE_CMD:		return("LOCATE_CMD");
+		case IDETAPE_READ_POSITION_CMD:		return("READ_POSITION_CMD");
+		case IDETAPE_READ_BUFFER_CMD:		return("READ_BUFFER_CMD");
+		case IDETAPE_SET_SPEED_CMD:		return("SET_SPEED_CMD");
+		default: {
+				char buf[20];
+				sprintf(buf, "CMD (0x%02x)", idetape_command_key);
+				return(buf);
+			}
+	}
+}
+#endif /* IDETAPE_DEBUG_LOG_VERBOSE */
+
+/*
+ *	Too bad. The drive wants to send us data which we are not ready to accept.
+ *	Just throw it away.
+ */
+static void idetape_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		IN_BYTE (IDE_DATA_REG);
+}
+
+static void idetape_input_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount)
+{
+	struct buffer_head *bh = pc->bh;
+	int count;
+
+	while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh == NULL) {
+			printk (KERN_ERR "ide-tape: bh == NULL in idetape_input_buffers\n");
+			idetape_discard_data (drive, bcount);
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+		count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), bcount);
+		atapi_input_bytes (drive, bh->b_data + atomic_read(&bh->b_count), count);
+		bcount -= count; atomic_add(count, &bh->b_count);
+		if (atomic_read(&bh->b_count) == bh->b_size) {
+			bh = bh->b_reqnext;
+			if (bh)
+				atomic_set(&bh->b_count, 0);
+		}
+	}
+	pc->bh = bh;
+}
+
+static void idetape_output_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount)
+{
+	struct buffer_head *bh = pc->bh;
+	int count;
+
+	while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh == NULL) {
+			printk (KERN_ERR "ide-tape: bh == NULL in idetape_output_buffers\n");
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+		count = IDE_MIN (pc->b_count, bcount);
+		atapi_output_bytes (drive, pc->b_data, count);
+		bcount -= count; pc->b_data += count; pc->b_count -= count;
+		if (!pc->b_count) {
+			pc->bh = bh = bh->b_reqnext;
+			if (bh) {
+				pc->b_data = bh->b_data;
+				pc->b_count = atomic_read(&bh->b_count);
+			}
+		}
+	}
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+static void idetape_update_buffers (idetape_pc_t *pc)
+{
+	struct buffer_head *bh = pc->bh;
+	int count, bcount = pc->actually_transferred;
+
+	if (test_bit (PC_WRITING, &pc->flags))
+		return;
+	while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh == NULL) {
+			printk (KERN_ERR "ide-tape: bh == NULL in idetape_update_buffers\n");
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+		count = IDE_MIN (bh->b_size, bcount);
+		atomic_set(&bh->b_count, count);
+		if (atomic_read(&bh->b_count) == bh->b_size)
+			bh = bh->b_reqnext;
+		bcount -= count;
+	}
+	pc->bh = bh;
+}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+/*
+ *	idetape_next_pc_storage returns a pointer to a place in which we can
+ *	safely store a packet command, even though we intend to leave the
+ *	driver. A storage space for a maximum of IDETAPE_PC_STACK packet
+ *	commands is allocated at initialization time.
+ */
+static idetape_pc_t *idetape_next_pc_storage (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 5)
+		printk (KERN_INFO "ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
+#endif /* IDETAPE_DEBUG_LOG */
+	if (tape->pc_stack_index==IDETAPE_PC_STACK)
+		tape->pc_stack_index=0;
+	return (&tape->pc_stack[tape->pc_stack_index++]);
+}
+
+/*
+ *	idetape_next_rq_storage is used along with idetape_next_pc_storage.
+ *	Since we queue packet commands in the request queue, we need to
+ *	allocate a request, along with the allocation of a packet command.
+ */
+ 
+/**************************************************************
+ *                                                            *
+ *  This should get fixed to use kmalloc(GFP_ATOMIC, ..)      *
+ *  followed later on by kfree().   -ml                       *
+ *                                                            *
+ **************************************************************/
+ 
+static struct request *idetape_next_rq_storage (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 5)
+		printk (KERN_INFO "ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
+#endif /* IDETAPE_DEBUG_LOG */
+	if (tape->rq_stack_index==IDETAPE_PC_STACK)
+		tape->rq_stack_index=0;
+	return (&tape->rq_stack[tape->rq_stack_index++]);
+}
+
+/*
+ *	idetape_init_pc initializes a packet command.
+ */
+static void idetape_init_pc (idetape_pc_t *pc)
+{
+	memset (pc->c, 0, 12);
+	pc->retries = 0;
+	pc->flags = 0;
+	pc->request_transfer = 0;
+	pc->buffer = pc->pc_buffer;
+	pc->buffer_size = IDETAPE_PC_BUFFER_SIZE;
+	pc->bh = NULL;
+	pc->b_data = NULL;
+}
+
+/*
+ *	idetape_analyze_error is called on each failed packet command retry
+ *	to analyze the request sense. We currently do not utilize this
+ *	information.
+ */
+static void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t *pc = tape->failed_pc;
+
+	tape->sense = *result;
+	tape->sense_key = result->sense_key; tape->asc = result->asc; tape->ascq = result->ascq;
+#if IDETAPE_DEBUG_LOG
+	/*
+	 *	Without debugging, we only log an error if we decided to
+	 *	give up retrying.
+	 */
+	if (tape->debug_level >= 1)
+		printk (KERN_INFO "ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq);
+#if IDETAPE_DEBUG_LOG_VERBOSE
+	if (tape->debug_level >= 1)
+		printk (KERN_INFO "ide-tape: pc = %s, sense key = %x, asc = %x, ascq = %x\n",
+			idetape_command_key_verbose((byte) pc->c[0]),
+			result->sense_key,
+			result->asc,
+			result->ascq);
+#endif /* IDETAPE_DEBUG_LOG_VERBOSE */
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (tape->onstream && result->sense_key == 2 && result->asc == 0x53 && result->ascq == 2) {
+		clear_bit(PC_DMA_ERROR, &pc->flags);
+		ide_stall_queue(drive, HZ / 2);
+		return;
+	}
+#ifdef CONFIG_BLK_DEV_IDEDMA
+
+	/*
+	 *	Correct pc->actually_transferred by asking the tape.
+	 */
+	if (test_bit (PC_DMA_ERROR, &pc->flags)) {
+		pc->actually_transferred = pc->request_transfer - tape->tape_block_size * ntohl (get_unaligned (&result->information));
+		idetape_update_buffers (pc);
+	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+	if (pc->c[0] == IDETAPE_READ_CMD && result->filemark) {
+		pc->error = IDETAPE_ERROR_FILEMARK;
+		set_bit (PC_ABORT, &pc->flags);
+	}
+	if (pc->c[0] == IDETAPE_WRITE_CMD) {
+		if (result->eom || (result->sense_key == 0xd && result->asc == 0x0 && result->ascq == 0x2)) {
+			pc->error = IDETAPE_ERROR_EOD;
+			set_bit (PC_ABORT, &pc->flags);
+		}
+	}
+	if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) {
+		if (result->sense_key == 8) {
+			pc->error = IDETAPE_ERROR_EOD;
+			set_bit (PC_ABORT, &pc->flags);
+		}
+		if (!test_bit (PC_ABORT, &pc->flags) && (tape->onstream || pc->actually_transferred))
+			pc->retries = IDETAPE_MAX_PC_RETRIES + 1;
+	}
+}
+
+static void idetape_abort_pipeline (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *stage = tape->next_stage;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk(KERN_INFO "ide-tape: %s: idetape_abort_pipeline called\n", tape->name);
+#endif
+	while (stage) {
+		if (stage->rq.cmd == IDETAPE_WRITE_RQ)
+			stage->rq.cmd = IDETAPE_ABORTED_WRITE_RQ;
+		else if (stage->rq.cmd == IDETAPE_READ_RQ)
+			stage->rq.cmd = IDETAPE_ABORTED_READ_RQ;
+		stage = stage->next;
+	}
+}
+
+/*
+ *	idetape_active_next_stage will declare the next stage as "active".
+ */
+static void idetape_active_next_stage (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *stage=tape->next_stage;
+	struct request *rq = &stage->rq;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_active_next_stage\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (stage == NULL) {
+		printk (KERN_ERR "ide-tape: bug: Trying to activate a non existing stage\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */	
+
+	rq->buffer = NULL;
+	rq->bh = stage->bh;
+	tape->active_data_request=rq;
+	tape->active_stage=stage;
+	tape->next_stage=stage->next;
+}
+
+/*
+ *	idetape_increase_max_pipeline_stages is a part of the feedback
+ *	loop which tries to find the optimum number of stages. In the
+ *	feedback loop, we are starting from a minimum maximum number of
+ *	stages, and if we sense that the pipeline is empty, we try to
+ *	increase it, until we reach the user compile time memory limit.
+ */
+static void idetape_increase_max_pipeline_stages (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int increase = (tape->max_pipeline - tape->min_pipeline) / 10;
+	
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_increase_max_pipeline_stages\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	tape->max_stages += increase;
+	tape->max_stages = IDE_MAX(tape->max_stages, tape->min_pipeline);
+	tape->max_stages = IDE_MIN(tape->max_stages, tape->max_pipeline);
+}
+
+/*
+ *	idetape_kfree_stage calls kfree to completely free a stage, along with
+ *	its related buffers.
+ */
+static void __idetape_kfree_stage (idetape_stage_t *stage)
+{
+	struct buffer_head *prev_bh, *bh = stage->bh;
+	int size;
+
+	while (bh != NULL) {
+		if (bh->b_data != NULL) {
+			size = (int) bh->b_size;
+			while (size > 0) {
+				free_page ((unsigned long) bh->b_data);
+				size -= PAGE_SIZE;
+				bh->b_data += PAGE_SIZE;
+			}
+		}
+		prev_bh = bh;
+		bh = bh->b_reqnext;
+		kfree (prev_bh);
+	}
+	kfree (stage);
+}
+
+static void idetape_kfree_stage (idetape_tape_t *tape, idetape_stage_t *stage)
+{
+	__idetape_kfree_stage (stage);
+}
+
+/*
+ *	idetape_remove_stage_head removes tape->first_stage from the pipeline.
+ *	The caller should avoid race conditions.
+ */
+static void idetape_remove_stage_head (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *stage;
+	
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_remove_stage_head\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (tape->first_stage == NULL) {
+		printk (KERN_ERR "ide-tape: bug: tape->first_stage is NULL\n");
+		return;		
+	}
+	if (tape->active_stage == tape->first_stage) {
+		printk (KERN_ERR "ide-tape: bug: Trying to free our active pipeline stage\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+	stage=tape->first_stage;
+	tape->first_stage=stage->next;
+	idetape_kfree_stage (tape, stage);
+	tape->nr_stages--;
+	if (tape->first_stage == NULL) {
+		tape->last_stage=NULL;
+#if IDETAPE_DEBUG_BUGS
+		if (tape->next_stage != NULL)
+			printk (KERN_ERR "ide-tape: bug: tape->next_stage != NULL\n");
+		if (tape->nr_stages)
+			printk (KERN_ERR "ide-tape: bug: nr_stages should be 0 now\n");
+#endif /* IDETAPE_DEBUG_BUGS */
+	}
+}
+
+/*
+ *	idetape_end_request is used to finish servicing a request, and to
+ *	insert a pending pipeline request into the main device queue.
+ */
+static void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive = hwgroup->drive;
+	struct request *rq = hwgroup->rq;
+	idetape_tape_t *tape = drive->driver_data;
+	unsigned long flags;
+	int error;
+	int remove_stage = 0;
+#if ONSTREAM_DEBUG
+	idetape_stage_t *stage;
+	os_aux_t *aux;
+	unsigned char *p;
+#endif
+
+#if IDETAPE_DEBUG_LOG
+        if (tape->debug_level >= 4)
+	printk (KERN_INFO "ide-tape: Reached idetape_end_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	switch (uptodate) {
+		case 0:	error = IDETAPE_ERROR_GENERAL; break;
+		case 1: error = 0; break;
+		default: error = uptodate;
+	}
+	rq->errors = error;
+	if (error)
+		tape->failed_pc = NULL;
+
+	spin_lock_irqsave(&tape->spinlock, flags);
+	if (tape->active_data_request == rq) {		/* The request was a pipelined data transfer request */
+		tape->active_stage = NULL;
+		tape->active_data_request = NULL;
+		tape->nr_pending_stages--;
+		if (rq->cmd == IDETAPE_WRITE_RQ) {
+#if ONSTREAM_DEBUG
+			if (tape->debug_level >= 2) {
+				if (tape->onstream) {
+					stage = tape->first_stage;
+					aux = stage->aux;
+					p = stage->bh->b_data;
+					if (ntohl(aux->logical_blk_num) < 11300 && ntohl(aux->logical_blk_num) > 11100)
+						printk(KERN_INFO "ide-tape: finished writing logical blk %u (data %x %x %x %x)\n", ntohl(aux->logical_blk_num), *p++, *p++, *p++, *p++);
+				}
+			}
+#endif
+			if (tape->onstream && !tape->raw) {
+				if (tape->first_frame_position == 0xba4) {
+#if ONSTREAM_DEBUG
+				if (tape->debug_level >= 2)
+					printk("ide-tape: %s: skipping over config parition..\n", tape->name);
+#endif
+					tape->onstream_write_error = 2;
+					if (tape->sem)
+						up(tape->sem);
+				}
+			}
+			remove_stage = 1;
+			if (error) {
+				set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+				if (error == IDETAPE_ERROR_EOD)
+					idetape_abort_pipeline (drive);
+				if (tape->onstream && !tape->raw && error == IDETAPE_ERROR_GENERAL && tape->sense.sense_key == 3) {
+					clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+					printk(KERN_ERR "ide-tape: %s: write error, enabling error recovery\n", tape->name);
+					tape->onstream_write_error = 1;
+					remove_stage = 0;
+					tape->nr_pending_stages++;
+					tape->next_stage = tape->first_stage;
+					rq->current_nr_sectors = rq->nr_sectors;
+					if (tape->sem)
+						up(tape->sem);
+				}
+			}
+		} else if (rq->cmd == IDETAPE_READ_RQ) {
+			if (error == IDETAPE_ERROR_EOD) {
+				set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+				idetape_abort_pipeline(drive);
+			}
+		}
+		if (tape->next_stage != NULL && !tape->onstream_write_error) {
+			idetape_active_next_stage (drive);
+
+			/*
+			 *	Insert the next request into the request queue.
+			 */
+			(void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
+		} else if (!error) {
+			if (!tape->onstream)
+				idetape_increase_max_pipeline_stages (drive);
+		}
+	}
+	ide_end_drive_cmd (drive, 0, 0);
+	if (remove_stage)
+		idetape_remove_stage_head (drive);
+	if (tape->active_data_request == NULL)
+		clear_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
+	spin_unlock_irqrestore(&tape->spinlock, flags);
+}
+
+static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_request_sense_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+	if (!tape->pc->error) {
+		idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
+		idetape_end_request (1,HWGROUP (drive));
+	} else {
+		printk (KERN_ERR "ide-tape: Error in REQUEST SENSE itself - Aborting request!\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return ide_stopped;
+}
+
+static void idetape_create_request_sense_cmd (idetape_pc_t *pc)
+{
+	idetape_init_pc (pc);	
+	pc->c[0] = IDETAPE_REQUEST_SENSE_CMD;
+	pc->c[4] = 20;
+	pc->request_transfer = 18;
+	pc->callback = &idetape_request_sense_callback;
+}
+
+/*
+ *	idetape_queue_pc_head generates a new packet command request in front
+ *	of the request queue, before the current request, so that it will be
+ *	processed immediately, on the next pass through the driver.
+ *
+ *	idetape_queue_pc_head is called from the request handling part of
+ *	the driver (the "bottom" part). Safe storage for the request should
+ *	be allocated with idetape_next_pc_storage and idetape_next_rq_storage
+ *	before calling idetape_queue_pc_head.
+ *
+ *	Memory for those requests is pre-allocated at initialization time, and
+ *	is limited to IDETAPE_PC_STACK requests. We assume that we have enough
+ *	space for the maximum possible number of inter-dependent packet commands.
+ *
+ *	The higher level of the driver - The ioctl handler and the character
+ *	device handling functions should queue request to the lower level part
+ *	and wait for their completion using idetape_queue_pc_tail or
+ *	idetape_queue_rw_tail.
+ */
+static void idetape_queue_pc_head (ide_drive_t *drive,idetape_pc_t *pc,struct request *rq)
+{
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->cmd = IDETAPE_PC_RQ1;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+/*
+ *	idetape_retry_pc is called when an error was detected during the
+ *	last packet command. We queue a request sense packet command in
+ *	the head of the request list.
+ */
+static ide_startstop_t idetape_retry_pc (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t *pc;
+	struct request *rq;
+	idetape_error_reg_t error;
+
+	error.all = IN_BYTE (IDE_ERROR_REG);
+	pc = idetape_next_pc_storage (drive);
+	rq = idetape_next_rq_storage (drive);
+	idetape_create_request_sense_cmd (pc);
+	set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
+	idetape_queue_pc_head (drive, pc, rq);
+	return ide_stopped;
+}
+
+/*
+ *	idetape_postpone_request postpones the current request so that
+ *	ide.c will be able to service requests from another device on
+ *	the same hwgroup while we are polling for DSC.
+ */
+static void idetape_postpone_request (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk(KERN_INFO "ide-tape: idetape_postpone_request\n");
+#endif
+	tape->postponed_rq = HWGROUP(drive)->rq;
+	ide_stall_queue(drive, tape->dsc_polling_frequency);
+}
+
+/*
+ *	idetape_pc_intr is the usual interrupt handler which will be called
+ *	during a packet command. We will transfer some of the data (as
+ *	requested by the drive) and will re-point interrupt handler to us.
+ *	When data transfer is finished, we will act according to the
+ *	algorithm described before idetape_issue_packet_command.
+ *
+ */
+static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_status_reg_t status;
+	idetape_bcount_reg_t bcount;
+	idetape_ireason_reg_t ireason;
+	idetape_pc_t *pc=tape->pc;
+
+	unsigned int temp;
+	unsigned long cmd_time;
+#if SIMULATE_ERRORS
+	static int error_sim_count = 0;
+#endif
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_pc_intr interrupt handler\n");
+#endif /* IDETAPE_DEBUG_LOG */	
+
+	status.all = GET_STAT();					/* Clear the interrupt */
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+		if (HWIF(drive)->dmaproc(ide_dma_end, drive)) {
+			/*
+			 * A DMA error is sometimes expected. For example,
+			 * if the tape is crossing a filemark during a
+			 * READ command, it will issue an irq and position
+			 * itself before the filemark, so that only a partial
+			 * data transfer will occur (which causes the DMA
+			 * error). In that case, we will later ask the tape
+			 * how much bytes of the original request were
+			 * actually transferred (we can't receive that
+			 * information from the DMA engine on most chipsets).
+			 */
+			set_bit (PC_DMA_ERROR, &pc->flags);
+		} else if (!status.b.check) {
+			pc->actually_transferred=pc->request_transfer;
+			idetape_update_buffers (pc);
+		}
+#if IDETAPE_DEBUG_LOG
+		if (tape->debug_level >= 4)
+			printk (KERN_INFO "ide-tape: DMA finished\n");
+#endif /* IDETAPE_DEBUG_LOG */
+	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+	if (!status.b.drq) {						/* No more interrupts */
+		cmd_time = (jiffies - tape->cmd_start_time) * 1000 / HZ;
+		tape->max_cmd_time = IDE_MAX(cmd_time, tape->max_cmd_time);
+#if IDETAPE_DEBUG_LOG
+		if (tape->debug_level >= 2)
+			printk (KERN_INFO "ide-tape: Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDETAPE_DEBUG_LOG */
+		clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+		ide__sti();	/* local CPU only */
+
+#if SIMULATE_ERRORS
+		if ((pc->c[0] == IDETAPE_WRITE_CMD || pc->c[0] == IDETAPE_READ_CMD) && (++error_sim_count % 100) == 0) {
+			printk(KERN_INFO "ide-tape: %s: simulating error\n", tape->name);
+			status.b.check = 1;
+		}
+#endif
+		if (status.b.check && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD)
+			status.b.check = 0;
+		if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) {	/* Error detected */
+#if IDETAPE_DEBUG_LOG
+			if (tape->debug_level >= 1)
+				printk (KERN_INFO "ide-tape: %s: I/O error, ",tape->name);
+#endif /* IDETAPE_DEBUG_LOG */
+			if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+				printk (KERN_ERR "ide-tape: I/O error in request sense command\n");
+				return ide_do_reset (drive);
+			}
+#if IDETAPE_DEBUG_LOG
+			if (tape->debug_level >= 1)
+				printk(KERN_INFO "ide-tape: [cmd %x]: check condition\n", pc->c[0]);
+#endif
+			return idetape_retry_pc (drive);				/* Retry operation */
+		}
+		pc->error = 0;
+		if (!tape->onstream && test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) {	/* Media access command */
+			tape->dsc_polling_start = jiffies;
+			tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST;
+			tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT;
+			idetape_postpone_request (drive);		/* Allow ide.c to handle other requests */
+			return ide_stopped;
+		}
+		if (tape->failed_pc == pc)
+			tape->failed_pc=NULL;
+		return pc->callback(drive);			/* Command finished - Call the callback function */
+	}
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+		printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n");
+		printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
+		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+		return ide_do_reset (drive);
+	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
+	bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);				/* on this interrupt */
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+	if (ireason.b.cod) {
+		printk (KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n");
+		return ide_do_reset (drive);
+	}
+	if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {	/* Hopefully, we will never get here */
+		printk (KERN_ERR "ide-tape: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+		printk (KERN_ERR "ide-tape: but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write");
+		return ide_do_reset (drive);
+	}
+	if (!test_bit (PC_WRITING, &pc->flags)) {			/* Reading - Check that we have enough space */
+		temp = pc->actually_transferred + bcount.all;
+		if ( temp > pc->request_transfer) {
+			if (temp > pc->buffer_size) {
+				printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n");
+				idetape_discard_data (drive,bcount.all);
+				ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD,NULL);
+				return ide_started;
+			}
+#if IDETAPE_DEBUG_LOG
+			if (tape->debug_level >= 2)
+				printk (KERN_NOTICE "ide-tape: The tape wants to send us more data than expected - allowing transfer\n");
+#endif /* IDETAPE_DEBUG_LOG */
+		}
+	}
+	if (test_bit (PC_WRITING, &pc->flags)) {
+		if (pc->bh != NULL)
+			idetape_output_buffers (drive, pc, bcount.all);
+		else
+			atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+	} else {
+		if (pc->bh != NULL)
+			idetape_input_buffers (drive, pc, bcount.all);
+		else
+			atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+	}
+	pc->actually_transferred+=bcount.all;					/* Update the current position */
+	pc->current_position+=bcount.all;
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 2)
+		printk(KERN_INFO "ide-tape: [cmd %x] transferred %d bytes on that interrupt\n", pc->c[0], bcount.all);
+#endif
+	ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD,NULL);		/* And set the interrupt handler again */
+	return ide_started;
+}
+
+/*
+ *	Packet Command Interface
+ *
+ *	The current Packet Command is available in tape->pc, and will not
+ *	change until we finish handling it. Each packet command is associated
+ *	with a callback function that will be called when the command is
+ *	finished.
+ *
+ *	The handling will be done in three stages:
+ *
+ *	1.	idetape_issue_packet_command will send the packet command to the
+ *		drive, and will set the interrupt handler to idetape_pc_intr.
+ *
+ *	2.	On each interrupt, idetape_pc_intr will be called. This step
+ *		will be repeated until the device signals us that no more
+ *		interrupts will be issued.
+ *
+ *	3.	ATAPI Tape media access commands have immediate status with a
+ *		delayed process. In case of a successful initiation of a
+ *		media access packet command, the DSC bit will be set when the
+ *		actual execution of the command is finished. 
+ *		Since the tape drive will not issue an interrupt, we have to
+ *		poll for this event. In this case, we define the request as
+ *		"low priority request" by setting rq_status to
+ *		IDETAPE_RQ_POSTPONED, 	set a timer to poll for DSC and exit
+ *		the driver.
+ *
+ *		ide.c will then give higher priority to requests which
+ *		originate from the other device, until will change rq_status
+ *		to RQ_ACTIVE.
+ *
+ *	4.	When the packet command is finished, it will be checked for errors.
+ *
+ *	5.	In case an error was found, we queue a request sense packet command
+ *		in front of the request queue and retry the operation up to
+ *		IDETAPE_MAX_PC_RETRIES times.
+ *
+ *	6.	In case no error was found, or we decided to give up and not
+ *		to retry again, the callback function will be called and then
+ *		we will handle the next request.
+ *
+ */
+static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t *pc = tape->pc;
+	idetape_ireason_reg_t ireason;
+	int retries = 100;
+	ide_startstop_t startstop;
+
+	if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+		printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return startstop;
+	}
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+	while (retries-- && (!ireason.b.cod || ireason.b.io)) {
+		printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, retrying\n");
+		udelay(100);
+		ireason.all = IN_BYTE(IDE_IREASON_REG);
+		if (retries == 0) {
+			printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, ignoring\n");
+			ireason.b.cod = 1;
+			ireason.b.io = 0;
+		}
+	}
+	if (!ireason.b.cod || ireason.b.io) {
+		printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
+		return ide_do_reset (drive);
+	}
+	tape->cmd_start_time = jiffies;
+	ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL);	/* Set the interrupt routine */
+	atapi_output_bytes (drive,pc->c,12);			/* Send the actual packet */
+	return ide_started;
+}
+
+static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_bcount_reg_t bcount;
+	int dma_ok=0;
+
+#if IDETAPE_DEBUG_BUGS
+	if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+		printk (KERN_ERR "ide-tape: possible ide-tape.c bug - Two request sense in serial were issued\n");
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+
+	if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
+		tape->failed_pc=pc;
+	tape->pc=pc;							/* Set the current packet command */
+
+	if (pc->retries > IDETAPE_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+		/*
+		 *	We will "abort" retrying a packet command in case
+		 *	a legitimate error code was received (crossing a
+		 *	filemark, or DMA error in the end of media, for
+		 *	example).
+		 */
+		if (!test_bit (PC_ABORT, &pc->flags)) {
+			if (!(pc->c[0] == 0 && tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8))) {
+				printk (KERN_ERR "ide-tape: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+					tape->name, pc->c[0], tape->sense_key, tape->asc, tape->ascq);
+				if (tape->onstream && pc->c[0] == 8 && tape->sense_key == 3 && tape->asc == 0x11)  /* AJN-1: 11 should be 0x11 */
+					printk(KERN_ERR "ide-tape: %s: enabling read error recovery\n", tape->name);
+			}
+			pc->error = IDETAPE_ERROR_GENERAL;		/* Giving up */
+		}
+		tape->failed_pc=NULL;
+		return pc->callback(drive);
+	}
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 2)
+		printk (KERN_INFO "ide-tape: Retry number - %d\n",pc->retries);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	pc->retries++;
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->current_position=pc->buffer;
+	bcount.all=pc->request_transfer;				/* Request to transfer the entire buffer at once */
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
+		printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n");
+		(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+	}
+	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+	if (IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);			/* Use PIO/DMA */
+	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (dma_ok) {						/* Begin DMA, if necessary */
+		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+	}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+	if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) {
+		ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL);
+		OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
+		return ide_started;
+	} else {
+		OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
+		return idetape_transfer_pc(drive);
+	}
+}
+
+/*
+ *	General packet command callback function.
+ */
+static ide_startstop_t idetape_pc_callback (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_pc_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive));
+	return ide_stopped;
+}
+
+/*
+ *	A mode sense command is used to "sense" tape parameters.
+ */
+static void idetape_create_mode_sense_cmd (idetape_pc_t *pc, byte page_code)
+{
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_MODE_SENSE_CMD;
+	pc->c[1] = 8;				/* DBD = 1 - Don't return block descriptors for now */
+	pc->c[2] = page_code;
+	pc->c[3] = 255;				/* Don't limit the returned information */
+	pc->c[4] = 255;				/* (We will just discard data in that case) */
+	if (page_code == IDETAPE_CAPABILITIES_PAGE)
+		pc->request_transfer = 24;
+	else
+		pc->request_transfer = 50;
+	pc->callback = &idetape_pc_callback;
+}
+
+static ide_startstop_t idetape_onstream_buffer_fill_callback (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	tape->max_frames = tape->pc->buffer[4 + 2];
+	tape->cur_frames = tape->pc->buffer[4 + 3];
+	if (tape->chrdev_direction == idetape_direction_write)
+		tape->tape_head = tape->buffer_head - tape->cur_frames;
+	else
+		tape->tape_head = tape->buffer_head + tape->cur_frames;
+	if (tape->tape_head != tape->last_tape_head) {
+		tape->last_tape_head = tape->tape_head;
+		tape->tape_still_time_begin = jiffies;
+		if (tape->tape_still_time > 200)
+			tape->measure_insert_time = 1;
+	}
+	tape->tape_still_time = (jiffies - tape->tape_still_time_begin) * 1000 / HZ;
+#if USE_IOTRACE
+	IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 1)
+		printk(KERN_INFO "ide-tape: buffer fill callback, %d/%d\n", tape->cur_frames, tape->max_frames);
+#endif
+	idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive));
+	return ide_stopped;
+}
+
+static void idetape_queue_onstream_buffer_fill (ide_drive_t *drive)
+{
+	idetape_pc_t *pc;
+	struct request *rq;
+
+	pc = idetape_next_pc_storage (drive);
+	rq = idetape_next_rq_storage (drive);
+	idetape_create_mode_sense_cmd (pc, 0x33);
+	pc->callback = idetape_onstream_buffer_fill_callback;
+	idetape_queue_pc_head (drive, pc, rq);
+}
+
+static void calculate_speeds(ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int full = 125, empty = 75;
+
+	if (jiffies > tape->controlled_pipeline_head_time + 120 * HZ) {
+		tape->controlled_previous_pipeline_head = tape->controlled_last_pipeline_head;
+		tape->controlled_previous_head_time = tape->controlled_pipeline_head_time;
+		tape->controlled_last_pipeline_head = tape->pipeline_head;
+		tape->controlled_pipeline_head_time = jiffies;
+	}
+	if (jiffies > tape->controlled_pipeline_head_time + 60 * HZ)
+		tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_last_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_pipeline_head_time);
+	else if (jiffies > tape->controlled_previous_head_time)
+		tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_previous_head_time);
+
+	if (tape->nr_pending_stages < tape->max_stages /*- 1 */) { /* -1 for read mode error recovery */
+		if (jiffies > tape->uncontrolled_previous_head_time + 10 * HZ) {
+			tape->uncontrolled_pipeline_head_time = jiffies;
+			tape->uncontrolled_pipeline_head_speed = (tape->pipeline_head - tape->uncontrolled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->uncontrolled_previous_head_time);
+		}
+	} else {
+		tape->uncontrolled_previous_head_time = jiffies;
+		tape->uncontrolled_previous_pipeline_head = tape->pipeline_head;
+		if (jiffies > tape->uncontrolled_pipeline_head_time + 30 * HZ) {
+			tape->uncontrolled_pipeline_head_time = jiffies;
+		}
+	}
+	tape->pipeline_head_speed = IDE_MAX(tape->uncontrolled_pipeline_head_speed, tape->controlled_pipeline_head_speed);
+	if (tape->speed_control == 0) {
+		tape->max_insert_speed = 5000;
+	} else if (tape->speed_control == 1) {
+		if (tape->nr_pending_stages >= tape->max_stages / 2)
+			tape->max_insert_speed = tape->pipeline_head_speed +
+				(1100 - tape->pipeline_head_speed) * 2 * (tape->nr_pending_stages - tape->max_stages / 2) / tape->max_stages;
+		else
+			tape->max_insert_speed = 500 +
+				(tape->pipeline_head_speed - 500) * 2 * tape->nr_pending_stages / tape->max_stages;
+		if (tape->nr_pending_stages >= tape->max_stages * 99 / 100)
+			tape->max_insert_speed = 5000;
+	} else if (tape->speed_control == 2) {
+		tape->max_insert_speed = tape->pipeline_head_speed * empty / 100 +
+			(tape->pipeline_head_speed * full / 100 - tape->pipeline_head_speed * empty / 100) * tape->nr_pending_stages / tape->max_stages;
+	} else
+		tape->max_insert_speed = tape->speed_control;
+	tape->max_insert_speed = IDE_MAX(tape->max_insert_speed, 500);
+}
+
+static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t *pc = tape->pc;
+	idetape_status_reg_t status;
+
+	if (tape->onstream)
+		printk(KERN_INFO "ide-tape: bug: onstream, media_access_finished\n");
+	status.all = GET_STAT();
+	if (status.b.dsc) {
+		if (status.b.check) {					/* Error detected */
+			printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name);
+			return idetape_retry_pc (drive);			/* Retry operation */
+		}
+		pc->error = 0;
+		if (tape->failed_pc == pc)
+			tape->failed_pc = NULL;
+	} else {
+		pc->error = IDETAPE_ERROR_GENERAL;
+		tape->failed_pc = NULL;
+	}
+	return pc->callback (drive);
+}
+
+static ide_startstop_t idetape_rw_callback (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	int blocks = tape->pc->actually_transferred / tape->tape_block_size;
+
+	tape->avg_size += blocks * tape->tape_block_size;
+	tape->insert_size += blocks * tape->tape_block_size;
+	if (tape->insert_size > 1024 * 1024)
+		tape->measure_insert_time = 1;
+	if (tape->measure_insert_time) {
+		tape->measure_insert_time = 0;
+		tape->insert_time = jiffies;
+		tape->insert_size = 0;
+	}
+	if (jiffies > tape->insert_time)
+		tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
+	if (jiffies - tape->avg_time >= HZ) {
+		tape->avg_speed = tape->avg_size * HZ / (jiffies - tape->avg_time) / 1024;
+		tape->avg_size = 0;
+		tape->avg_time = jiffies;
+	}
+
+#if IDETAPE_DEBUG_LOG	
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_rw_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	tape->first_frame_position += blocks;
+	rq->current_nr_sectors -= blocks;
+
+	if (!tape->pc->error)
+		idetape_end_request (1, HWGROUP (drive));
+	else
+		idetape_end_request (tape->pc->error, HWGROUP (drive));
+	return ide_stopped;
+}
+
+static void idetape_create_read_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
+{
+	struct buffer_head *p = bh;
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_READ_CMD;
+	put_unaligned (htonl (length), (unsigned int *) &pc->c[1]);
+	pc->c[1] = 1;
+	pc->callback = &idetape_rw_callback;
+	pc->bh = bh;
+	atomic_set(&bh->b_count, 0);
+	pc->buffer = NULL;
+	if (tape->onstream) {
+		while (p) {
+			atomic_set(&p->b_count, 0);
+			p = p->b_reqnext;
+		}
+	}
+	if (!tape->onstream) {
+		pc->request_transfer = pc->buffer_size = length * tape->tape_block_size;
+		if (pc->request_transfer == tape->stage_size)
+			set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+	} else  {
+		if (length) {
+			pc->request_transfer = pc->buffer_size = 32768 + 512;
+			set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+		} else
+			pc->request_transfer = 0;
+	}
+}
+
+static void idetape_create_read_buffer_cmd(idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
+{
+	int size = 32768;
+
+	struct buffer_head *p = bh;
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_READ_BUFFER_CMD;
+	pc->c[1] = IDETAPE_RETRIEVE_FAULTY_BLOCK;
+	pc->c[7] = size >> 8;
+	pc->c[8] = size & 0xff;
+	pc->callback = &idetape_pc_callback;
+	pc->bh = bh;
+	atomic_set(&bh->b_count, 0);
+	pc->buffer = NULL;
+	while (p) {
+		atomic_set(&p->b_count, 0);
+		p = p->b_reqnext;
+	}
+	pc->request_transfer = pc->buffer_size = size;
+}
+
+static void idetape_create_write_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
+{
+	struct buffer_head *p = bh;
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_WRITE_CMD;
+	put_unaligned (htonl (length), (unsigned int *) &pc->c[1]);
+	pc->c[1] = 1;
+	pc->callback = &idetape_rw_callback;
+	set_bit (PC_WRITING, &pc->flags);
+	if (tape->onstream) {
+		while (p) {
+			atomic_set(&p->b_count, p->b_size);
+			p = p->b_reqnext;
+		}
+	}
+	pc->bh = bh;
+	pc->b_data = bh->b_data;
+	pc->b_count = atomic_read(&bh->b_count);
+	pc->buffer = NULL;
+	if (!tape->onstream) {
+		pc->request_transfer = pc->buffer_size = length * tape->tape_block_size;
+		if (pc->request_transfer == tape->stage_size)
+			set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+	} else  {
+		if (length) {
+			pc->request_transfer = pc->buffer_size = 32768 + 512;
+			set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+		} else
+			pc->request_transfer = 0;
+	}
+}
+
+/*
+ *	idetape_do_request is our request handling function.	
+ */
+static ide_startstop_t idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t *pc;
+	struct request *postponed_rq = tape->postponed_rq;
+	idetape_status_reg_t status;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 5)
+		printk (KERN_INFO "ide-tape: rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	if (tape->debug_level >= 2)
+		printk (KERN_INFO "ide-tape: sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (!IDETAPE_RQ_CMD (rq->cmd)) {
+		/*
+		 *	We do not support buffer cache originated requests.
+		 */
+		printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue (%d)\n", drive->name, rq->cmd);
+		ide_end_request (0,HWGROUP (drive));			/* Let the common code handle it */
+		return ide_stopped;
+	}
+
+	/*
+	 *	Retry a failed packet command
+	 */
+	if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+		return idetape_issue_packet_command (drive, tape->failed_pc);
+	}
+#if IDETAPE_DEBUG_BUGS
+	if (postponed_rq != NULL)
+		if (rq != postponed_rq) {
+			printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
+			idetape_end_request (0,HWGROUP (drive));
+			return ide_stopped;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+
+	tape->postponed_rq = NULL;
+
+	/*
+	 *	If the tape is still busy, postpone our request and service
+	 *	the other device meanwhile.
+	 */
+	status.all = GET_STAT();
+
+	/*
+	 * The OnStream tape drive doesn't support DSC. Assume
+	 * that DSC is always set.
+	 */
+	if (tape->onstream)
+		status.b.dsc = 1;
+	if (!drive->dsc_overlap && rq->cmd != IDETAPE_PC_RQ2)
+		set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
+
+	/*
+	 * For the OnStream tape, check the current status of the tape
+	 * internal buffer using data gathered from the buffer fill
+	 * mode page, and postpone our request, effectively "disconnecting"
+	 * from the IDE bus, in case the buffer is full (writing) or
+	 * empty (reading), and there is a danger that our request will
+	 * hold the IDE bus during actual media access.
+	 */
+	if (tape->tape_still_time > 100 && tape->tape_still_time < 200)
+		tape->measure_insert_time = 1;
+	if (tape->req_buffer_fill && (rq->cmd == IDETAPE_WRITE_RQ || rq->cmd == IDETAPE_READ_RQ)) {
+		tape->req_buffer_fill = 0;
+		tape->writes_since_buffer_fill = 0;
+		tape->reads_since_buffer_fill = 0;
+		tape->last_buffer_fill = jiffies;
+		idetape_queue_onstream_buffer_fill(drive);
+		if (jiffies > tape->insert_time)
+			tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
+		return ide_stopped;
+	}
+	if (jiffies > tape->insert_time)
+		tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
+	calculate_speeds(drive);
+	if (tape->onstream && tape->max_frames &&
+	   ((rq->cmd == IDETAPE_WRITE_RQ && (tape->cur_frames == tape->max_frames || (tape->speed_control && tape->cur_frames > 5 && (tape->insert_speed > tape->max_insert_speed || (0 /* tape->cur_frames > 30 && tape->tape_still_time > 200 */))))) ||
+	    (rq->cmd == IDETAPE_READ_RQ && (tape->cur_frames == 0 || (tape->speed_control && (tape->cur_frames < tape->max_frames - 5) && tape->insert_speed > tape->max_insert_speed)) && rq->nr_sectors))) {
+#if IDETAPE_DEBUG_LOG
+		if (tape->debug_level >= 4)
+			printk(KERN_INFO "ide-tape: postponing request, cmd %d, cur %d, max %d\n",
+				rq->cmd, tape->cur_frames, tape->max_frames);
+#endif
+		if (tape->postpone_cnt++ < 500) {
+			status.b.dsc = 0;
+			tape->req_buffer_fill = 1;
+		}
+#if ONSTREAM_DEBUG
+		else if (tape->debug_level >= 4) 
+			printk(KERN_INFO "ide-tape: %s: postpone_cnt %d\n", tape->name, tape->postpone_cnt);
+#endif
+	}
+	if (!test_and_clear_bit (IDETAPE_IGNORE_DSC, &tape->flags) && !status.b.dsc) {
+		if (postponed_rq == NULL) {
+			tape->dsc_polling_start = jiffies;
+			tape->dsc_polling_frequency = tape->best_dsc_rw_frequency;
+			tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT;
+		} else if ((signed long) (jiffies - tape->dsc_timeout) > 0) {
+			printk (KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name);
+			if (rq->cmd == IDETAPE_PC_RQ2) {
+				idetape_media_access_finished (drive);
+				return ide_stopped;
+			} else {
+				return ide_do_reset (drive);
+			}
+		} else if (jiffies - tape->dsc_polling_start > IDETAPE_DSC_MA_THRESHOLD)
+			tape->dsc_polling_frequency = IDETAPE_DSC_MA_SLOW;
+		idetape_postpone_request (drive);
+		return ide_stopped;
+	}
+	switch (rq->cmd) {
+		case IDETAPE_READ_RQ:
+			tape->buffer_head++;
+#if USE_IOTRACE
+			IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+			tape->postpone_cnt = 0;
+			tape->reads_since_buffer_fill++;
+			if (tape->onstream) {
+				if (tape->cur_frames - tape->reads_since_buffer_fill <= 0)
+					tape->req_buffer_fill = 1;
+				if (jiffies > tape->last_buffer_fill + 5 * HZ / 100)
+					tape->req_buffer_fill = 1;
+			}
+			pc=idetape_next_pc_storage (drive);
+			idetape_create_read_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
+			break;
+		case IDETAPE_WRITE_RQ:
+			tape->buffer_head++;
+#if USE_IOTRACE
+			IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+			tape->postpone_cnt = 0;
+			tape->writes_since_buffer_fill++;
+			if (tape->onstream) {
+				if (tape->cur_frames + tape->writes_since_buffer_fill >= tape->max_frames)
+					tape->req_buffer_fill = 1;
+				if (jiffies > tape->last_buffer_fill + 5 * HZ / 100)
+					tape->req_buffer_fill = 1;
+				calculate_speeds(drive);
+			}
+			pc=idetape_next_pc_storage (drive);
+			idetape_create_write_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
+			break;
+		case IDETAPE_READ_BUFFER_RQ:
+			tape->postpone_cnt = 0;
+			pc=idetape_next_pc_storage (drive);
+			idetape_create_read_buffer_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
+			break;
+		case IDETAPE_ABORTED_WRITE_RQ:
+			rq->cmd = IDETAPE_WRITE_RQ;
+			idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive));
+			return ide_stopped;
+		case IDETAPE_ABORTED_READ_RQ:
+#if IDETAPE_DEBUG_LOG
+			if (tape->debug_level >= 2)
+				printk(KERN_INFO "ide-tape: %s: detected aborted read rq\n", tape->name);
+#endif
+			rq->cmd = IDETAPE_READ_RQ;
+			idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive));
+			return ide_stopped;
+		case IDETAPE_PC_RQ1:
+			pc=(idetape_pc_t *) rq->buffer;
+			rq->cmd = IDETAPE_PC_RQ2;
+			break;
+		case IDETAPE_PC_RQ2:
+			idetape_media_access_finished (drive);
+			return ide_stopped;
+		default:
+			printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n");
+			idetape_end_request (0,HWGROUP (drive));
+			return ide_stopped;
+	}
+	return idetape_issue_packet_command (drive, pc);
+}
+
+/*
+ *	Pipeline related functions
+ */
+static inline int idetape_pipeline_active (idetape_tape_t *tape)
+{
+	int rc1, rc2;
+
+	rc1 = test_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
+	rc2 = (tape->active_data_request != NULL);
+	return rc1;
+}
+
+/*
+ *	idetape_kmalloc_stage uses __get_free_page to allocate a pipeline
+ *	stage, along with all the necessary small buffers which together make
+ *	a buffer of size tape->stage_size (or a bit more). We attempt to
+ *	combine sequential pages as much as possible.
+ *
+ *	Returns a pointer to the new allocated stage, or NULL if we
+ *	can't (or don't want to) allocate a stage.
+ *
+ *	Pipeline stages are optional and are used to increase performance.
+ *	If we can't allocate them, we'll manage without them.
+ */
+static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape, int full, int clear)
+{
+	idetape_stage_t *stage;
+	struct buffer_head *prev_bh, *bh;
+	int pages = tape->pages_per_stage;
+	char *b_data;
+
+	if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL)
+		return NULL;
+	stage->next = NULL;
+
+	bh = stage->bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL);
+	if (bh == NULL)
+		goto abort;
+	bh->b_reqnext = NULL;
+	if ((bh->b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL)
+		goto abort;
+	if (clear)
+		memset(bh->b_data, 0, PAGE_SIZE);
+	bh->b_size = PAGE_SIZE;
+	atomic_set(&bh->b_count, full ? bh->b_size : 0);
+	set_bit (BH_Lock, &bh->b_state);
+
+	while (--pages) {
+		if ((b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL)
+			goto abort;
+		if (clear)
+			memset(b_data, 0, PAGE_SIZE);
+		if (bh->b_data == b_data + PAGE_SIZE) {
+			bh->b_size += PAGE_SIZE;
+			bh->b_data -= PAGE_SIZE;
+			if (full)
+				atomic_add(PAGE_SIZE, &bh->b_count);
+			continue;
+		}
+		if (b_data == bh->b_data + bh->b_size) {
+			bh->b_size += PAGE_SIZE;
+			if (full)
+				atomic_add(PAGE_SIZE, &bh->b_count);
+			continue;
+		}
+		prev_bh = bh;
+		if ((bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL)) == NULL) {
+			free_page ((unsigned long) b_data);
+			goto abort;
+		}
+		bh->b_reqnext = NULL;
+		bh->b_data = b_data;
+		bh->b_size = PAGE_SIZE;
+		atomic_set(&bh->b_count, full ? bh->b_size : 0);
+		set_bit (BH_Lock, &bh->b_state);
+		prev_bh->b_reqnext = bh;
+	}
+	bh->b_size -= tape->excess_bh_size;
+	if (full)
+		atomic_sub(tape->excess_bh_size, &bh->b_count);
+	if (tape->onstream)
+		stage->aux = (os_aux_t *) (bh->b_data + bh->b_size - OS_AUX_SIZE);
+	return stage;
+abort:
+	__idetape_kfree_stage (stage);
+	return NULL;
+}
+
+static idetape_stage_t *idetape_kmalloc_stage (idetape_tape_t *tape)
+{
+	idetape_stage_t *cache_stage = tape->cache_stage;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_kmalloc_stage\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (tape->nr_stages >= tape->max_stages)
+		return NULL;
+	if (cache_stage != NULL) {
+		tape->cache_stage = NULL;
+		return cache_stage;
+	}
+	return __idetape_kmalloc_stage (tape, 0, 0);
+}
+
+static void idetape_copy_stage_from_user (idetape_tape_t *tape, idetape_stage_t *stage, const char *buf, int n)
+{
+	struct buffer_head *bh = tape->bh;
+	int count;
+
+	while (n) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh == NULL) {
+			printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_from_user\n");
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+		count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), n);
+		copy_from_user (bh->b_data + atomic_read(&bh->b_count), buf, count);
+		n -= count; atomic_add(count, &bh->b_count); buf += count;
+		if (atomic_read(&bh->b_count) == bh->b_size) {
+			bh = bh->b_reqnext;
+			if (bh)
+				atomic_set(&bh->b_count, 0);
+		}
+	}
+	tape->bh = bh;
+}
+
+static void idetape_copy_stage_to_user (idetape_tape_t *tape, char *buf, idetape_stage_t *stage, int n)
+{
+	struct buffer_head *bh = tape->bh;
+	int count;
+
+	while (n) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh == NULL) {
+			printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_to_user\n");
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+		count = IDE_MIN (tape->b_count, n);
+		copy_to_user (buf, tape->b_data, count);
+		n -= count; tape->b_data += count; tape->b_count -= count; buf += count;
+		if (!tape->b_count) {
+			tape->bh = bh = bh->b_reqnext;
+			if (bh) {
+				tape->b_data = bh->b_data;
+				tape->b_count = atomic_read(&bh->b_count);
+			}
+		}
+	}
+}
+
+static void idetape_init_merge_stage (idetape_tape_t *tape)
+{
+	struct buffer_head *bh = tape->merge_stage->bh;
+	
+	tape->bh = bh;
+	if (tape->chrdev_direction == idetape_direction_write)
+		atomic_set(&bh->b_count, 0);
+	else {
+		tape->b_data = bh->b_data;
+		tape->b_count = atomic_read(&bh->b_count);
+	}
+}
+
+static void idetape_switch_buffers (idetape_tape_t *tape, idetape_stage_t *stage)
+{
+	struct buffer_head *tmp;
+	os_aux_t *tmp_aux;
+
+	tmp = stage->bh; tmp_aux = stage->aux;
+	stage->bh = tape->merge_stage->bh; stage->aux = tape->merge_stage->aux;
+	tape->merge_stage->bh = tmp; tape->merge_stage->aux = tmp_aux;
+	idetape_init_merge_stage (tape);
+}
+
+/*
+ *	idetape_add_stage_tail adds a new stage at the end of the pipeline.
+ */
+static void idetape_add_stage_tail (ide_drive_t *drive,idetape_stage_t *stage)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	unsigned long flags;
+	
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_add_stage_tail\n");
+#endif /* IDETAPE_DEBUG_LOG */
+	spin_lock_irqsave(&tape->spinlock, flags);
+	stage->next=NULL;
+	if (tape->last_stage != NULL)
+		tape->last_stage->next=stage;
+	else
+		tape->first_stage=tape->next_stage=stage;
+	tape->last_stage=stage;
+	if (tape->next_stage == NULL)
+		tape->next_stage=tape->last_stage;
+	tape->nr_stages++;
+	tape->nr_pending_stages++;
+	spin_unlock_irqrestore(&tape->spinlock, flags);
+}
+
+/*
+ * Initialize the OnStream AUX
+ */
+static void idetape_init_stage (ide_drive_t *drive, idetape_stage_t *stage, int frame_type, int logical_blk_num)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	os_aux_t *aux = stage->aux;
+	os_partition_t *par = &aux->partition;
+	os_dat_t *dat = &aux->dat;
+
+	if (!tape->onstream || tape->raw)
+		return;
+	memset(aux, 0, sizeof(*aux));
+	aux->format_id = htonl(0);
+	memcpy(aux->application_sig, "LIN3", 4);
+	aux->hdwr = htonl(0);
+	aux->frame_type = frame_type;
+
+	if (frame_type == OS_FRAME_TYPE_HEADER) {
+		aux->update_frame_cntr = htonl(tape->update_frame_cntr);
+		par->partition_num = OS_CONFIG_PARTITION;
+		par->par_desc_ver = OS_PARTITION_VERSION;
+		par->wrt_pass_cntr = htons(0xffff);
+		par->first_frame_addr = htonl(0);
+		par->last_frame_addr = htonl(0xbb7);
+	} else {
+		aux->update_frame_cntr = htonl(0);
+		par->partition_num = OS_DATA_PARTITION;
+		par->par_desc_ver = OS_PARTITION_VERSION;
+		par->wrt_pass_cntr = htons(tape->wrt_pass_cntr);
+		par->first_frame_addr = htonl(0x14);
+		par->last_frame_addr = htonl(19239 * 24);
+	}
+	if (frame_type != OS_FRAME_TYPE_HEADER) {
+		aux->frame_seq_num = htonl(logical_blk_num);
+		aux->logical_blk_num_high = htonl(0);
+		aux->logical_blk_num = htonl(logical_blk_num);
+	} else {
+		aux->frame_seq_num = htonl(0);
+		aux->logical_blk_num_high = htonl(0);
+		aux->logical_blk_num = htonl(0);
+	}
+
+	if (frame_type != OS_FRAME_TYPE_HEADER) {
+		dat->dat_sz = 8;
+		dat->reserved1 = 0;
+		dat->entry_cnt = 1;
+		dat->reserved3 = 0;
+		if (frame_type == OS_FRAME_TYPE_DATA)
+			dat->dat_list[0].blk_sz = htonl(32 * 1024);
+		else
+			dat->dat_list[0].blk_sz = 0;
+		dat->dat_list[0].blk_cnt = htons(1);
+		if (frame_type == OS_FRAME_TYPE_MARKER)
+			dat->dat_list[0].flags = OS_DAT_FLAGS_MARK;
+		else
+			dat->dat_list[0].flags = OS_DAT_FLAGS_DATA;
+		dat->dat_list[0].reserved = 0;
+	} else
+		aux->next_mark_addr = htonl(tape->first_mark_addr);
+	aux->filemark_cnt = ntohl(tape->filemark_cnt);
+	aux->phys_fm = ntohl(0xffffffff);
+	aux->last_mark_addr = ntohl(tape->last_mark_addr);
+}
+
+/*
+ *	idetape_wait_for_request installs a semaphore in a pending request
+ *	and sleeps until it is serviced.
+ *
+ *	The caller should ensure that the request will not be serviced
+ *	before we install the semaphore (usually by disabling interrupts).
+ */
+static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq)
+{
+	DECLARE_MUTEX_LOCKED(sem);
+	idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_BUGS
+	if (rq == NULL || !IDETAPE_RQ_CMD (rq->cmd)) {
+		printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+	rq->sem = &sem;
+	tape->sem = &sem;
+	spin_unlock(&tape->spinlock);
+	down(&sem);
+	rq->sem = NULL;
+	tape->sem = NULL;
+	spin_lock_irq(&tape->spinlock);
+}
+
+static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_read_position_result_t *result;
+	
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_read_position_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (!tape->pc->error) {
+		result = (idetape_read_position_result_t *) tape->pc->buffer;
+#if IDETAPE_DEBUG_LOG
+		if (tape->debug_level >= 2)
+			printk (KERN_INFO "ide-tape: BOP - %s\n",result->bop ? "Yes":"No");
+		if (tape->debug_level >= 2)
+			printk (KERN_INFO "ide-tape: EOP - %s\n",result->eop ? "Yes":"No");
+#endif /* IDETAPE_DEBUG_LOG */
+		if (result->bpu) {
+			printk (KERN_INFO "ide-tape: Block location is unknown to the tape\n");
+			clear_bit (IDETAPE_ADDRESS_VALID, &tape->flags);
+			idetape_end_request (0,HWGROUP (drive));
+		} else {
+#if IDETAPE_DEBUG_LOG
+			if (tape->debug_level >= 2)
+				printk (KERN_INFO "ide-tape: Block Location - %u\n", ntohl (result->first_block));
+#endif /* IDETAPE_DEBUG_LOG */
+			tape->partition = result->partition;
+			tape->first_frame_position = ntohl (result->first_block);
+			tape->last_frame_position = ntohl (result->last_block);
+			tape->blocks_in_buffer = result->blocks_in_buffer[2];
+			set_bit (IDETAPE_ADDRESS_VALID, &tape->flags);
+			idetape_end_request (1,HWGROUP (drive));
+		}
+	} else {
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return ide_stopped;
+}
+
+/*
+ *	idetape_create_write_filemark_cmd will:
+ *
+ *		1.	Write a filemark if write_filemark=1.
+ *		2.	Flush the device buffers without writing a filemark
+ *			if write_filemark=0.
+ *
+ */
+static void idetape_create_write_filemark_cmd (ide_drive_t *drive, idetape_pc_t *pc,int write_filemark)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD;
+	if (tape->onstream)
+		pc->c[1] = 1;
+	pc->c[4] = write_filemark;
+	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+	pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_test_unit_ready_cmd(idetape_pc_t *pc)
+{
+	idetape_init_pc(pc);
+	pc->c[0] = IDETAPE_TEST_UNIT_READY_CMD;
+	pc->callback = &idetape_pc_callback;
+}
+
+/*
+ *	idetape_queue_pc_tail is based on the following functions:
+ *
+ *	ide_do_drive_cmd from ide.c
+ *	cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c
+ *
+ *	We add a special packet command request to the tail of the request queue,
+ *	and wait for it to be serviced.
+ *
+ *	This is not to be called from within the request handling part
+ *	of the driver ! We allocate here data in the stack, and it is valid
+ *	until the request is finished. This is not the case for the bottom
+ *	part of the driver, where we are always leaving the functions to wait
+ *	for an interrupt or a timer event.
+ *
+ *	From the bottom part of the driver, we should allocate safe memory
+ *	using idetape_next_pc_storage and idetape_next_rq_storage, and add
+ *	the request to the request list without waiting for it to be serviced !
+ *	In that case, we usually use idetape_queue_pc_head.
+ */
+static int __idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc)
+{
+	struct request rq;
+
+	ide_init_drive_cmd (&rq);
+	rq.buffer = (char *) pc;
+	rq.cmd = IDETAPE_PC_RQ1;
+	return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+static void idetape_create_load_unload_cmd (ide_drive_t *drive, idetape_pc_t *pc,int cmd)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_LOAD_UNLOAD_CMD;
+	pc->c[4] = cmd;
+	if (tape->onstream)
+		pc->c[1] = 1;
+	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+	pc->callback = &idetape_pc_callback;
+}
+
+static int idetape_wait_ready (ide_drive_t *drive, unsigned long long timeout)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t pc;
+
+	/*
+	 * Wait for the tape to become ready
+	 */
+	timeout += jiffies;
+	while (jiffies < timeout) {
+		idetape_create_test_unit_ready_cmd(&pc);
+		if (!__idetape_queue_pc_tail(drive, &pc))
+			return 0;
+		if (tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2) {
+			idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK);
+			__idetape_queue_pc_tail(drive,&pc);
+			idetape_create_test_unit_ready_cmd(&pc);
+			if (!__idetape_queue_pc_tail(drive, &pc))
+				return 0;
+		}
+		if (!(tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8)))
+			break;
+		current->state = TASK_INTERRUPTIBLE;
+  		schedule_timeout(HZ / 10);
+	}
+	return -EIO;
+}
+
+static int idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int rc;
+
+	rc = __idetape_queue_pc_tail(drive, pc);
+	if (rc)	return rc;
+	if (tape->onstream && test_bit(PC_WAIT_FOR_DSC, &pc->flags))
+		rc = idetape_wait_ready(drive, 60 * 10 * HZ);   /* AJN-4: Changed from 5 to 10 minutes;
+                          because retension takes approx. 8:20 with Onstream 30GB tape */
+	return rc;
+}
+
+static int idetape_flush_tape_buffers (ide_drive_t *drive)
+{
+	idetape_pc_t pc;
+	int rc;
+
+	idetape_create_write_filemark_cmd(drive, &pc, 0);
+	if ((rc = idetape_queue_pc_tail (drive,&pc)))
+		return rc;
+	idetape_wait_ready(drive, 60 * 5 * HZ);
+	return 0;
+}
+
+static void idetape_create_read_position_cmd (idetape_pc_t *pc)
+{
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_READ_POSITION_CMD;
+	pc->request_transfer = 20;
+	pc->callback = &idetape_read_position_callback;
+}
+
+static int idetape_read_position (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t pc;
+	int position;
+
+#ifdef NO_LONGER_REQUIRED
+	idetape_flush_tape_buffers(drive);
+#endif
+	idetape_create_read_position_cmd(&pc);
+	if (idetape_queue_pc_tail (drive,&pc))
+		return -1;
+	position = tape->first_frame_position;
+#ifdef NO_LONGER_REQUIRED
+	if (tape->onstream) {
+		if ((position != tape->last_frame_position - tape->blocks_in_buffer) &&
+		    (position != tape->last_frame_position + tape->blocks_in_buffer)) {
+			if (tape->blocks_in_buffer == 0) {
+				printk("ide-tape: %s: correcting read position %d, %d, %d\n", tape->name, position, tape->last_frame_position, tape->blocks_in_buffer);
+				position = tape->last_frame_position;
+				tape->first_frame_position = position;
+			}
+		}
+	}
+#endif
+	return position;
+}
+
+static void idetape_create_locate_cmd (ide_drive_t *drive, idetape_pc_t *pc, unsigned int block, byte partition, int skip)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_LOCATE_CMD;
+	if (tape->onstream)
+		pc->c[1] = 1;
+	else
+		pc->c[1] = 2;
+	put_unaligned (htonl (block), (unsigned int *) &pc->c[3]);
+	pc->c[8] = partition;
+	if (tape->onstream)
+		pc->c[9] = skip << 7;
+	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+	pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_prevent_cmd (ide_drive_t *drive, idetape_pc_t *pc, int prevent)
+{
+	idetape_init_pc(pc);
+	pc->c[0] = IDETAPE_PREVENT_CMD;
+	pc->c[4] = prevent;
+	pc->callback = &idetape_pc_callback;
+}
+
+static int __idetape_discard_read_pipeline (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	unsigned long flags;
+	int cnt;
+
+	if (tape->chrdev_direction != idetape_direction_read)
+		return 0;
+	tape->merge_stage_size = 0;
+	if (tape->merge_stage != NULL) {
+		__idetape_kfree_stage (tape->merge_stage);
+		tape->merge_stage = NULL;
+	}
+	tape->chrdev_direction = idetape_direction_none;
+	
+	if (tape->first_stage == NULL)
+		return 0;
+
+	spin_lock_irqsave(&tape->spinlock, flags);
+	tape->next_stage = NULL;
+	if (idetape_pipeline_active (tape))
+		idetape_wait_for_request(drive, tape->active_data_request);
+	spin_unlock_irqrestore(&tape->spinlock, flags);
+
+	cnt = tape->nr_stages - tape->nr_pending_stages;
+	while (tape->first_stage != NULL)
+		idetape_remove_stage_head (drive);
+	tape->nr_pending_stages = 0;
+	tape->max_stages = tape->min_pipeline;
+	return cnt;
+}
+
+/*
+ *	idetape_position_tape positions the tape to the requested block
+ *	using the LOCATE packet command. A READ POSITION command is then
+ *	issued to check where we are positioned.
+ *
+ *	Like all higher level operations, we queue the commands at the tail
+ *	of the request queue and wait for their completion.
+ *	
+ */
+static int idetape_position_tape (ide_drive_t *drive, unsigned int block, byte partition, int skip)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int retval;
+	idetape_pc_t pc;
+
+	if (tape->chrdev_direction == idetape_direction_read)
+		__idetape_discard_read_pipeline(drive);
+	idetape_wait_ready(drive, 60 * 5 * HZ);
+	idetape_create_locate_cmd (drive, &pc, block, partition, skip);
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval) return (retval);
+
+	idetape_create_read_position_cmd (&pc);
+	return (idetape_queue_pc_tail (drive,&pc));
+}
+
+static void idetape_discard_read_pipeline (ide_drive_t *drive, int restore_position)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int cnt;
+	int seek, position;
+
+	cnt = __idetape_discard_read_pipeline(drive);
+	if (restore_position) {
+		position = idetape_read_position(drive);
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 2)
+			printk(KERN_INFO "ide-tape: address %u, nr_stages %d\n", position, cnt);
+#endif
+		seek = position > cnt ? position - cnt : 0;
+		if (idetape_position_tape(drive, seek, 0, 0)) {
+			printk(KERN_INFO "ide-tape: %s: position_tape failed in discard_pipeline()\n", tape->name);
+			return;
+		}
+	}
+}
+
+static void idetape_update_stats (ide_drive_t *drive)
+{
+	idetape_pc_t pc;
+
+	idetape_create_mode_sense_cmd (&pc, 0x33);
+	pc.callback = idetape_onstream_buffer_fill_callback;
+	(void) idetape_queue_pc_tail(drive, &pc);
+}
+
+/*
+ *	idetape_queue_rw_tail generates a read/write request for the block
+ *	device interface and wait for it to be serviced.
+ */
+static int idetape_queue_rw_tail (ide_drive_t *drive, int cmd, int blocks, struct buffer_head *bh)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	struct request rq;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 2)
+		printk (KERN_INFO "ide-tape: idetape_queue_rw_tail: cmd=%d\n",cmd);
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (idetape_pipeline_active (tape)) {
+		printk (KERN_ERR "ide-tape: bug: the pipeline is active in idetape_queue_rw_tail\n");
+		return (0);
+	}
+#endif /* IDETAPE_DEBUG_BUGS */	
+
+	ide_init_drive_cmd (&rq);
+	rq.bh = bh;
+	rq.cmd = cmd;
+	rq.sector = tape->first_frame_position;
+	rq.nr_sectors = rq.current_nr_sectors = blocks;
+	if (tape->onstream)
+		tape->postpone_cnt = 600;
+	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
+
+	if (cmd != IDETAPE_READ_RQ && cmd != IDETAPE_WRITE_RQ)
+		return 0;
+
+	if (tape->merge_stage)
+		idetape_init_merge_stage (tape);
+	if (rq.errors == IDETAPE_ERROR_GENERAL)
+		return -EIO;
+	return (tape->tape_block_size * (blocks-rq.current_nr_sectors));
+}
+
+/*
+ * Read back the drive's internal buffer contents, as a part
+ * of the write error recovery mechanism for old OnStream
+ * firmware revisions.
+ */
+static void idetape_onstream_read_back_buffer (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int frames, i, logical_blk_num;
+	idetape_stage_t *stage, *first = NULL, *last = NULL;
+	os_aux_t *aux;
+	struct request *rq;
+	unsigned char *p;
+	unsigned long flags;
+
+	idetape_update_stats(drive);
+	frames = tape->cur_frames;
+	logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num) - frames;
+	printk(KERN_INFO "ide-tape: %s: reading back %d frames from the drive's internal buffer\n", tape->name, frames);
+	for (i = 0; i < frames; i++) {
+		stage = __idetape_kmalloc_stage(tape, 0, 0);
+		if (!first)
+			first = stage;
+		aux = stage->aux;
+		p = stage->bh->b_data;
+		idetape_queue_rw_tail(drive, IDETAPE_READ_BUFFER_RQ, tape->capabilities.ctl, stage->bh);
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 2)
+			printk(KERN_INFO "ide-tape: %s: read back logical block %d, data %x %x %x %x\n", tape->name, logical_blk_num, *p++, *p++, *p++, *p++);
+#endif
+		rq = &stage->rq;
+		ide_init_drive_cmd (rq);
+		rq->cmd = IDETAPE_WRITE_RQ;
+		rq->sector = tape->first_frame_position;
+		rq->nr_sectors = rq->current_nr_sectors = tape->capabilities.ctl;
+		idetape_init_stage(drive, stage, OS_FRAME_TYPE_DATA, logical_blk_num++);
+		stage->next = NULL;
+		if (last)
+			last->next = stage;
+		last = stage;
+	}
+	if (frames) {
+		spin_lock_irqsave(&tape->spinlock, flags);
+		last->next = tape->first_stage;
+		tape->next_stage = tape->first_stage = first;
+		tape->nr_stages += frames;
+		tape->nr_pending_stages += frames;
+		spin_unlock_irqrestore(&tape->spinlock, flags);
+	}
+	idetape_update_stats(drive);
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 2)
+		printk(KERN_INFO "ide-tape: %s: frames left in buffer: %d\n", tape->name, tape->cur_frames);
+#endif
+}
+
+/*
+ * Error recovery algorithm for the OnStream tape.
+ */
+static void idetape_onstream_write_error_recovery (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	unsigned int block;
+
+	if (tape->onstream_write_error == 1) {
+		printk(KERN_ERR "ide-tape: %s: detected physical bad block at %u\n", tape->name, ntohl(tape->sense.information));
+		block = ntohl(tape->sense.information) + 80;
+		idetape_update_stats(drive);
+		printk(KERN_ERR "ide-tape: %s: relocating %d buffered logical blocks to physical block %u\n", tape->name, tape->cur_frames, block);
+		idetape_update_stats(drive);
+		if (tape->firmware_revision_num >= 106)
+			idetape_position_tape(drive, block, 0, 1);
+		else {
+			idetape_onstream_read_back_buffer(drive);
+			idetape_position_tape(drive, block, 0, 0);
+		}
+		idetape_read_position(drive);
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 1)
+			printk(KERN_ERR "ide-tape: %s: positioning complete, cur_frames %d, pos %d, tape pos %d\n", tape->name, tape->cur_frames, tape->first_frame_position, tape->last_frame_position);
+#endif
+	} else if (tape->onstream_write_error == 2) {
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 1)
+			printk(KERN_INFO "ide-tape: %s: skipping over config partition\n", tape->name);
+#endif
+		idetape_flush_tape_buffers(drive);
+		block = idetape_read_position(drive);
+		if (block != 0xba4) 
+			printk(KERN_ERR "ide-tape: warning, current position %d, expected %d\n", block, 0xba4);
+		idetape_position_tape(drive, 0xbb8, 0, 0);
+	}
+	tape->onstream_write_error = 0;
+}
+
+/*
+ *	idetape_insert_pipeline_into_queue is used to start servicing the
+ *	pipeline stages, starting from tape->next_stage.
+ */
+static void idetape_insert_pipeline_into_queue (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	if (tape->next_stage == NULL)
+		return;
+	if (!idetape_pipeline_active (tape)) {
+		if (tape->onstream_write_error)
+			idetape_onstream_write_error_recovery(drive);
+		set_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
+		idetape_active_next_stage (drive);
+		(void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
+	}
+}
+
+static void idetape_create_inquiry_cmd (idetape_pc_t *pc)
+{
+	idetape_init_pc(pc);
+	pc->c[0] = IDETAPE_INQUIRY_CMD;
+	pc->c[4] = pc->request_transfer = 254;
+	pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_rewind_cmd (ide_drive_t *drive, idetape_pc_t *pc)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_REWIND_CMD;
+	if (tape->onstream)
+		pc->c[1] = 1;
+	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+	pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_mode_select_cmd (idetape_pc_t *pc, int length)
+{
+	idetape_init_pc (pc);
+	set_bit (PC_WRITING, &pc->flags);
+	pc->c[0] = IDETAPE_MODE_SELECT_CMD;
+	pc->c[1] = 0x10;
+	put_unaligned (htons(length), (unsigned short *) &pc->c[3]);
+	pc->request_transfer = 255;
+	pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_erase_cmd (idetape_pc_t *pc)
+{
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_ERASE_CMD;
+	pc->c[1] = 1;
+	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+	pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_space_cmd (idetape_pc_t *pc,int count,byte cmd)
+{
+	idetape_init_pc (pc);
+	pc->c[0] = IDETAPE_SPACE_CMD;
+	put_unaligned (htonl (count), (unsigned int *) &pc->c[1]);
+	pc->c[1] = cmd;
+	set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+	pc->callback = &idetape_pc_callback;
+}
+
+/*
+ * Verify that we have the correct tape frame
+ */
+static int idetape_verify_stage (ide_drive_t *drive, idetape_stage_t *stage, int logical_blk_num, int quiet)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	os_aux_t *aux = stage->aux;
+	os_partition_t *par = &aux->partition;
+	struct request *rq = &stage->rq;
+	struct buffer_head *bh;
+
+	if (!tape->onstream)
+		return 1;
+	if (tape->raw) {
+		if (rq->errors) {
+			bh = stage->bh;
+			while (bh) {
+				memset(bh->b_data, 0, bh->b_size);
+				bh = bh->b_reqnext;
+			}
+			strcpy(stage->bh->b_data, "READ ERROR ON FRAME");
+		}
+		return 1;
+	}
+	if (rq->errors == IDETAPE_ERROR_GENERAL) {
+		printk(KERN_INFO "ide-tape: %s: skipping frame, read error\n", tape->name);
+		return 0;
+	}
+	if (rq->errors == IDETAPE_ERROR_EOD) {
+		printk(KERN_INFO "ide-tape: %s: skipping frame, eod\n", tape->name);
+		return 0;
+	}
+	if (ntohl(aux->format_id) != 0) {
+		printk(KERN_INFO "ide-tape: %s: skipping frame, format_id %u\n", tape->name, ntohl(aux->format_id));
+		return 0;
+	}
+	if (memcmp(aux->application_sig, tape->application_sig, 4) != 0) {
+		printk(KERN_INFO "ide-tape: %s: skipping frame, incorrect application signature\n", tape->name);
+		return 0;
+	}
+	if (aux->frame_type != OS_FRAME_TYPE_DATA &&
+	    aux->frame_type != OS_FRAME_TYPE_EOD &&
+	    aux->frame_type != OS_FRAME_TYPE_MARKER) {
+		printk(KERN_INFO "ide-tape: %s: skipping frame, frame type %x\n", tape->name, aux->frame_type);
+		return 0;
+	}
+	if (par->partition_num != OS_DATA_PARTITION) {
+		if (!tape->linux_media || tape->linux_media_version != 2) {
+			printk(KERN_INFO "ide-tape: %s: skipping frame, partition num %d\n", tape->name, par->partition_num);
+			return 0;
+		}
+	}
+	if (par->par_desc_ver != OS_PARTITION_VERSION) {
+		printk(KERN_INFO "ide-tape: %s: skipping frame, partition version %d\n", tape->name, par->par_desc_ver);
+		return 0;
+	}
+	if (ntohs(par->wrt_pass_cntr) != tape->wrt_pass_cntr) {
+		printk(KERN_INFO "ide-tape: %s: skipping frame, wrt_pass_cntr %d (expected %d)(logical_blk_num %u)\n", tape->name, ntohs(par->wrt_pass_cntr), tape->wrt_pass_cntr, ntohl(aux->logical_blk_num));
+		return 0;
+	}
+	if (aux->frame_seq_num != aux->logical_blk_num) {
+		printk(KERN_INFO "ide-tape: %s: skipping frame, seq != logical\n", tape->name);
+		return 0;
+	}
+	if (logical_blk_num != -1 && ntohl(aux->logical_blk_num) != logical_blk_num) {
+		if (!quiet)
+			printk(KERN_INFO "ide-tape: %s: skipping frame, logical_blk_num %u (expected %d)\n", tape->name, ntohl(aux->logical_blk_num), logical_blk_num);
+		return 0;
+	}
+	if (aux->frame_type == OS_FRAME_TYPE_MARKER) {
+		rq->errors = IDETAPE_ERROR_FILEMARK;
+		rq->current_nr_sectors = rq->nr_sectors;
+	}
+	return 1;
+}
+
+static void idetape_wait_first_stage (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	unsigned long flags;
+
+	if (tape->first_stage == NULL)
+		return;
+	spin_lock_irqsave(&tape->spinlock, flags);
+	if (tape->active_stage == tape->first_stage)
+		idetape_wait_for_request(drive, tape->active_data_request);
+	spin_unlock_irqrestore(&tape->spinlock, flags);
+}
+
+/*
+ *	idetape_add_chrdev_write_request tries to add a character device
+ *	originated write request to our pipeline. In case we don't succeed,
+ *	we revert to non-pipelined operation mode for this request.
+ *
+ *	1.	Try to allocate a new pipeline stage.
+ *	2.	If we can't, wait for more and more requests to be serviced
+ *		and try again each time.
+ *	3.	If we still can't allocate a stage, fallback to
+ *		non-pipelined operation mode for this request.
+ */
+static int idetape_add_chrdev_write_request (ide_drive_t *drive, int blocks)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *new_stage;
+	unsigned long flags;
+	struct request *rq;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 3)
+		printk (KERN_INFO "ide-tape: Reached idetape_add_chrdev_write_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+     	/*
+     	 *	Attempt to allocate a new stage.
+	 *	Pay special attention to possible race conditions.
+	 */
+	while ((new_stage = idetape_kmalloc_stage (tape)) == NULL) {
+		spin_lock_irqsave(&tape->spinlock, flags);
+		if (idetape_pipeline_active (tape)) {
+			idetape_wait_for_request(drive, tape->active_data_request);
+			spin_unlock_irqrestore(&tape->spinlock, flags);
+		} else {
+			spin_unlock_irqrestore(&tape->spinlock, flags);
+			idetape_insert_pipeline_into_queue (drive);
+			if (idetape_pipeline_active (tape))
+				continue;
+			/*
+			 *	Linux is short on memory. Fallback to
+			 *	non-pipelined operation mode for this request.
+			 */
+			return idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bh);
+		}
+	}
+	rq = &new_stage->rq;
+	ide_init_drive_cmd (rq);
+	rq->cmd = IDETAPE_WRITE_RQ;
+	rq->sector = tape->first_frame_position;	/* Doesn't actually matter - We always assume sequential access */
+	rq->nr_sectors = rq->current_nr_sectors = blocks;
+
+	idetape_switch_buffers (tape, new_stage);
+	idetape_init_stage(drive, new_stage, OS_FRAME_TYPE_DATA, tape->logical_blk_num);
+	tape->logical_blk_num++;
+	idetape_add_stage_tail (drive,new_stage);
+	tape->pipeline_head++;
+#if USE_IOTRACE
+	IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+	calculate_speeds(drive);
+
+	/*
+	 *	Estimate whether the tape has stopped writing by checking
+	 *	if our write pipeline is currently empty. If we are not
+	 *	writing anymore, wait for the pipeline to be full enough
+	 *	(90%) before starting to service requests, so that we will
+	 *	be able to keep up with the higher speeds of the tape.
+	 *
+	 *	For the OnStream drive, we can query the number of pending
+	 *	frames in the drive's internal buffer. As long as the tape
+	 *	is still writing, it is better to write frames immediately
+	 *	rather than gather them in the pipeline. This will give the
+	 *	tape's firmware the ability to sense the current incoming
+	 *	data rate more accurately, and since the OnStream tape
+	 *	supports variable speeds, it can try to adjust itself to the
+	 *	incoming data rate.
+	 */
+	if (!idetape_pipeline_active(tape)) {
+		if (tape->nr_stages >= tape->max_stages * 9 / 10 ||
+		    tape->nr_stages >= tape->max_stages - tape->uncontrolled_pipeline_head_speed * 3 * 1024 / tape->tape_block_size) {
+			tape->measure_insert_time = 1;
+			tape->insert_time = jiffies;
+			tape->insert_size = 0;
+			tape->insert_speed = 0;
+			idetape_insert_pipeline_into_queue (drive);
+		} else if (tape->onstream) {
+			idetape_update_stats(drive);
+			if (tape->cur_frames > 5)
+				idetape_insert_pipeline_into_queue (drive);
+		}
+	}
+	if (test_and_clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags))		/* Return a deferred error */
+		return -EIO;
+	return blocks;
+}
+
+/*
+ *	idetape_wait_for_pipeline will wait until all pending pipeline
+ *	requests are serviced. Typically called on device close.
+ */
+static void idetape_wait_for_pipeline (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	unsigned long flags;
+
+	while (tape->next_stage || idetape_pipeline_active(tape)) {
+		idetape_insert_pipeline_into_queue (drive);
+		spin_lock_irqsave(&tape->spinlock, flags);
+		if (idetape_pipeline_active(tape))
+			idetape_wait_for_request(drive, tape->active_data_request);
+		spin_unlock_irqrestore(&tape->spinlock, flags);
+	}
+}
+
+static void idetape_empty_write_pipeline (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int blocks, i, min;
+	struct buffer_head *bh;
+	
+#if IDETAPE_DEBUG_BUGS
+	if (tape->chrdev_direction != idetape_direction_write) {
+		printk (KERN_ERR "ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n");
+		return;
+	}
+	if (tape->merge_stage_size > tape->stage_size) {
+		printk (KERN_ERR "ide-tape: bug: merge_buffer too big\n");
+		tape->merge_stage_size = tape->stage_size;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+	if (tape->merge_stage_size) {
+		blocks=tape->merge_stage_size/tape->tape_block_size;
+		if (tape->merge_stage_size % tape->tape_block_size) {
+			blocks++;
+			i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size;
+			bh = tape->bh->b_reqnext;
+			while (bh) {
+				atomic_set(&bh->b_count, 0);
+				bh = bh->b_reqnext;
+			}
+			bh = tape->bh;
+			while (i) {
+				if (bh == NULL) {
+					printk(KERN_INFO "ide-tape: bug, bh NULL\n");
+					break;
+				}
+				min = IDE_MIN(i, bh->b_size - atomic_read(&bh->b_count));
+				memset(bh->b_data + atomic_read(&bh->b_count), 0, min);
+				atomic_add(min, &bh->b_count);
+				i -= min;
+				bh = bh->b_reqnext;
+			}
+		}
+		(void) idetape_add_chrdev_write_request (drive, blocks);
+		tape->merge_stage_size = 0;
+	}
+	idetape_wait_for_pipeline (drive);
+	if (tape->merge_stage != NULL) {
+		__idetape_kfree_stage (tape->merge_stage);
+		tape->merge_stage = NULL;
+	}
+	clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+	tape->chrdev_direction=idetape_direction_none;
+
+	/*
+	 *	On the next backup, perform the feedback loop again.
+	 *	(I don't want to keep sense information between backups,
+	 *	 as some systems are constantly on, and the system load
+	 *	 can be totally different on the next backup).
+	 */
+	tape->max_stages = tape->min_pipeline;
+#if IDETAPE_DEBUG_BUGS
+	if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) {
+		printk (KERN_ERR "ide-tape: ide-tape pipeline bug, "
+		"first_stage %p, next_stage %p, last_stage %p, nr_stages %d\n",
+		tape->first_stage, tape->next_stage, tape->last_stage, tape->nr_stages);
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+}
+
+static void idetape_restart_speed_control (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	tape->restart_speed_control_req = 0;
+	tape->pipeline_head = 0;
+	tape->buffer_head = tape->tape_head = tape->cur_frames;
+	tape->controlled_last_pipeline_head = tape->uncontrolled_last_pipeline_head = 0;
+	tape->controlled_previous_pipeline_head = tape->uncontrolled_previous_pipeline_head = 0;
+	tape->pipeline_head_speed = tape->controlled_pipeline_head_speed = 5000;
+	tape->uncontrolled_pipeline_head_speed = 0;
+	tape->controlled_pipeline_head_time = tape->uncontrolled_pipeline_head_time = jiffies;
+	tape->controlled_previous_head_time = tape->uncontrolled_previous_head_time = jiffies;
+}
+
+static int idetape_initiate_read (ide_drive_t *drive, int max_stages)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *new_stage;
+	struct request rq;
+	int bytes_read;
+	int blocks = tape->capabilities.ctl;
+
+	if (tape->chrdev_direction != idetape_direction_read) {		/* Initialize read operation */
+		if (tape->chrdev_direction == idetape_direction_write) {
+			idetape_empty_write_pipeline (drive);
+			idetape_flush_tape_buffers (drive);
+		}
+#if IDETAPE_DEBUG_BUGS
+		if (tape->merge_stage || tape->merge_stage_size) {
+			printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n");
+			tape->merge_stage_size = 0;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+		if ((tape->merge_stage = __idetape_kmalloc_stage (tape, 0, 0)) == NULL)
+			return -ENOMEM;
+		tape->chrdev_direction = idetape_direction_read;
+		tape->logical_blk_num = 0;
+
+		/*
+		 *	Issue a read 0 command to ensure that DSC handshake
+		 *	is switched from completion mode to buffer available
+		 *	mode.
+		 */
+		bytes_read = idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 0, tape->merge_stage->bh);
+		if (bytes_read < 0) {
+			kfree (tape->merge_stage);
+			tape->merge_stage = NULL;
+			tape->chrdev_direction = idetape_direction_none;
+			return bytes_read;
+		}
+	}
+	if (tape->restart_speed_control_req)
+		idetape_restart_speed_control(drive);
+	ide_init_drive_cmd (&rq);
+	rq.cmd = IDETAPE_READ_RQ;
+	rq.sector = tape->first_frame_position;
+	rq.nr_sectors = rq.current_nr_sectors = blocks;
+	if (!test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags) && tape->nr_stages <= max_stages) {
+		new_stage=idetape_kmalloc_stage (tape);
+		while (new_stage != NULL) {
+			new_stage->rq=rq;
+			idetape_add_stage_tail (drive,new_stage);
+			if (tape->nr_stages >= max_stages)
+				break;
+			new_stage=idetape_kmalloc_stage (tape);
+		}
+	}
+	if (!idetape_pipeline_active(tape)) {
+		if (tape->nr_pending_stages >= 3 * max_stages / 4) {
+			tape->measure_insert_time = 1;
+			tape->insert_time = jiffies;
+			tape->insert_size = 0;
+			tape->insert_speed = 0;
+			idetape_insert_pipeline_into_queue (drive);
+		} else if (tape->onstream) {
+			idetape_update_stats(drive);
+			if (tape->cur_frames < tape->max_frames - 5)
+				idetape_insert_pipeline_into_queue (drive);
+		}
+	}
+	return 0;
+}
+
+static int idetape_get_logical_blk (ide_drive_t *drive, int logical_blk_num, int max_stages, int quiet)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	unsigned long flags;
+	int cnt = 0, x, position;
+
+	/*
+	 * Search and wait for the next logical tape block
+	 */
+	while (1) {
+		if (cnt++ > 1000) {   /* AJN: was 100 */
+			printk(KERN_INFO "ide-tape: %s: couldn't find logical block %d, aborting\n", tape->name, logical_blk_num);
+			return 0;
+		}
+		idetape_initiate_read(drive, max_stages);
+		if (tape->first_stage == NULL) {
+			if (tape->onstream) {
+#if ONSTREAM_DEBUG
+				if (tape->debug_level >= 1)
+					printk(KERN_INFO "ide-tape: %s: first_stage == NULL, pipeline error %ld\n", tape->name, (long)test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags));
+#endif
+				clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags);
+				position = idetape_read_position(drive);
+				printk(KERN_INFO "ide-tape: %s: blank block detected, positioning tape to block %d\n", tape->name, position + 60);
+				idetape_position_tape(drive, position + 60, 0, 1);
+				cnt += 40;
+				continue;
+			} else
+				return 0;
+		}
+		idetape_wait_first_stage(drive);
+		if (idetape_verify_stage(drive, tape->first_stage, logical_blk_num, quiet))
+			break;
+		if (tape->first_stage->rq.errors == IDETAPE_ERROR_EOD)
+			cnt--;
+		if (idetape_verify_stage(drive, tape->first_stage, -1, quiet)) {
+			x = ntohl(tape->first_stage->aux->logical_blk_num);
+			if (x > logical_blk_num) {
+				printk(KERN_ERR "ide-tape: %s: couldn't find logical block %d, aborting (block %d found)\n", tape->name, logical_blk_num, x);
+				return 0;
+			}
+		}
+		spin_lock_irqsave(&tape->spinlock, flags);
+		idetape_remove_stage_head(drive);
+		spin_unlock_irqrestore(&tape->spinlock, flags);
+	}
+	if (tape->onstream)
+		tape->logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num);
+	return 1;
+}
+
+/*
+ *	idetape_add_chrdev_read_request is called from idetape_chrdev_read
+ *	to service a character device read request and add read-ahead
+ *	requests to our pipeline.
+ */
+static int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	unsigned long flags;
+	struct request *rq_ptr;
+	int bytes_read;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_add_chrdev_read_request, %d blocks\n", blocks);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	/*
+	 * Wait for the next logical block to be available at the head
+	 * of the pipeline
+	 */
+	if (!idetape_get_logical_blk(drive, tape->logical_blk_num, tape->max_stages, 0)) {
+		if (tape->onstream) {
+			set_bit(IDETAPE_READ_ERROR, &tape->flags);
+			return 0;
+		}
+		if (test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags))
+		 	return 0;
+		return idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, blocks, tape->merge_stage->bh);
+	}
+	rq_ptr = &tape->first_stage->rq;
+	bytes_read = tape->tape_block_size * (rq_ptr->nr_sectors - rq_ptr->current_nr_sectors);
+	rq_ptr->nr_sectors = rq_ptr->current_nr_sectors = 0;
+
+
+	if (tape->onstream && !tape->raw && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 2)
+			printk(KERN_INFO "ide-tape: %s: EOD reached\n", tape->name);
+#endif
+		return 0;
+	}
+	if (rq_ptr->errors == IDETAPE_ERROR_EOD)
+		return 0;
+	else if (rq_ptr->errors == IDETAPE_ERROR_FILEMARK)
+		set_bit (IDETAPE_FILEMARK, &tape->flags);
+	else {
+		idetape_switch_buffers (tape, tape->first_stage);
+		if (rq_ptr->errors == IDETAPE_ERROR_GENERAL) {
+#if ONSTREAM_DEBUG
+			if (tape->debug_level >= 1)
+				printk(KERN_INFO "ide-tape: error detected, bytes_read %d\n", bytes_read);
+#endif
+		}
+		clear_bit (IDETAPE_FILEMARK, &tape->flags);
+		spin_lock_irqsave(&tape->spinlock, flags);
+		idetape_remove_stage_head (drive);
+		spin_unlock_irqrestore(&tape->spinlock, flags);
+		tape->logical_blk_num++;
+		tape->pipeline_head++;
+#if USE_IOTRACE
+		IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+		calculate_speeds(drive);
+	}
+#if IDETAPE_DEBUG_BUGS
+	if (bytes_read > blocks*tape->tape_block_size) {
+		printk (KERN_ERR "ide-tape: bug: trying to return more bytes than requested\n");
+		bytes_read=blocks*tape->tape_block_size;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+	return (bytes_read);
+}
+
+static void idetape_pad_zeros (ide_drive_t *drive, int bcount)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	struct buffer_head *bh;
+	int count, blocks;
+	
+	while (bcount) {
+		bh = tape->merge_stage->bh;
+		count = IDE_MIN (tape->stage_size, bcount);
+		bcount -= count;
+		blocks = count / tape->tape_block_size;
+		while (count) {
+			atomic_set(&bh->b_count, IDE_MIN (count, bh->b_size));
+			memset (bh->b_data, 0, atomic_read(&bh->b_count));
+			count -= atomic_read(&bh->b_count);
+			bh = bh->b_reqnext;
+		}
+		idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bh);
+	}
+}
+
+static int idetape_pipeline_size (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *stage;
+	struct request *rq;
+	int size = 0;
+
+	idetape_wait_for_pipeline (drive);
+	stage = tape->first_stage;
+	while (stage != NULL) {
+		rq = &stage->rq;
+		size += tape->tape_block_size * (rq->nr_sectors-rq->current_nr_sectors);
+		if (rq->errors == IDETAPE_ERROR_FILEMARK)
+			size += tape->tape_block_size;
+		stage = stage->next;
+	}
+	size += tape->merge_stage_size;
+	return size;
+}
+
+/*
+ *	Rewinds the tape to the Beginning Of the current Partition (BOP).
+ *
+ *	We currently support only one partition.
+ */ 
+static int idetape_rewind_tape (ide_drive_t *drive)
+{
+	int retval;
+	idetape_pc_t pc;
+	idetape_tape_t *tape = drive->driver_data;
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 2)
+		printk (KERN_INFO "ide-tape: Reached idetape_rewind_tape\n");
+#endif /* IDETAPE_DEBUG_LOG */	
+	
+	idetape_create_rewind_cmd (drive, &pc);
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval) return retval;
+
+	idetape_create_read_position_cmd (&pc);
+	retval = idetape_queue_pc_tail (drive,&pc);
+	if (retval) return retval;
+	tape->logical_blk_num = 0;
+	return 0;
+}
+
+/*
+ *	Our special ide-tape ioctl's.
+ *
+ *	Currently there aren't any ioctl's.
+ *	mtio.h compatible commands should be issued to the character device
+ *	interface.
+ */
+static int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+				 unsigned int cmd, unsigned long arg)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_config_t config;
+
+#if IDETAPE_DEBUG_LOG	
+	if (tape->debug_level >= 4)
+		printk (KERN_INFO "ide-tape: Reached idetape_blkdev_ioctl\n");
+#endif /* IDETAPE_DEBUG_LOG */
+	switch (cmd) {
+		case 0x0340:
+			if (copy_from_user ((char *) &config, (char *) arg, sizeof (idetape_config_t)))
+				return -EFAULT;
+			tape->best_dsc_rw_frequency = config.dsc_rw_frequency;
+			tape->max_stages = config.nr_stages;
+			break;
+		case 0x0350:
+			config.dsc_rw_frequency = (int) tape->best_dsc_rw_frequency;
+			config.nr_stages = tape->max_stages; 
+			if (copy_to_user ((char *) arg, (char *) &config, sizeof (idetape_config_t)))
+				return -EFAULT;
+			break;
+		default:
+			return -EIO;
+	}
+	return 0;
+}
+
+/*
+ *	The block device interface should not be used for data transfers.
+ *	However, we still allow opening it so that we can issue general
+ *	ide driver configuration ioctl's, such as the interrupt unmask feature.
+ */
+static int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_INC_USE_COUNT;
+#if ONSTREAM_DEBUG
+        printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_blkdev_open\n");
+#endif
+	return 0;
+}
+
+static void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+        printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_blkdev_release\n");
+#endif
+}
+
+/*
+ *	idetape_pre_reset is called before an ATAPI/ATA software reset.
+ */
+static void idetape_pre_reset (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	if (tape != NULL)
+		set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
+}
+
+/*
+ *	Character device interface functions
+ */
+static ide_drive_t *get_drive_ptr (kdev_t i_rdev)
+{
+	unsigned int i = MINOR(i_rdev) & ~0xc0;
+
+	if (i >= MAX_HWIFS * MAX_DRIVES)
+		return NULL;
+	return (idetape_chrdevs[i].drive);
+}
+
+static int idetape_onstream_space_over_filemarks_backward (ide_drive_t *drive,short mt_op,int mt_count)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int cnt = 0;
+	int last_mark_addr;
+	unsigned long flags;
+
+	if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+		printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_bwd\n", tape->name);
+		return -EIO;
+	}
+	while (cnt != mt_count) {
+		last_mark_addr = ntohl(tape->first_stage->aux->last_mark_addr);
+		if (last_mark_addr == -1)
+			return -EIO;
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 2)
+			printk(KERN_INFO "ide-tape: positioning to last mark at %d\n", last_mark_addr);
+#endif
+		idetape_position_tape(drive, last_mark_addr, 0, 0);
+		cnt++;
+		if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+			printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
+			return -EIO;
+		}
+		if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+			printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, last_mark_addr);
+			return -EIO;
+		}
+	}
+	if (mt_op == MTBSFM) {
+		spin_lock_irqsave(&tape->spinlock, flags);
+		idetape_remove_stage_head (drive);
+		tape->logical_blk_num++;
+		spin_unlock_irqrestore(&tape->spinlock, flags);
+	}
+	return 0;
+}
+
+/*
+ * ADRL 1.1 compatible "slow" space filemarks fwd version
+ *
+ * Just scans for the filemark sequentially.
+ */
+static int idetape_onstream_space_over_filemarks_forward_slow (ide_drive_t *drive,short mt_op,int mt_count)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int cnt = 0;
+	unsigned long flags;
+
+	if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+		printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name);
+		return -EIO;
+	}
+	while (1) {
+		if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+			printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
+			return -EIO;
+		}
+		if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER)
+			cnt++;
+		if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if ONSTREAM_DEBUG
+			if (tape->debug_level >= 2)
+				printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name);
+#endif
+			return -EIO;
+		}
+		if (cnt == mt_count)
+			break;
+		spin_lock_irqsave(&tape->spinlock, flags);
+		idetape_remove_stage_head (drive);
+		spin_unlock_irqrestore(&tape->spinlock, flags);
+	}
+	if (mt_op == MTFSF) {
+		spin_lock_irqsave(&tape->spinlock, flags);
+		idetape_remove_stage_head (drive);
+		tape->logical_blk_num++;
+		spin_unlock_irqrestore(&tape->spinlock, flags);
+	}
+	return 0;
+}
+
+
+/*
+ * Fast linux specific version of OnStream FSF
+ */
+static int idetape_onstream_space_over_filemarks_forward_fast (ide_drive_t *drive,short mt_op,int mt_count)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int cnt = 0, next_mark_addr;
+	unsigned long flags;
+
+	if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+		printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name);
+		return -EIO;
+	}
+
+	/*
+	 * Find nearest (usually previous) marker
+	 */
+	while (1) {
+		if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER)
+			break;
+		if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if ONSTREAM_DEBUG
+			if (tape->debug_level >= 2)
+				printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name);
+#endif
+			return -EIO;
+		}
+		if (ntohl(tape->first_stage->aux->filemark_cnt) == 0) {
+			if (tape->first_mark_addr == -1) {
+				printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name);
+				return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count);
+			}
+			idetape_position_tape(drive, tape->first_mark_addr, 0, 0);
+			if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+				printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd_fast\n", tape->name);
+				return -EIO;
+			}
+			if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+				printk(KERN_INFO "ide-tape: %s: expected to find filemark at %d\n", tape->name, tape->first_mark_addr);
+				return -EIO;
+			}
+		} else {
+			if (idetape_onstream_space_over_filemarks_backward(drive, MTBSF, 1) < 0)
+				return -EIO;
+			mt_count++;
+		}
+	}
+	cnt++;
+	while (cnt != mt_count) {
+		next_mark_addr = ntohl(tape->first_stage->aux->next_mark_addr);
+		if (!next_mark_addr || next_mark_addr > tape->eod_frame_addr) {
+			printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name);
+			return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count - cnt);
+#if ONSTREAM_DEBUG
+		} else if (tape->debug_level >= 2) {
+		     printk(KERN_INFO "ide-tape: positioning to next mark at %d\n", next_mark_addr);
+#endif
+		}
+		idetape_position_tape(drive, next_mark_addr, 0, 0);
+		cnt++;
+		if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+			printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
+			return -EIO;
+		}
+		if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+			printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, next_mark_addr);
+			return -EIO;
+		}
+	}
+	if (mt_op == MTFSF) {
+		spin_lock_irqsave(&tape->spinlock, flags);
+		idetape_remove_stage_head (drive);
+		tape->logical_blk_num++;
+		spin_unlock_irqrestore(&tape->spinlock, flags);
+	}
+	return 0;
+}
+
+/*
+ *	idetape_space_over_filemarks is now a bit more complicated than just
+ *	passing the command to the tape since we may have crossed some
+ *	filemarks during our pipelined read-ahead mode.
+ *
+ *	As a minor side effect, the pipeline enables us to support MTFSFM when
+ *	the filemark is in our internal pipeline even if the tape doesn't
+ *	support spacing over filemarks in the reverse direction.
+ */
+static int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t pc;
+	unsigned long flags;
+	int retval,count=0;
+	int speed_control;
+
+	if (tape->onstream) {
+		if (tape->raw)
+			return -EIO;
+		speed_control = tape->speed_control;
+		tape->speed_control = 0;
+		if (mt_op == MTFSF || mt_op == MTFSFM) {
+			if (tape->linux_media)
+				retval = idetape_onstream_space_over_filemarks_forward_fast(drive, mt_op, mt_count);
+			else
+				retval = idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count);
+		} else
+			retval = idetape_onstream_space_over_filemarks_backward(drive, mt_op, mt_count);
+		tape->speed_control = speed_control;
+		tape->restart_speed_control_req = 1;
+		return retval;
+	}
+
+	if (tape->chrdev_direction == idetape_direction_read) {
+		/*
+		 *	We have a read-ahead buffer. Scan it for crossed
+		 *	filemarks.
+		 */
+		tape->merge_stage_size = 0;
+		clear_bit (IDETAPE_FILEMARK, &tape->flags);
+		while (tape->first_stage != NULL) {
+			idetape_wait_first_stage(drive);
+			if (tape->first_stage->rq.errors == IDETAPE_ERROR_FILEMARK)
+				count++;
+			if (count == mt_count) {
+				switch (mt_op) {
+					case MTFSF:
+						spin_lock_irqsave(&tape->spinlock, flags);
+						idetape_remove_stage_head (drive);
+						spin_unlock_irqrestore(&tape->spinlock, flags);
+					case MTFSFM:
+						return (0);
+					default:
+						break;
+				}
+			}
+			spin_lock_irqsave(&tape->spinlock, flags);
+			idetape_remove_stage_head (drive);
+			spin_unlock_irqrestore(&tape->spinlock, flags);
+		}
+		idetape_discard_read_pipeline (drive, 1);
+	}
+
+	/*
+	 *	The filemark was not found in our internal pipeline.
+	 *	Now we can issue the space command.
+	 */
+	switch (mt_op) {
+		case MTFSF:
+			idetape_create_space_cmd (&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTFSFM:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			retval = idetape_space_over_filemarks (drive, MTFSF, mt_count-count);
+			if (retval) return (retval);
+			return (idetape_space_over_filemarks (drive, MTBSF, 1));
+		case MTBSF:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			idetape_create_space_cmd (&pc,-(mt_count+count),IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTBSFM:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			retval = idetape_space_over_filemarks (drive, MTBSF, mt_count+count);
+			if (retval) return (retval);
+			return (idetape_space_over_filemarks (drive, MTFSF, 1));
+		default:
+			printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op);
+			return (-EIO);
+	}
+}
+
+
+/*
+ *	Our character device read / write functions.
+ *
+ *	The tape is optimized to maximize throughput when it is transferring
+ *	an integral number of the "continuous transfer limit", which is
+ *	a parameter of the specific tape (26 KB on my particular tape).
+ *      (32 kB for Onstream)
+ *
+ *	As of version 1.3 of the driver, the character device provides an
+ *	abstract continuous view of the media - any mix of block sizes (even 1
+ *	byte) on the same backup/restore procedure is supported. The driver
+ *	will internally convert the requests to the recommended transfer unit,
+ *	so that an unmatch between the user's block size to the recommended
+ *	size will only result in a (slightly) increased driver overhead, but
+ *	will no longer hit performance.
+ *      This is not applicable to Onstream.
+ */
+static ssize_t idetape_chrdev_read (struct file *file, char *buf,
+				    size_t count, loff_t *ppos)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+	idetape_tape_t *tape = drive->driver_data;
+	ssize_t bytes_read,temp,actually_read=0, rc;
+
+	if (ppos != &file->f_pos) {
+		/* "A request was outside the capabilities of the device." */
+		return -ENXIO;
+	}
+	if (tape->onstream && (count != tape->tape_block_size)) {
+		printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%Zd used)\n", tape->name, tape->tape_block_size, count);
+		return -EINVAL;
+	}
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 3)
+		printk (KERN_INFO "ide-tape: Reached idetape_chrdev_read, count %Zd\n", count);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (tape->chrdev_direction != idetape_direction_read) {
+		if (test_bit (IDETAPE_DETECT_BS, &tape->flags))
+			if (count > tape->tape_block_size && (count % tape->tape_block_size) == 0)
+				tape->user_bs_factor = count / tape->tape_block_size;
+	}
+	if ((rc = idetape_initiate_read(drive, tape->max_stages)) < 0)
+		return rc;
+	if (count==0)
+		return (0);
+	if (tape->merge_stage_size) {
+		actually_read=IDE_MIN (tape->merge_stage_size,count);
+		idetape_copy_stage_to_user (tape, buf, tape->merge_stage, actually_read);
+		buf += actually_read; tape->merge_stage_size -= actually_read; count-=actually_read;
+	}
+	while (count >= tape->stage_size) {
+		bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
+		if (bytes_read <= 0)
+			goto finish;
+		idetape_copy_stage_to_user (tape, buf, tape->merge_stage, bytes_read);
+		buf += bytes_read; count -= bytes_read; actually_read += bytes_read;
+	}
+	if (count) {
+		bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
+		if (bytes_read <= 0)
+			goto finish;
+		temp=IDE_MIN (count,bytes_read);
+		idetape_copy_stage_to_user (tape, buf, tape->merge_stage, temp);
+		actually_read+=temp;
+		tape->merge_stage_size=bytes_read-temp;
+	}
+finish:
+	if (!actually_read && test_bit (IDETAPE_FILEMARK, &tape->flags)) {
+#if IDETAPE_DEBUG_LOG
+		if (tape->debug_level >= 2)
+			printk(KERN_INFO "ide-tape: %s: spacing over filemark\n", tape->name);
+#endif
+		idetape_space_over_filemarks (drive, MTFSF, 1);
+		return 0;
+	}
+	if (tape->onstream && !actually_read && test_and_clear_bit(IDETAPE_READ_ERROR, &tape->flags)) {
+		printk(KERN_ERR "ide-tape: %s: unrecovered read error on logical block number %d, skipping\n", tape->name, tape->logical_blk_num);
+		tape->logical_blk_num++;
+		return -EIO;
+	}
+	return actually_read;
+}
+
+static void idetape_update_last_marker (ide_drive_t *drive, int last_mark_addr, int next_mark_addr)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *stage;
+	os_aux_t *aux;
+	int position;
+
+	if (!tape->onstream || tape->raw)
+		return;
+	if (last_mark_addr == -1)
+		return;
+	stage = __idetape_kmalloc_stage(tape, 0, 0);
+	if (stage == NULL)
+		return;
+	idetape_flush_tape_buffers(drive);
+	position = idetape_read_position(drive);
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 2)
+		printk(KERN_INFO "ide-tape: current position (2) %d, lblk %d\n", position, tape->logical_blk_num);
+	if (tape->debug_level >= 2)
+		printk(KERN_INFO "ide-tape: current position (2) tape block %d\n", tape->last_frame_position);
+#endif
+	idetape_position_tape(drive, last_mark_addr, 0, 0);
+	if (!idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 1, stage->bh)) {
+		printk(KERN_INFO "ide-tape: %s: couldn't read last marker\n", tape->name);
+		__idetape_kfree_stage (stage);
+		idetape_position_tape(drive, position, 0, 0);
+		return;
+	}
+	aux = stage->aux;
+	if (aux->frame_type != OS_FRAME_TYPE_MARKER) {
+		printk(KERN_INFO "ide-tape: %s: expected to find marker at addr %d\n", tape->name, last_mark_addr);
+		__idetape_kfree_stage (stage);
+		idetape_position_tape(drive, position, 0, 0);
+		return;
+	}
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 2)
+		printk(KERN_INFO "ide-tape: writing back marker\n");
+#endif
+	aux->next_mark_addr = htonl(next_mark_addr);
+	idetape_position_tape(drive, last_mark_addr, 0, 0);
+	if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) {
+		printk(KERN_INFO "ide-tape: %s: couldn't write back marker frame at %d\n", tape->name, last_mark_addr);
+		__idetape_kfree_stage (stage);
+		idetape_position_tape(drive, position, 0, 0);
+		return;
+	}
+	__idetape_kfree_stage (stage);
+	idetape_flush_tape_buffers (drive);
+	idetape_position_tape(drive, position, 0, 0);
+	return;
+}
+
+static void __idetape_write_header (ide_drive_t *drive, int block, int cnt)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *stage;
+	os_header_t header;
+
+	stage = __idetape_kmalloc_stage(tape, 1, 1);
+	if (stage == NULL)
+		return;
+	idetape_init_stage(drive, stage, OS_FRAME_TYPE_HEADER, tape->logical_blk_num);
+	idetape_wait_ready(drive, 60 * 5 * HZ);
+	idetape_position_tape(drive, block, 0, 0);
+	memset(&header, 0, sizeof(header));
+	strcpy(header.ident_str, "ADR_SEQ");
+	header.major_rev = 1;
+	header.minor_rev = 2;
+	header.par_num = 1;
+	header.partition.partition_num = OS_DATA_PARTITION;
+	header.partition.par_desc_ver = OS_PARTITION_VERSION;
+	header.partition.first_frame_addr = htonl(0x14);
+	header.partition.last_frame_addr = htonl(19239 * 24);
+	header.partition.wrt_pass_cntr = htons(tape->wrt_pass_cntr);
+	header.partition.eod_frame_addr = htonl(tape->eod_frame_addr);
+	memcpy(stage->bh->b_data, &header, sizeof(header));
+	while (cnt--) {
+		if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) {
+			printk(KERN_INFO "ide-tape: %s: couldn't write header frame\n", tape->name);
+			__idetape_kfree_stage (stage);
+			return;
+		}
+	}
+	__idetape_kfree_stage (stage);
+	idetape_flush_tape_buffers (drive);
+}
+
+static void idetape_write_header (ide_drive_t *drive, int locate_eod)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 2)
+		printk(KERN_INFO "ide-tape: %s: writing tape header\n", tape->name);
+#endif
+	if (!tape->onstream || tape->raw)
+		return;
+	tape->update_frame_cntr++;
+	__idetape_write_header(drive, 5, 5);
+	__idetape_write_header(drive, 0xbae, 5);
+	if (locate_eod) {
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 2)
+			printk(KERN_INFO "ide-tape: %s: locating back to eod frame addr %d\n", tape->name, tape->eod_frame_addr);
+#endif
+		idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
+	}
+}
+
+static ssize_t idetape_chrdev_write (struct file *file, const char *buf,
+				     size_t count, loff_t *ppos)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+	idetape_tape_t *tape = drive->driver_data;
+	ssize_t retval,actually_written=0;
+	int position;
+
+	if (ppos != &file->f_pos) {
+		/* "A request was outside the capabilities of the device." */
+		return -ENXIO;
+	}
+	if (tape->onstream && (count != tape->tape_block_size)) {
+		printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%Zd used)\n", tape->name, tape->tape_block_size, count);
+		return -EINVAL;
+	}
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 3)
+		printk (KERN_INFO "ide-tape: Reached idetape_chrdev_write, count %Zd\n", count);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (tape->chrdev_direction != idetape_direction_write) {	/* Initialize write operation */
+		if (tape->chrdev_direction == idetape_direction_read)
+			idetape_discard_read_pipeline (drive, 1);
+#if IDETAPE_DEBUG_BUGS
+		if (tape->merge_stage || tape->merge_stage_size) {
+			printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n");
+			tape->merge_stage_size = 0;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+		if ((tape->merge_stage = __idetape_kmalloc_stage (tape, 0, 0)) == NULL)
+			return -ENOMEM;
+		tape->chrdev_direction = idetape_direction_write;
+		idetape_init_merge_stage (tape);
+
+		if (tape->onstream) {
+			position = idetape_read_position(drive);
+			if (position <= 20) {
+				tape->logical_blk_num = 0;
+				tape->wrt_pass_cntr++;
+#if ONSTREAM_DEBUG
+				if (tape->debug_level >= 2)
+					printk(KERN_INFO "ide-tape: %s: logical block num 0, setting eod to 20\n", tape->name);
+				if (tape->debug_level >= 2)
+					printk(KERN_INFO "ide-tape: %s: allocating new write pass counter %d\n", tape->name, tape->wrt_pass_cntr);
+#endif
+				tape->filemark_cnt = 0;
+				tape->eod_frame_addr = 20;
+				tape->first_mark_addr = tape->last_mark_addr = -1;
+				idetape_write_header(drive, 1);
+			}
+#if ONSTREAM_DEBUG
+			if (tape->debug_level >= 2)
+				printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr);
+#endif
+			position = idetape_read_position(drive);
+			if (position != tape->eod_frame_addr)
+				idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
+#if ONSTREAM_DEBUG
+			if (tape->debug_level >= 2)
+				printk(KERN_INFO "ide-tape: %s: first_frame_position %d\n", tape->name, tape->first_frame_position);
+#endif
+		}
+
+		/*
+		 *	Issue a write 0 command to ensure that DSC handshake
+		 *	is switched from completion mode to buffer available
+		 *	mode.
+		 */
+		retval = idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 0, tape->merge_stage->bh);
+		if (retval < 0) {
+			kfree (tape->merge_stage);
+			tape->merge_stage = NULL;
+			tape->chrdev_direction = idetape_direction_none;
+			return retval;
+		}
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 2)
+			printk("ide-tape: first_frame_position %d\n", tape->first_frame_position);
+#endif
+	}
+	if (count==0)
+		return (0);
+	if (tape->restart_speed_control_req)
+		idetape_restart_speed_control(drive);
+	if (tape->merge_stage_size) {
+#if IDETAPE_DEBUG_BUGS
+		if (tape->merge_stage_size >= tape->stage_size) {
+			printk (KERN_ERR "ide-tape: bug: merge buffer too big\n");
+			tape->merge_stage_size=0;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
+		actually_written=IDE_MIN (tape->stage_size-tape->merge_stage_size,count);
+		idetape_copy_stage_from_user (tape, tape->merge_stage, buf, actually_written);
+		buf+=actually_written;tape->merge_stage_size+=actually_written;count-=actually_written;
+
+		if (tape->merge_stage_size == tape->stage_size) {
+			tape->merge_stage_size = 0;
+			retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl);
+			if (retval <= 0)
+				return (retval);
+		}
+	}
+	while (count >= tape->stage_size) {
+		idetape_copy_stage_from_user (tape, tape->merge_stage, buf, tape->stage_size);
+		buf+=tape->stage_size;count-=tape->stage_size;
+		retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl);
+		actually_written+=tape->stage_size;
+		if (retval <= 0)
+			return (retval);
+	}
+	if (count) {
+		actually_written+=count;
+		idetape_copy_stage_from_user (tape, tape->merge_stage, buf, count);
+		tape->merge_stage_size+=count;
+	}
+	return (actually_written);
+}
+
+static int idetape_write_filemark (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int last_mark_addr;
+	idetape_pc_t pc;
+
+	if (!tape->onstream) {
+		idetape_create_write_filemark_cmd(drive, &pc,1);	/* Write a filemark */
+		if (idetape_queue_pc_tail (drive,&pc)) {
+			printk (KERN_ERR "ide-tape: Couldn't write a filemark\n");
+			return -EIO;
+		}
+	} else if (!tape->raw) {
+		last_mark_addr = idetape_read_position(drive);
+		tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
+		if (tape->merge_stage != NULL) {
+			idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_MARKER, tape->logical_blk_num);
+			idetape_pad_zeros (drive, tape->stage_size);
+			tape->logical_blk_num++;
+			__idetape_kfree_stage (tape->merge_stage);
+			tape->merge_stage = NULL;
+		}
+		if (tape->filemark_cnt)
+			idetape_update_last_marker(drive, tape->last_mark_addr, last_mark_addr);
+		tape->last_mark_addr = last_mark_addr;
+		if (tape->filemark_cnt++ == 0)
+			tape->first_mark_addr = last_mark_addr;
+	}
+	return 0;
+}
+
+static void idetape_write_eod (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	if (!tape->onstream || tape->raw)
+		return;
+	tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
+	if (tape->merge_stage != NULL) {
+		tape->eod_frame_addr = idetape_read_position(drive);
+		idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_EOD, tape->logical_blk_num);
+		idetape_pad_zeros (drive, tape->stage_size);
+		__idetape_kfree_stage (tape->merge_stage);
+		tape->merge_stage = NULL;
+	}
+	return;
+}
+
+int idetape_seek_logical_blk (ide_drive_t *drive, int logical_blk_num)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int estimated_address = logical_blk_num + 20;
+	int retries = 0;
+	int speed_control;
+
+	speed_control = tape->speed_control;
+	tape->speed_control = 0;
+	if (logical_blk_num < 0)
+		logical_blk_num = 0;
+	if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1))
+		goto ok;
+	while (++retries < 10) {
+		idetape_discard_read_pipeline(drive, 0);
+		idetape_position_tape(drive, estimated_address, 0, 0);
+		if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1))
+			goto ok;
+		if (!idetape_get_logical_blk(drive, -1, 10, 1))
+			goto error;
+		if (tape->logical_blk_num < logical_blk_num)
+			estimated_address += logical_blk_num - tape->logical_blk_num;
+		else
+			break;
+	}
+error:
+	tape->speed_control = speed_control;
+	tape->restart_speed_control_req = 1;
+	printk(KERN_INFO "ide-tape: %s: couldn't seek to logical block %d (at %d), %d retries\n", tape->name, logical_blk_num, tape->logical_blk_num, retries);
+	return -EIO;
+ok:
+	tape->speed_control = speed_control;
+	tape->restart_speed_control_req = 1;
+	return 0;
+}
+
+/*
+ *	idetape_mtioctop is called from idetape_chrdev_ioctl when
+ *	the general mtio MTIOCTOP ioctl is requested.
+ *
+ *	We currently support the following mtio.h operations:
+ *
+ *	MTFSF	-	Space over mt_count filemarks in the positive direction.
+ *			The tape is positioned after the last spaced filemark.
+ *
+ *	MTFSFM	-	Same as MTFSF, but the tape is positioned before the
+ *			last filemark.
+ *
+ *	MTBSF	-	Steps background over mt_count filemarks, tape is
+ *			positioned before the last filemark.
+ *
+ *	MTBSFM	-	Like MTBSF, only tape is positioned after the last filemark.
+ *
+ *	Note:
+ *
+ *		MTBSF and MTBSFM are not supported when the tape doesn't
+ *		supports spacing over filemarks in the reverse direction.
+ *		In this case, MTFSFM is also usually not supported (it is
+ *		supported in the rare case in which we crossed the filemark
+ *		during our read-ahead pipelined operation mode).
+ *		
+ *	MTWEOF	-	Writes mt_count filemarks. Tape is positioned after
+ *			the last written filemark.
+ *
+ *	MTREW	-	Rewinds tape.
+ *
+ *	MTLOAD	-	Loads the tape.
+ *
+ *	MTOFFL	-	Puts the tape drive "Offline": Rewinds the tape and
+ *	MTUNLOAD	prevents further access until the media is replaced.
+ *
+ *	MTNOP	-	Flushes tape buffers.
+ *
+ *	MTRETEN	-	Retension media. This typically consists of one end
+ *			to end pass on the media.
+ *
+ *	MTEOM	-	Moves to the end of recorded data.
+ *
+ *	MTERASE	-	Erases tape.
+ *
+ *	MTSETBLK - 	Sets the user block size to mt_count bytes. If
+ *			mt_count is 0, we will attempt to autodetect
+ *			the block size.
+ *
+ *	MTSEEK	-	Positions the tape in a specific block number, where
+ *			each block is assumed to contain which user_block_size
+ *			bytes.
+ *
+ *	MTSETPART - 	Switches to another tape partition.
+ *
+ *	MTLOCK - 	Locks the tape door.
+ *
+ *	MTUNLOCK - 	Unlocks the tape door.
+ *
+ *	The following commands are currently not supported:
+ *
+ *	MTFSS, MTBSS, MTWSM, MTSETDENSITY,
+ *	MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
+ */
+static int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t pc;
+	int i,retval;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 1)
+		printk (KERN_INFO "ide-tape: Handling MTIOCTOP ioctl: mt_op=%d, mt_count=%d\n",mt_op,mt_count);
+#endif /* IDETAPE_DEBUG_LOG */
+	/*
+	 *	Commands which need our pipelined read-ahead stages.
+	 */
+	switch (mt_op) {
+		case MTFSF:
+		case MTFSFM:
+		case MTBSF:
+		case MTBSFM:
+			if (!mt_count)
+				return (0);
+			return (idetape_space_over_filemarks (drive,mt_op,mt_count));
+		default:
+			break;
+	}
+	switch (mt_op) {
+		case MTWEOF:
+			idetape_discard_read_pipeline (drive, 1);
+			for (i = 0; i < mt_count; i++) {
+				retval = idetape_write_filemark(drive);
+				if (retval) return retval;
+			}
+			return (0);
+		case MTREW:
+			idetape_discard_read_pipeline (drive, 0);
+			if (idetape_rewind_tape(drive))
+				return -EIO;
+			if (tape->onstream && !tape->raw)
+				return idetape_position_tape(drive, 20, 0, 0);
+			return 0;
+		case MTLOAD:
+			idetape_discard_read_pipeline (drive, 0);
+			idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTUNLOAD:
+		case MTOFFL:
+			idetape_discard_read_pipeline (drive, 0);
+			idetape_create_load_unload_cmd (drive, &pc,!IDETAPE_LU_LOAD_MASK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTNOP:
+			idetape_discard_read_pipeline (drive, 0);
+			return (idetape_flush_tape_buffers (drive));
+		case MTRETEN:
+			idetape_discard_read_pipeline (drive, 0);
+			idetape_create_load_unload_cmd (drive, &pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTEOM:
+			if (tape->onstream) {
+#if ONSTREAM_DEBUG
+				if (tape->debug_level >= 2)
+					printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr);
+#endif
+				idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
+				if (!idetape_get_logical_blk(drive, -1, 10, 0))
+					return -EIO;
+				if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_EOD)
+					return -EIO;
+				return 0;
+			}
+			idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTERASE:
+			if (tape->onstream) {
+				tape->eod_frame_addr = 20;
+				tape->logical_blk_num = 0;
+				tape->first_mark_addr = tape->last_mark_addr = -1;
+				idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
+				idetape_write_eod(drive);
+				idetape_flush_tape_buffers (drive);
+				idetape_write_header(drive, 0);
+				idetape_flush_tape_buffers (drive);
+				(void) idetape_rewind_tape (drive);
+				return 0;
+			}
+			(void) idetape_rewind_tape (drive);
+			idetape_create_erase_cmd (&pc);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTSETBLK:
+			if (tape->onstream) {
+				if (mt_count != tape->tape_block_size) {
+					printk(KERN_INFO "ide-tape: %s: MTSETBLK %d -- only %d bytes block size supported\n", tape->name, mt_count, tape->tape_block_size);
+					return -EINVAL;
+				}
+				return 0;
+			}
+			if (mt_count) {
+				if (mt_count < tape->tape_block_size || mt_count % tape->tape_block_size)
+					return -EIO;
+				tape->user_bs_factor = mt_count / tape->tape_block_size;
+				clear_bit (IDETAPE_DETECT_BS, &tape->flags);
+			} else
+				set_bit (IDETAPE_DETECT_BS, &tape->flags);
+			return 0;
+		case MTSEEK:
+			if (!tape->onstream || tape->raw) {
+				idetape_discard_read_pipeline (drive, 0);
+				return idetape_position_tape (drive, mt_count * tape->user_bs_factor, tape->partition, 0);
+			}
+			return idetape_seek_logical_blk(drive, mt_count);
+		case MTSETPART:
+			idetape_discard_read_pipeline (drive, 0);
+			if (tape->onstream)
+				return -EIO;
+			return (idetape_position_tape (drive, 0, mt_count, 0));
+		case MTFSR:
+		case MTBSR:
+			if (tape->onstream) {
+				if (!idetape_get_logical_blk(drive, -1, 10, 0))
+					return -EIO;
+				if (mt_op == MTFSR)
+					return idetape_seek_logical_blk(drive, tape->logical_blk_num + mt_count);
+				else {
+					idetape_discard_read_pipeline (drive, 0);
+					return idetape_seek_logical_blk(drive, tape->logical_blk_num - mt_count);
+				}
+			}
+		case MTLOCK:
+			idetape_create_prevent_cmd(drive, &pc, 1);
+			retval = idetape_queue_pc_tail (drive,&pc);
+			if (retval) return retval;
+			tape->door_locked = DOOR_EXPLICITLY_LOCKED;
+			return 0;
+		case MTUNLOCK:
+			idetape_create_prevent_cmd(drive, &pc, 0);
+			retval = idetape_queue_pc_tail (drive,&pc);
+			if (retval) return retval;
+			tape->door_locked = DOOR_UNLOCKED;
+			return 0;
+		default:
+			printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op);
+			return (-EIO);
+	}
+}
+
+/*
+ *	Our character device ioctls.
+ *
+ *	General mtio.h magnetic io commands are supported here, and not in
+ *	the corresponding block interface.
+ *
+ *	The following ioctls are supported:
+ *
+ *	MTIOCTOP -	Refer to idetape_mtioctop for detailed description.
+ *
+ *	MTIOCGET - 	The mt_dsreg field in the returned mtget structure
+ *			will be set to (user block size in bytes <<
+ *			MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK.
+ *
+ *			The mt_blkno is set to the current user block number.
+ *			The other mtget fields are not supported.
+ *
+ *	MTIOCPOS -	The current tape "block position" is returned. We
+ *			assume that each block contains user_block_size
+ *			bytes.
+ *
+ *	Our own ide-tape ioctls are supported on both interfaces.
+ */
+static int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+	idetape_tape_t *tape = drive->driver_data;
+	struct mtop mtop;
+	struct mtget mtget;
+	struct mtpos mtpos;
+	int block_offset = 0, position = tape->first_frame_position;
+
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 3)
+		printk (KERN_INFO "ide-tape: Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	tape->restart_speed_control_req = 1;
+	if (tape->chrdev_direction == idetape_direction_write) {
+		idetape_empty_write_pipeline (drive);
+		idetape_flush_tape_buffers (drive);
+	}
+	if (cmd == MTIOCGET || cmd == MTIOCPOS) {
+		block_offset = idetape_pipeline_size (drive) / (tape->tape_block_size * tape->user_bs_factor);
+		if ((position = idetape_read_position(drive)) < 0)
+			return -EIO;
+	}
+	switch (cmd) {
+		case MTIOCTOP:
+			if (copy_from_user ((char *) &mtop, (char *) arg, sizeof (struct mtop)))
+				return -EFAULT;
+			return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
+		case MTIOCGET:
+			memset (&mtget, 0, sizeof (struct mtget));
+			mtget.mt_type = MT_ISSCSI2;
+			if (!tape->onstream || tape->raw)
+				mtget.mt_blkno = position / tape->user_bs_factor - block_offset;
+			else {
+				if (!idetape_get_logical_blk(drive, -1, 10, 0))
+					mtget.mt_blkno = -1;
+				else
+					mtget.mt_blkno = tape->logical_blk_num;
+			}
+			mtget.mt_dsreg = ((tape->tape_block_size * tape->user_bs_factor) << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK;
+			if (tape->onstream) {
+				mtget.mt_gstat |= GMT_ONLINE(0xffffffff);
+				if (tape->first_stage && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD)
+					mtget.mt_gstat |= GMT_EOD(0xffffffff);
+				if (position <= 20)
+					mtget.mt_gstat |= GMT_BOT(0xffffffff);
+			}
+			if (copy_to_user ((char *) arg,(char *) &mtget, sizeof (struct mtget)))
+				return -EFAULT;
+			return 0;
+		case MTIOCPOS:
+			if (tape->onstream && !tape->raw) {
+				if (!idetape_get_logical_blk(drive, -1, 10, 0))
+					return -EIO;
+				mtpos.mt_blkno = tape->logical_blk_num;
+			} else
+				mtpos.mt_blkno = position / tape->user_bs_factor - block_offset;
+			if (copy_to_user ((char *) arg,(char *) &mtpos, sizeof (struct mtpos)))
+				return -EFAULT;
+			return 0;
+		default:
+			if (tape->chrdev_direction == idetape_direction_read)
+				idetape_discard_read_pipeline (drive, 1);
+			return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
+	}
+}
+
+static int __idetape_analyze_headers (ide_drive_t *drive, int block)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_stage_t *stage;
+	os_header_t *header;
+	os_aux_t *aux;
+
+	if (!tape->onstream || tape->raw) {
+		tape->header_ok = tape->linux_media = 1;
+		return 1;
+	}
+	tape->header_ok = tape->linux_media = 0;
+	tape->update_frame_cntr = 0;
+	tape->wrt_pass_cntr = 0;
+	tape->eod_frame_addr = 20;
+	tape->first_mark_addr = tape->last_mark_addr = -1;
+	stage = __idetape_kmalloc_stage (tape, 0, 0);
+	if (stage == NULL)
+		return 0;
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 2)
+		printk(KERN_INFO "ide-tape: %s: reading header\n", tape->name);
+#endif
+	idetape_position_tape(drive, block, 0, 0);
+	if (!idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 1, stage->bh)) {
+		printk(KERN_INFO "ide-tape: %s: couldn't read header frame\n", tape->name);
+		__idetape_kfree_stage (stage);
+		return 0;
+	}
+	header = (os_header_t *) stage->bh->b_data;
+	aux = stage->aux;
+	if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0) {
+		printk(KERN_INFO "ide-tape: %s: invalid header identification string\n", tape->name);
+		__idetape_kfree_stage (stage);
+		return 0;
+	}
+	if (header->major_rev != 1 || (header->minor_rev != 1 && header->minor_rev != 2))
+		printk(KERN_INFO "ide-tape: warning: revision %d.%d detected (1.1/1.2 supported)\n", header->major_rev, header->minor_rev);
+	if (header->par_num != 1)
+		printk(KERN_INFO "ide-tape: warning: %d partitions defined, only one supported\n", header->par_num);
+	tape->wrt_pass_cntr = ntohs(header->partition.wrt_pass_cntr);
+	tape->eod_frame_addr = ntohl(header->partition.eod_frame_addr);
+	tape->filemark_cnt = ntohl(aux->filemark_cnt);
+	tape->first_mark_addr = ntohl(aux->next_mark_addr);
+	tape->last_mark_addr = ntohl(aux->last_mark_addr);
+	tape->update_frame_cntr = ntohl(aux->update_frame_cntr);
+	memcpy(tape->application_sig, aux->application_sig, 4); tape->application_sig[4] = 0;
+	if (memcmp(tape->application_sig, "LIN", 3) == 0) {
+		tape->linux_media = 1;
+		tape->linux_media_version = tape->application_sig[3] - '0';
+		if (tape->linux_media_version != 3)
+			printk(KERN_INFO "ide-tape: %s: Linux media version %d detected (current 3)\n", tape->name, tape->linux_media_version);
+	} else {
+		printk(KERN_INFO "ide-tape: %s: non Linux media detected (%s)\n", tape->name, tape->application_sig);
+		tape->linux_media = 0;
+	}
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 2)
+		printk(KERN_INFO "ide-tape: %s: detected write pass counter %d, eod frame addr %d\n", tape->name, tape->wrt_pass_cntr, tape->eod_frame_addr);
+#endif
+	__idetape_kfree_stage (stage);
+	return 1;
+}
+
+static int idetape_analyze_headers (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int position, block;
+
+	if (!tape->onstream || tape->raw) {
+		tape->header_ok = tape->linux_media = 1;
+		return 1;
+	}
+	tape->header_ok = tape->linux_media = 0;
+	position = idetape_read_position(drive);
+	for (block = 5; block < 10; block++)
+		if (__idetape_analyze_headers(drive, block))
+			goto ok;
+#if 0
+	for (block = 0xbae; block < 0xbb8; block++)
+#else
+	for (block = 0xbae; block < 0xbb3; block++)
+#endif
+		if (__idetape_analyze_headers(drive, block))
+			goto ok;
+	printk(KERN_ERR "ide-tape: %s: failed to find valid ADRL header\n", tape->name);
+	return 0;
+ok:
+	if (position < 20)
+		position = 20;
+	idetape_position_tape(drive, position, 0, 0);
+	tape->header_ok = 1;
+	return 1;
+}
+
+/*
+ *	Our character device open function.
+ */
+static int idetape_chrdev_open (struct inode *inode, struct file *filp)
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	idetape_pc_t pc;
+	unsigned int minor=MINOR (inode->i_rdev);
+			
+#if IDETAPE_DEBUG_LOG
+	printk (KERN_INFO "ide-tape: Reached idetape_chrdev_open\n");
+#endif /* IDETAPE_DEBUG_LOG */
+	
+	if ((drive = get_drive_ptr (inode->i_rdev)) == NULL)
+		return -ENXIO;
+	tape = drive->driver_data;
+
+	if (test_and_set_bit (IDETAPE_BUSY, &tape->flags))
+		return -EBUSY;
+	MOD_INC_USE_COUNT;
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 6)
+		printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_chrdev_open-1\n");
+#endif
+	if (!tape->onstream) {
+		idetape_read_position(drive);
+		if (!test_bit (IDETAPE_ADDRESS_VALID, &tape->flags))
+			(void) idetape_rewind_tape (drive);
+	} else {
+		if (minor & 64) {
+			tape->tape_block_size = tape->stage_size = 32768 + 512;
+			tape->raw = 1;
+		} else {
+			tape->tape_block_size = tape->stage_size = 32768;
+			tape->raw = 0;
+		}
+	}
+	if (idetape_wait_ready(drive, 60 * HZ)) {
+		clear_bit(IDETAPE_BUSY, &tape->flags);
+		MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 6)
+		printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_open-1\n");
+#endif
+		printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name);
+		return -EBUSY;
+	}
+	idetape_read_position(drive);
+	MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+	if (tape->debug_level >= 6)
+		printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_open-2\n");
+#endif
+	clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+
+	if (tape->chrdev_direction == idetape_direction_none) {
+		MOD_INC_USE_COUNT;
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 6)
+			printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_chrdev_open-2\n");
+#endif
+		idetape_create_prevent_cmd(drive, &pc, 1);
+		if (!idetape_queue_pc_tail (drive,&pc)) {
+			if (tape->door_locked != DOOR_EXPLICITLY_LOCKED)
+				tape->door_locked = DOOR_LOCKED;
+		}
+		idetape_analyze_headers(drive);
+	}
+	tape->max_frames = tape->cur_frames = tape->req_buffer_fill = 0;
+	idetape_restart_speed_control(drive);
+	tape->restart_speed_control_req = 0;
+	return 0;
+}
+
+/*
+ *	Our character device release function.
+ */
+static int idetape_chrdev_release (struct inode *inode, struct file *filp)
+{
+	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t pc;
+	unsigned int minor=MINOR (inode->i_rdev);
+			
+#if IDETAPE_DEBUG_LOG
+	if (tape->debug_level >= 3)
+		printk (KERN_INFO "ide-tape: Reached idetape_chrdev_release\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (tape->chrdev_direction == idetape_direction_write) {
+		idetape_empty_write_pipeline (drive);
+		tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
+		if (tape->merge_stage != NULL) {
+			idetape_pad_zeros (drive, tape->tape_block_size * (tape->user_bs_factor - 1));
+			__idetape_kfree_stage (tape->merge_stage);
+			tape->merge_stage = NULL;
+		}
+		idetape_write_filemark(drive);
+		idetape_write_eod(drive);
+		idetape_flush_tape_buffers (drive);
+		idetape_write_header(drive, minor >= 128);
+		idetape_flush_tape_buffers (drive);
+	}
+	if (tape->chrdev_direction == idetape_direction_read) {
+		if (minor < 128)
+			idetape_discard_read_pipeline (drive, 1);
+		else
+			idetape_wait_for_pipeline (drive);
+	}
+	if (tape->cache_stage != NULL) {
+		__idetape_kfree_stage (tape->cache_stage);
+		tape->cache_stage = NULL;
+	}
+	if (minor < 128)
+		(void) idetape_rewind_tape (drive);
+	if (tape->chrdev_direction == idetape_direction_none) {
+		if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) {
+			idetape_create_prevent_cmd(drive, &pc, 0);
+			if (!idetape_queue_pc_tail (drive,&pc))
+				tape->door_locked = DOOR_UNLOCKED;
+		}
+		MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+		if (tape->debug_level >= 6)
+			printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_release\n");
+#endif
+	}
+	clear_bit (IDETAPE_BUSY, &tape->flags);
+	return 0;
+}
+
+/*
+ *	idetape_identify_device is called to check the contents of the
+ *	ATAPI IDENTIFY command results. We return:
+ *
+ *	1	If the tape can be supported by us, based on the information
+ *		we have so far.
+ *
+ *	0 	If this tape driver is not currently supported by us.
+ */
+static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+	struct idetape_id_gcw gcw;
+#if IDETAPE_DEBUG_INFO
+	unsigned short mask,i;
+#endif /* IDETAPE_DEBUG_INFO */
+
+	if (!id)
+		return 0;
+
+	*((unsigned short *) &gcw) = id->config;
+
+#if IDETAPE_DEBUG_INFO
+	printk (KERN_INFO "ide-tape: Dumping ATAPI Identify Device tape parameters\n");
+	printk (KERN_INFO "ide-tape: Protocol Type: ");
+	switch (gcw.protocol) {
+		case 0: case 1: printk (KERN_INFO "ATA\n");break;
+		case 2:	printk (KERN_INFO "ATAPI\n");break;
+		case 3: printk (KERN_INFO "Reserved (Unknown to ide-tape)\n");break;
+	}
+	printk (KERN_INFO "ide-tape: Device Type: %x - ",gcw.device_type);	
+	switch (gcw.device_type) {
+		case 0: printk (KERN_INFO "Direct-access Device\n");break;
+		case 1: printk (KERN_INFO "Streaming Tape Device\n");break;
+		case 2: case 3: case 4: printk (KERN_INFO "Reserved\n");break;
+		case 5: printk (KERN_INFO "CD-ROM Device\n");break;
+		case 6: printk (KERN_INFO "Reserved\n");
+		case 7: printk (KERN_INFO "Optical memory Device\n");break;
+		case 0x1f: printk (KERN_INFO "Unknown or no Device type\n");break;
+		default: printk (KERN_INFO "Reserved\n");
+	}
+	printk (KERN_INFO "ide-tape: Removable: %s",gcw.removable ? "Yes\n":"No\n");	
+	printk (KERN_INFO "ide-tape: Command Packet DRQ Type: ");
+	switch (gcw.drq_type) {
+		case 0: printk (KERN_INFO "Microprocessor DRQ\n");break;
+		case 1: printk (KERN_INFO "Interrupt DRQ\n");break;
+		case 2: printk (KERN_INFO "Accelerated DRQ\n");break;
+		case 3: printk (KERN_INFO "Reserved\n");break;
+	}
+	printk (KERN_INFO "ide-tape: Command Packet Size: ");
+	switch (gcw.packet_size) {
+		case 0: printk (KERN_INFO "12 bytes\n");break;
+		case 1: printk (KERN_INFO "16 bytes\n");break;
+		default: printk (KERN_INFO "Reserved\n");break;
+	}
+	printk (KERN_INFO "ide-tape: Model: %.40s\n",id->model);
+	printk (KERN_INFO "ide-tape: Firmware Revision: %.8s\n",id->fw_rev);
+	printk (KERN_INFO "ide-tape: Serial Number: %.20s\n",id->serial_no);
+	printk (KERN_INFO "ide-tape: Write buffer size: %d bytes\n",id->buf_size*512);
+	printk (KERN_INFO "ide-tape: DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+	printk (KERN_INFO "ide-tape: LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+	printk (KERN_INFO "ide-tape: IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+	printk (KERN_INFO "ide-tape: IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+	printk (KERN_INFO "ide-tape: ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+	printk (KERN_INFO "ide-tape: PIO Cycle Timing Category: %d\n",id->tPIO);
+	printk (KERN_INFO "ide-tape: DMA Cycle Timing Category: %d\n",id->tDMA);
+	printk (KERN_INFO "ide-tape: Single Word DMA supported modes: ");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_1word & mask)
+			printk (KERN_INFO "%d ",i);
+		if (id->dma_1word & (mask << 8))
+			printk (KERN_INFO "(active) ");
+	}
+	printk (KERN_INFO "\n");
+	printk (KERN_INFO "ide-tape: Multi Word DMA supported modes: ");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_mword & mask)
+			printk (KERN_INFO "%d ",i);
+		if (id->dma_mword & (mask << 8))
+			printk (KERN_INFO "(active) ");
+	}
+	printk (KERN_INFO "\n");
+	if (id->field_valid & 0x0002) {
+		printk (KERN_INFO "ide-tape: Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+		printk (KERN_INFO "ide-tape: Minimum Multi-word DMA cycle per word: ");
+		if (id->eide_dma_min == 0)
+			printk (KERN_INFO "Not supported\n");
+		else
+			printk (KERN_INFO "%d ns\n",id->eide_dma_min);
+
+		printk (KERN_INFO "ide-tape: Manufacturer\'s Recommended Multi-word cycle: ");
+		if (id->eide_dma_time == 0)
+			printk (KERN_INFO "Not supported\n");
+		else
+			printk (KERN_INFO "%d ns\n",id->eide_dma_time);
+
+		printk (KERN_INFO "ide-tape: Minimum PIO cycle without IORDY: ");
+		if (id->eide_pio == 0)
+			printk (KERN_INFO "Not supported\n");
+		else
+			printk (KERN_INFO "%d ns\n",id->eide_pio);
+
+		printk (KERN_INFO "ide-tape: Minimum PIO cycle with IORDY: ");
+		if (id->eide_pio_iordy == 0)
+			printk (KERN_INFO "Not supported\n");
+		else
+			printk (KERN_INFO "%d ns\n",id->eide_pio_iordy);
+		
+	} else
+		printk (KERN_INFO "ide-tape: According to the device, fields 64-70 are not valid.\n");
+#endif /* IDETAPE_DEBUG_INFO */
+
+	/* Check that we can support this device */
+
+	if (gcw.protocol !=2 )
+		printk (KERN_ERR "ide-tape: Protocol is not ATAPI\n");
+	else if (gcw.device_type != 1)
+		printk (KERN_ERR "ide-tape: Device type is not set to tape\n");
+	else if (!gcw.removable)
+		printk (KERN_ERR "ide-tape: The removable flag is not set\n");
+	else if (gcw.packet_size != 0) {
+		printk (KERN_ERR "ide-tape: Packet size is not 12 bytes long\n");
+		if (gcw.packet_size == 1)
+			printk (KERN_ERR "ide-tape: Sorry, padding to 16 bytes is still not supported\n");
+	} else
+		return 1;
+	return 0;
+}
+
+/*
+ * Notify vendor ID to the OnStream tape drive
+ */
+static void idetape_onstream_set_vendor (ide_drive_t *drive, char *vendor)
+{
+	idetape_pc_t pc;
+	idetape_mode_parameter_header_t *header;
+
+	idetape_create_mode_select_cmd(&pc, sizeof(*header) + 8);
+	pc.buffer[0] = 3 + 8;	/* Mode Data Length */
+	pc.buffer[1] = 0;	/* Medium Type - ignoring */
+	pc.buffer[2] = 0;	/* Reserved */
+	pc.buffer[3] = 0;	/* Block Descriptor Length */
+	pc.buffer[4 + 0] = 0x36 | (1 << 7);
+	pc.buffer[4 + 1] = 6;
+	pc.buffer[4 + 2] = vendor[0];
+	pc.buffer[4 + 3] = vendor[1];
+	pc.buffer[4 + 4] = vendor[2];
+	pc.buffer[4 + 5] = vendor[3];
+	pc.buffer[4 + 6] = 0;
+	pc.buffer[4 + 7] = 0;
+	if (idetape_queue_pc_tail (drive,&pc))
+		printk (KERN_ERR "ide-tape: Couldn't set vendor name to %s\n", vendor);
+
+}
+
+/*
+ * Various unused OnStream commands
+ */
+#if ONSTREAM_DEBUG
+static void idetape_onstream_set_retries (ide_drive_t *drive, int retries)
+{
+	idetape_pc_t pc;
+
+	idetape_create_mode_select_cmd(&pc, sizeof(idetape_mode_parameter_header_t) + 4);
+	pc.buffer[0] = 3 + 4;
+	pc.buffer[1] = 0;	/* Medium Type - ignoring */
+	pc.buffer[2] = 0;	/* Reserved */
+	pc.buffer[3] = 0;	/* Block Descriptor Length */
+	pc.buffer[4 + 0] = 0x2f | (1 << 7);
+	pc.buffer[4 + 1] = 2;
+	pc.buffer[4 + 2] = 4;
+	pc.buffer[4 + 3] = retries;
+	if (idetape_queue_pc_tail (drive,&pc))
+		printk (KERN_ERR "ide-tape: Couldn't set retries to %d\n", retries);
+}
+#endif
+
+/*
+ * Configure 32.5KB block size.
+ */
+static void idetape_onstream_configure_block_size (ide_drive_t *drive)
+{
+	idetape_pc_t pc;
+	idetape_mode_parameter_header_t *header;
+	idetape_block_size_page_t *bs;
+
+	/*
+	 * Get the current block size from the block size mode page
+	 */
+	idetape_create_mode_sense_cmd (&pc,IDETAPE_BLOCK_SIZE_PAGE);
+	if (idetape_queue_pc_tail (drive,&pc))
+		printk (KERN_ERR "ide-tape: can't get tape block size mode page\n");
+	header = (idetape_mode_parameter_header_t *) pc.buffer;
+	bs = (idetape_block_size_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl);
+
+#if IDETAPE_DEBUG_INFO
+	printk(KERN_INFO "ide-tape: 32KB play back: %s\n", bs->play32 ? "Yes" : "No");
+	printk(KERN_INFO "ide-tape: 32.5KB play back: %s\n", bs->play32_5 ? "Yes" : "No");
+	printk(KERN_INFO "ide-tape: 32KB record: %s\n", bs->record32 ? "Yes" : "No");
+	printk(KERN_INFO "ide-tape: 32.5KB record: %s\n", bs->record32_5 ? "Yes" : "No");
+#endif /* IDETAPE_DEBUG_INFO */
+
+	/*
+	 * Configure default auto columns mode, 32.5KB block size
+	 */ 
+	bs->one = 1;
+	bs->play32 = 0;
+	bs->play32_5 = 1;
+	bs->record32 = 0;
+	bs->record32_5 = 1;
+	idetape_create_mode_select_cmd(&pc, sizeof(*header) + sizeof(*bs));
+	if (idetape_queue_pc_tail (drive,&pc))
+		printk (KERN_ERR "ide-tape: Couldn't set tape block size mode page\n");
+
+#if ONSTREAM_DEBUG
+	/*
+	 * In debug mode, we want to see as many errors as possible
+	 * to test the error recovery mechanism.
+	 */
+	idetape_onstream_set_retries(drive, 0);
+#endif
+}
+
+/*
+ * Use INQUIRY to get the firmware revision
+ */
+static void idetape_get_inquiry_results (ide_drive_t *drive)
+{
+	char *r;
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t pc;
+	idetape_inquiry_result_t *inquiry;
+	
+	idetape_create_inquiry_cmd(&pc);
+	if (idetape_queue_pc_tail (drive,&pc)) {
+		printk (KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", tape->name);
+		return;
+	}
+	inquiry = (idetape_inquiry_result_t *) pc.buffer;
+	memcpy(tape->vendor_id, inquiry->vendor_id, 8);
+	memcpy(tape->product_id, inquiry->product_id, 16);
+	memcpy(tape->firmware_revision, inquiry->revision_level, 4);
+	ide_fixstring(tape->vendor_id, 10, 0);
+	ide_fixstring(tape->product_id, 18, 0);
+	ide_fixstring(tape->firmware_revision, 6, 0);
+	r = tape->firmware_revision;
+	if (*(r + 1) == '.')
+		tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 2) - '0') * 10 + *(r + 3) - '0';
+	else if (tape->onstream)
+		tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 1) - '0') * 10 + *(r + 2) - '0';
+	printk(KERN_INFO "ide-tape: %s <-> %s: %s %s rev %s\n", drive->name, tape->name, tape->vendor_id, tape->product_id, tape->firmware_revision);
+}
+
+/*
+ * Configure the OnStream ATAPI tape drive for default operation
+ */
+static void idetape_configure_onstream (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+	if (tape->firmware_revision_num < 105) {
+		printk(KERN_INFO "ide-tape: %s: Old OnStream firmware revision detected (%s)\n", tape->name, tape->firmware_revision);
+		printk(KERN_INFO "ide-tape: %s: An upgrade to version 1.05 or above is recommended\n", tape->name);
+	}
+
+	/*
+	 * Configure 32.5KB (data+aux) block size.
+	 */
+	idetape_onstream_configure_block_size(drive);
+
+	/*
+	 * Set vendor name to 'LIN3' for "Linux support version 3".
+	 */
+	idetape_onstream_set_vendor(drive, "LIN3");
+}
+
+/*
+ *	idetape_get_mode_sense_results asks the tape about its various
+ *	parameters. In particular, we will adjust our data transfer buffer
+ *	size to the recommended value as returned by the tape.
+ */
+static void idetape_get_mode_sense_results (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	idetape_pc_t pc;
+	idetape_mode_parameter_header_t *header;
+	idetape_capabilities_page_t *capabilities;
+	
+	idetape_create_mode_sense_cmd (&pc,IDETAPE_CAPABILITIES_PAGE);
+	if (idetape_queue_pc_tail (drive,&pc)) {
+		printk (KERN_ERR "ide-tape: Can't get tape parameters - assuming some default values\n");
+		tape->tape_block_size = 512; tape->capabilities.ctl = 52;
+		tape->capabilities.speed = 450; tape->capabilities.buffer_size = 6 * 52;
+		return;
+	}
+	header = (idetape_mode_parameter_header_t *) pc.buffer;
+	capabilities = (idetape_capabilities_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl);
+
+	capabilities->max_speed = ntohs (capabilities->max_speed);
+	capabilities->ctl = ntohs (capabilities->ctl);
+	capabilities->speed = ntohs (capabilities->speed);
+	capabilities->buffer_size = ntohs (capabilities->buffer_size);
+
+	if (!capabilities->speed) {
+		printk(KERN_INFO "ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name);
+		capabilities->speed = 650;
+	}
+	if (!capabilities->max_speed) {
+		printk(KERN_INFO "ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name);
+		capabilities->max_speed = 650;
+	}
+
+	tape->capabilities = *capabilities;		/* Save us a copy */
+	if (capabilities->blk512)
+		tape->tape_block_size = 512;
+	else if (capabilities->blk1024)
+		tape->tape_block_size = 1024;
+	else if (tape->onstream && capabilities->blk32768)
+		tape->tape_block_size = 32768;
+
+#if IDETAPE_DEBUG_INFO
+	printk (KERN_INFO "ide-tape: Dumping the results of the MODE SENSE packet command\n");
+	printk (KERN_INFO "ide-tape: Mode Parameter Header:\n");
+	printk (KERN_INFO "ide-tape: Mode Data Length - %d\n",header->mode_data_length);
+	printk (KERN_INFO "ide-tape: Medium Type - %d\n",header->medium_type);
+	printk (KERN_INFO "ide-tape: Device Specific Parameter - %d\n",header->dsp);
+	printk (KERN_INFO "ide-tape: Block Descriptor Length - %d\n",header->bdl);
+	
+	printk (KERN_INFO "ide-tape: Capabilities and Mechanical Status Page:\n");
+	printk (KERN_INFO "ide-tape: Page code - %d\n",capabilities->page_code);
+	printk (KERN_INFO "ide-tape: Page length - %d\n",capabilities->page_length);
+	printk (KERN_INFO "ide-tape: Read only - %s\n",capabilities->ro ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports reverse space - %s\n",capabilities->sprev ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports erase initiated formatting - %s\n",capabilities->efmt ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports QFA two Partition format - %s\n",capabilities->qfa ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports locking the medium - %s\n",capabilities->lock ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: The volume is currently locked - %s\n",capabilities->locked ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: The device defaults in the prevent state - %s\n",capabilities->prevent ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports ejecting the medium - %s\n",capabilities->eject ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports error correction - %s\n",capabilities->ecc ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Supports 32768 bytes block size / Restricted byte count for PIO transfers - %s\n",capabilities->blk32768 ? "Yes":"No");
+	printk (KERN_INFO "ide-tape: Maximum supported speed in KBps - %d\n",capabilities->max_speed);
+	printk (KERN_INFO "ide-tape: Continuous transfer limits in blocks - %d\n",capabilities->ctl);
+	printk (KERN_INFO "ide-tape: Current speed in KBps - %d\n",capabilities->speed);	
+	printk (KERN_INFO "ide-tape: Buffer size - %d\n",capabilities->buffer_size*512);
+#endif /* IDETAPE_DEBUG_INFO */
+}
+
+static void idetape_add_settings (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+
+/*
+ *			drive	setting name	read/write	ioctl	ioctl		data type	min			max			mul_factor			div_factor			data pointer				set function
+ */
+	ide_add_setting(drive,	"buffer",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				2,				&tape->capabilities.buffer_size,	NULL);
+	ide_add_setting(drive,	"pipeline_min",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->min_pipeline,			NULL);
+	ide_add_setting(drive,	"pipeline",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->max_stages,			NULL);
+	ide_add_setting(drive,	"pipeline_max",	SETTING_RW,	-1,	-1,		TYPE_INT,	2,			0xffff,			tape->stage_size / 1024,	1,				&tape->max_pipeline,			NULL);
+	ide_add_setting(drive,	"pipeline_used",SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->nr_stages,			NULL);
+	ide_add_setting(drive,	"pipeline_pending",SETTING_READ,-1,	-1,		TYPE_INT,	0,			0xffff,			tape->stage_size / 1024,	1,				&tape->nr_pending_stages,		NULL);
+	ide_add_setting(drive,	"speed",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->capabilities.speed,		NULL);
+	ide_add_setting(drive,	"stage",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1024,				&tape->stage_size,			NULL);
+	ide_add_setting(drive,	"tdsc",		SETTING_RW,	-1,	-1,		TYPE_INT,	IDETAPE_DSC_RW_MIN,	IDETAPE_DSC_RW_MAX,	1000,				HZ,				&tape->best_dsc_rw_frequency,		NULL);
+	ide_add_setting(drive,	"dsc_overlap",	SETTING_RW,	-1,	-1,		TYPE_BYTE,	0,			1,			1,				1,				&drive->dsc_overlap,			NULL);
+	ide_add_setting(drive,	"pipeline_head_speed_c",SETTING_READ,	-1,	-1,	TYPE_INT,	0,			0xffff,			1,				1,				&tape->controlled_pipeline_head_speed,	NULL);
+	ide_add_setting(drive,	"pipeline_head_speed_u",SETTING_READ,	-1,	-1,	TYPE_INT,	0,			0xffff,			1,				1,				&tape->uncontrolled_pipeline_head_speed,	NULL);
+	ide_add_setting(drive,	"avg_speed",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->avg_speed,		NULL);
+	if (tape->onstream) {
+		ide_add_setting(drive,	"cur_frames",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->cur_frames,		NULL);
+		ide_add_setting(drive,	"max_frames",	SETTING_READ,	-1,	-1,		TYPE_SHORT,	0,			0xffff,			1,				1,				&tape->max_frames,		NULL);
+		ide_add_setting(drive,	"insert_speed",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->insert_speed,		NULL);
+		ide_add_setting(drive,	"speed_control",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->speed_control,		NULL);
+		ide_add_setting(drive,	"debug_level",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->debug_level,		NULL);
+		ide_add_setting(drive,	"tape_still_time",SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->tape_still_time,		NULL);
+		ide_add_setting(drive,	"max_insert_speed",SETTING_RW,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->max_insert_speed,	NULL);
+		ide_add_setting(drive,	"insert_size",	SETTING_READ,	-1,	-1,		TYPE_INT,	0,			0xffff,			1,				1,				&tape->insert_size,		NULL);
+	}
+}
+
+/*
+ *	ide_setup is called to:
+ *
+ *		1.	Initialize our various state variables.
+ *		2.	Ask the tape for its capabilities.
+ *		3.	Allocate a buffer which will be used for data
+ *			transfer. The buffer size is chosen based on
+ *			the recommendation which we received in step (2).
+ *
+ *	Note that at this point ide.c already assigned us an irq, so that
+ *	we can queue requests here and wait for their completion.
+ */
+static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor)
+{
+	unsigned long t1, tmid, tn, t;
+	int speed;
+	struct idetape_id_gcw gcw;
+	int stage_size;
+
+	memset (tape, 0, sizeof (idetape_tape_t));
+	spin_lock_init(&tape->spinlock);
+	drive->driver_data = tape;
+	drive->ready_stat = 0;			/* An ATAPI device ignores DRDY */
+	if (strstr(drive->id->model, "OnStream DI-30"))
+		tape->onstream = 1;
+	drive->dsc_overlap = 1;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+	if (!tape->onstream && HWIF(drive)->pci_dev != NULL) {
+		/*
+		 * These two ide-pci host adapters appear to need DSC overlap disabled.
+		 * This probably needs further analysis.
+		 */
+		if ((HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) ||
+		    (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_TTI_HPT343)) {
+			printk(KERN_INFO "ide-tape: %s: disabling DSC overlap\n", tape->name);
+		    	drive->dsc_overlap = 0;
+		}
+	}
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+	tape->drive = drive;
+	tape->minor = minor;
+	tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor;
+	tape->chrdev_direction = idetape_direction_none;
+	tape->pc = tape->pc_stack;
+	tape->max_insert_speed = 10000;
+	tape->speed_control = 1;
+	*((unsigned short *) &gcw) = drive->id->config;
+	if (gcw.drq_type == 1)
+		set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags);
+
+	tape->min_pipeline = tape->max_pipeline = tape->max_stages = 10;
+	
+	idetape_get_inquiry_results(drive);
+	idetape_get_mode_sense_results(drive);
+	if (tape->onstream)
+		idetape_configure_onstream(drive);
+
+	tape->user_bs_factor = 1;
+	tape->stage_size = tape->capabilities.ctl * tape->tape_block_size;
+	while (tape->stage_size > 0xffff) {
+		printk (KERN_NOTICE "ide-tape: decreasing stage size\n");
+		tape->capabilities.ctl /= 2;
+		tape->stage_size = tape->capabilities.ctl * tape->tape_block_size;
+	}
+	stage_size = tape->stage_size;
+	if (tape->onstream)
+		stage_size = 32768 + 512;
+	tape->pages_per_stage = stage_size / PAGE_SIZE;
+	if (stage_size % PAGE_SIZE) {
+		tape->pages_per_stage++;
+		tape->excess_bh_size = PAGE_SIZE - stage_size % PAGE_SIZE;
+	}
+
+	/*
+	 *	Select the "best" DSC read/write polling frequency
+	 *	and pipeline size.
+	 */
+	speed = IDE_MAX (tape->capabilities.speed, tape->capabilities.max_speed);
+
+	tape->max_stages = speed * 1000 * 10 / tape->stage_size;
+	tape->min_pipeline = tape->max_stages;
+	tape->max_pipeline = tape->max_stages * 2;
+
+	t1 = (tape->stage_size * HZ) / (speed * 1000);
+	tmid = (tape->capabilities.buffer_size * 32 * HZ) / (speed * 125);
+	tn = (IDETAPE_FIFO_THRESHOLD * tape->stage_size * HZ) / (speed * 1000);
+
+	if (tape->max_stages)
+		t = tn;
+	else
+		t = t1;
+
+	/*
+	 *	Ensure that the number we got makes sense; limit
+	 *	it within IDETAPE_DSC_RW_MIN and IDETAPE_DSC_RW_MAX.
+	 */
+	tape->best_dsc_rw_frequency = IDE_MAX (IDE_MIN (t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN);
+	printk (KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, %dkB pipeline, %lums tDSC%s\n",
+		drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size,
+		tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024,
+		tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":"");
+
+	idetape_add_settings(drive);
+}
+
+static int idetape_cleanup (ide_drive_t *drive)
+{
+	idetape_tape_t *tape = drive->driver_data;
+	int minor = tape->minor;
+	unsigned long flags;
+
+	save_flags (flags);	/* all CPUs (overkill?) */
+	cli();			/* all CPUs (overkill?) */
+	if (test_bit (IDETAPE_BUSY, &tape->flags) || tape->first_stage != NULL || tape->merge_stage_size || drive->usage) {
+		restore_flags(flags);	/* all CPUs (overkill?) */
+		return 1;
+	}
+	idetape_chrdevs[minor].drive = NULL;
+	restore_flags (flags);	/* all CPUs (overkill?) */
+	DRIVER(drive)->busy = 0;
+	(void) ide_unregister_subdriver (drive);
+	drive->driver_data = NULL;
+	devfs_unregister (tape->de_r);
+	devfs_unregister (tape->de_n);
+	kfree (tape);
+	for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++)
+		if (idetape_chrdevs[minor].drive != NULL)
+			return 0;
+	devfs_unregister_chrdev (IDETAPE_MAJOR, "ht");
+	idetape_chrdev_present = 0;
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static int proc_idetape_read_name
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	idetape_tape_t	*tape = drive->driver_data;
+	char		*out = page;
+	int		len;
+
+	len = sprintf(out,"%s\n", tape->name);
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t idetape_proc[] = {
+	{ "name",	S_IFREG|S_IRUGO,	proc_idetape_read_name,	NULL },
+	{ NULL, 0, NULL, NULL }
+};
+
+#else
+
+#define	idetape_proc	NULL
+
+#endif
+
+/*
+ *	IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idetape_driver = {
+	"ide-tape",		/* name */
+	IDETAPE_VERSION,	/* version */
+	ide_tape,		/* media */
+	1,			/* busy */
+	1,			/* supports_dma */
+	1,			/* supports_dsc_overlap */
+	idetape_cleanup,	/* cleanup */
+	idetape_do_request,	/* do_request */
+	idetape_end_request,	/* end_request */
+	idetape_blkdev_ioctl,	/* ioctl */
+	idetape_blkdev_open,	/* open */
+	idetape_blkdev_release,	/* release */
+	NULL,			/* media_change */
+	idetape_pre_reset,	/* pre_reset */
+	NULL,			/* capacity */
+	NULL,			/* special */
+	idetape_proc		/* proc */
+};
+
+int idetape_init (void);
+static ide_module_t idetape_module = {
+	IDE_DRIVER_MODULE,
+	idetape_init,
+	&idetape_driver,
+	NULL
+};
+
+/*
+ *	Our character device supporting functions, passed to register_chrdev.
+ */
+static struct file_operations idetape_fops = {
+	read:		idetape_chrdev_read,
+	write:		idetape_chrdev_write,
+	ioctl:		idetape_chrdev_ioctl,
+	open:		idetape_chrdev_open,
+	release:	idetape_chrdev_release,
+};
+
+/*
+ *	idetape_init will register the driver for each tape.
+ */
+int idetape_init (void)
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	int minor, failed = 0, supported = 0;
+
+	MOD_INC_USE_COUNT;
+#if ONSTREAM_DEBUG
+        printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_init\n");
+#endif
+	if (!idetape_chrdev_present)
+		for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ )
+			idetape_chrdevs[minor].drive = NULL;
+
+	if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) {
+		ide_register_module (&idetape_module);
+		MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+		printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
+#endif
+		return 0;
+	}
+	if (!idetape_chrdev_present &&
+	    devfs_register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) {
+		printk (KERN_ERR "ide-tape: Failed to register character device interface\n");
+		MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+		printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
+#endif
+		return -EBUSY;
+	}
+	do {
+		if (!idetape_identify_device (drive, drive->id)) {
+			printk (KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name);
+			continue;
+		}
+		if (drive->scsi) {
+			if (strstr(drive->id->model, "OnStream DI-30")) {
+				printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model);
+			} else {
+				printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name);
+				continue;
+			}
+		}
+		tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL);
+		if (tape == NULL) {
+			printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name);
+			continue;
+		}
+		if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) {
+			printk (KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name);
+			kfree (tape);
+			continue;
+		}
+		for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++);
+		idetape_setup (drive, tape, minor);
+		idetape_chrdevs[minor].drive = drive;
+		tape->de_r =
+		    devfs_register (drive->de, "mt", 2, DEVFS_FL_DEFAULT,
+				    HWIF(drive)->major, minor,
+				    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+				    &idetape_fops, NULL);
+		tape->de_n =
+		    devfs_register (drive->de, "mtn", 3, DEVFS_FL_DEFAULT,
+				    HWIF(drive)->major, minor + 128,
+				    S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+				    &idetape_fops, NULL);
+		devfs_register_tape (tape->de_r);
+		supported++; failed--;
+	} while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL);
+	if (!idetape_chrdev_present && !supported) {
+		devfs_unregister_chrdev (IDETAPE_MAJOR, "ht");
+	} else
+		idetape_chrdev_present = 1;
+	ide_register_module (&idetape_module);
+	MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+	printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
+#endif
+	return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return idetape_init ();
+}
+
+void cleanup_module (void)
+{
+	ide_drive_t *drive;
+	int minor;
+
+	for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) {
+		drive = idetape_chrdevs[minor].drive;
+		if (drive != NULL && idetape_cleanup (drive))
+			printk (KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name);
+	}
+	ide_unregister_module(&idetape_module);
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide.c linux/drivers/ide/ide.c
--- v2.3.51/linux/drivers/ide/ide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide.c	Sun Mar 12 19:32:58 2000
@@ -0,0 +1,3639 @@
+/*
+ *  linux/drivers/block/ide.c		Version 6.30	Dec 28, 1999
+ *
+ *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
+ */
+
+/*
+ *  Mostly written by Mark Lord  <mlord@pobox.com>
+ *                and Gadi Oxman <gadio@netvision.net.il>
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
+ *
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to MAX_HWIFS IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary:    ide0, port 0x1f0; major=3;  hda is minor=0; hdb is minor=64
+ * Secondary:  ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
+ * Tertiary:   ide2, port 0x???; major=33; hde is minor=0; hdf is minor=64
+ * Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64
+ * ...
+ *
+ *  From hd.c:
+ *  |
+ *  | It traverses the request-list, using interrupts to jump between functions.
+ *  | As nearly all functions can be called within interrupts, we may not sleep.
+ *  | Special care is recommended.  Have Fun!
+ *  |
+ *  | modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ *  |
+ *  | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ *  | in the early extended-partition checks and added DM partitions.
+ *  |
+ *  | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
+ *  |
+ *  | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ *  | and general streamlining by Mark Lord (mlord@pobox.com).
+ *
+ *  October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
+ *
+ *	Mark Lord	(mlord@pobox.com)		(IDE Perf.Pkg)
+ *	Delman Lee	(delman@ieee.org)		("Mr. atdisk2")
+ *	Scott Snyder	(snyder@fnald0.fnal.gov)	(ATAPI IDE cd-rom)
+ *
+ *  This was a rewrite of just about everything from hd.c, though some original
+ *  code is still sprinkled about.  Think of it as a major evolution, with
+ *  inspiration from lots of linux users, esp.  hamish@zot.apana.org.au
+ *
+ *  Version 1.0 ALPHA	initial code, primary i/f working okay
+ *  Version 1.3 BETA	dual i/f on shared irq tested & working!
+ *  Version 1.4 BETA	added auto probing for irq(s)
+ *  Version 1.5 BETA	added ALPHA (untested) support for IDE cd-roms,
+ *  ...
+ * Version 5.50		allow values as small as 20 for idebus=
+ * Version 5.51		force non io_32bit in drive_cmd_intr()
+ *			change delay_10ms() to delay_50ms() to fix problems
+ * Version 5.52		fix incorrect invalidation of removable devices
+ *			add "hdx=slow" command line option
+ * Version 5.60		start to modularize the driver; the disk and ATAPI
+ *			 drivers can be compiled as loadable modules.
+ *			move IDE probe code to ide-probe.c
+ *			move IDE disk code to ide-disk.c
+ *			add support for generic IDE device subdrivers
+ *			add m68k code from Geert Uytterhoeven
+ *			probe all interfaces by default
+ *			add ioctl to (re)probe an interface
+ * Version 6.00		use per device request queues
+ *			attempt to optimize shared hwgroup performance
+ *			add ioctl to manually adjust bandwidth algorithms
+ *			add kerneld support for the probe module
+ *			fix bug in ide_error()
+ *			fix bug in the first ide_get_lock() call for Atari
+ *			don't flush leftover data for ATAPI devices
+ * Version 6.01		clear hwgroup->active while the hwgroup sleeps
+ *			support HDIO_GETGEO for floppies
+ * Version 6.02		fix ide_ack_intr() call
+ *			check partition table on floppies
+ * Version 6.03		handle bad status bit sequencing in ide_wait_stat()
+ * Version 6.10		deleted old entries from this list of updates
+ *			replaced triton.c with ide-dma.c generic PCI DMA
+ *			added support for BIOS-enabled UltraDMA
+ *			rename all "promise" things to "pdc4030"
+ *			fix EZ-DRIVE handling on small disks
+ * Version 6.11		fix probe error in ide_scan_devices()
+ *			fix ancient "jiffies" polling bugs
+ *			mask all hwgroup interrupts on each irq entry
+ * Version 6.12		integrate ioctl and proc interfaces
+ *			fix parsing of "idex=" command line parameter
+ * Version 6.13		add support for ide4/ide5 courtesy rjones@orchestream.com
+ * Version 6.14		fixed IRQ sharing among PCI devices
+ * Version 6.15		added SMP awareness to IDE drivers
+ * Version 6.16		fixed various bugs; even more SMP friendly
+ * Version 6.17		fix for newest EZ-Drive problem
+ * Version 6.18		default unpartitioned-disk translation now "BIOS LBA"
+ * Version 6.19		Re-design for a UNIFORM driver for all platforms,
+ *			  model based on suggestions from Russell King and
+ *			  Geert Uytterhoeven
+ *			Promise DC4030VL now supported.
+ *			add support for ide6/ide7
+ *			delay_50ms() changed to ide_delay_50ms() and exported.
+ * Version 6.20		Added/Fixed Generic ATA-66 support and hwif detection.
+ *			Added hdx=flash to allow for second flash disk
+ *			  detection w/o the hang loop.
+ *			Added support for ide8/ide9
+ *			Added idex=ata66 for the quirky chipsets that are
+ *			  ATA-66 compliant, but have yet to determine a method
+ *			  of verification of the 80c cable presence.
+ *			  Specifically Promise's PDC20262 chipset.
+ * Version 6.21		Fixing/Fixed SMP spinlock issue with insight from an old
+ *			  hat that clarified original low level driver design.
+ * Version 6.30		Added SMP support; fixed multmode issues.  -ml
+ *
+ *  Some additional driver compile-time options are in ./include/linux/ide.h
+ *
+ *  To do, in likely order of completion:
+ *	- modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
+ *
+ */
+
+#define	REVISION	"Revision: 6.30"
+#define	VERSION		"Id: ide.c 6.30 1999/12/28"
+
+#undef REALLY_SLOW_IO		/* most systems can safely undef this */
+
+#define _IDE_C			/* Tell ide.h it's really us */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/blkpg.h>
+#include <linux/malloc.h>
+#ifndef MODULE
+#include <linux/init.h>
+#endif /* MODULE */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#include "ide_modes.h"
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif /* CONFIG_KMOD */
+
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+extern byte fifoconfig;		/* defined in via82cxxx.c used by ide_setup() */
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+
+static const byte ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR };
+
+static int	idebus_parameter = 0; /* holds the "idebus=" parameter */
+static int	system_bus_speed = 0; /* holds what we think is VESA/PCI bus speed */
+static int	initializing;     /* set while initializing built-in drivers */
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+static int	ide_scan_direction = 0;	/* THIS was formerly 2.2.x pci=reverse */
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+/*
+ * ide_lock is used by the Atari code to obtain access to the IDE interrupt,
+ * which is shared between several drivers.
+ */
+static int	ide_lock = 0;
+#endif /* __mc68000__ || CONFIG_APUS */
+
+/*
+ * ide_modules keeps track of the available IDE chipset/probe/driver modules.
+ */
+ide_module_t *ide_modules	= NULL;
+ide_module_t *ide_probe		= NULL;
+
+/*
+ * This is declared extern in ide.h, for access by other IDE modules:
+ */
+ide_hwif_t	ide_hwifs[MAX_HWIFS];	/* master data repository */
+
+#if (DISK_RECOVERY_TIME > 0)
+/*
+ * For really screwy hardware (hey, at least it *can* be used with Linux)
+ * we can enforce a minimum delay time between successive operations.
+ */
+static unsigned long read_timer (void)
+{
+	unsigned long t, flags;
+	int i;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	t = jiffies * 11932;
+    	outb_p(0, 0x43);
+	i = inb_p(0x40);
+	i |= inb(0x40) << 8;
+	__restore_flags(flags);	/* local CPU only */
+	return (t - i);
+}
+#endif /* DISK_RECOVERY_TIME */
+
+static inline void set_recovery_timer (ide_hwif_t *hwif)
+{
+#if (DISK_RECOVERY_TIME > 0)
+	hwif->last_time = read_timer();
+#endif /* DISK_RECOVERY_TIME */
+}
+
+/*
+ * Do not even *think* about calling this!
+ */
+static void init_hwif_data (unsigned int index)
+{
+	unsigned int unit;
+	hw_regs_t hw;
+	ide_hwif_t *hwif = &ide_hwifs[index];
+
+	/* bulk initialize hwif & drive info with zeros */
+	memset(hwif, 0, sizeof(ide_hwif_t));
+	memset(&hw, 0, sizeof(hw_regs_t));
+
+	/* fill in any non-zero initial values */
+	hwif->index     = index;
+	ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &hwif->irq);
+	memcpy(&hwif->hw, &hw, sizeof(hw));
+	memcpy(hwif->io_ports, hw.io_ports, sizeof(hw.io_ports));
+	hwif->noprobe	= !hwif->io_ports[IDE_DATA_OFFSET];
+#ifdef CONFIG_BLK_DEV_HD
+	if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
+		hwif->noprobe = 1; /* may be overridden by ide_setup() */
+#endif /* CONFIG_BLK_DEV_HD */
+	hwif->major	= ide_hwif_to_major[index];
+	hwif->name[0]	= 'i';
+	hwif->name[1]	= 'd';
+	hwif->name[2]	= 'e';
+	hwif->name[3]	= '0' + index;
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		ide_drive_t *drive = &hwif->drives[unit];
+
+		drive->media			= ide_disk;
+		drive->select.all		= (unit<<4)|0xa0;
+		drive->hwif			= hwif;
+		drive->ctl			= 0x08;
+		drive->ready_stat		= READY_STAT;
+		drive->bad_wstat		= BAD_W_STAT;
+		drive->special.b.recalibrate	= 1;
+		drive->special.b.set_geometry	= 1;
+		drive->name[0]			= 'h';
+		drive->name[1]			= 'd';
+		drive->name[2]			= 'a' + (index * MAX_DRIVES) + unit;
+		init_waitqueue_head(&drive->wqueue);
+	}
+}
+
+/*
+ * init_ide_data() sets reasonable default values into all fields
+ * of all instances of the hwifs and drives, but only on the first call.
+ * Subsequent calls have no effect (they don't wipe out anything).
+ *
+ * This routine is normally called at driver initialization time,
+ * but may also be called MUCH earlier during kernel "command-line"
+ * parameter processing.  As such, we cannot depend on any other parts
+ * of the kernel (such as memory allocation) to be functioning yet.
+ *
+ * This is too bad, as otherwise we could dynamically allocate the
+ * ide_drive_t structs as needed, rather than always consuming memory
+ * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
+ */
+#define MAGIC_COOKIE 0x12345678
+static void __init init_ide_data (void)
+{
+	unsigned int index;
+	static unsigned long magic_cookie = MAGIC_COOKIE;
+
+	if (magic_cookie != MAGIC_COOKIE)
+		return;		/* already initialized */
+	magic_cookie = 0;
+
+	/* Initialise all interface structures */
+	for (index = 0; index < MAX_HWIFS; ++index)
+		init_hwif_data(index);
+
+	/* Add default hw interfaces */
+	ide_init_default_hwifs();
+
+	idebus_parameter = 0;
+	system_bus_speed = 0;
+}
+
+/*
+ * CompactFlash cards and their brethern pretend to be removable hard disks, except:
+ *	(1) they never have a slave unit, and
+ *	(2) they don't have doorlock mechanisms.
+ * This test catches them, and is invoked elsewhere when setting appropriate config bits.
+ *
+ * FIXME: This treatment is probably applicable for *all* PCMCIA (PC CARD) devices,
+ * so in linux 2.3.x we should change this to just treat all PCMCIA drives this way,
+ * and get rid of the model-name tests below (too big of an interface change for 2.2.x).
+ * At that time, we might also consider parameterizing the timeouts and retries,
+ * since these are MUCH faster than mechanical drives.	-M.Lord
+ */
+int drive_is_flashcard (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+
+	if (drive->removable && id != NULL) {
+		if (id->config == 0x848a) return 1;	/* CompactFlash */
+		if (!strncmp(id->model, "KODAK ATA_FLASH", 15)	/* Kodak */
+		 || !strncmp(id->model, "Hitachi CV", 10)	/* Hitachi */
+		 || !strncmp(id->model, "SunDisk SDCFB", 13)	/* SunDisk */
+		 || !strncmp(id->model, "HAGIWARA HPC", 12)	/* Hagiwara */
+		 || !strncmp(id->model, "ATA_FLASH", 9))	/* Simple Tech */
+		{
+			return 1;	/* yes, it is a flash memory card */
+		}
+	}
+	return 0;	/* no, it is not a flash memory card */
+}
+
+/*
+ * ide_system_bus_speed() returns what we think is the system VESA/PCI
+ * bus speed (in MHz).  This is used for calculating interface PIO timings.
+ * The default is 40 for known PCI systems, 50 otherwise.
+ * The "idebus=xx" parameter can be used to override this value.
+ * The actual value to be used is computed/displayed the first time through.
+ */
+int ide_system_bus_speed (void)
+{
+	if (!system_bus_speed) {
+		if (idebus_parameter)
+			system_bus_speed = idebus_parameter;	/* user supplied value */
+#ifdef CONFIG_PCI
+		else if (pci_present())
+			system_bus_speed = 40;	/* safe default value for PCI */
+#endif /* CONFIG_PCI */
+		else
+			system_bus_speed = 50;	/* safe default value for VESA and PCI */
+		printk("ide: Assuming %dMHz system bus speed for PIO modes%s\n", system_bus_speed,
+			idebus_parameter ? "" : "; override with idebus=xx");
+	}
+	return system_bus_speed;
+}
+
+#if SUPPORT_VLB_SYNC
+/*
+ * Some localbus EIDE interfaces require a special access sequence
+ * when using 32-bit I/O instructions to transfer data.  We call this
+ * the "vlb_sync" sequence, which consists of three successive reads
+ * of the sector count register location, with interrupts disabled
+ * to ensure that the reads all happen together.
+ */
+static inline void do_vlb_sync (ide_ioreg_t port) {
+	(void) inb (port);
+	(void) inb (port);
+	(void) inb (port);
+}
+#endif /* SUPPORT_VLB_SYNC */
+
+/*
+ * This is used for most PIO data transfers *from* the IDE interface
+ */
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+	byte io_32bit = drive->io_32bit;
+
+	if (io_32bit) {
+#if SUPPORT_VLB_SYNC
+		if (io_32bit & 2) {
+			unsigned long flags;
+			__save_flags(flags);	/* local CPU only */
+			__cli();		/* local CPU only */
+			do_vlb_sync(IDE_NSECTOR_REG);
+			insl(IDE_DATA_REG, buffer, wcount);
+			__restore_flags(flags);	/* local CPU only */
+		} else
+#endif /* SUPPORT_VLB_SYNC */
+			insl(IDE_DATA_REG, buffer, wcount);
+	} else {
+#if SUPPORT_SLOW_DATA_PORTS
+		if (drive->slow) {
+			unsigned short *ptr = (unsigned short *) buffer;
+			while (wcount--) {
+				*ptr++ = inw_p(IDE_DATA_REG);
+				*ptr++ = inw_p(IDE_DATA_REG);
+			}
+		} else
+#endif /* SUPPORT_SLOW_DATA_PORTS */
+			insw(IDE_DATA_REG, buffer, wcount<<1);
+	}
+}
+
+/*
+ * This is used for most PIO data transfers *to* the IDE interface
+ */
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+	byte io_32bit = drive->io_32bit;
+
+	if (io_32bit) {
+#if SUPPORT_VLB_SYNC
+		if (io_32bit & 2) {
+			unsigned long flags;
+			__save_flags(flags);	/* local CPU only */
+			__cli();		/* local CPU only */
+			do_vlb_sync(IDE_NSECTOR_REG);
+			outsl(IDE_DATA_REG, buffer, wcount);
+			__restore_flags(flags);	/* local CPU only */
+		} else
+#endif /* SUPPORT_VLB_SYNC */
+			outsl(IDE_DATA_REG, buffer, wcount);
+	} else {
+#if SUPPORT_SLOW_DATA_PORTS
+		if (drive->slow) {
+			unsigned short *ptr = (unsigned short *) buffer;
+			while (wcount--) {
+				outw_p(*ptr++, IDE_DATA_REG);
+				outw_p(*ptr++, IDE_DATA_REG);
+			}
+		} else
+#endif /* SUPPORT_SLOW_DATA_PORTS */
+			outsw(IDE_DATA_REG, buffer, wcount<<1);
+	}
+}
+
+/*
+ * The following routines are mainly used by the ATAPI drivers.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+	++bytecount;
+#if defined(CONFIG_ATARI) || defined(CONFIG_Q40)
+	if (MACH_IS_ATARI || MACH_IS_Q40) {
+		/* Atari has a byte-swapped IDE interface */
+		insw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
+		return;
+	}
+#endif /* CONFIG_ATARI */
+	ide_input_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2)
+		insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+	++bytecount;
+#if defined(CONFIG_ATARI) || defined(CONFIG_Q40)
+	if (MACH_IS_ATARI || MACH_IS_Q40) {
+		/* Atari has a byte-swapped IDE interface */
+		outsw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
+		return;
+	}
+#endif /* CONFIG_ATARI */
+	ide_output_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2)
+		outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+/*
+ * Needed for PCI irq sharing
+ */
+static inline int drive_is_ready (ide_drive_t *drive)
+{
+	if (drive->waiting_for_dma)
+		return HWIF(drive)->dmaproc(ide_dma_test_irq, drive);
+#if 0
+	udelay(1);	/* need to guarantee 400ns since last command was issued */
+#endif
+	if (GET_STAT() & BUSY_STAT)	/* Note: this may clear a pending IRQ!! */
+		return 0;	/* drive busy:  definitely not interrupting */
+	return 1;		/* drive ready: *might* be interrupting */
+}
+
+/*
+ * This is our end_request replacement function.
+ */
+void ide_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	struct request *rq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	rq = hwgroup->rq;
+
+	if (!end_that_request_first(rq, uptodate, hwgroup->drive->name)) {
+		add_blkdev_randomness(MAJOR(rq->rq_dev));
+		blkdev_dequeue_request(rq);
+        	hwgroup->rq = NULL;
+		end_that_request_last(rq);
+	}
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+/*
+ * This should get invoked any time we exit the driver to
+ * wait for an interrupt response from a drive.  handler() points
+ * at the appropriate code to handle the next interrupt, and a
+ * timer is started to prevent us from waiting forever in case
+ * something goes wrong (see the ide_timer_expiry() handler later on).
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
+		      unsigned int timeout, ide_expiry_t *expiry)
+{
+	unsigned long flags;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	if (hwgroup->handler != NULL) {
+		printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
+			drive->name, hwgroup->handler, handler);
+	}
+	hwgroup->handler	= handler;
+	hwgroup->expiry		= expiry;
+	hwgroup->timer.expires	= jiffies + timeout;
+	add_timer(&hwgroup->timer);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+/*
+ * current_capacity() returns the capacity (in sectors) of a drive
+ * according to its current geometry/LBA settings.
+ */
+unsigned long current_capacity (ide_drive_t *drive)
+{
+	if (!drive->present)
+		return 0;
+	if (drive->driver != NULL)
+		return DRIVER(drive)->capacity(drive);
+	return 0;
+}
+
+extern struct block_device_operations ide_fops[];
+/*
+ * ide_geninit() is called exactly *once* for each interface.
+ */
+void ide_geninit (ide_hwif_t *hwif)
+{
+	unsigned int unit;
+	struct gendisk *gd = hwif->gd;
+
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		ide_drive_t *drive = &hwif->drives[unit];
+
+		if (!drive->present)
+			continue;
+		if (drive->media!=ide_disk && drive->media!=ide_floppy)
+			continue;
+		register_disk(gd,MKDEV(hwif->major,unit<<PARTN_BITS),
+#ifdef CONFIG_BLK_DEV_ISAPNP
+			(drive->forced_geom && drive->noprobe) ? 1 :
+#endif /* CONFIG_BLK_DEV_ISAPNP */
+			1<<PARTN_BITS, ide_fops,
+			current_capacity(drive));
+	}
+}
+
+static ide_startstop_t do_reset1 (ide_drive_t *, int);		/* needed below */
+
+/*
+ * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an atapi drive reset operation. If the drive has not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	byte stat;
+
+	SELECT_DRIVE(HWIF(drive),drive);
+	udelay (10);
+
+	if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
+		printk("%s: ATAPI reset complete\n", drive->name);
+	} else {
+		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
+			ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL);
+			return ide_started;	/* continue polling */
+		}
+		hwgroup->poll_timeout = 0;	/* end of polling */
+		printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
+		return do_reset1 (drive, 1);	/* do it the old fashioned way */
+	}
+	hwgroup->poll_timeout = 0;	/* done polling */
+	return ide_stopped;
+}
+
+/*
+ * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an ide reset operation. If the drives have not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static ide_startstop_t reset_pollfunc (ide_drive_t *drive)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	ide_hwif_t *hwif = HWIF(drive);
+	byte tmp;
+
+	if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
+		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
+			ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL);
+			return ide_started;	/* continue polling */
+		}
+		printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
+	} else  {
+		printk("%s: reset: ", hwif->name);
+		if ((tmp = GET_ERR()) == 1)
+			printk("success\n");
+		else {
+#if FANCY_STATUS_DUMPS
+			printk("master: ");
+			switch (tmp & 0x7f) {
+				case 1: printk("passed");
+					break;
+				case 2: printk("formatter device error");
+					break;
+				case 3: printk("sector buffer error");
+					break;
+				case 4: printk("ECC circuitry error");
+					break;
+				case 5: printk("controlling MPU error");
+					break;
+				default:printk("error (0x%02x?)", tmp);
+			}
+			if (tmp & 0x80)
+				printk("; slave: failed");
+			printk("\n");
+#else
+			printk("failed\n");
+#endif /* FANCY_STATUS_DUMPS */
+		}
+	}
+	hwgroup->poll_timeout = 0;	/* done polling */
+	return ide_stopped;
+}
+
+static void pre_reset (ide_drive_t *drive)
+{
+	if (drive->driver != NULL)
+		DRIVER(drive)->pre_reset(drive);
+
+	if (!drive->keep_settings) {
+		if (drive->using_dma) {
+			(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+		} else {
+			drive->unmask = 0;
+			drive->io_32bit = 0;
+		}
+	}
+}
+
+/*
+ * do_reset1() attempts to recover a confused drive by resetting it.
+ * Unfortunately, resetting a disk drive actually resets all devices on
+ * the same interface, so it can really be thought of as resetting the
+ * interface rather than resetting the drive.
+ *
+ * ATAPI devices have their own reset mechanism which allows them to be
+ * individually reset without clobbering other devices on the same interface.
+ *
+ * Unfortunately, the IDE interface does not generate an interrupt to let
+ * us know when the reset operation has finished, so we must poll for this.
+ * Equally poor, though, is the fact that this may a very long time to complete,
+ * (up to 30 seconds worstcase).  So, instead of busy-waiting here for it,
+ * we set a timer to poll at 50ms intervals.
+ */
+static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
+{
+	unsigned int unit;
+	unsigned long flags;
+	ide_hwif_t *hwif = HWIF(drive);
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+
+	/* For an ATAPI device, first try an ATAPI SRST. */
+	if (drive->media != ide_disk && !do_not_try_atapi) {
+		pre_reset(drive);
+		SELECT_DRIVE(hwif,drive);
+		udelay (20);
+		OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
+		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+		ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL);
+		__restore_flags (flags);	/* local CPU only */
+		return ide_started;
+	}
+
+	/*
+	 * First, reset any device state data we were maintaining
+	 * for any of the drives on this interface.
+	 */
+	for (unit = 0; unit < MAX_DRIVES; ++unit)
+		pre_reset(&hwif->drives[unit]);
+
+#if OK_TO_RESET_CONTROLLER
+	if (!IDE_CONTROL_REG) {
+		__restore_flags(flags);
+		return ide_stopped;
+	}
+	/*
+	 * Note that we also set nIEN while resetting the device,
+	 * to mask unwanted interrupts from the interface during the reset.
+	 * However, due to the design of PC hardware, this will cause an
+	 * immediate interrupt due to the edge transition it produces.
+	 * This single interrupt gives us a "fast poll" for drives that
+	 * recover from reset very quickly, saving us the first 50ms wait time.
+	 */
+	OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG);	/* set SRST and nIEN */
+	udelay(10);			/* more than enough time */
+	OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG);	/* clear SRST, leave nIEN */
+	udelay(10);			/* more than enough time */
+	hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+	ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL);
+
+	/*
+	 * Some weird controller like resetting themselves to a strange
+	 * state when the disks are reset this way. At least, the Winbond
+	 * 553 documentation says that
+	 */
+	if (hwif->resetproc != NULL)
+		hwif->resetproc(drive);
+
+#endif	/* OK_TO_RESET_CONTROLLER */
+
+	__restore_flags (flags);	/* local CPU only */
+	return ide_started;
+}
+
+/*
+ * ide_do_reset() is the entry point to the drive/interface reset code.
+ */
+ide_startstop_t ide_do_reset (ide_drive_t *drive)
+{
+	return do_reset1 (drive, 0);
+}
+
+/*
+ * Clean up after success/failure of an explicit drive cmd
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
+{
+	unsigned long flags;
+	struct request *rq = HWGROUP(drive)->rq;
+
+	if (rq->cmd == IDE_DRIVE_CMD) {
+		byte *args = (byte *) rq->buffer;
+		rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+		if (args) {
+			args[0] = stat;
+			args[1] = err;
+			args[2] = IN_BYTE(IDE_NSECTOR_REG);
+		}
+	}
+	spin_lock_irqsave(&io_request_lock, flags);
+	blkdev_dequeue_request(rq);
+	HWGROUP(drive)->rq = NULL;
+	rq->rq_status = RQ_INACTIVE;
+	spin_unlock_irqrestore(&io_request_lock, flags);
+	if (rq->sem != NULL)
+		up(rq->sem);	/* inform originator that rq has been serviced */
+}
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
+{
+	unsigned long flags;
+	byte err = 0;
+
+	__save_flags (flags);	/* local CPU only */
+	ide__sti();		/* local CPU only */
+	printk("%s: %s: status=0x%02x", drive->name, msg, stat);
+#if FANCY_STATUS_DUMPS
+	printk(" { ");
+	if (stat & BUSY_STAT)
+		printk("Busy ");
+	else {
+		if (stat & READY_STAT)	printk("DriveReady ");
+		if (stat & WRERR_STAT)	printk("DeviceFault ");
+		if (stat & SEEK_STAT)	printk("SeekComplete ");
+		if (stat & DRQ_STAT)	printk("DataRequest ");
+		if (stat & ECC_STAT)	printk("CorrectedError ");
+		if (stat & INDEX_STAT)	printk("Index ");
+		if (stat & ERR_STAT)	printk("Error ");
+	}
+	printk("}");
+#endif	/* FANCY_STATUS_DUMPS */
+	printk("\n");
+	if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
+		err = GET_ERR();
+		printk("%s: %s: error=0x%02x", drive->name, msg, err);
+#if FANCY_STATUS_DUMPS
+		if (drive->media == ide_disk) {
+			printk(" { ");
+			if (err & ABRT_ERR)	printk("DriveStatusError ");
+			if (err & ICRC_ERR)	printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
+			if (err & ECC_ERR)	printk("UncorrectableError ");
+			if (err & ID_ERR)	printk("SectorIdNotFound ");
+			if (err & TRK0_ERR)	printk("TrackZeroNotFound ");
+			if (err & MARK_ERR)	printk("AddrMarkNotFound ");
+			printk("}");
+			if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) {
+				byte cur = IN_BYTE(IDE_SELECT_REG);
+				if (cur & 0x40) {	/* using LBA? */
+					printk(", LBAsect=%ld", (unsigned long)
+					 ((cur&0xf)<<24)
+					 |(IN_BYTE(IDE_HCYL_REG)<<16)
+					 |(IN_BYTE(IDE_LCYL_REG)<<8)
+					 | IN_BYTE(IDE_SECTOR_REG));
+				} else {
+					printk(", CHS=%d/%d/%d",
+					 (IN_BYTE(IDE_HCYL_REG)<<8) +
+					  IN_BYTE(IDE_LCYL_REG),
+					  cur & 0xf,
+					  IN_BYTE(IDE_SECTOR_REG));
+				}
+				if (HWGROUP(drive)->rq)
+					printk(", sector=%ld", HWGROUP(drive)->rq->sector);
+			}
+		}
+#endif	/* FANCY_STATUS_DUMPS */
+		printk("\n");
+	}
+	__restore_flags (flags);	/* local CPU only */
+	return err;
+}
+
+/*
+ * try_to_flush_leftover_data() is invoked in response to a drive
+ * unexpectedly having its DRQ_STAT bit set.  As an alternative to
+ * resetting the drive, this routine tries to clear the condition
+ * by read a sector's worth of data from the drive.  Of course,
+ * this may not help if the drive is *waiting* for data from *us*.
+ */
+static void try_to_flush_leftover_data (ide_drive_t *drive)
+{
+	int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
+
+	if (drive->media != ide_disk)
+		return;
+	while (i > 0) {
+		u32 buffer[16];
+		unsigned int wcount = (i > 16) ? 16 : i;
+		i -= wcount;
+		ide_input_data (drive, buffer, wcount);
+	}
+}
+
+/*
+ * ide_error() takes action based on the error returned by the drive.
+ */
+ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat)
+{
+	struct request *rq;
+	byte err;
+
+	err = ide_dump_status(drive, msg, stat);
+	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
+		return ide_stopped;
+	/* retry only "normal" I/O: */
+	if (rq->cmd == IDE_DRIVE_CMD) {
+		rq->errors = 1;
+		ide_end_drive_cmd(drive, stat, err);
+		return ide_stopped;
+	}
+	if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */
+		rq->errors |= ERROR_RESET;
+	} else {
+		if (drive->media == ide_disk && (stat & ERR_STAT)) {
+			/* err has different meaning on cdrom and tape */
+			if (err == ABRT_ERR) {
+				if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY)
+					return ide_stopped; /* some newer drives don't support WIN_SPECIFY */
+			} else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR))
+				; /* UDMA crc error -- just retry the operation */
+			else if (err & (BBD_ERR | ECC_ERR))	/* retries won't help these */
+				rq->errors = ERROR_MAX;
+			else if (err & TRK0_ERR)	/* help it find track zero */
+				rq->errors |= ERROR_RECAL;
+		}
+		if ((stat & DRQ_STAT) && rq->cmd != WRITE)
+			try_to_flush_leftover_data(drive);
+	}
+	if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
+		OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG);	/* force an abort */
+
+	if (rq->errors >= ERROR_MAX) {
+		if (drive->driver != NULL)
+			DRIVER(drive)->end_request(0, HWGROUP(drive));
+		else
+	 		ide_end_request(0, HWGROUP(drive));
+	} else {
+		if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+			++rq->errors;
+			return ide_do_reset(drive);
+		}
+		if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+			drive->special.b.recalibrate = 1;
+		++rq->errors;
+	}
+	return ide_stopped;
+}
+
+/*
+ * Issue a simple drive command
+ * The drive must be selected beforehand.
+ */
+void ide_cmd (ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
+{
+	ide_set_handler (drive, handler, WAIT_CMD, NULL);
+	if (IDE_CONTROL_REG)
+		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* clear nIEN */
+	OUT_BYTE(nsect,IDE_NSECTOR_REG);
+	OUT_BYTE(cmd,IDE_COMMAND_REG);
+}
+
+/*
+ * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
+ */
+static ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	byte *args = (byte *) rq->buffer;
+	byte stat = GET_STAT();
+	int retries = 10;
+
+	ide__sti();	/* local CPU only */
+	if ((stat & DRQ_STAT) && args && args[3]) {
+		byte io_32bit = drive->io_32bit;
+		drive->io_32bit = 0;
+		ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
+		drive->io_32bit = io_32bit;
+		while (((stat = GET_STAT()) & BUSY_STAT) && retries--)
+			udelay(100);
+	}
+
+	if (!OK_STAT(stat, READY_STAT, BAD_STAT))
+		return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
+	ide_end_drive_cmd (drive, stat, GET_ERR());
+	return ide_stopped;
+}
+
+/*
+ * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
+ * commands to a drive.  It used to do much more, but has been scaled back.
+ */
+static ide_startstop_t do_special (ide_drive_t *drive)
+{
+	special_t *s = &drive->special;
+
+#ifdef DEBUG
+	printk("%s: do_special: 0x%02x\n", drive->name, s->all);
+#endif
+	if (s->b.set_tune) {
+		ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+		s->b.set_tune = 0;
+		if (tuneproc != NULL)
+			tuneproc(drive, drive->tune_req);
+	} else if (drive->driver != NULL) {
+		return DRIVER(drive)->special(drive);
+	} else if (s->all) {
+		printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
+		s->all = 0;
+	}
+	return ide_stopped;
+}
+
+/*
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0.  All other
+ * cases return 1 after invoking ide_error() -- caller should just return.
+ *
+ * This routine should get fixed to not hog the cpu during extra long waits..
+ * That could be done by busy-waiting for the first jiffy or two, and then
+ * setting a timer to wake up at half second intervals thereafter,
+ * until timeout is achieved, before timing out.
+ */
+int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) {
+	byte stat;
+	int i;
+	unsigned long flags;
+ 
+	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
+	if ((stat = GET_STAT()) & BUSY_STAT) {
+		__save_flags(flags);	/* local CPU only */
+		ide__sti();		/* local CPU only */
+		timeout += jiffies;
+		while ((stat = GET_STAT()) & BUSY_STAT) {
+			if (0 < (signed long)(jiffies - timeout)) {
+				__restore_flags(flags);	/* local CPU only */
+				*startstop = ide_error(drive, "status timeout", stat);
+				return 1;
+			}
+		}
+		__restore_flags(flags);	/* local CPU only */
+	}
+	/*
+	 * Allow status to settle, then read it again.
+	 * A few rare drives vastly violate the 400ns spec here,
+	 * so we'll wait up to 10usec for a "good" status
+	 * rather than expensively fail things immediately.
+	 * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
+	 */
+	for (i = 0; i < 10; i++) {
+		udelay(1);
+		if (OK_STAT((stat = GET_STAT()), good, bad))
+			return 0;
+	}
+	*startstop = ide_error(drive, "status error", stat);
+	return 1;
+}
+
+/*
+ * execute_drive_cmd() issues a special drive command,
+ * usually initiated by ioctl() from the external hdparm program.
+ */
+static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq)
+{
+	byte *args = rq->buffer;
+	if (args) {
+#ifdef DEBUG
+		printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
+		 drive->name, args[0], args[1], args[2], args[3]);
+#endif
+		if (args[0] == WIN_SMART) {
+			OUT_BYTE(0x4f, IDE_LCYL_REG);
+			OUT_BYTE(0xc2, IDE_HCYL_REG);
+			OUT_BYTE(args[2],IDE_FEATURE_REG);
+			OUT_BYTE(args[1],IDE_SECTOR_REG);
+			ide_cmd(drive, args[0], args[3], &drive_cmd_intr);
+			return ide_started;
+		}
+		OUT_BYTE(args[2],IDE_FEATURE_REG);
+		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+		return ide_started;
+	} else {
+		/*
+		 * NULL is actually a valid way of waiting for
+		 * all current requests to be flushed from the queue.
+		 */
+#ifdef DEBUG
+		printk("%s: DRIVE_CMD (null)\n", drive->name);
+#endif
+		ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+		return ide_stopped;
+	}
+}
+
+/*
+ * start_request() initiates handling of a new I/O request
+ */
+static ide_startstop_t start_request (ide_drive_t *drive)
+{
+	ide_startstop_t startstop;
+	unsigned long block, blockend;
+	struct request *rq = blkdev_entry_next_request(&drive->queue.queue_head);
+	unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
+	ide_hwif_t *hwif = HWIF(drive);
+
+#ifdef DEBUG
+	printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
+#endif
+	if (unit >= MAX_DRIVES) {
+		printk("%s: bad device number: %s\n", hwif->name, kdevname(rq->rq_dev));
+		goto kill_rq;
+	}
+#ifdef DEBUG
+	if (rq->bh && !buffer_locked(rq->bh)) {
+		printk("%s: block not locked\n", drive->name);
+		goto kill_rq;
+	}
+#endif
+	block    = rq->sector;
+	blockend = block + rq->nr_sectors;
+#if 0
+	if ((rq->cmd == READ || rq->cmd == WRITE) &&
+	    (drive->media == ide_disk || drive->media == ide_floppy))
+#endif
+	{
+		if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {
+			printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,
+			 (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);
+			goto kill_rq;
+		}
+		block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
+	}
+	/* Yecch - this will shift the entire interval,
+	   possibly killing some innocent following sector */
+	if (block == 0 && drive->remap_0_to_1 == 1)
+		block = 1;  /* redirect MBR access to EZ-Drive partn table */
+
+#if (DISK_RECOVERY_TIME > 0)
+	while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
+#endif
+
+	SELECT_DRIVE(hwif, drive);
+	if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+		printk("%s: drive not ready for command\n", drive->name);
+		return startstop;
+	}
+	if (!drive->special.all) {
+		if (rq->cmd == IDE_DRIVE_CMD) {
+			return execute_drive_cmd(drive, rq);
+		}
+		if (drive->driver != NULL) {
+			return (DRIVER(drive)->do_request(drive, rq, block));
+		}
+		printk("%s: media type %d not supported\n", drive->name, drive->media);
+		goto kill_rq;
+	}
+	return do_special(drive);
+kill_rq:
+	if (drive->driver != NULL)
+		DRIVER(drive)->end_request(0, HWGROUP(drive));
+	else
+		ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
+}
+
+/*
+ * ide_stall_queue() can be used by a drive to give excess bandwidth back
+ * to the hwgroup by sleeping for timeout jiffies.
+ */
+void ide_stall_queue (ide_drive_t *drive, unsigned long timeout)
+{
+	if (timeout > WAIT_WORSTCASE)
+		timeout = WAIT_WORSTCASE;
+	drive->sleep = timeout + jiffies;
+}
+
+#define WAKEUP(drive)	((drive)->service_start + 2 * (drive)->service_time)
+
+/*
+ * choose_drive() selects the next drive which will be serviced.
+ */
+static inline ide_drive_t *choose_drive (ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive, *best;
+
+repeat:	
+	best = NULL;
+	drive = hwgroup->drive;
+	do {
+		if (!list_empty(&drive->queue.queue_head) && (!drive->sleep || 0 <= (signed long)(jiffies - drive->sleep))) {
+			if (!best
+			 || (drive->sleep && (!best->sleep || 0 < (signed long)(best->sleep - drive->sleep)))
+			 || (!best->sleep && 0 < (signed long)(WAKEUP(best) - WAKEUP(drive))))
+			{
+				if( !drive->queue.plugged )
+					best = drive;
+			}
+		}
+	} while ((drive = drive->next) != hwgroup->drive);
+	if (best && best->nice1 && !best->sleep && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) {
+		long t = (signed long)(WAKEUP(best) - jiffies);
+		if (t >= WAIT_MIN_SLEEP) {
+			/*
+			 * We *may* have some time to spare, but first let's see if
+			 * someone can potentially benefit from our nice mood today..
+			 */
+			drive = best->next;
+			do {
+				if (!drive->sleep
+				 && 0 < (signed long)(WAKEUP(drive) - (jiffies - best->service_time))
+				 && 0 < (signed long)((jiffies + t) - WAKEUP(drive)))
+				{
+					ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP));
+					goto repeat;
+				}
+			} while ((drive = drive->next) != best);
+		}
+	}
+	return best;
+}
+
+/*
+ * Issue a new request to a drive from hwgroup
+ * Caller must have already done spin_lock_irqsave(&io_request_lock, ..);
+ *
+ * A hwgroup is a serialized group of IDE interfaces.  Usually there is
+ * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
+ * may have both interfaces in a single hwgroup to "serialize" access.
+ * Or possibly multiple ISA interfaces can share a common IRQ by being grouped
+ * together into one hwgroup for serialized access.
+ *
+ * Note also that several hwgroups can end up sharing a single IRQ,
+ * possibly along with many other devices.  This is especially common in
+ * PCI-based systems with off-board IDE controller cards.
+ *
+ * The IDE driver uses the single global io_request_lock spinlock to protect
+ * access to the request queues, and to protect the hwgroup->busy flag.
+ *
+ * The first thread into the driver for a particular hwgroup sets the
+ * hwgroup->busy flag to indicate that this hwgroup is now active,
+ * and then initiates processing of the top request from the request queue.
+ *
+ * Other threads attempting entry notice the busy setting, and will simply
+ * queue their new requests and exit immediately.  Note that hwgroup->busy
+ * remains set even when the driver is merely awaiting the next interrupt.
+ * Thus, the meaning is "this hwgroup is busy processing a request".
+ *
+ * When processing of a request completes, the completing thread or IRQ-handler
+ * will start the next request from the queue.  If no more work remains,
+ * the driver will clear the hwgroup->busy flag and exit.
+ *
+ * The io_request_lock (spinlock) is used to protect all access to the
+ * hwgroup->busy flag, but is otherwise not needed for most processing in
+ * the driver.  This makes the driver much more friendlier to shared IRQs
+ * than previous designs, while remaining 100% (?) SMP safe and capable.
+ */
+static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
+{
+	ide_drive_t	*drive;
+	ide_hwif_t	*hwif;
+	ide_startstop_t	startstop;
+
+	ide_get_lock(&ide_lock, ide_intr, hwgroup);	/* for atari only: POSSIBLY BROKEN HERE(?) */
+
+	__cli();	/* necessary paranoia: ensure IRQs are masked on local CPU */
+
+	while (!hwgroup->busy) {
+		hwgroup->busy = 1;
+		drive = choose_drive(hwgroup);
+		if (drive == NULL) {
+			unsigned long sleep = 0;
+			hwgroup->rq = NULL;
+			drive = hwgroup->drive;
+			do {
+				if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep)))
+					sleep = drive->sleep;
+			} while ((drive = drive->next) != hwgroup->drive);
+			if (sleep) {
+				/*
+				 * Take a short snooze, and then wake up this hwgroup again.
+				 * This gives other hwgroups on the same a chance to
+				 * play fairly with us, just in case there are big differences
+				 * in relative throughputs.. don't want to hog the cpu too much.
+				 */
+				if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) 
+					sleep = jiffies + WAIT_MIN_SLEEP;
+#if 1
+				if (hwgroup->timer.next || hwgroup->timer.prev)
+					printk("ide_set_handler: timer already active\n");
+#endif
+				hwgroup->sleeping = 1;	/* so that ide_timer_expiry knows what to do */
+				mod_timer(&hwgroup->timer, sleep);
+				/* we purposely leave hwgroup->busy==1 while sleeping */
+			} else {
+				/* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
+				ide_release_lock(&ide_lock);	/* for atari only */
+				hwgroup->busy = 0;
+			}
+			return;		/* no more work for this hwgroup (for now) */
+		}
+		hwif = HWIF(drive);
+		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && hwif->io_ports[IDE_CONTROL_OFFSET]) {
+			/* set nIEN for previous hwif */
+			OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
+		}
+		hwgroup->hwif = hwif;
+		hwgroup->drive = drive;
+		drive->sleep = 0;
+		drive->service_start = jiffies;
+
+		if ( drive->queue.plugged )	/* paranoia */
+			printk("%s: Huh? nuking plugged queue\n", drive->name);
+		hwgroup->rq = blkdev_entry_next_request(&drive->queue.queue_head);
+		/*
+		 * Some systems have trouble with IDE IRQs arriving while
+		 * the driver is still setting things up.  So, here we disable
+		 * the IRQ used by this interface while the request is being started.
+		 * This may look bad at first, but pretty much the same thing
+		 * happens anyway when any interrupt comes in, IDE or otherwise
+		 *  -- the kernel masks the IRQ while it is being handled.
+		 */
+		if (hwif->irq != masked_irq)
+			disable_irq_nosync(hwif->irq);
+		spin_unlock(&io_request_lock);
+		ide__sti();	/* allow other IRQs while we start this request */
+		startstop = start_request(drive);
+		spin_lock_irq(&io_request_lock);
+		if (hwif->irq != masked_irq)
+			enable_irq(hwif->irq);
+		if (startstop == ide_stopped)
+			hwgroup->busy = 0;
+	}
+}
+
+/*
+ * ide_get_queue() returns the queue which corresponds to a given device.
+ */
+request_queue_t *ide_get_queue (kdev_t dev)
+{
+	ide_hwif_t *hwif = (ide_hwif_t *)blk_dev[MAJOR(dev)].data;
+
+	return &hwif->drives[DEVICE_NR(dev) & 1].queue;
+}
+
+void do_ide0_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[0].hwgroup, 0);
+}
+
+#if MAX_HWIFS > 1
+void do_ide1_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[1].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 1 */
+
+#if MAX_HWIFS > 2
+void do_ide2_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[2].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 2 */
+
+#if MAX_HWIFS > 3
+void do_ide3_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[3].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 3 */
+
+#if MAX_HWIFS > 4
+void do_ide4_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[4].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 4 */
+
+#if MAX_HWIFS > 5
+void do_ide5_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[5].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 5 */
+
+#if MAX_HWIFS > 6
+void do_ide6_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[6].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 6 */
+
+#if MAX_HWIFS > 7
+void do_ide7_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[7].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 7 */
+
+#if MAX_HWIFS > 8
+void do_ide8_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[8].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 8 */
+
+#if MAX_HWIFS > 9
+void do_ide9_request (request_queue_t *q)
+{
+	ide_do_request (ide_hwifs[9].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 9 */
+
+/*
+ * ide_timer_expiry() is our timeout function for all drive operations.
+ * But note that it can also be invoked as a result of a "sleep" operation
+ * triggered by the mod_timer() call in ide_do_request.
+ */
+void ide_timer_expiry (unsigned long data)
+{
+	ide_hwgroup_t	*hwgroup = (ide_hwgroup_t *) data;
+	ide_handler_t	*handler;
+	ide_expiry_t	*expiry;
+ 	unsigned long	flags;
+	unsigned long	wait;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	del_timer(&hwgroup->timer);
+
+	if ((handler = hwgroup->handler) == NULL) {
+		/*
+		 * Either a marginal timeout occured
+		 * (got the interrupt just as timer expired),
+		 * or we were "sleeping" to give other devices a chance.
+		 * Either way, we don't really want to complain about anything.
+		 */
+		if (hwgroup->sleeping) {
+			hwgroup->sleeping = 0;
+			hwgroup->busy = 0;
+		}
+	} else {
+		ide_drive_t *drive = hwgroup->drive;
+		if (!drive) {
+			printk("ide_timer_expiry: hwgroup->drive was NULL\n");
+			hwgroup->handler = NULL;
+		} else {
+			ide_hwif_t *hwif;
+			ide_startstop_t startstop;
+			if (!hwgroup->busy) {
+				hwgroup->busy = 1;	/* paranoia */
+				printk("%s: ide_timer_expiry: hwgroup->busy was 0 ??\n", drive->name);
+			}
+			if ((expiry = hwgroup->expiry) != NULL) {
+				/* continue */
+				if ((wait = expiry(drive)) != 0) {
+					/* reset timer */
+					hwgroup->timer.expires  = jiffies + wait;
+					add_timer(&hwgroup->timer);
+					spin_unlock_irqrestore(&io_request_lock, flags);
+					return;
+				}
+			}
+			hwgroup->handler = NULL;
+			/*
+			 * We need to simulate a real interrupt when invoking
+			 * the handler() function, which means we need to globally
+			 * mask the specific IRQ:
+			 */
+			spin_unlock(&io_request_lock);
+			hwif  = HWIF(drive);
+			disable_irq(hwif->irq);	/* disable_irq_nosync ?? */
+			__cli();	/* local CPU only, as if we were handling an interrupt */
+			if (hwgroup->poll_timeout != 0) {
+				startstop = handler(drive);
+			} else if (drive_is_ready(drive)) {
+				if (drive->waiting_for_dma)
+					(void) hwgroup->hwif->dmaproc(ide_dma_lostirq, drive);
+				(void)ide_ack_intr(hwif);
+				printk("%s: lost interrupt\n", drive->name);
+				startstop = handler(drive);
+			} else {
+				if (drive->waiting_for_dma) {
+					(void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
+					printk("%s: timeout waiting for DMA\n", drive->name);
+					(void) hwgroup->hwif->dmaproc(ide_dma_timeout, drive);
+				}
+				startstop = ide_error(drive, "irq timeout", GET_STAT());
+			}
+			set_recovery_timer(hwif);
+			drive->service_time = jiffies - drive->service_start;
+			enable_irq(hwif->irq);
+			spin_lock_irq(&io_request_lock);
+			if (startstop == ide_stopped)
+				hwgroup->busy = 0;
+		}
+	}
+	ide_do_request(hwgroup, 0);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+/*
+ * There's nothing really useful we can do with an unexpected interrupt,
+ * other than reading the status register (to clear it), and logging it.
+ * There should be no way that an irq can happen before we're ready for it,
+ * so we needn't worry much about losing an "important" interrupt here.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ *
+ * This routine assumes __cli() is in effect when called.
+ *
+ * If an unexpected interrupt happens on irq15 while we are handling irq14
+ * and if the two interfaces are "serialized" (CMD640), then it looks like
+ * we could screw up by interfering with a new request being set up for irq15.
+ *
+ * In reality, this is a non-issue.  The new command is not sent unless the
+ * drive is ready to accept one, in which case we know the drive is not
+ * trying to interrupt us.  And ide_set_handler() is always invoked before
+ * completing the issuance of any new drive command, so we will not be
+ * accidently invoked as a result of any valid command completion interrupt.
+ *
+ */
+static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
+{
+	byte stat;
+	ide_hwif_t *hwif = hwgroup->hwif;
+
+	/*
+	 * handle the unexpected interrupt
+	 */
+	do {
+		if (hwif->irq == irq) {
+			stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+			if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
+				/* Try to not flood the console with msgs */
+				static unsigned long last_msgtime = 0, count = 0;
+				++count;
+				if (0 < (signed long)(jiffies - (last_msgtime + HZ))) {
+					last_msgtime = jiffies;
+					printk("%s%s: unexpected interrupt, status=0x%02x, count=%ld\n",
+					 hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count);
+				}
+			}
+		}
+	} while ((hwif = hwif->next) != hwgroup->hwif);
+}
+
+/*
+ * entry point for all interrupts, caller does __cli() for us
+ */
+void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long flags;
+	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
+	ide_hwif_t *hwif;
+	ide_drive_t *drive;
+	ide_handler_t *handler;
+	ide_startstop_t startstop;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	hwif = hwgroup->hwif;
+
+	if (!ide_ack_intr(hwif)) {
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		return;
+	}
+
+	if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) {
+		/*
+		 * Not expecting an interrupt from this drive.
+		 * That means this could be:
+		 *	(1) an interrupt from another PCI device
+		 *	sharing the same PCI INT# as us.
+		 * or	(2) a drive just entered sleep or standby mode,
+		 *	and is interrupting to let us know.
+		 * or	(3) a spurious interrupt of unknown origin.
+		 *
+		 * For PCI, we cannot tell the difference,
+		 * so in that case we just ignore it and hope it goes away.
+		 */
+#ifdef CONFIG_BLK_DEV_IDEPCI
+		if (IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
+		{
+			/*
+			 * Probably not a shared PCI interrupt,
+			 * so we can safely try to do something about it:
+			 */
+			unexpected_intr(irq, hwgroup);
+#ifdef CONFIG_BLK_DEV_IDEPCI
+		} else {
+			/*
+			 * Whack the status register, just in case we have a leftover pending IRQ.
+			 */
+			(void) IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+		}
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		return;
+	}
+	drive = hwgroup->drive;
+	if (!drive) {
+		/*
+		 * This should NEVER happen, and there isn't much we could do about it here.
+		 */
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		return;
+	}
+	if (!drive_is_ready(drive)) {
+		/*
+		 * This happens regularly when we share a PCI IRQ with another device.
+		 * Unfortunately, it can also happen with some buggy drives that trigger
+		 * the IRQ before their status register is up to date.  Hopefully we have
+		 * enough advance overhead that the latter isn't a problem.
+		 */
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		return;
+	}
+	if (!hwgroup->busy) {
+		hwgroup->busy = 1;	/* paranoia */
+		printk("%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
+	}
+	hwgroup->handler = NULL;
+	del_timer(&hwgroup->timer);
+	spin_unlock(&io_request_lock);
+
+	if (drive->unmask)
+		ide__sti();	/* local CPU only */
+	startstop = handler(drive);		/* service this interrupt, may set handler for next interrupt */
+	spin_lock_irq(&io_request_lock);
+
+	/*
+	 * Note that handler() may have set things up for another
+	 * interrupt to occur soon, but it cannot happen until
+	 * we exit from this routine, because it will be the
+	 * same irq as is currently being serviced here, and Linux
+	 * won't allow another of the same (on any CPU) until we return.
+	 */
+	set_recovery_timer(HWIF(drive));
+	drive->service_time = jiffies - drive->service_start;
+	if (startstop == ide_stopped) {
+		if (hwgroup->handler == NULL) {	/* paranoia */
+			hwgroup->busy = 0;
+			ide_do_request(hwgroup, hwif->irq);
+		} else {
+			printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
+		}
+	}
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+/*
+ * get_info_ptr() returns the (ide_drive_t *) for a given device number.
+ * It returns NULL if the given device number does not match any present drives.
+ */
+ide_drive_t *get_info_ptr (kdev_t i_rdev)
+{
+	int		major = MAJOR(i_rdev);
+#if 0
+	int		minor = MINOR(i_rdev) & PARTN_MASK;
+#endif
+	unsigned int	h;
+
+	for (h = 0; h < MAX_HWIFS; ++h) {
+		ide_hwif_t  *hwif = &ide_hwifs[h];
+		if (hwif->present && major == hwif->major) {
+			unsigned unit = DEVICE_NR(i_rdev);
+			if (unit < MAX_DRIVES) {
+				ide_drive_t *drive = &hwif->drives[unit];
+#if 0
+				if ((drive->present) && (drive->part[minor].nr_sects))
+#else
+				if (drive->present)
+#endif
+					return drive;
+			}
+			break;
+		}
+	}
+	return NULL;
+}
+
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq)
+{
+	rq->buffer = NULL;
+	rq->cmd = IDE_DRIVE_CMD;
+	rq->sector = 0;
+	rq->nr_sectors = 0;
+	rq->current_nr_sectors = 0;
+	rq->sem = NULL;
+	rq->bh = NULL;
+	rq->bhtail = NULL;
+	rq->q = NULL;
+}
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then the rq is queued at the end of the
+ * request queue, and the function sleeps until it has been processed.
+ * This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed.  This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed.  As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code.
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
+{
+	unsigned long flags;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	unsigned int major = HWIF(drive)->major;
+	struct list_head * queue_head;
+	DECLARE_MUTEX_LOCKED(sem);
+
+#ifdef CONFIG_BLK_DEV_PDC4030
+	if (HWIF(drive)->chipset == ide_pdc4030 && rq->buffer != NULL)
+		return -ENOSYS;  /* special drive cmds not supported */
+#endif
+	rq->errors = 0;
+	rq->rq_status = RQ_ACTIVE;
+	rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
+	if (action == ide_wait)
+		rq->sem = &sem;
+	spin_lock_irqsave(&io_request_lock, flags);
+	queue_head = &drive->queue.queue_head;
+	if (list_empty(queue_head) || action == ide_preempt) {
+		if (action == ide_preempt)
+			hwgroup->rq = NULL;
+	} else {
+		if (action == ide_wait || action == ide_end) {
+			queue_head = queue_head->prev;
+		} else
+			queue_head = queue_head->next;
+	}
+	list_add(&rq->queue, queue_head);
+	ide_do_request(hwgroup, 0);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+	if (action == ide_wait) {
+		down(&sem);			/* wait for it to be serviced */
+		return rq->errors ? -EIO : 0;	/* return -EIO if errors */
+	}
+	return 0;
+
+}
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0.  If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+int ide_revalidate_disk (kdev_t i_rdev)
+{
+	ide_drive_t *drive;
+	ide_hwgroup_t *hwgroup;
+	unsigned int p, major, minor;
+	long flags;
+
+	if ((drive = get_info_ptr(i_rdev)) == NULL)
+		return -ENODEV;
+	major = MAJOR(i_rdev);
+	minor = drive->select.b.unit << PARTN_BITS;
+	hwgroup = HWGROUP(drive);
+	spin_lock_irqsave(&io_request_lock, flags);
+	if (drive->busy || (drive->usage > 1)) {
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		return -EBUSY;
+	};
+	drive->busy = 1;
+	MOD_INC_USE_COUNT;
+	spin_unlock_irqrestore(&io_request_lock, flags);
+
+	for (p = 0; p < (1<<PARTN_BITS); ++p) {
+		if (drive->part[p].nr_sects > 0) {
+			kdev_t devp = MKDEV(major, minor+p);
+			struct super_block * sb = get_super(devp);
+			fsync_dev          (devp);
+			if (sb)
+				invalidate_inodes(sb);
+			invalidate_buffers (devp);
+			set_blocksize(devp, 1024);
+		}
+		drive->part[p].start_sect = 0;
+		drive->part[p].nr_sects   = 0;
+	};
+
+	grok_partitions(HWIF(drive)->gd, drive->select.b.unit,
+			(drive->media != ide_disk &&
+			 drive->media != ide_floppy) ? 1 : 1<<PARTN_BITS,
+				current_capacity(drive));
+
+	drive->busy = 0;
+	wake_up(&drive->wqueue);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static void revalidate_drives (void)
+{
+	ide_hwif_t *hwif;
+	ide_drive_t *drive;
+	int index, unit;
+
+	for (index = 0; index < MAX_HWIFS; ++index) {
+		hwif = &ide_hwifs[index];
+		for (unit = 0; unit < MAX_DRIVES; ++unit) {
+			drive = &ide_hwifs[index].drives[unit];
+			if (drive->revalidate) {
+				drive->revalidate = 0;
+				if (!initializing)
+					(void) ide_revalidate_disk(MKDEV(hwif->major, unit<<PARTN_BITS));
+			}
+		}
+	}
+}
+
+static void ide_probe_module (void)
+{
+	if (!ide_probe) {
+#ifdef CONFIG_KMOD
+		(void) request_module("ide-probe-mod");
+#endif /* CONFIG_KMOD */
+	} else {
+		(void) ide_probe->init();
+	}
+	revalidate_drives();
+}
+
+static void ide_driver_module (void)
+{
+	int index;
+	ide_module_t *module = ide_modules;
+
+	for (index = 0; index < MAX_HWIFS; ++index)
+		if (ide_hwifs[index].present)
+			goto search;
+	ide_probe_module();
+search:
+	while (module) {
+		(void) module->init();
+		module = module->next;
+	}
+	revalidate_drives();
+}
+
+static int ide_open (struct inode * inode, struct file * filp)
+{
+	ide_drive_t *drive;
+	int rc;
+
+	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+		return -ENXIO;
+	MOD_INC_USE_COUNT;
+	if (drive->driver == NULL)
+		ide_driver_module();
+#ifdef CONFIG_KMOD
+	if (drive->driver == NULL) {
+		if (drive->media == ide_disk)
+			(void) request_module("ide-disk");
+		if (drive->media == ide_cdrom)
+			(void) request_module("ide-cd");
+		if (drive->media == ide_tape)
+			(void) request_module("ide-tape");
+		if (drive->media == ide_floppy)
+			(void) request_module("ide-floppy");
+	}
+#endif /* CONFIG_KMOD */
+	while (drive->busy)
+		sleep_on(&drive->wqueue);
+	drive->usage++;
+	if (drive->driver != NULL) {
+		if ((rc = DRIVER(drive)->open(inode, filp, drive)))
+			MOD_DEC_USE_COUNT;
+		return rc;
+	}
+	printk ("%s: driver not present\n", drive->name);
+	drive->usage--;
+	MOD_DEC_USE_COUNT;
+	return -ENXIO;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static int ide_release (struct inode * inode, struct file * file)
+{
+	ide_drive_t *drive;
+
+	if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
+		drive->usage--;
+		if (drive->driver != NULL)
+			DRIVER(drive)->release(inode, file, drive);
+		MOD_DEC_USE_COUNT;
+	}
+	return 0;
+}
+
+int ide_replace_subdriver (ide_drive_t *drive, const char *driver)
+{
+	if (!drive->present || drive->busy || drive->usage)
+		goto abort;
+	if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+		goto abort;
+	strncpy(drive->driver_req, driver, 9);
+	ide_driver_module();
+	drive->driver_req[0] = 0;
+	ide_driver_module();
+	if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver))
+		return 0;
+abort:
+	return 1;
+}
+
+#ifdef CONFIG_PROC_FS
+ide_proc_entry_t generic_subdriver_entries[] = {
+	{ "capacity",	S_IFREG|S_IRUGO,	proc_ide_read_capacity,	NULL },
+	{ NULL, 0, NULL, NULL }
+};
+#endif
+
+/*
+ * Note that we only release the standard ports,
+ * and do not even try to handle any extra ports
+ * allocated for weird IDE interface chipsets.
+ */
+void hwif_unregister (ide_hwif_t *hwif)
+{
+	if (hwif->straight8) {
+		ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8);
+		goto jump_eight;
+	}
+	if (hwif->io_ports[IDE_DATA_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
+	if (hwif->io_ports[IDE_ERROR_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
+	if (hwif->io_ports[IDE_NSECTOR_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
+	if (hwif->io_ports[IDE_SECTOR_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1);
+	if (hwif->io_ports[IDE_LCYL_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_LCYL_OFFSET], 1);
+	if (hwif->io_ports[IDE_HCYL_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
+	if (hwif->io_ports[IDE_SELECT_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
+	if (hwif->io_ports[IDE_STATUS_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
+jump_eight:
+	if (hwif->io_ports[IDE_CONTROL_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
+	if (hwif->io_ports[IDE_IRQ_OFFSET])
+		ide_release_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
+}
+
+void ide_unregister (unsigned int index)
+{
+	struct gendisk *gd, **gdp;
+	ide_drive_t *drive, *d;
+	ide_hwif_t *hwif, *g;
+	ide_hwgroup_t *hwgroup;
+	int irq_count = 0, unit, i;
+	unsigned long flags;
+	unsigned int p, minor;
+	ide_hwif_t old_hwif;
+
+	if (index >= MAX_HWIFS)
+		return;
+	save_flags(flags);	/* all CPUs */
+	cli();			/* all CPUs */
+	hwif = &ide_hwifs[index];
+	if (!hwif->present)
+		goto abort;
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		drive = &hwif->drives[unit];
+		if (!drive->present)
+			continue;
+		if (drive->busy || drive->usage)
+			goto abort;
+		if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+			goto abort;
+	}
+	hwif->present = 0;
+	
+	/*
+	 * All clear?  Then blow away the buffer cache
+	 */
+	sti();
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		drive = &hwif->drives[unit];
+		if (!drive->present)
+			continue;
+		minor = drive->select.b.unit << PARTN_BITS;
+		for (p = 0; p < (1<<PARTN_BITS); ++p) {
+			if (drive->part[p].nr_sects > 0) {
+				kdev_t devp = MKDEV(hwif->major, minor+p);
+				struct super_block * sb = get_super(devp);
+				if (sb) invalidate_inodes(sb);
+				invalidate_buffers (devp);
+			}
+		}
+#ifdef CONFIG_PROC_FS
+		destroy_proc_ide_drives(hwif);
+#endif
+	}
+	cli();
+	hwgroup = hwif->hwgroup;
+
+	/*
+	 * free the irq if we were the only hwif using it
+	 */
+	g = hwgroup->hwif;
+	do {
+		if (g->irq == hwif->irq)
+			++irq_count;
+		g = g->next;
+	} while (g != hwgroup->hwif);
+	if (irq_count == 1)
+		free_irq(hwif->irq, hwgroup);
+
+	/*
+	 * Note that we only release the standard ports,
+	 * and do not even try to handle any extra ports
+	 * allocated for weird IDE interface chipsets.
+	 */
+	hwif_unregister(hwif);
+
+	/*
+	 * Remove us from the hwgroup, and free
+	 * the hwgroup if we were the only member
+	 */
+	d = hwgroup->drive;
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		drive = &hwif->drives[i];
+		if (drive->de) {
+			devfs_unregister (drive->de);
+			drive->de = NULL;
+		}
+		if (!drive->present)
+			continue;
+		while (hwgroup->drive->next != drive)
+			hwgroup->drive = hwgroup->drive->next;
+		hwgroup->drive->next = drive->next;
+		if (hwgroup->drive == drive)
+			hwgroup->drive = NULL;
+		if (drive->id != NULL) {
+			kfree(drive->id);
+			drive->id = NULL;
+		}
+		drive->present = 0;
+	}
+	if (d->present)
+		hwgroup->drive = d;
+	while (hwgroup->hwif->next != hwif)
+		hwgroup->hwif = hwgroup->hwif->next;
+	hwgroup->hwif->next = hwif->next;
+	if (hwgroup->hwif == hwif)
+		kfree(hwgroup);
+	else
+		hwgroup->hwif = HWIF(hwgroup->drive);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+	if (hwif->dma_base)
+		(void) ide_release_dma(hwif);
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+	/*
+	 * Remove us from the kernel's knowledge
+	 */
+	unregister_blkdev(hwif->major, hwif->name);
+	kfree(blksize_size[hwif->major]);
+	kfree(max_sectors[hwif->major]);
+	kfree(max_readahead[hwif->major]);
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(hwif->major));
+	blk_dev[hwif->major].data = NULL;
+	blk_dev[hwif->major].queue = NULL;
+	blksize_size[hwif->major] = NULL;
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+		if (*gdp == hwif->gd)
+			break;
+	if (*gdp == NULL)
+		printk("gd not in disk chain!\n");
+	else {
+		gd = *gdp; *gdp = gd->next;
+		kfree(gd->sizes);
+		kfree(gd->part);
+		if (gd->de_arr)
+			kfree (gd->de_arr);
+		if (gd->flags)
+			kfree (gd->flags);
+		kfree(gd);
+	}
+	old_hwif		= *hwif;
+	init_hwif_data (index);	/* restore hwif data to pristine status */
+	hwif->hwgroup		= old_hwif.hwgroup;
+	hwif->tuneproc		= old_hwif.tuneproc;
+	hwif->selectproc	= old_hwif.selectproc;
+	hwif->resetproc		= old_hwif.resetproc;
+	hwif->dmaproc		= old_hwif.dmaproc;
+	hwif->dma_base		= old_hwif.dma_base;
+	hwif->dma_extra		= old_hwif.dma_extra;
+	hwif->config_data	= old_hwif.config_data;
+	hwif->select_data	= old_hwif.select_data;
+	hwif->proc		= old_hwif.proc;
+	hwif->irq		= old_hwif.irq;
+	hwif->major		= old_hwif.major;
+	hwif->chipset		= old_hwif.chipset;
+	hwif->autodma		= old_hwif.autodma;
+	hwif->udma_four		= old_hwif.udma_four;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+	hwif->pci_dev		= old_hwif.pci_dev;
+	hwif->pci_devid		= old_hwif.pci_devid;
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+	hwif->straight8		= old_hwif.straight8;
+
+abort:
+	restore_flags(flags);	/* all CPUs */
+}
+
+/*
+ * Setup hw_regs_t structure described by parameters.  You
+ * may set up the hw structure yourself OR use this routine to
+ * do it for you.
+ */
+void ide_setup_ports (	hw_regs_t *hw,
+			ide_ioreg_t base, int *offsets,
+			ide_ioreg_t ctrl, ide_ioreg_t intr,
+			ide_ack_intr_t *ack_intr, int irq)
+{
+	int i;
+
+	for (i = 0; i < IDE_NR_PORTS; i++) {
+		if (offsets[i] == -1) {
+			switch(i) {
+				case IDE_CONTROL_OFFSET:
+					hw->io_ports[i] = ctrl;
+					break;
+				case IDE_IRQ_OFFSET:
+					hw->io_ports[i] = intr;
+					break;
+				default:
+					hw->io_ports[i] = 0;
+					break;
+			}
+		} else {
+			hw->io_ports[i] = base + offsets[i];
+		}
+	}
+	hw->irq = irq;
+	hw->dma = NO_DMA;
+	hw->ack_intr = ack_intr;
+}
+
+/*
+ * Register an IDE interface, specifing exactly the registers etc
+ * Set init=1 iff calling before probes have taken place.
+ */
+int ide_register_hw (hw_regs_t *hw, ide_hwif_t **hwifp)
+{
+	int index, retry = 1;
+	ide_hwif_t *hwif;
+
+	do {
+		for (index = 0; index < MAX_HWIFS; ++index) {
+			hwif = &ide_hwifs[index];
+			if (hwif->hw.io_ports[IDE_DATA_OFFSET] == hw->io_ports[IDE_DATA_OFFSET])
+				goto found;
+		}
+		for (index = 0; index < MAX_HWIFS; ++index) {
+			hwif = &ide_hwifs[index];
+			if ((!hwif->present && !hwif->mate && !initializing) ||
+			    (!hwif->hw.io_ports[IDE_DATA_OFFSET] && initializing))
+				goto found;
+		}
+		for (index = 0; index < MAX_HWIFS; index++)
+			ide_unregister(index);
+	} while (retry--);
+	return -1;
+found:
+	if (hwif->present)
+		ide_unregister(index);
+	if (hwif->present)
+		return -1;
+	memcpy(&hwif->hw, hw, sizeof(*hw));
+	memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports));
+	hwif->irq = hw->irq;
+	hwif->noprobe = 0;
+
+	if (!initializing) {
+		ide_probe_module();
+#ifdef CONFIG_PROC_FS
+		create_proc_ide_interfaces();
+#endif
+		ide_driver_module();
+	}
+
+	if (hwifp)
+		*hwifp = hwif;
+
+	return (initializing || hwif->present) ? index : -1;
+}
+
+/*
+ * Compatability function with existing drivers.  If you want
+ * something different, use the function above.
+ */
+int ide_register (int arg1, int arg2, int irq)
+{
+	hw_regs_t hw;
+	ide_init_hwif_ports(&hw, (ide_ioreg_t) arg1, (ide_ioreg_t) arg2, NULL);
+	hw.irq = irq;
+	return ide_register_hw(&hw, NULL);
+}
+
+void ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
+{
+	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
+
+	while ((*p) && strcmp((*p)->name, name) < 0)
+		p = &((*p)->next);
+	if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
+		goto abort;
+	memset(setting, 0, sizeof(*setting));
+	if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
+		goto abort;
+	strcpy(setting->name, name);		setting->rw = rw;
+	setting->read_ioctl = read_ioctl;	setting->write_ioctl = write_ioctl;
+	setting->data_type = data_type;		setting->min = min;
+	setting->max = max;			setting->mul_factor = mul_factor;
+	setting->div_factor = div_factor;	setting->data = data;
+	setting->set = set;			setting->next = *p;
+	if (drive->driver)
+		setting->auto_remove = 1;
+	*p = setting;
+	return;
+abort:
+	if (setting)
+		kfree(setting);
+}
+
+void ide_remove_setting (ide_drive_t *drive, char *name)
+{
+	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting;
+
+	while ((*p) && strcmp((*p)->name, name))
+		p = &((*p)->next);
+	if ((setting = (*p)) == NULL)
+		return;
+	(*p) = setting->next;
+	kfree(setting->name);
+	kfree(setting);
+}
+
+static ide_settings_t *ide_find_setting_by_ioctl (ide_drive_t *drive, int cmd)
+{
+	ide_settings_t *setting = drive->settings;
+
+	while (setting) {
+		if (setting->read_ioctl == cmd || setting->write_ioctl == cmd)
+			break;
+		setting = setting->next;
+	}
+	return setting;
+}
+
+ide_settings_t *ide_find_setting_by_name (ide_drive_t *drive, char *name)
+{
+	ide_settings_t *setting = drive->settings;
+
+	while (setting) {
+		if (strcmp(setting->name, name) == 0)
+			break;
+		setting = setting->next;
+	}
+	return setting;
+}
+
+static void auto_remove_settings (ide_drive_t *drive)
+{
+	ide_settings_t *setting;
+repeat:
+	setting = drive->settings;
+	while (setting) {
+		if (setting->auto_remove) {
+			ide_remove_setting(drive, setting->name);
+			goto repeat;
+		}
+		setting = setting->next;
+	}
+}
+
+int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting)
+{
+	int		val = -EINVAL;
+	unsigned long	flags;
+
+	if ((setting->rw & SETTING_READ)) {
+		spin_lock_irqsave(&io_request_lock, flags);
+		switch(setting->data_type) {
+			case TYPE_BYTE:
+				val = *((u8 *) setting->data);
+				break;
+			case TYPE_SHORT:
+				val = *((u16 *) setting->data);
+				break;
+			case TYPE_INT:
+			case TYPE_INTA:
+				val = *((u32 *) setting->data);
+				break;
+		}
+		spin_unlock_irqrestore(&io_request_lock, flags);
+	}
+	return val;
+}
+
+int ide_spin_wait_hwgroup (ide_drive_t *drive, unsigned long *flags)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	unsigned long timeout = jiffies + (3 * HZ);
+
+	spin_lock_irqsave(&io_request_lock, *flags);
+	while (hwgroup->busy) {
+		unsigned long lflags;
+		spin_unlock_irqrestore(&io_request_lock, *flags);
+		__save_flags(lflags);	/* local CPU only */
+		__sti();		/* local CPU only; needed for jiffies */
+		if (0 < (signed long)(jiffies - timeout)) {
+			__restore_flags(lflags);	/* local CPU only */
+			printk("%s: channel busy\n", drive->name);
+			return -EBUSY;
+		}
+		__restore_flags(lflags);	/* local CPU only */
+		spin_lock_irqsave(&io_request_lock, *flags);
+	}
+	return 0;
+}
+
+/*
+ * FIXME:  This should be changed to enqueue a special request
+ * to the driver to change settings, and then wait on a sema for completion.
+ * The current scheme of polling is kludgey, though safe enough.
+ */
+int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val)
+{
+	unsigned long flags;
+	int i;
+	u32 *p;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (!(setting->rw & SETTING_WRITE))
+		return -EPERM;
+	if (val < setting->min || val > setting->max)
+		return -EINVAL;
+	if (setting->set)
+		return setting->set(drive, val);
+	if (ide_spin_wait_hwgroup(drive, &flags))
+		return -EBUSY;
+	switch (setting->data_type) {
+		case TYPE_BYTE:
+			*((u8 *) setting->data) = val;
+			break;
+		case TYPE_SHORT:
+			*((u16 *) setting->data) = val;
+			break;
+		case TYPE_INT:
+			*((u32 *) setting->data) = val;
+			break;
+		case TYPE_INTA:
+			p = (u32 *) setting->data;
+			for (i = 0; i < 1 << PARTN_BITS; i++, p++)
+				*p = val;
+			break;
+	}
+	spin_unlock_irqrestore(&io_request_lock, flags);
+	return 0;
+}
+
+static int set_io_32bit(ide_drive_t *drive, int arg)
+{
+	drive->io_32bit = arg;
+#ifdef CONFIG_BLK_DEV_DTC2278
+	if (HWIF(drive)->chipset == ide_dtc2278)
+		HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+	return 0;
+}
+
+static int set_using_dma (ide_drive_t *drive, int arg)
+{
+	if (!drive->driver || !DRIVER(drive)->supports_dma)
+		return -EPERM;
+	if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
+		return -EPERM;
+	if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive))
+		return -EIO;
+	return 0;
+}
+
+static int set_pio_mode (ide_drive_t *drive, int arg)
+{
+	struct request rq;
+
+	if (!HWIF(drive)->tuneproc)
+		return -ENOSYS;
+	if (drive->special.b.set_tune)
+		return -EBUSY;
+	ide_init_drive_cmd(&rq);
+	drive->tune_req = (byte) arg;
+	drive->special.b.set_tune = 1;
+	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
+	return 0;
+}
+
+void ide_add_generic_settings (ide_drive_t *drive)
+{
+/*
+ *			drive	setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function
+ */
+	ide_add_setting(drive,	"io_32bit",		drive->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->io_32bit,		set_io_32bit);
+	ide_add_setting(drive,	"keepsettings",		SETTING_RW,					HDIO_GET_KEEPSETTINGS,	HDIO_SET_KEEPSETTINGS,	TYPE_BYTE,	0,	1,				1,		1,		&drive->keep_settings,		NULL);
+	ide_add_setting(drive,	"nice1",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->nice1,			NULL);
+	ide_add_setting(drive,	"pio_mode",		SETTING_WRITE,					-1,			HDIO_SET_PIO_MODE,	TYPE_BYTE,	0,	255,				1,		1,		NULL,				set_pio_mode);
+	ide_add_setting(drive,	"slow",			SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->slow,			NULL);
+	ide_add_setting(drive,	"unmaskirq",		drive->no_unmask ? SETTING_READ : SETTING_RW,	HDIO_GET_UNMASKINTR,	HDIO_SET_UNMASKINTR,	TYPE_BYTE,	0,	1,				1,		1,		&drive->unmask,			NULL);
+	ide_add_setting(drive,	"using_dma",		SETTING_RW,					HDIO_GET_DMA,		HDIO_SET_DMA,		TYPE_BYTE,	0,	1,				1,		1,		&drive->using_dma,		set_using_dma);
+	ide_add_setting(drive,	"ide_scsi",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->scsi,			NULL);
+}
+
+int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf)
+{
+	struct request rq;
+	byte buffer[4];
+
+	if (!buf)
+		buf = buffer;
+	memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors);
+	ide_init_drive_cmd(&rq);
+	rq.buffer = buf;
+	*buf++ = cmd;
+	*buf++ = nsect;
+	*buf++ = feature;
+	*buf++ = sectors;
+	return ide_do_drive_cmd(drive, &rq, ide_wait);
+}
+
+/*
+ * Delay for *at least* 50ms.  As we don't know how much time is left
+ * until the next tick occurs, we wait an extra tick to be safe.
+ * This is used only during the probing/polling for drives at boot time.
+ *
+ * However, its usefullness may be needed in other places, thus we export it now.
+ * The future may change this to a millisecond setable delay.
+ */
+void ide_delay_50ms (void)
+{
+	unsigned long timeout = jiffies + ((HZ + 19)/20) + 1;
+	while (0 < (signed long)(timeout - jiffies));
+}
+
+static int ide_ioctl (struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	int err = 0, major, minor;
+	ide_drive_t *drive;
+	struct request rq;
+	kdev_t dev;
+	ide_settings_t *setting;
+
+	if (!inode || !(dev = inode->i_rdev))
+		return -EINVAL;
+	major = MAJOR(dev); minor = MINOR(dev);
+	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+		return -ENODEV;
+
+	if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) {
+		if (cmd == setting->read_ioctl) {
+			err = ide_read_setting(drive, setting);
+			return err >= 0 ? put_user(err, (long *) arg) : err;
+		} else {
+			if ((MINOR(inode->i_rdev) & PARTN_MASK))
+				return -EINVAL;
+			return ide_write_setting(drive, setting, arg);
+		}
+	}
+
+	ide_init_drive_cmd (&rq);
+	switch (cmd) {
+		case HDIO_GETGEO:
+		{
+			struct hd_geometry *loc = (struct hd_geometry *) arg;
+			unsigned short bios_cyl = drive->bios_cyl; /* truncate */
+			if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
+			if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
+			if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
+			if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
+			if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+				(unsigned long *) &loc->start)) return -EFAULT;
+			return 0;
+		}
+
+	 	case BLKGETSIZE:   /* Return device size */
+			return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
+
+		case BLKRRPART: /* Re-read partition tables */
+			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+			return ide_revalidate_disk(inode->i_rdev);
+
+		case HDIO_OBSOLETE_IDENTITY:
+		case HDIO_GET_IDENTITY:
+			if (MINOR(inode->i_rdev) & PARTN_MASK)
+				return -EINVAL;
+			if (drive->id == NULL)
+				return -ENOMSG;
+			if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
+				return -EFAULT;
+			return 0;
+
+		case HDIO_GET_NICE:
+			return put_user(drive->dsc_overlap	<<	IDE_NICE_DSC_OVERLAP	|
+					drive->atapi_overlap	<<	IDE_NICE_ATAPI_OVERLAP	|
+					drive->nice0		<< 	IDE_NICE_0		|
+					drive->nice1		<<	IDE_NICE_1		|
+					drive->nice2		<<	IDE_NICE_2,
+					(long *) arg);
+		case HDIO_DRIVE_CMD:
+		{
+			byte args[4], *argbuf = args;
+			int argsize = 4;
+			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+			if (NULL == (void *) arg)
+				return ide_do_drive_cmd(drive, &rq, ide_wait);
+			if (copy_from_user(args, (void *)arg, 4))
+				return -EFAULT;
+			if (args[3]) {
+				argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
+				argbuf = kmalloc(argsize, GFP_KERNEL);
+				if (argbuf == NULL)
+					return -ENOMEM;
+				memcpy(argbuf, args, 4);
+			}
+			if (ide_ata66_check(drive, args[0], args[1], args[2]))
+				goto abort;
+
+			err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
+
+			if (!err && set_transfer(drive, args[0], args[1], args[2])) {
+#if 0
+				/* active-retuning-calls future */
+				if (HWIF(drive)->tune2proc)
+					HWIF(drive)->tune2proc(drive, args[1]);
+#endif
+				ide_driveid_update(drive);
+			}
+		abort:
+			if (copy_to_user((void *)arg, argbuf, argsize))
+				err = -EFAULT;
+			if (argsize > 4)
+				kfree(argbuf);
+			return err;
+		}
+
+		case HDIO_SCAN_HWIF:
+		{
+			int args[3];
+			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+			if (copy_from_user(args, (void *)arg, 3 * sizeof(int)))
+				return -EFAULT;
+			if (ide_register(args[0], args[1], args[2]) == -1)
+				return -EIO;
+			return 0;
+		}
+	        case HDIO_UNREGISTER_HWIF:
+			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+			/* should I check here for arg > MAX_HWIFS, or
+			   just let ide_unregister fail silently? -- shaver */
+			ide_unregister(arg);
+			return 0;
+		case HDIO_SET_NICE:
+			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+			if (drive->driver == NULL)
+				return -EPERM;
+			if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
+				return -EPERM;
+			drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1;
+			if (drive->dsc_overlap && !DRIVER(drive)->supports_dsc_overlap) {
+				drive->dsc_overlap = 0;
+				return -EPERM;
+			}
+			drive->nice1 = (arg >> IDE_NICE_1) & 1;
+			return 0;
+
+		case BLKROSET:
+		case BLKROGET:
+		case BLKFLSBUF:
+		case BLKSSZGET:
+		case BLKPG:
+		case BLKELVGET:
+		case BLKELVSET:
+			return blk_ioctl(inode->i_rdev, cmd, arg);
+
+		default:
+			if (drive->driver != NULL)
+				return DRIVER(drive)->ioctl(drive, inode, file, cmd, arg);
+			return -EPERM;
+	}
+}
+
+static int ide_check_media_change (kdev_t i_rdev)
+{
+	ide_drive_t *drive;
+
+	if ((drive = get_info_ptr(i_rdev)) == NULL)
+		return -ENODEV;
+	if (drive->driver != NULL)
+		return DRIVER(drive)->media_change(drive);
+	return 0;
+}
+
+void ide_fixstring (byte *s, const int bytecount, const int byteswap)
+{
+	byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+
+	if (byteswap) {
+		/* convert from big-endian to host byte order */
+		for (p = end ; p != s;) {
+			unsigned short *pp = (unsigned short *) (p -= 2);
+			*pp = ntohs(*pp);
+		}
+	}
+
+	/* strip leading blanks */
+	while (s != end && *s == ' ')
+		++s;
+
+	/* compress internal blanks and strip trailing blanks */
+	while (s != end && *s) {
+		if (*s++ != ' ' || (s != end && *s && *s != ' '))
+			*p++ = *(s-1);
+	}
+
+	/* wipe out trailing garbage */
+	while (p != end)
+		*p++ = '\0';
+}
+
+/*
+ * stridx() returns the offset of c within s,
+ * or -1 if c is '\0' or not found within s.
+ */
+static int __init stridx (const char *s, char c)
+{
+	char *i = strchr(s, c);
+	return (i && c) ? i - s : -1;
+}
+
+/*
+ * match_parm() does parsing for ide_setup():
+ *
+ * 1. the first char of s must be '='.
+ * 2. if the remainder matches one of the supplied keywords,
+ *     the index (1 based) of the keyword is negated and returned.
+ * 3. if the remainder is a series of no more than max_vals numbers
+ *     separated by commas, the numbers are saved in vals[] and a
+ *     count of how many were saved is returned.  Base10 is assumed,
+ *     and base16 is allowed when prefixed with "0x".
+ * 4. otherwise, zero is returned.
+ */
+static int __init match_parm (char *s, const char *keywords[], int vals[], int max_vals)
+{
+	static const char *decimal = "0123456789";
+	static const char *hex = "0123456789abcdef";
+	int i, n;
+
+	if (*s++ == '=') {
+		/*
+		 * Try matching against the supplied keywords,
+		 * and return -(index+1) if we match one
+		 */
+		if (keywords != NULL) {
+			for (i = 0; *keywords != NULL; ++i) {
+				if (!strcmp(s, *keywords++))
+					return -(i+1);
+			}
+		}
+		/*
+		 * Look for a series of no more than "max_vals"
+		 * numeric values separated by commas, in base10,
+		 * or base16 when prefixed with "0x".
+		 * Return a count of how many were found.
+		 */
+		for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
+			vals[n] = i;
+			while ((i = stridx(decimal, *++s)) >= 0)
+				vals[n] = (vals[n] * 10) + i;
+			if (*s == 'x' && !vals[n]) {
+				while ((i = stridx(hex, *++s)) >= 0)
+					vals[n] = (vals[n] * 0x10) + i;
+			}
+			if (++n == max_vals)
+				break;
+			if (*s == ',' || *s == ';')
+				++s;
+		}
+		if (!*s)
+			return n;
+	}
+	return 0;	/* zero = nothing matched */
+}
+
+/*
+ * ide_setup() gets called VERY EARLY during initialization,
+ * to handle kernel "command line" strings beginning with "hdx="
+ * or "ide".  Here is the complete set currently supported:
+ *
+ * "hdx="  is recognized for all "x" from "a" to "h", such as "hdc".
+ * "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
+ *
+ * "hdx=noprobe"	: drive may be present, but do not probe for it
+ * "hdx=none"		: drive is NOT present, ignore cmos and do not probe
+ * "hdx=nowerr"		: ignore the WRERR_STAT bit on this drive
+ * "hdx=cdrom"		: drive is present, and is a cdrom drive
+ * "hdx=cyl,head,sect"	: disk drive is present, with specified geometry
+ * "hdx=noremap"	: do not remap 0->1 even though EZD was detected
+ * "hdx=autotune"	: driver will attempt to tune interface speed
+ *				to the fastest PIO mode supported,
+ *				if possible for this drive only.
+ *				Not fully supported by all chipset types,
+ *				and quite likely to cause trouble with
+ *				older/odd IDE drives.
+ *
+ * "hdx=slow"		: insert a huge pause after each access to the data
+ *				port. Should be used only as a last resort.
+ *
+ * "hdx=swapdata"	: when the drive is a disk, byte swap all data
+ * "hdx=bswap"		: same as above..........
+ * "hdxlun=xx"          : set the drive last logical unit.
+ * "hdx=flash"		: allows for more than one ata_flash disk to be
+ *				registered. In most cases, only one device
+ *				will be present.
+ * "hdx=scsi"		: the return of the ide-scsi flag, this is useful for
+ *				allowwing ide-floppy, ide-tape, and ide-cdrom|writers
+ *				to use ide-scsi emulation on a device specific option.
+ * "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in MHz,
+ *				where "xx" is between 20 and 66 inclusive,
+ *				used when tuning chipset PIO modes.
+ *				For PCI bus, 25 is correct for a P75 system,
+ *				30 is correct for P90,P120,P180 systems,
+ *				and 33 is used for P100,P133,P166 systems.
+ *				If in doubt, use idebus=33 for PCI.
+ *				As for VLB, it is safest to not specify it.
+ *
+ * "idex=noprobe"	: do not attempt to access/use this interface
+ * "idex=base"		: probe for an interface at the addr specified,
+ *				where "base" is usually 0x1f0 or 0x170
+ *				and "ctl" is assumed to be "base"+0x206
+ * "idex=base,ctl"	: specify both base and ctl
+ * "idex=base,ctl,irq"	: specify base, ctl, and irq number
+ * "idex=autotune"	: driver will attempt to tune interface speed
+ *				to the fastest PIO mode supported,
+ *				for all drives on this interface.
+ *				Not fully supported by all chipset types,
+ *				and quite likely to cause trouble with
+ *				older/odd IDE drives.
+ * "idex=noautotune"	: driver will NOT attempt to tune interface speed
+ *				This is the default for most chipsets,
+ *				except the cmd640.
+ * "idex=serialize"	: do not overlap operations on idex and ide(x^1)
+ * "idex=four"		: four drives on idex and ide(x^1) share same ports
+ * "idex=reset"		: reset interface before first use
+ * "idex=dma"		: enable DMA by default on both drives if possible
+ * "idex=ata66"		: informs the interface that it has an 80c cable
+ *				for chipsets that are ATA-66 capable, but
+ *				the ablity to bit test for detection is
+ *				currently unknown.
+ * "ide=reverse"	: Formerly called to pci sub-system, but now local.
+ *
+ * "splitfifo=betweenChan"
+ *			: FIFO Configuration of VIA 82c586(<nothing>,"A"or"B").
+ *                                 --see what follows...
+ * "splitfifo=betweenChan,thresholdprim,thresholdsec"
+ *			: FIFO Configuration of VIA 82c586(<nothing>,"A" or "B").
+ *				betweenChan = 1(all FIFO's to primary channel)
+ *                                          , 2(all FIFO's to secondary channel)
+ *                                          , 3 or 4(evenly shared between them).
+ *				note: without FIFO, a channel is (u)dma disabled!
+ *				thresholdprim = 4, 3, 2 or 1
+ *						(standing for 1, 3/4, 1/2, 1/4).
+ *                                    Sets the threshold of FIFO to begin dma
+ *                                    transfer on the primary channel.
+ *				thresholdsec = cf upper, but for secondary channel.
+ *
+ * The following are valid ONLY on ide0, (except dc4030)
+ * and the defaults for the base,ctl ports must not be altered.
+ *
+ * "ide0=dtc2278"	: probe/support DTC2278 interface
+ * "ide0=ht6560b"	: probe/support HT6560B interface
+ * "ide0=cmd640_vlb"	: *REQUIRED* for VLB cards with the CMD640 chip
+ *			  (not for PCI -- automatically detected)
+ * "ide0=qd6580"	: probe/support qd6580 interface
+ * "ide0=ali14xx"	: probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
+ * "ide0=umc8672"	: probe/support umc8672 chipsets
+ * "idex=dc4030"	: probe/support Promise DC4030VL interface
+ * "ide=doubler"	: probe/support IDE doublers on Amiga
+ */
+int __init ide_setup (char *s)
+{
+	int i, vals[3];
+	ide_hwif_t *hwif;
+	ide_drive_t *drive;
+	unsigned int hw, unit;
+	const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
+	const char max_hwif  = '0' + (MAX_HWIFS - 1);
+
+	printk("ide_setup: %s", s);
+	init_ide_data ();
+
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+	if (!strcmp(s, "ide=doubler")) {
+		extern int ide_doubler;
+
+		printk(" : Enabled support for IDE doublers\n");
+		ide_doubler = 1;
+		return 0;
+	}
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+	if (!strcmp(s, "ide=reverse")) {
+		ide_scan_direction = 1;
+		printk(" : Enabled support for IDE inverse scan order.\n");
+		return 0;
+	}
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+
+	/*
+	 * Look for drive options:  "hdx="
+	 */
+	if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
+		const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
+				"serialize", "autotune", "noautotune",
+				"slow", "swapdata", "bswap", "flash",
+				"remap", "noremap", "scsi", NULL};
+		unit = s[2] - 'a';
+		hw   = unit / MAX_DRIVES;
+		unit = unit % MAX_DRIVES;
+		hwif = &ide_hwifs[hw];
+		drive = &hwif->drives[unit];
+		if (strncmp(s + 4, "ide-", 4) == 0) {
+			strncpy(drive->driver_req, s + 4, 9);
+			goto done;
+		}
+		/*
+		 * Look for last lun option:  "hdxlun="
+		 */
+		if (s[3] == 'l' && s[4] == 'u' && s[5] == 'n') {
+			if (match_parm(&s[6], NULL, vals, 1) != 1)
+				goto bad_option;
+			if (vals[0] >= 0 && vals[0] <= 7) {
+				drive->last_lun = vals[0];
+				drive->forced_lun = 1;
+			} else
+				printk(" -- BAD LAST LUN! Expected value from 0 to 7");
+			goto done;
+		}
+		switch (match_parm(&s[3], hd_words, vals, 3)) {
+			case -1: /* "none" */
+				drive->nobios = 1;  /* drop into "noprobe" */
+			case -2: /* "noprobe" */
+				drive->noprobe = 1;
+				goto done;
+			case -3: /* "nowerr" */
+				drive->bad_wstat = BAD_R_STAT;
+				hwif->noprobe = 0;
+				goto done;
+			case -4: /* "cdrom" */
+				drive->present = 1;
+				drive->media = ide_cdrom;
+				hwif->noprobe = 0;
+				goto done;
+			case -5: /* "serialize" */
+				printk(" -- USE \"ide%d=serialize\" INSTEAD", hw);
+				goto do_serialize;
+			case -6: /* "autotune" */
+				drive->autotune = 1;
+				goto done;
+			case -7: /* "noautotune" */
+				drive->autotune = 2;
+				goto done;
+			case -8: /* "slow" */
+				drive->slow = 1;
+				goto done;
+			case -9: /* "swapdata" or "bswap" */
+			case -10:
+				drive->bswap = 1;
+				goto done;
+			case -11: /* "flash" */
+				drive->ata_flash = 1;
+				goto done;
+			case -12: /* "remap" */
+				drive->remap_0_to_1 = 1;
+				goto done;
+			case -13: /* "noremap" */
+				drive->remap_0_to_1 = 2;
+				goto done;
+			case -14: /* "scsi" */
+#if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI)
+				drive->scsi = 1;
+				goto done;
+#else
+				drive->scsi = 0;
+				goto bad_option;
+#endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */
+			case 3: /* cyl,head,sect */
+				drive->media	= ide_disk;
+				drive->cyl	= drive->bios_cyl  = vals[0];
+				drive->head	= drive->bios_head = vals[1];
+				drive->sect	= drive->bios_sect = vals[2];
+				drive->present	= 1;
+				drive->forced_geom = 1;
+				hwif->noprobe = 0;
+				goto done;
+			default:
+				goto bad_option;
+		}
+	}
+
+#if defined(CONFIG_BLK_DEV_VIA82CXXX)
+	/*
+	 *  Look for drive option "splitfifo=..."
+	 */
+
+	if (s[0] == 's' && s[1] == 'p' && s[2] == 'l' &&
+	    s[3] == 'i' && s[4] == 't' && s[5] == 'f' &&
+	    s[6] == 'i' && s[7] == 'f' && s[8] == 'o') {
+		byte tmp = 0x3a;		/* default config byte */
+
+		i = match_parm(&s[9], NULL, vals, 3);
+		switch(i) {
+		case 3:	
+			tmp &= 0xf0;
+			if ((vals[1] > 0) && (vals[1] < 5)) {
+				/* sets threshold for primary Channel: */
+				byte x = 4 - vals[1];
+				tmp |= (x << 2);
+			}
+			else
+				goto bad_option;
+			if ((vals[2] > 0) && (vals[2] < 5)) {
+				/* sets threshold for secondary Channel: */
+				byte x = 4 - vals[2];
+				tmp |= x;
+			}
+			else
+				goto bad_option;
+		case 1:
+			/* set the FIFO config between channels to 0: */
+			tmp &= 0x9f;
+			/* set the needed FIFO config between channels: */
+			if (vals[0] == 1)	/* primary fifo only */
+				tmp |= 0x10;
+			else if (vals[0] == 2)	/* secondary fifo only */
+				tmp |= 0x70;
+			else if (vals[0] == 4)	/* other shared fifo config */
+				tmp |= 0x50;
+			else if (vals[0] == 3)	/* default config */
+				tmp |= 0x30;
+			else
+				goto bad_option;
+			break;
+		default:
+			goto bad_option;
+		}
+		/* set the found option in fifoconfig */
+		fifoconfig = tmp;		
+		goto done;
+	}
+#endif  /* defined(CONFIG_BLK_DEV_VIA82CXXX) */
+
+	if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e')
+		goto bad_option;
+	/*
+	 * Look for bus speed option:  "idebus="
+	 */
+	if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
+		if (match_parm(&s[6], NULL, vals, 1) != 1)
+			goto bad_option;
+		if (vals[0] >= 20 && vals[0] <= 66) {
+			idebus_parameter = vals[0];
+		} else
+			printk(" -- BAD BUS SPEED! Expected value from 20 to 66");
+		goto done;
+	}
+	/*
+	 * Look for interface options:  "idex="
+	 */
+	if (s[3] >= '0' && s[3] <= max_hwif) {
+		/*
+		 * Be VERY CAREFUL changing this: note hardcoded indexes below
+		 * -8,-9,-10 : are reserved for future idex calls to ease the hardcoding.
+		 */
+		const char *ide_words[] = {
+			"noprobe", "serialize", "autotune", "noautotune", "reset", "dma", "ata66",
+			"minus8", "minus9", "minus10",
+			"four", "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL };
+		hw = s[3] - '0';
+		hwif = &ide_hwifs[hw];
+		i = match_parm(&s[4], ide_words, vals, 3);
+
+		/*
+		 * Cryptic check to ensure chipset not already set for hwif:
+		 */
+		if (i > 0 || i <= -11) {			/* is parameter a chipset name? */
+			if (hwif->chipset != ide_unknown)
+				goto bad_option;	/* chipset already specified */
+			if (i <= -11 && i != -18 && hw != 0)
+				goto bad_hwif;		/* chipset drivers are for "ide0=" only */
+			if (i <= -11 && i != -18 && ide_hwifs[hw+1].chipset != ide_unknown)
+				goto bad_option;	/* chipset for 2nd port already specified */
+			printk("\n");
+		}
+
+		switch (i) {
+#ifdef CONFIG_BLK_DEV_PDC4030
+			case -18: /* "dc4030" */
+			{
+				extern void init_pdc4030(void);
+				init_pdc4030();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+#ifdef CONFIG_BLK_DEV_ALI14XX
+			case -17: /* "ali14xx" */
+			{
+				extern void init_ali14xx (void);
+				init_ali14xx();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_ALI14XX */
+#ifdef CONFIG_BLK_DEV_UMC8672
+			case -16: /* "umc8672" */
+			{
+				extern void init_umc8672 (void);
+				init_umc8672();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_UMC8672 */
+#ifdef CONFIG_BLK_DEV_DTC2278
+			case -15: /* "dtc2278" */
+			{
+				extern void init_dtc2278 (void);
+				init_dtc2278();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+#ifdef CONFIG_BLK_DEV_CMD640
+			case -14: /* "cmd640_vlb" */
+			{
+				extern int cmd640_vlb; /* flag for cmd640.c */
+				cmd640_vlb = 1;
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_CMD640 */
+#ifdef CONFIG_BLK_DEV_HT6560B
+			case -13: /* "ht6560b" */
+			{
+				extern void init_ht6560b (void);
+				init_ht6560b();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_HT6560B */
+#if CONFIG_BLK_DEV_QD6580
+			case -12: /* "qd6580" */
+			{
+				extern void init_qd6580 (void);
+				init_qd6580();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_QD6580 */
+#ifdef CONFIG_BLK_DEV_4DRIVES
+			case -11: /* "four" drives on one set of ports */
+			{
+				ide_hwif_t *mate = &ide_hwifs[hw^1];
+				mate->drives[0].select.all ^= 0x20;
+				mate->drives[1].select.all ^= 0x20;
+				hwif->chipset = mate->chipset = ide_4drives;
+				mate->irq = hwif->irq;
+				memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports));
+				goto do_serialize;
+			}
+#endif /* CONFIG_BLK_DEV_4DRIVES */
+			case -10: /* minus10 */
+			case -9: /* minus9 */
+			case -8: /* minus8 */
+				goto bad_option;
+			case -7: /* ata66 */
+#ifdef CONFIG_BLK_DEV_IDEPCI
+				hwif->udma_four = 1;
+				goto done;
+#else /* !CONFIG_BLK_DEV_IDEPCI */
+				hwif->udma_four = 0;
+				goto bad_hwif;
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+			case -6: /* dma */
+				hwif->autodma = 1;
+				goto done;
+			case -5: /* "reset" */
+				hwif->reset = 1;
+				goto done;
+			case -4: /* "noautotune" */
+				hwif->drives[0].autotune = 2;
+				hwif->drives[1].autotune = 2;
+				goto done;
+			case -3: /* "autotune" */
+				hwif->drives[0].autotune = 1;
+				hwif->drives[1].autotune = 1;
+				goto done;
+			case -2: /* "serialize" */
+			do_serialize:
+				hwif->mate = &ide_hwifs[hw^1];
+				hwif->mate->mate = hwif;
+				hwif->serialized = hwif->mate->serialized = 1;
+				goto done;
+
+			case -1: /* "noprobe" */
+				hwif->noprobe = 1;
+				goto done;
+
+			case 1:	/* base */
+				vals[1] = vals[0] + 0x206; /* default ctl */
+			case 2: /* base,ctl */
+				vals[2] = 0;	/* default irq = probe for it */
+			case 3: /* base,ctl,irq */
+				hwif->hw.irq = vals[2];
+				ide_init_hwif_ports(&hwif->hw, (ide_ioreg_t) vals[0], (ide_ioreg_t) vals[1], &hwif->irq);
+				memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
+				hwif->irq      = vals[2];
+				hwif->noprobe  = 0;
+				hwif->chipset  = ide_generic;
+				goto done;
+
+			case 0: goto bad_option;
+			default:
+				printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
+				return 0;
+		}
+	}
+bad_option:
+	printk(" -- BAD OPTION\n");
+	return 0;
+bad_hwif:
+	printk("-- NOT SUPPORTED ON ide%d", hw);
+done:
+	printk("\n");
+	return 0;
+}
+
+/*
+ * probe_for_hwifs() finds/initializes "known" IDE interfaces
+ */
+static void __init probe_for_hwifs (void)
+{
+#ifdef CONFIG_PCI
+	if (pci_present())
+	{
+#ifdef CONFIG_BLK_DEV_IDEPCI
+		ide_scan_pcibus(ide_scan_direction);
+#else
+#ifdef CONFIG_BLK_DEV_RZ1000
+		{
+			extern void ide_probe_for_rz100x(void);
+			ide_probe_for_rz100x();
+		}
+#endif /* CONFIG_BLK_DEV_RZ1000 */
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+	}
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_BLK_DEV_CMD640
+	{
+		extern void ide_probe_for_cmd640x(void);
+		ide_probe_for_cmd640x();
+	}
+#endif /* CONFIG_BLK_DEV_CMD640 */
+#ifdef CONFIG_BLK_DEV_PDC4030
+	{
+		extern int ide_probe_for_pdc4030(void);
+		(void) ide_probe_for_pdc4030();
+	}
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+	{
+		extern void pmac_ide_probe(void);
+		pmac_ide_probe();
+	}
+#endif /* CONFIG_BLK_DEV_IDE_PMAC */
+#ifdef CONFIG_BLK_DEV_IDE_ICSIDE
+	{
+		extern void icside_init(void);
+		icside_init();
+	}
+#endif /* CONFIG_BLK_DEV_IDE_ICSIDE */
+#ifdef CONFIG_BLK_DEV_IDE_RAPIDE
+	{
+		extern void rapide_init(void);
+		rapide_init();
+	}
+#endif /* CONFIG_BLK_DEV_IDE_RAPIDE */
+#ifdef CONFIG_BLK_DEV_GAYLE
+	{
+		extern void gayle_init(void);
+		gayle_init();
+	}
+#endif /* CONFIG_BLK_DEV_GAYLE */
+#ifdef CONFIG_BLK_DEV_FALCON_IDE
+	{
+		extern void falconide_init(void);
+		falconide_init();
+	}
+#endif /* CONFIG_BLK_DEV_FALCON_IDE */
+#ifdef CONFIG_BLK_DEV_MAC_IDE
+	{
+		extern void macide_init(void);
+		macide_init();
+	}
+#endif /* CONFIG_BLK_DEV_MAC_IDE */
+#ifdef CONFIG_BLK_DEV_BUDDHA
+	{
+		extern void buddha_init(void);
+		buddha_init();
+	}
+#endif /* CONFIG_BLK_DEV_BUDDHA */
+#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP)
+	{
+		extern void pnpide_init(int enable);
+		pnpide_init(1);
+	}
+#endif /* CONFIG_BLK_DEV_ISAPNP */
+}
+
+void __init ide_init_builtin_drivers (void)
+{
+	/*
+	 * Probe for special PCI and other "known" interface chipsets
+	 */
+	probe_for_hwifs ();
+
+#ifdef CONFIG_BLK_DEV_IDE
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
+		ide_get_lock(&ide_lock, NULL, NULL);	/* for atari only */
+		disable_irq(ide_hwifs[0].irq);	/* disable_irq_nosync ?? */
+	}
+#endif /* __mc68000__ || CONFIG_APUS */
+
+	(void) ideprobe_init();
+
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
+		enable_irq(ide_hwifs[0].irq);
+		ide_release_lock(&ide_lock);	/* for atari only */
+	}
+#endif /* __mc68000__ || CONFIG_APUS */
+#endif /* CONFIG_BLK_DEV_IDE */
+
+#ifdef CONFIG_PROC_FS
+	proc_ide_create();
+#endif
+
+	/*
+	 * Attempt to match drivers for the available drives
+	 */
+#ifdef CONFIG_BLK_DEV_IDEDISK
+	(void) idedisk_init();
+#endif /* CONFIG_BLK_DEV_IDEDISK */
+#ifdef CONFIG_BLK_DEV_IDECD
+	(void) ide_cdrom_init();
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+	(void) idetape_init();
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	(void) idefloppy_init();
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+ #ifdef CONFIG_SCSI
+	(void) idescsi_init();
+ #else
+    #warning ide scsi-emulation selected but no SCSI-subsystem in kernel
+ #endif
+#endif /* CONFIG_BLK_DEV_IDESCSI */
+}
+
+static int default_cleanup (ide_drive_t *drive)
+{
+	return ide_unregister_subdriver(drive);
+}
+
+static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
+}
+ 
+static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_end_request(uptodate, hwgroup);
+}
+  
+static int default_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	return -EIO;
+}
+
+static int default_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	drive->usage--;
+	return -EIO;
+}
+
+static void default_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+}
+
+static int default_check_media_change (ide_drive_t *drive)
+{
+	return 1;
+}
+
+static void default_pre_reset (ide_drive_t *drive)
+{
+}
+
+static unsigned long default_capacity (ide_drive_t *drive)
+{
+	return 0x7fffffff;	/* cdrom or tape */
+}
+
+static ide_startstop_t default_special (ide_drive_t *drive)
+{
+	special_t *s = &drive->special;
+
+	s->all = 0;
+	drive->mult_req = 0;
+	return ide_stopped;
+}
+
+static void setup_driver_defaults (ide_drive_t *drive)
+{
+	ide_driver_t *d = drive->driver;
+
+	if (d->cleanup == NULL)		d->cleanup = default_cleanup;
+	if (d->do_request == NULL)	d->do_request = default_do_request;
+	if (d->end_request == NULL)	d->end_request = default_end_request;
+	if (d->ioctl == NULL)		d->ioctl = default_ioctl;
+	if (d->open == NULL)		d->open = default_open;
+	if (d->release == NULL)		d->release = default_release;
+	if (d->media_change == NULL)	d->media_change = default_check_media_change;
+	if (d->pre_reset == NULL)	d->pre_reset = default_pre_reset;
+	if (d->capacity == NULL)	d->capacity = default_capacity;
+	if (d->special == NULL)		d->special = default_special;
+}
+
+ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n)
+{
+	unsigned int unit, index, i;
+
+	for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
+		ide_hwif_t *hwif = &ide_hwifs[index];
+		if (!hwif->present)
+			continue;
+		for (unit = 0; unit < MAX_DRIVES; ++unit) {
+			ide_drive_t *drive = &hwif->drives[unit];
+			char *req = drive->driver_req;
+			if (*req && !strstr(name, req))
+				continue;
+			if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
+				return drive;
+		}
+	}
+	return NULL;
+}
+
+int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version)
+{
+	unsigned long flags;
+	
+	save_flags(flags);		/* all CPUs */
+	cli();				/* all CPUs */
+	if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL || drive->busy || drive->usage) {
+		restore_flags(flags);	/* all CPUs */
+		return 1;
+	}
+	drive->driver = driver;
+	setup_driver_defaults(drive);
+	restore_flags(flags);		/* all CPUs */
+	if (drive->autotune != 2) {
+		if (driver->supports_dma && HWIF(drive)->dmaproc != NULL) {
+			/*
+			 * Force DMAing for the beginning of the check.
+			 * Some chipsets appear to do interesting things,
+			 * if not checked and cleared.
+			 *   PARANOIA!!!
+			 */
+			(void) (HWIF(drive)->dmaproc(ide_dma_off_quietly, drive));
+			(void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
+		}
+		drive->dsc_overlap = (drive->next != drive && driver->supports_dsc_overlap);
+		drive->nice1 = 1;
+	}
+	drive->revalidate = 1;
+#ifdef CONFIG_PROC_FS
+	ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
+	ide_add_proc_entries(drive->proc, driver->proc, drive);
+#endif
+	return 0;
+}
+
+int ide_unregister_subdriver (ide_drive_t *drive)
+{
+	unsigned long flags;
+	
+	save_flags(flags);		/* all CPUs */
+	cli();				/* all CPUs */
+	if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) {
+		restore_flags(flags);	/* all CPUs */
+		return 1;
+	}
+#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE)
+	pnpide_init(0);
+#endif /* CONFIG_BLK_DEV_ISAPNP */
+#ifdef CONFIG_PROC_FS
+	ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc);
+	ide_remove_proc_entries(drive->proc, generic_subdriver_entries);
+#endif
+	auto_remove_settings(drive);
+	drive->driver = NULL;
+	restore_flags(flags);		/* all CPUs */
+	return 0;
+}
+
+int ide_register_module (ide_module_t *module)
+{
+	ide_module_t *p = ide_modules;
+
+	while (p) {
+		if (p == module)
+			return 1;
+		p = p->next;
+	}
+	module->next = ide_modules;
+	ide_modules = module;
+	revalidate_drives();
+	return 0;
+}
+
+void ide_unregister_module (ide_module_t *module)
+{
+	ide_module_t **p;
+
+	for (p = &ide_modules; (*p) && (*p) != module; p = &((*p)->next));
+	if (*p)
+		*p = (*p)->next;
+}
+
+struct block_device_operations ide_fops[] = {{
+	open:			ide_open,
+	release:		ide_release,
+	ioctl:			ide_ioctl,
+	check_media_change:	ide_check_media_change,
+	revalidate:		ide_revalidate_disk
+}};
+
+EXPORT_SYMBOL(ide_hwifs);
+EXPORT_SYMBOL(ide_register_module);
+EXPORT_SYMBOL(ide_unregister_module);
+EXPORT_SYMBOL(ide_spin_wait_hwgroup);
+
+/*
+ * Probe module
+ */
+devfs_handle_t ide_devfs_handle = NULL;
+
+EXPORT_SYMBOL(ide_probe);
+EXPORT_SYMBOL(drive_is_flashcard);
+EXPORT_SYMBOL(ide_timer_expiry);
+EXPORT_SYMBOL(ide_intr);
+EXPORT_SYMBOL(ide_fops);
+EXPORT_SYMBOL(ide_get_queue);
+EXPORT_SYMBOL(do_ide0_request);
+EXPORT_SYMBOL(ide_add_generic_settings);
+EXPORT_SYMBOL(ide_devfs_handle);
+#if MAX_HWIFS > 1
+EXPORT_SYMBOL(do_ide1_request);
+#endif /* MAX_HWIFS > 1 */
+#if MAX_HWIFS > 2
+EXPORT_SYMBOL(do_ide2_request);
+#endif /* MAX_HWIFS > 2 */
+#if MAX_HWIFS > 3
+EXPORT_SYMBOL(do_ide3_request);
+#endif /* MAX_HWIFS > 3 */
+#if MAX_HWIFS > 4
+EXPORT_SYMBOL(do_ide4_request);
+#endif /* MAX_HWIFS > 4 */
+#if MAX_HWIFS > 5
+EXPORT_SYMBOL(do_ide5_request);
+#endif /* MAX_HWIFS > 5 */
+#if MAX_HWIFS > 6
+EXPORT_SYMBOL(do_ide6_request);
+#endif /* MAX_HWIFS > 6 */
+#if MAX_HWIFS > 7
+EXPORT_SYMBOL(do_ide7_request);
+#endif /* MAX_HWIFS > 7 */
+#if MAX_HWIFS > 8
+EXPORT_SYMBOL(do_ide8_request);
+#endif /* MAX_HWIFS > 8 */
+#if MAX_HWIFS > 9
+EXPORT_SYMBOL(do_ide9_request);
+#endif /* MAX_HWIFS > 9 */
+
+/*
+ * Driver module
+ */
+EXPORT_SYMBOL(ide_scan_devices);
+EXPORT_SYMBOL(ide_register_subdriver);
+EXPORT_SYMBOL(ide_unregister_subdriver);
+EXPORT_SYMBOL(ide_replace_subdriver);
+EXPORT_SYMBOL(ide_input_data);
+EXPORT_SYMBOL(ide_output_data);
+EXPORT_SYMBOL(atapi_input_bytes);
+EXPORT_SYMBOL(atapi_output_bytes);
+EXPORT_SYMBOL(ide_set_handler);
+EXPORT_SYMBOL(ide_dump_status);
+EXPORT_SYMBOL(ide_error);
+EXPORT_SYMBOL(ide_fixstring);
+EXPORT_SYMBOL(ide_wait_stat);
+EXPORT_SYMBOL(ide_do_reset);
+EXPORT_SYMBOL(ide_init_drive_cmd);
+EXPORT_SYMBOL(ide_do_drive_cmd);
+EXPORT_SYMBOL(ide_end_drive_cmd);
+EXPORT_SYMBOL(ide_end_request);
+EXPORT_SYMBOL(ide_revalidate_disk);
+EXPORT_SYMBOL(ide_cmd);
+EXPORT_SYMBOL(ide_wait_cmd);
+EXPORT_SYMBOL(ide_delay_50ms);
+EXPORT_SYMBOL(ide_stall_queue);
+#ifdef CONFIG_PROC_FS
+EXPORT_SYMBOL(ide_add_proc_entries);
+EXPORT_SYMBOL(ide_remove_proc_entries);
+EXPORT_SYMBOL(proc_ide_read_geometry);
+EXPORT_SYMBOL(create_proc_ide_interfaces);
+#endif
+EXPORT_SYMBOL(ide_add_setting);
+EXPORT_SYMBOL(ide_remove_setting);
+
+EXPORT_SYMBOL(ide_register_hw);
+EXPORT_SYMBOL(ide_register);
+EXPORT_SYMBOL(ide_unregister);
+EXPORT_SYMBOL(ide_setup_ports);
+EXPORT_SYMBOL(hwif_unregister);
+EXPORT_SYMBOL(get_info_ptr);
+EXPORT_SYMBOL(current_capacity);
+
+/*
+ * This is gets invoked once during initialization, to set *everything* up
+ */
+int __init ide_init (void)
+{
+	static char banner_printed = 0;
+	int i;
+
+	if (!banner_printed) {
+		printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n");
+		ide_devfs_handle = devfs_mk_dir (NULL, "ide", 3, NULL);
+		(void) ide_system_bus_speed();
+		banner_printed = 1;
+	}
+
+	init_ide_data ();
+
+	initializing = 1;
+	ide_init_builtin_drivers();
+	initializing = 0;
+
+	for (i = 0; i < MAX_HWIFS; ++i) {
+		ide_hwif_t  *hwif = &ide_hwifs[i];
+		if (hwif->present)
+			ide_geninit(hwif);
+	}
+
+	return 0;
+}
+
+static void __init parse_options (char *line)
+{
+	char *next = line;
+
+	if (line == NULL || !*line)
+		return;
+	while ((line = next) != NULL) {
+ 		if ((next = strchr(line,' ')) != NULL)
+			*next++ = 0;
+		if (!strncmp(line,"ide",3) ||
+		    !strncmp(line,"idebus",6) ||
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+		    !strncmp(line,"splitfifo",9) ||
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+		    !strncmp(line,"hdxlun",6) ||
+		    (!strncmp(line,"hd",2) && line[2] != '='))
+			(void) ide_setup(line);
+	}
+}
+
+#ifdef MODULE
+char *options = NULL;
+MODULE_PARM(options,"s");
+
+int init_module (void)
+{
+	parse_options(options);
+	return ide_init();
+}
+
+void cleanup_module (void)
+{
+	int index;
+
+	for (index = 0; index < MAX_HWIFS; ++index)
+		ide_unregister(index);
+
+#ifdef CONFIG_PROC_FS
+	proc_ide_destroy();
+#endif
+	devfs_unregister (ide_devfs_handle);
+}
+
+#else /* !MODULE */
+
+static int parse_ide_setup (char *line)
+{
+	parse_options(line);
+	return 0;
+}
+__setup("", parse_ide_setup);
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide_modes.h linux/drivers/ide/ide_modes.h
--- v2.3.51/linux/drivers/ide/ide_modes.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ide_modes.h	Tue Mar 14 18:38:08 2000
@@ -0,0 +1,233 @@
+/*
+ *  linux/drivers/block/ide_modes.h
+ *
+ *  Copyright (C) 1996  Linus Torvalds, Igor Abramov, and Mark Lord
+ */
+
+#ifndef _IDE_MODES_H
+#define _IDE_MODES_H
+
+#include <linux/config.h>
+
+/*
+ * Shared data/functions for determining best PIO mode for an IDE drive.
+ * Most of this stuff originally lived in cmd640.c, and changes to the
+ * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
+ * breaking the fragile cmd640.c support.
+ */
+
+#ifdef CONFIG_BLK_DEV_IDE_MODES
+
+/*
+ * Standard (generic) timings for PIO modes, from ATA2 specification.
+ * These timings are for access to the IDE data port register *only*.
+ * Some drives may specify a mode, while also specifying a different
+ * value for cycle_time (from drive identification data).
+ */
+typedef struct ide_pio_timings_s {
+	int	setup_time;	/* Address setup (ns) minimum */
+	int	active_time;	/* Active pulse (ns) minimum */
+	int	cycle_time;	/* Cycle time (ns) minimum = (setup + active + recovery) */
+} ide_pio_timings_t;
+
+typedef struct ide_pio_data_s {
+	byte pio_mode;
+	byte use_iordy;
+	byte overridden;
+	byte blacklisted;
+	unsigned int cycle_time;
+} ide_pio_data_t;
+	
+#ifndef _IDE_C
+
+int ide_scan_pio_blacklist (char *model);
+byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d);
+extern const ide_pio_timings_t ide_pio_timings[6];
+
+#else /* _IDE_C */
+
+const ide_pio_timings_t ide_pio_timings[6] = {
+	{ 70,	165,	600 },	/* PIO Mode 0 */
+	{ 50,	125,	383 },	/* PIO Mode 1 */
+	{ 30,	100,	240 },	/* PIO Mode 2 */
+	{ 30,	80,	180 },	/* PIO Mode 3 with IORDY */
+	{ 25,	70,	120 },	/* PIO Mode 4 with IORDY */
+	{ 20,	50,	100 }	/* PIO Mode 5 with IORDY (nonstandard) */
+};
+
+/*
+ * Black list. Some drives incorrectly report their maximal PIO mode,
+ * at least in respect to CMD640. Here we keep info on some known drives.
+ */
+static struct ide_pio_info {
+	const char	*name;
+	int		pio;
+} ide_pio_blacklist [] = {
+/*	{ "Conner Peripherals 1275MB - CFS1275A", 4 }, */
+	{ "Conner Peripherals 540MB - CFS540A", 3 },
+
+	{ "WDC AC2700",  3 },
+	{ "WDC AC2540",  3 },
+	{ "WDC AC2420",  3 },
+	{ "WDC AC2340",  3 },
+	{ "WDC AC2250",  0 },
+	{ "WDC AC2200",  0 },
+	{ "WDC AC21200", 4 },
+	{ "WDC AC2120",  0 },
+	{ "WDC AC2850",  3 },
+	{ "WDC AC1270",  3 },
+	{ "WDC AC1170",  1 },
+	{ "WDC AC1210",  1 },
+	{ "WDC AC280",   0 },
+/*	{ "WDC AC21000", 4 }, */
+	{ "WDC AC31000", 3 },
+	{ "WDC AC31200", 3 },
+/*	{ "WDC AC31600", 4 }, */
+
+	{ "Maxtor 7131 AT", 1 },
+	{ "Maxtor 7171 AT", 1 },
+	{ "Maxtor 7213 AT", 1 },
+	{ "Maxtor 7245 AT", 1 },
+	{ "Maxtor 7345 AT", 1 },
+	{ "Maxtor 7546 AT", 3 },
+	{ "Maxtor 7540 AV", 3 },
+
+	{ "SAMSUNG SHD-3121A", 1 },
+	{ "SAMSUNG SHD-3122A", 1 },
+	{ "SAMSUNG SHD-3172A", 1 },
+
+/*	{ "ST51080A", 4 },
+ *	{ "ST51270A", 4 },
+ *	{ "ST31220A", 4 },
+ *	{ "ST31640A", 4 },
+ *	{ "ST32140A", 4 },
+ *	{ "ST3780A",  4 },
+ */
+	{ "ST5660A",  3 },
+	{ "ST3660A",  3 },
+	{ "ST3630A",  3 },
+	{ "ST3655A",  3 },
+	{ "ST3391A",  3 },
+	{ "ST3390A",  1 },
+	{ "ST3600A",  1 },
+	{ "ST3290A",  0 },
+	{ "ST3144A",  0 },
+	{ "ST3491A",  1 },	/* reports 3, should be 1 or 2 (depending on */	
+				/* drive) according to Seagates FIND-ATA program */
+
+	{ "QUANTUM ELS127A", 0 },
+	{ "QUANTUM ELS170A", 0 },
+	{ "QUANTUM LPS240A", 0 },
+	{ "QUANTUM LPS210A", 3 },
+	{ "QUANTUM LPS270A", 3 },
+	{ "QUANTUM LPS365A", 3 },
+	{ "QUANTUM LPS540A", 3 },
+	{ "QUANTUM LIGHTNING 540A", 3 },
+	{ "QUANTUM LIGHTNING 730A", 3 },
+	{ "QUANTUM FIREBALL", 3 }, /* For models 540/640/1080/1280 */
+				   /* 1080A works fine in mode4 with triton */
+	{ NULL,	0 }
+};
+
+/*
+ * This routine searches the ide_pio_blacklist for an entry
+ * matching the start/whole of the supplied model name.
+ *
+ * Returns -1 if no match found.
+ * Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
+ */
+int ide_scan_pio_blacklist (char *model)
+{
+	struct ide_pio_info *p;
+
+	for (p = ide_pio_blacklist; p->name != NULL; p++) {
+		if (strncmp(p->name, model, strlen(p->name)) == 0)
+			return p->pio;
+	}
+	return -1;
+}
+
+/*
+ * This routine returns the recommended PIO settings for a given drive,
+ * based on the drive->id information and the ide_pio_blacklist[].
+ * This is used by most chipset support modules when "auto-tuning".
+ */
+
+/*
+ * Drive PIO mode auto selection
+ */
+byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d)
+{
+	int pio_mode;
+	int cycle_time = 0;
+	int use_iordy = 0;
+	struct hd_driveid* id = drive->id;
+	int overridden  = 0;
+	int blacklisted = 0;
+
+	if (mode_wanted != 255) {
+		pio_mode = mode_wanted;
+	} else if (!drive->id) {
+		pio_mode = 0;
+	} else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
+		overridden = 1;
+		blacklisted = 1;
+		use_iordy = (pio_mode > 2);
+	} else {
+		pio_mode = id->tPIO;
+		if (pio_mode > 2) {	/* 2 is maximum allowed tPIO value */
+			pio_mode = 2;
+			overridden = 1;
+		}
+		if (id->field_valid & 2) {	  /* drive implements ATA2? */
+			if (id->capability & 8) { /* drive supports use_iordy? */
+				use_iordy = 1;
+				cycle_time = id->eide_pio_iordy;
+				if (id->eide_pio_modes & 7) {
+					overridden = 0;
+					if (id->eide_pio_modes & 4)
+						pio_mode = 5;
+					else if (id->eide_pio_modes & 2)
+						pio_mode = 4;
+					else
+						pio_mode = 3;
+				}
+			} else {
+				cycle_time = id->eide_pio;
+			}
+		}
+
+#if 0
+		if (drive->id->major_rev_num & 0x0004) printf("ATA-2 ");
+#endif
+
+		/*
+		 * Conservative "downgrade" for all pre-ATA2 drives
+		 */
+		if (pio_mode && pio_mode < 4) {
+			pio_mode--;
+			overridden = 1;
+#if 0
+			use_iordy = (pio_mode > 2);
+#endif
+			if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time)
+				cycle_time = 0; /* use standard timing */
+		}
+	}
+	if (pio_mode > max_mode) {
+		pio_mode = max_mode;
+		cycle_time = 0;
+	}
+	if (d) {
+		d->pio_mode = pio_mode;
+		d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time;
+		d->use_iordy = use_iordy;
+		d->overridden = overridden;
+		d->blacklisted = blacklisted;
+	}
+	return pio_mode;
+}
+
+#endif /* _IDE_C */
+#endif /* CONFIG_BLK_DEV_IDE_MODES */
+#endif /* _IDE_MODES_H */
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/macide.c linux/drivers/ide/macide.c
--- v2.3.51/linux/drivers/ide/macide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/macide.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,116 @@
+/*
+ *  linux/drivers/ide/macide.c -- Macintosh IDE Driver
+ *
+ *     Copyright (C) 1998 by Michael Schmitz
+ *
+ *  This driver was written based on information obtained from the MacOS IDE
+ *  driver binary by Mikael Forselius
+ *
+ *  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 <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/machw.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+
+    /*
+     *  Base of the IDE interface (see ATAManager ROM code)
+     */
+
+#define MAC_HD_BASE	0x50f1a000
+
+    /*
+     *  Offsets from the above base (scaling 4)
+     */
+
+#define MAC_HD_DATA	0x00
+#define MAC_HD_ERROR	0x04		/* see err-bits */
+#define MAC_HD_NSECTOR	0x08		/* nr of sectors to read/write */
+#define MAC_HD_SECTOR	0x0c		/* starting sector */
+#define MAC_HD_LCYL	0x10		/* starting cylinder */
+#define MAC_HD_HCYL	0x14		/* high byte of starting cyl */
+#define MAC_HD_SELECT	0x18		/* 101dhhhh , d=drive, hhhh=head */
+#define MAC_HD_STATUS	0x1c		/* see status-bits */
+#define MAC_HD_CONTROL	0x38		/* control/altstatus */
+
+static int __init macide_offsets[IDE_NR_PORTS] = {
+    MAC_HD_DATA, MAC_HD_ERROR, MAC_HD_NSECTOR, MAC_HD_SECTOR, MAC_HD_LCYL,
+    MAC_HD_HCYL, MAC_HD_SELECT, MAC_HD_STATUS, MAC_HD_CONTROL
+};
+
+	/*
+	 * Other registers
+	 */
+
+	/* 
+	 * IDE interrupt status register for both (?) hwifs on Quadra
+	 * Initial setting: 0xc
+	 * Guessing again:
+	 * Bit 0+1: some interrupt flags
+	 * Bit 2+3: some interrupt enable
+	 * Bit 4:   ??
+	 * Bit 5:   IDE interrupt flag (any hwif)
+	 * Bit 6:   maybe IDE interrupt enable (any hwif) ??
+	 * Bit 7:   Any interrupt condition
+	 *
+	 * Only relevant item: bit 5, to be checked by mac_ack_intr
+	 */
+
+#define MAC_HD_ISR	0x101
+
+static int mac_ack_intr(ide_hwif_t* hwif)
+{
+	unsigned char isr;
+	isr = readb(MAC_HD_BASE + MAC_HD_ISR);
+	if (isr & (1<<5)) {
+		writeb(isr & ~(1<<5), MAC_HD_BASE + MAC_HD_ISR);
+		return 1;
+	}
+
+	return 0;
+}
+
+    /*
+     *  Probe for a Macintosh IDE interface
+     */
+
+void __init macide_init(void)
+{
+	hw_regs_t hw;
+	int index = -1;
+
+	if (!MACH_IS_MAC || macintosh_config->ide_type == 0)
+		return;
+
+	switch (macintosh_config->ide_type) {
+	case MAC_IDE_QUADRA:
+		ide_setup_ports(&hw, (ide_ioreg_t)MAC_HD_BASE, macide_offsets,
+				0, (ide_ioreg_t)(MAC_HD_BASE+MAC_HD_ISR),
+				mac_ack_intr, IRQ_NUBUS_F);
+		index = ide_register_hw(&hw, NULL);
+		break;
+
+	default:
+	    ide_setup_ports(&hw, (ide_ioreg_t)MAC_HD_BASE, macide_offsets,
+	    		    0, 0, NULL, IRQ_NUBUS_C);
+	    index = ide_register_hw(&hw, NULL);
+	    break;
+	}
+
+        if (index != -1) {
+		if (macintosh_config->ide_type == MAC_IDE_QUADRA)
+			printk("ide%d: Macintosh Quadra IDE interface\n", index);
+		else
+			printk("ide%d: Macintosh Powerbook IDE interface\n", index);
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ns87415.c linux/drivers/ide/ns87415.c
--- v2.3.51/linux/drivers/ide/ns87415.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/ns87415.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,185 @@
+/*
+ * linux/drivers/block/ns87415.c		Version 1.00  December 7, 1997
+ *
+ * Copyright (C) 1997-1998  Mark Lord
+ * Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be)
+ *
+ * Inspired by an earlier effort from David S. Miller (davem@caipfs.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 };
+
+/*
+ * This routine either enables/disables (according to drive->present)
+ * the IRQ associated with the port (HWIF(drive)),
+ * and selects either PIO or DMA handshaking for the next I/O operation.
+ */
+static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data;
+	struct pci_dev *dev = hwif->pci_dev;
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	new = *old;
+
+	/* Adjust IRQ enable bit */
+	bit = 1 << (8 + hwif->channel);
+	new = drive->present ? (new & ~bit) : (new | bit);
+
+	/* Select PIO or DMA, DMA may only be selected for one drive/channel. */
+	bit   = 1 << (20 + drive->select.b.unit       + (hwif->channel << 1));
+	other = 1 << (20 + (1 - drive->select.b.unit) + (hwif->channel << 1));
+	new = use_dma ? ((new & ~other) | bit) : (new & ~bit);
+
+	if (new != *old) {
+		unsigned char stat;
+
+		/*
+		 * Don't change DMA engine settings while Write Buffers
+		 * are busy.
+		 */
+		(void) pci_read_config_byte(dev, 0x43, &stat);
+		while (stat & 0x03) {
+			udelay(1);
+			(void) pci_read_config_byte(dev, 0x43, &stat);
+		}
+
+		*old = new;
+		(void) pci_write_config_dword(dev, 0x40, new);
+
+		/*
+		 * And let things settle...
+		 */
+		udelay(10);
+	}
+
+	__restore_flags(flags);	/* local CPU only */
+}
+
+static void ns87415_selectproc (ide_drive_t *drive)
+{
+	ns87415_prepare_drive (drive, drive->using_dma);
+}
+
+static int ns87415_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t	*hwif = HWIF(drive);
+	byte		dma_stat;
+
+	switch (func) {
+		case ide_dma_end: /* returns 1 on error, 0 otherwise */
+			drive->waiting_for_dma = 0;
+			dma_stat = inb(hwif->dma_base+2);
+			outb(inb(hwif->dma_base)&~1, hwif->dma_base);	/* stop DMA */
+			outb(inb(hwif->dma_base)|6, hwif->dma_base);	/* from ERRATA: clear the INTR & ERROR bits */
+			ide_destroy_dmatable(drive);			/* and free any DMA resources */
+			return (dma_stat & 7) != 4;		/* verify good DMA status */
+		case ide_dma_write:
+		case ide_dma_read:
+			ns87415_prepare_drive(drive, 1);	/* select DMA xfer */
+			if (!ide_dmaproc(func, drive))		/* use standard DMA stuff */
+				return 0;
+			ns87415_prepare_drive(drive, 0);	/* DMA failed: select PIO xfer */
+			return 1;
+		case ide_dma_check:
+			if (drive->media != ide_disk)
+				return ide_dmaproc(ide_dma_off_quietly, drive);
+			/* Fallthrough... */
+		default:
+			return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+	}
+}
+
+void __init ide_init_ns87415 (ide_hwif_t *hwif)
+{
+	struct pci_dev *dev = hwif->pci_dev;
+	unsigned int ctrl, using_inta;
+	byte progif;
+#ifdef __sparc_v9__
+	int timeout;
+	byte stat;
+#endif
+
+	/* Set a good latency timer and cache line size value. */
+	(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+#ifdef __sparc_v9__
+	(void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
+#endif
+
+	/*
+	 * We cannot probe for IRQ: both ports share common IRQ on INTA.
+	 * Also, leave IRQ masked during drive probing, to prevent infinite
+	 * interrupts from a potentially floating INTA..
+	 *
+	 * IRQs get unmasked in selectproc when drive is first used.
+	 */
+	(void) pci_read_config_dword(dev, 0x40, &ctrl);
+	(void) pci_read_config_byte(dev, 0x09, &progif);
+	/* is irq in "native" mode? */
+	using_inta = progif & (1 << (hwif->channel << 1));
+	if (!using_inta)
+		using_inta = ctrl & (1 << (4 + hwif->channel));
+	if (hwif->mate) {
+		hwif->select_data = hwif->mate->select_data;
+	} else {
+		hwif->select_data = (unsigned long)
+					&ns87415_control[ns87415_count++];
+		ctrl |= (1 << 8) | (1 << 9);	/* mask both IRQs */
+		if (using_inta)
+			ctrl &= ~(1 << 6);	/* unmask INTA */
+		*((unsigned int *)hwif->select_data) = ctrl;
+		(void) pci_write_config_dword(dev, 0x40, ctrl);
+
+		/*
+		 * Set prefetch size to 512 bytes for both ports,
+		 * but don't turn on/off prefetching here.
+		 */
+		pci_write_config_byte(dev, 0x55, 0xee);
+
+#ifdef __sparc_v9__
+		/*
+		 * XXX: Reset the device, if we don't it will not respond
+		 *      to SELECT_DRIVE() properly during first probe_hwif().
+		 */
+		timeout = 10000;
+		outb(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
+		udelay(10);
+		outb(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
+		do {
+			udelay(50);
+			stat = inb(hwif->io_ports[IDE_STATUS_OFFSET]);
+                	if (stat == 0xff)
+                        	break;
+        	} while ((stat & BUSY_STAT) && --timeout);
+#endif
+	}
+
+	if (hwif->dma_base)
+		outb(0x60, hwif->dma_base + 2);
+
+	if (!using_inta)
+		hwif->irq = hwif->channel ? 15 : 14;	/* legacy mode */
+	else if (!hwif->irq && hwif->mate && hwif->mate->irq)
+		hwif->irq = hwif->mate->irq;	/* share IRQ with mate */
+
+	if (hwif->dma_base)
+		hwif->dmaproc = &ns87415_dmaproc;
+	hwif->selectproc = &ns87415_selectproc;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/opti621.c linux/drivers/ide/opti621.c
--- v2.3.51/linux/drivers/ide/opti621.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/opti621.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,315 @@
+/*
+ *  linux/drivers/block/opti621.c		Version 0.6	Jan 02, 1999
+ *
+ *  Copyright (C) 1996-1998  Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Authors:
+ * Jaromir Koutek <miri@punknet.cz>,
+ * Jan Harkes <jaharkes@cwi.nl>,
+ * Mark Lord <mlord@pobox.com>
+ * Some parts of code are from ali14xx.c and from rz1000.c.
+ *
+ * OPTi is trademark of OPTi, Octek is trademark of Octek.
+ *
+ * I used docs from OPTi databook, from ftp.opti.com, file 9123-0002.ps
+ * and disassembled/traced setupvic.exe (DOS program).
+ * It increases kernel code about 2 kB.
+ * I don't have this card no more, but I hope I can get some in case
+ * of needed development.
+ * My card is Octek PIDE 1.01 (on card) or OPTiViC (program).
+ * It has a place for a secondary connector in circuit, but nothing
+ * is there. Also BIOS says no address for
+ * secondary controller (see bellow in ide_init_opti621).
+ * I've only tested this on my system, which only has one disk.
+ * It's Western Digital WDAC2850, with PIO mode 3. The PCI bus
+ * is at 20 MHz (I have DX2/80, I tried PCI at 40, but I got random
+ * lockups). I tried the OCTEK double speed CD-ROM and
+ * it does not work! But I can't boot DOS also, so it's probably
+ * hardware fault. I have connected Conner 80MB, the Seagate 850MB (no
+ * problems) and Seagate 1GB (as slave, WD as master). My experiences
+ * with the third, 1GB drive: I got 3MB/s (hdparm), but sometimes
+ * it slows to about 100kB/s! I don't know why and I have
+ * not this drive now, so I can't try it again.
+ * I write this driver because I lost the paper ("manual") with
+ * settings of jumpers on the card and I have to boot Linux with
+ * Loadlin except LILO, cause I have to run the setupvic.exe program
+ * already or I get disk errors (my test: rpm -Vf
+ * /usr/X11R6/bin/XF86_SVGA - or any big file).
+ * Some numbers from hdparm -t /dev/hda:
+ * Timing buffer-cache reads:   32 MB in  3.02 seconds =10.60 MB/sec
+ * Timing buffered disk reads:  16 MB in  5.52 seconds = 2.90 MB/sec
+ * I have 4 Megs/s before, but I don't know why (maybe changes
+ * in hdparm test).
+ * After release of 0.1, I got some successful reports, so it might work.
+ *
+ * The main problem with OPTi is that some timings for master
+ * and slave must be the same. For example, if you have master
+ * PIO 3 and slave PIO 0, driver have to set some timings of
+ * master for PIO 0. Second problem is that opti621_tune_drive
+ * got only one drive to set, but have to set both drives.
+ * This is solved in compute_pios. If you don't set
+ * the second drive, compute_pios use ide_get_best_pio_mode
+ * for autoselect mode (you can change it to PIO 0, if you want).
+ * If you then set the second drive to another PIO, the old value
+ * (automatically selected) will be overrided by yours.
+ * There is a 25/33MHz switch in configuration
+ * register, but driver is written for use at any frequency which get
+ * (use idebus=xx to select PCI bus speed).
+ * Use ide0=autotune for automatical tune of the PIO modes.
+ * If you get strange results, do not use this and set PIO manually
+ * by hdparm.
+ *
+ * Version 0.1, Nov 8, 1996
+ * by Jaromir Koutek, for 2.1.8. 
+ * Initial version of driver.
+ * 
+ * Version 0.2
+ * Number 0.2 skipped.
+ *
+ * Version 0.3, Nov 29, 1997
+ * by Mark Lord (probably), for 2.1.68
+ * Updates for use with new IDE block driver.
+ *
+ * Version 0.4, Dec 14, 1997
+ * by Jan Harkes
+ * Fixed some errors and cleaned the code.
+ *
+ * Version 0.5, Jan 2, 1998
+ * by Jaromir Koutek
+ * Updates for use with (again) new IDE block driver.
+ * Update of documentation.
+ * 
+ * Version 0.6, Jan 2, 1999
+ * by Jaromir Koutek
+ * Reversed to version 0.3 of the driver, because
+ * 0.5 doesn't work.
+ */
+
+#undef REALLY_SLOW_IO	/* most systems can safely undef this */
+#define OPTI621_DEBUG		/* define for debug messages */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+#define OPTI621_MAX_PIO 3
+/* In fact, I do not have any PIO 4 drive
+ * (address: 25 ns, data: 70 ns, recovery: 35 ns),
+ * but OPTi 82C621 is programmable and it can do (minimal values):
+ * on 40MHz PCI bus (pulse 25 ns):
+ *  address: 25 ns, data: 25 ns, recovery: 50 ns;
+ * on 20MHz PCI bus (pulse 50 ns):
+ *  address: 50 ns, data: 50 ns, recovery: 100 ns.
+ */
+
+/* #define READ_PREFETCH 0 */
+/* Uncommnent for disable read prefetch.
+ * There is some readprefetch capatibility in hdparm,
+ * but when I type hdparm -P 1 /dev/hda, I got errors
+ * and till reset drive is inacessible.
+ * This (hw) read prefetch is safe on my drive.
+ */
+
+#ifndef READ_PREFETCH
+#define READ_PREFETCH 0x40 /* read prefetch is enabled */
+#endif /* else read prefetch is disabled */
+
+#define READ_REG 0	/* index of Read cycle timing register */
+#define WRITE_REG 1	/* index of Write cycle timing register */
+#define CNTRL_REG 3	/* index of Control register */
+#define STRAP_REG 5	/* index of Strap register */
+#define MISC_REG 6	/* index of Miscellaneous register */
+
+int reg_base;
+
+#define PIO_NOT_EXIST 254
+#define PIO_DONT_KNOW 255
+
+/* there are stored pio numbers from other calls of opti621_tune_drive */
+
+static void compute_pios(ide_drive_t *drive, byte pio)
+/* Store values into drive->drive_data
+ *	second_contr - 0 for primary controller, 1 for secondary
+ *	slave_drive - 0 -> pio is for master, 1 -> pio is for slave
+ *	pio - PIO mode for selected drive (for other we don't know)
+ */
+{
+	int d;
+	ide_hwif_t *hwif = HWIF(drive);
+
+	drive->drive_data = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL);
+	for (d = 0; d < 2; ++d) {
+		drive = &hwif->drives[d];
+		if (drive->present) {
+			if (drive->drive_data == PIO_DONT_KNOW)
+				drive->drive_data = ide_get_best_pio_mode(drive, 255, OPTI621_MAX_PIO, NULL);
+#ifdef OPTI621_DEBUG
+			printk("%s: Selected PIO mode %d\n", drive->name, drive->drive_data);
+#endif
+		} else {
+			drive->drive_data = PIO_NOT_EXIST;
+		}
+	}
+}
+
+int cmpt_clk(int time, int bus_speed)
+/* Returns (rounded up) time in clocks for time in ns,
+ * with bus_speed in MHz.
+ * Example: bus_speed = 40 MHz, time = 80 ns
+ * 1000/40 = 25 ns (clk value),
+ * 80/25 = 3.2, rounded up to 4 (I hope ;-)).
+ * Use idebus=xx to select right frequency.
+ */
+{
+	return ((time*bus_speed+999)/1000);
+}
+
+static void write_reg(byte value, int reg)
+/* Write value to register reg, base of register
+ * is at reg_base (0x1f0 primary, 0x170 secondary,
+ * if not changed by PCI configuration).
+ * This is from setupvic.exe program.
+ */
+{
+	inw(reg_base+1);
+	inw(reg_base+1);
+	outb(3, reg_base+2);
+	outb(value, reg_base+reg);
+	outb(0x83, reg_base+2);
+}
+
+static byte read_reg(int reg)
+/* Read value from register reg, base of register
+ * is at reg_base (0x1f0 primary, 0x170 secondary,
+ * if not changed by PCI configuration).
+ * This is from setupvic.exe program.
+ */
+{
+	byte ret;
+	inw(reg_base+1);
+	inw(reg_base+1);
+	outb(3, reg_base+2);
+	ret=inb(reg_base+reg);
+	outb(0x83, reg_base+2);
+	return ret;
+}
+
+typedef struct pio_clocks_s {
+	int	address_time;	/* Address setup (clocks) */
+	int	data_time;	/* Active/data pulse (clocks) */
+	int	recovery_time;	/* Recovery time (clocks) */
+} pio_clocks_t;
+
+static void compute_clocks(int pio, pio_clocks_t *clks)
+{
+        if (pio != PIO_NOT_EXIST) {
+        	int adr_setup, data_pls, bus_speed;
+        	bus_speed = ide_system_bus_speed();
+ 	       	adr_setup = ide_pio_timings[pio].setup_time;
+  	      	data_pls = ide_pio_timings[pio].active_time;
+	  	clks->address_time = cmpt_clk(adr_setup, bus_speed);
+	     	clks->data_time = cmpt_clk(data_pls, bus_speed);
+     		clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time
+     			- adr_setup-data_pls, bus_speed);
+     		if (clks->address_time<1) clks->address_time = 1;
+     		if (clks->address_time>4) clks->address_time = 4;
+     		if (clks->data_time<1) clks->data_time = 1;
+     		if (clks->data_time>16) clks->data_time = 16;
+     		if (clks->recovery_time<2) clks->recovery_time = 2;
+     		if (clks->recovery_time>17) clks->recovery_time = 17;
+	} else {
+		clks->address_time = 1;
+		clks->data_time = 1;
+		clks->recovery_time = 2;
+		/* minimal values */
+	}
+ 
+}
+
+/* Main tune procedure, called from tuneproc. */
+static void opti621_tune_drive (ide_drive_t *drive, byte pio)
+{
+	/* primary and secondary drives share some registers,
+	 * so we have to program both drives
+	 */
+	unsigned long flags;
+	byte pio1, pio2;
+	pio_clocks_t first, second;
+	int ax, drdy;
+	byte cycle1, cycle2, misc;
+	ide_hwif_t *hwif = HWIF(drive);
+
+	/* sets drive->drive_data for both drives */
+	compute_pios(drive, pio);
+ 	pio1 = hwif->drives[0].drive_data;
+ 	pio2 = hwif->drives[1].drive_data;
+
+	compute_clocks(pio1, &first);
+	compute_clocks(pio2, &second);
+
+	/* ax = max(a1,a2) */
+	ax = (first.address_time < second.address_time) ? second.address_time : first.address_time;
+
+	drdy = 2; /* DRDY is default 2 (by OPTi Databook) */
+
+	cycle1 = ((first.data_time-1)<<4)  | (first.recovery_time-2);
+	cycle2 = ((second.data_time-1)<<4) | (second.recovery_time-2);
+	misc = READ_PREFETCH | ((ax-1)<<4) | ((drdy-2)<<1);
+
+#ifdef OPTI621_DEBUG
+	printk("%s: master: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n",
+		hwif->name, ax, first.data_time, first.recovery_time, drdy);
+	printk("%s: slave:  address: %d, data: %d, recovery: %d, drdy: %d [clk]\n",
+		hwif->name, ax, second.data_time, second.recovery_time, drdy);
+#endif
+
+	save_flags(flags);	/* all CPUs */
+	cli();			/* all CPUs */
+
+     	reg_base = hwif->io_ports[IDE_DATA_OFFSET];
+	outb(0xc0, reg_base+CNTRL_REG);	/* allow Register-B */
+	outb(0xff, reg_base+5);		/* hmm, setupvic.exe does this ;-) */
+	inb(reg_base+CNTRL_REG); 	/* if reads 0xff, adapter not exist? */
+	read_reg(CNTRL_REG);		/* if reads 0xc0, no interface exist? */
+	read_reg(STRAP_REG);		/* read version, probably 0 */
+
+	/* program primary drive */
+	write_reg(0,      MISC_REG);	/* select Index-0 for Register-A */
+	write_reg(cycle1, READ_REG);	/* set read cycle timings */
+	write_reg(cycle1, WRITE_REG);	/* set write cycle timings */
+
+	/* program secondary drive */
+	write_reg(1,      MISC_REG);	/* select Index-1 for Register-B */
+	write_reg(cycle2, READ_REG);	/* set read cycle timings */
+	write_reg(cycle2, WRITE_REG);	/* set write cycle timings */
+
+	write_reg(0x85, CNTRL_REG);	/* use Register-A for drive 0 */
+					/* use Register-B for drive 1 */
+
+ 	write_reg(misc, MISC_REG);	/* set address setup, DRDY timings,   */
+ 					/*  and read prefetch for both drives */
+
+	restore_flags(flags);	/* all CPUs */
+}
+
+/*
+ * ide_init_opti621() is called once for each hwif found at boot.
+ */
+void ide_init_opti621 (ide_hwif_t *hwif)
+{
+	hwif->drives[0].drive_data = PIO_DONT_KNOW;
+	hwif->drives[1].drive_data = PIO_DONT_KNOW;
+	hwif->tuneproc = &opti621_tune_drive;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/pdc202xx.c linux/drivers/ide/pdc202xx.c
--- v2.3.51/linux/drivers/ide/pdc202xx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/pdc202xx.c	Wed Mar  8 11:40:25 2000
@@ -0,0 +1,731 @@
+/*
+ *  linux/drivers/block/pdc202xx.c	Version 0.29	Feb. 10, 2000
+ *
+ *  Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
+ *  May be copied or modified under the terms of the GNU General Public License
+ *
+ *  Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this
+ *  compiled into the kernel if you have more than one card installed.
+ *  Note that BIOS v1.29 is reported to fix the problem.  Since this is
+ *  safe chipset tuning, including this support is harmless
+ *
+ *  The latest chipset code will support the following ::
+ *  Three Ultra33 controllers and 12 drives.
+ *  8 are UDMA supported and 4 are limited to DMA mode 2 multi-word.
+ *  The 8/4 ratio is a BIOS code limit by promise.
+ *
+ *  UNLESS you enable "CONFIG_PDC202XX_BURST"
+ *
+ *  There is only one BIOS in the three contollers.
+ *
+ *  May  8 20:56:17 Orion kernel:
+ *  Uniform Multi-Platform E-IDE driver Revision: 6.19
+ *  PDC20246: IDE controller on PCI bus 00 dev a0
+ *  PDC20246: not 100% native mode: will probe irqs later
+ *  PDC20246: ROM enabled at 0xfebd0000
+ *  PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ *      ide0: BM-DMA at 0xef80-0xef87, BIOS settings: hda:DMA, hdb:DMA
+ *      ide1: BM-DMA at 0xef88-0xef8f, BIOS settings: hdc:pio, hdd:pio
+ *  PDC20246: IDE controller on PCI bus 00 dev 98
+ *  PDC20246: not 100% native mode: will probe irqs later
+ *  PDC20246: ROM enabled at 0xfebc0000
+ *  PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ *      ide2: BM-DMA at 0xef40-0xef47, BIOS settings: hde:DMA, hdf:DMA
+ *      ide3: BM-DMA at 0xef48-0xef4f, BIOS settings: hdg:DMA, hdh:DMA
+ *  PDC20246: IDE controller on PCI bus 00 dev 90
+ *  PDC20246: not 100% native mode: will probe irqs later
+ *  PDC20246: ROM enabled at 0xfebb0000
+ *  PDC20246: (U)DMA Burst Bit DISABLED Primary PCI Mode Secondary PCI Mode.
+ *  PDC20246: FORCING BURST BIT 0x00 -> 0x01 ACTIVE
+ *      ide4: BM-DMA at 0xef00-0xef07, BIOS settings: hdi:DMA, hdj:pio
+ *      ide5: BM-DMA at 0xef08-0xef0f, BIOS settings: hdk:pio, hdl:pio
+ *  PIIX3: IDE controller on PCI bus 00 dev 39
+ *  PIIX3: device not capable of full native PCI mode
+ *
+ *  ide0 at 0xeff0-0xeff7,0xefe6 on irq 19
+ *  ide1 at 0xefa8-0xefaf,0xebe6 on irq 19
+ *  ide2 at 0xefa0-0xefa7,0xef7e on irq 18
+ *  ide3 at 0xef68-0xef6f,0xef66 on irq 18
+ *  ide4 at 0xef38-0xef3f,0xef62 on irq 17
+ *  hda: QUANTUM FIREBALL ST6.4A, 6149MB w/81kB Cache, CHS=13328/15/63, UDMA(33)
+ *  hdb: QUANTUM FIREBALL ST3.2A, 3079MB w/81kB Cache, CHS=6256/16/63, UDMA(33)
+ *  hde: Maxtor 72004 AP, 1916MB w/128kB Cache, CHS=3893/16/63, DMA
+ *  hdf: Maxtor 71626 A, 1554MB w/64kB Cache, CHS=3158/16/63, DMA
+ *  hdi: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
+ *  hdj: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
+ *
+ *  Promise Ultra66 cards with BIOS v1.11 this
+ *  compiled into the kernel if you have more than one card installed.
+ *
+ *  PDC20262: IDE controller on PCI bus 00 dev a0
+ *  PDC20262: not 100% native mode: will probe irqs later
+ *  PDC20262: ROM enabled at 0xfebb0000
+ *  PDC20262: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ *      ide0: BM-DMA at 0xef00-0xef07, BIOS settings: hda:pio, hdb:pio
+ *      ide1: BM-DMA at 0xef08-0xef0f, BIOS settings: hdc:pio, hdd:pio
+ *
+ *  UDMA 4/2 and UDMA 3/1 only differ by the testing bit 13 in word93.
+ *  Chipset timing speeds must be identical
+ *
+ *  drive_number
+ *      = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ *      = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ */
+
+/*
+ *  Portions Copyright (C) 1999 Promise Technology, Inc.
+ *  Author: Frank Tiernan (frankt@promise.com)
+ *  Released under terms of General Public License
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#define PDC202XX_DEBUG_DRIVE_INFO		0
+#define PDC202XX_DECODE_REGISTER_INFO		0
+
+#undef DISPLAY_PDC202XX_TIMINGS
+
+#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int pdc202xx_get_info(char *, char **, off_t, int);
+extern int (*pdc202xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int pdc202xx_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p = buffer;
+
+	u32 bibma = bmide_dev->resource[4].start;
+	u8 c0 = 0, c1 = 0;
+
+        /*
+         * at that point bibma+0x2 et bibma+0xa are byte registers
+         * to investigate:
+         */
+	c0 = inb_p((unsigned short)bibma + 0x02);
+	c1 = inb_p((unsigned short)bibma + 0x0a);
+
+	switch(bmide_dev->device) {	
+		case PCI_DEVICE_ID_PROMISE_20262:
+			p += sprintf(p, "\n                                PDC20262 Chipset.\n");
+			break;
+		case PCI_DEVICE_ID_PROMISE_20246:
+			p += sprintf(p, "\n                                PDC20246 Chipset.\n");
+			break;
+		default:
+			p += sprintf(p, "\n                                PDC202XX Chipset.\n");
+			break;
+	}
+
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	p += sprintf(p, "                %sabled                         %sabled\n",
+			(c0&0x80) ? "dis" : " en",
+			(c1&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
+			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+	p += sprintf(p, "UDMA\n");
+	p += sprintf(p, "DMA\n");
+	p += sprintf(p, "PIO\n");
+
+	return p-buffer;	/* => must be less than 4k! */
+}
+#endif  /* defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte pdc202xx_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/* A Register */
+#define	SYNC_ERRDY_EN	0xC0
+
+#define	SYNC_IN		0x80	/* control bit, different for master vs. slave drives */
+#define	ERRDY_EN	0x40	/* control bit, different for master vs. slave drives */
+#define	IORDY_EN	0x20	/* PIO: IOREADY */
+#define	PREFETCH_EN	0x10	/* PIO: PREFETCH */
+
+#define	PA3		0x08	/* PIO"A" timing */
+#define	PA2		0x04	/* PIO"A" timing */
+#define	PA1		0x02	/* PIO"A" timing */
+#define	PA0		0x01	/* PIO"A" timing */
+
+/* B Register */
+
+#define	MB2		0x80	/* DMA"B" timing */
+#define	MB1		0x40	/* DMA"B" timing */
+#define	MB0		0x20	/* DMA"B" timing */
+
+#define	PB4		0x10	/* PIO_FORCE 1:0 */
+
+#define	PB3		0x08	/* PIO"B" timing */	/* PIO flow Control mode */
+#define	PB2		0x04	/* PIO"B" timing */	/* PIO 4 */
+#define	PB1		0x02	/* PIO"B" timing */	/* PIO 3 half */
+#define	PB0		0x01	/* PIO"B" timing */	/* PIO 3 other half */
+
+/* C Register */
+#define	IORDYp_NO_SPEED	0x4F
+#define	SPEED_DIS	0x0F
+
+#define	DMARQp		0x80
+#define	IORDYp		0x40
+#define	DMAR_EN		0x20
+#define	DMAW_EN		0x10
+
+#define	MC3		0x08	/* DMA"C" timing */
+#define	MC2		0x04	/* DMA"C" timing */
+#define	MC1		0x02	/* DMA"C" timing */
+#define	MC0		0x01	/* DMA"C" timing */
+
+#if PDC202XX_DECODE_REGISTER_INFO
+
+#define REG_A		0x01
+#define REG_B		0x02
+#define REG_C		0x04
+#define REG_D		0x08
+
+static void decode_registers (byte registers, byte value)
+{
+	byte	bit = 0, bit1 = 0, bit2 = 0;
+
+	switch(registers) {
+		case REG_A:
+			bit2 = 0;
+			printk("A Register ");
+			if (value & 0x80) printk("SYNC_IN ");
+			if (value & 0x40) printk("ERRDY_EN ");
+			if (value & 0x20) printk("IORDY_EN ");
+			if (value & 0x10) printk("PREFETCH_EN ");
+			if (value & 0x08) { printk("PA3 ");bit2 |= 0x08; }
+			if (value & 0x04) { printk("PA2 ");bit2 |= 0x04; }
+			if (value & 0x02) { printk("PA1 ");bit2 |= 0x02; }
+			if (value & 0x01) { printk("PA0 ");bit2 |= 0x01; }
+			printk("PIO(A) = %d ", bit2);
+			break;
+		case REG_B:
+			bit1 = 0;bit2 = 0;
+			printk("B Register ");
+			if (value & 0x80) { printk("MB2 ");bit1 |= 0x80; }
+			if (value & 0x40) { printk("MB1 ");bit1 |= 0x40; }
+			if (value & 0x20) { printk("MB0 ");bit1 |= 0x20; }
+			printk("DMA(B) = %d ", bit1 >> 5);
+			if (value & 0x10) printk("PIO_FORCED/PB4 ");
+			if (value & 0x08) { printk("PB3 ");bit2 |= 0x08; }
+			if (value & 0x04) { printk("PB2 ");bit2 |= 0x04; }
+			if (value & 0x02) { printk("PB1 ");bit2 |= 0x02; }
+			if (value & 0x01) { printk("PB0 ");bit2 |= 0x01; }
+			printk("PIO(B) = %d ", bit2);
+			break;
+		case REG_C:
+			bit2 = 0;
+			printk("C Register ");
+			if (value & 0x80) printk("DMARQp ");
+			if (value & 0x40) printk("IORDYp ");
+			if (value & 0x20) printk("DMAR_EN ");
+			if (value & 0x10) printk("DMAW_EN ");
+
+			if (value & 0x08) { printk("MC3 ");bit2 |= 0x08; }
+			if (value & 0x04) { printk("MC2 ");bit2 |= 0x04; }
+			if (value & 0x02) { printk("MC1 ");bit2 |= 0x02; }
+			if (value & 0x01) { printk("MC0 ");bit2 |= 0x01; }
+			printk("DMA(C) = %d ", bit2);
+			break;
+		case REG_D:
+			printk("D Register ");
+			break;
+		default:
+			return;
+	}
+	printk("\n        %s ", (registers & REG_D) ? "DP" :
+				(registers & REG_C) ? "CP" :
+				(registers & REG_B) ? "BP" :
+				(registers & REG_A) ? "AP" : "ERROR");
+	for (bit=128;bit>0;bit/=2)
+		printk("%s", (value & bit) ? "1" : "0");
+	printk("\n");
+}
+
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+	struct hd_driveid *id	= drive->id;
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	unsigned long high_16	= dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
+
+	int			err;
+	unsigned int		drive_conf;
+	byte			drive_pci;
+	byte			test1, test2, speed = -1;
+	byte			AP, BP, CP, DP, TB, TC;
+	unsigned short		EP;
+	byte CLKSPD		= IN_BYTE(high_16 + 0x11);
+	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+	byte udma_33		= ultra ? (inb(high_16 + 0x001f) & 1) : 0;
+
+	/*
+	 * Set the control register to use the 66Mhz system
+	 * clock for UDMA 3/4 mode operation. If one drive on
+	 * a channel is U66 capable but the other isn't we
+	 * fall back to U33 mode. The BIOS INT 13 hooks turn
+	 * the clock on then off for each read/write issued. I don't
+	 * do that here because it would require modifying the
+	 * kernel, seperating the fop routines from the kernel or
+	 * somehow hooking the fops calls. It may also be possible to
+	 * leave the 66Mhz clock on and readjust the timing
+	 * parameters.
+	 */
+
+	byte mask		= hwif->channel ? 0x08 : 0x02;
+	unsigned short c_mask	= hwif->channel ? (1<<11) : (1<<10);
+	byte ultra_66		= ((id->dma_ultra & 0x0010) || (id->dma_ultra & 0x0008)) ? 1 : 0;
+
+	pci_read_config_word(dev, 0x50, &EP);
+
+	if ((ultra_66) && (EP & c_mask)) {
+#ifdef DEBUG
+		printk("ULTRA66: %s channel of Ultra 66 requires an 80-pin cable for Ultra66 operation.\n", hwif->channel ? "Secondary", "Primary");
+		printk("         Switching to Ultra33 mode.\n");
+#endif /* DEBUG */
+		/* Primary   : zero out second bit */
+		/* Secondary : zero out fourth bit */
+		OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11));
+	} else {
+		if (ultra_66) {
+			/*
+			 * check to make sure drive on same channel
+			 * is u66 capable
+			 */
+			if (hwif->drives[!(drive_number%2)].id) {
+				if ((hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0010) ||
+				    (hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0008)) {
+					OUT_BYTE(CLKSPD | mask, (high_16 + 0x11));
+				} else {
+					OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11));
+				}
+			} else { /* udma4 drive by itself */
+				OUT_BYTE(CLKSPD | mask, (high_16 + 0x11));
+			}
+		}
+	}
+
+	switch(drive_number) {
+		case 0:	drive_pci = 0x60;
+			pci_read_config_dword(dev, drive_pci, &drive_conf);
+			if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+				goto chipset_is_set;
+			pci_read_config_byte(dev, (drive_pci), &test1);
+			if (!(test1 & SYNC_ERRDY_EN))
+				pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
+			break;
+		case 1:	drive_pci = 0x64;
+			pci_read_config_dword(dev, drive_pci, &drive_conf);
+			if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+				goto chipset_is_set;
+			pci_read_config_byte(dev, 0x60, &test1);
+			pci_read_config_byte(dev, (drive_pci), &test2);
+			if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
+				pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
+			break;
+		case 2:	drive_pci = 0x68;
+			pci_read_config_dword(dev, drive_pci, &drive_conf);
+			if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+				goto chipset_is_set;
+			pci_read_config_byte(dev, (drive_pci), &test1);
+			if (!(test1 & SYNC_ERRDY_EN))
+				pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
+			break;
+		case 3:	drive_pci = 0x6c;
+			pci_read_config_dword(dev, drive_pci, &drive_conf);
+			if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+				goto chipset_is_set;
+			pci_read_config_byte(dev, 0x68, &test1);
+			pci_read_config_byte(dev, (drive_pci), &test2);
+			if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
+				pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
+			break;
+		default:
+			return ide_dma_off;
+	}
+
+chipset_is_set:
+
+	if (drive->media != ide_disk)
+		return ide_dma_off_quietly;
+
+	pci_read_config_byte(dev, (drive_pci), &AP);
+	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+	pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
+
+	if (id->capability & 4) {		/* IORDY_EN */
+		pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN);
+		pci_read_config_byte(dev, (drive_pci), &AP);
+	}
+
+	if (drive->media == ide_disk) {		/* PREFETCH_EN */
+		pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN);
+		pci_read_config_byte(dev, (drive_pci), &AP);
+	}
+
+	if ((BP & 0xF0) && (CP & 0x0F)) {
+		/* clear DMA modes of upper 842 bits of B Register */
+		/* clear PIO forced mode upper 1 bit of B Register */
+		pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0xF0);
+		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+		/* clear DMA modes of lower 8421 bits of C Register */
+		pci_write_config_byte(dev, (drive_pci)|0x02, CP & ~0x0F);
+		pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+	}
+
+	pci_read_config_byte(dev, (drive_pci), &AP);
+	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+
+	if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
+		/* speed 8 == UDMA mode 4 == speed 6 plus cable */
+		speed = XFER_UDMA_4; TB = 0x20; TC = 0x01;
+	} else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
+		/* speed 7 == UDMA mode 3 == speed 5 plus cable */
+		speed = XFER_UDMA_3; TB = 0x40; TC = 0x02;
+	} else if ((id->dma_ultra & 0x0004) && (udma_33)) {
+		/* speed 6 == UDMA mode 2 */
+		speed = XFER_UDMA_2; TB = 0x20; TC = 0x01;
+	} else if ((id->dma_ultra & 0x0002) && (udma_33)) {
+		/* speed 5 == UDMA mode 1 */
+		speed = XFER_UDMA_1; TB = 0x40; TC = 0x02;
+	} else if ((id->dma_ultra & 0x0001) && (udma_33)) {
+		/* speed 4 == UDMA mode 0 */
+		speed = XFER_UDMA_0; TB = 0x60; TC = 0x03;
+	} else if (id->dma_mword & 0x0004) {
+		/* speed 4 == DMA mode 2 multi-word */
+		speed = XFER_MW_DMA_2; TB = 0x60; TC = 0x03;
+	} else if (id->dma_mword & 0x0002) {
+		/* speed 3 == DMA mode 1 multi-word */
+		speed = XFER_MW_DMA_1; TB = 0x60; TC = 0x04;
+	} else if (id->dma_mword & 0x0001) {
+		/* speed 2 == DMA mode 0 multi-word */
+		speed = XFER_MW_DMA_0; TB = 0x60; TC = 0x05;
+	} else if (id->dma_1word & 0x0004) {
+		/* speed 2 == DMA mode 2 single-word */
+		speed = XFER_SW_DMA_2; TB = 0x60; TC = 0x05;
+	} else if (id->dma_1word & 0x0002) {
+		/* speed 1 == DMA mode 1 single-word */
+		speed = XFER_SW_DMA_1; TB = 0x80; TC = 0x06;
+	} else if (id->dma_1word & 0x0001) {
+		/* speed 0 == DMA mode 0 single-word */
+		speed = XFER_SW_DMA_0; TB = 0xC0; TC = 0x0B;
+	} else {
+		/* restore original pci-config space */
+		pci_write_config_dword(dev, drive_pci, drive_conf);
+		return ide_dma_off_quietly;
+	}
+
+	pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
+	pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC);
+
+#if PDC202XX_DECODE_REGISTER_INFO
+	pci_read_config_byte(dev, (drive_pci), &AP);
+	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+
+	decode_registers(REG_A, AP);
+	decode_registers(REG_B, BP);
+	decode_registers(REG_C, CP);
+	decode_registers(REG_D, DP);
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+	err = ide_config_drive_speed(drive, speed);
+
+#if PDC202XX_DEBUG_DRIVE_INFO
+	printk("%s: %s drive%d 0x%08x ",
+		drive->name, ide_xfer_verbose(speed),
+		drive_number, drive_conf);
+	pci_read_config_dword(dev, drive_pci, &drive_conf);
+	printk("0x%08x\n", drive_conf);
+#endif /* PDC202XX_DEBUG_DRIVE_INFO */
+
+	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on : 
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+}
+
+/*   0    1    2    3    4    5    6   7   8
+ * 960, 480, 390, 300, 240, 180, 120, 90, 60
+ *           180, 150, 120,  90,  60
+ * DMA_Speed
+ * 180, 120,  90,  90,  90,  60,  30
+ *  11,   5,   4,   3,   2,   1,   0
+ */
+
+static int config_chipset_for_pio (ide_drive_t *drive, byte pio)
+{
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	byte			drive_pci, speed;
+	byte			AP, BP, TA, TB;
+
+	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+	int err;
+
+	switch (drive_number) {
+		case 0: drive_pci = 0x60;	break;
+		case 1: drive_pci = 0x64;	break;
+		case 2: drive_pci = 0x68;	break;
+		case 3: drive_pci = 0x6c;	break;
+		default:			return 1;
+	}
+
+        pci_read_config_byte(dev, (drive_pci), &AP);
+        pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+
+        if ((AP & 0x0F) || (BP & 0x07)) {
+		/* clear PIO modes of lower 8421 bits of A Register */
+		pci_write_config_byte(dev, (drive_pci), AP & ~0x0F);
+		pci_read_config_byte(dev, (drive_pci), &AP);
+
+		/* clear PIO modes of lower 421 bits of B Register */
+		pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07);
+		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+		pci_read_config_byte(dev, (drive_pci), &AP);
+		pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+	}
+
+	pio = (pio == 5) ? 4 : pio;
+	switch (ide_get_best_pio_mode(drive, 255, pio, NULL)) {
+		case 4:  speed = XFER_PIO_4; TA=0x01; TB=0x04; break;
+		case 3:  speed = XFER_PIO_3; TA=0x02; TB=0x06; break;
+		case 2:  speed = XFER_PIO_2; TA=0x03; TB=0x08; break;
+		case 1:  speed = XFER_PIO_1; TA=0x05; TB=0x0C; break;
+		case 0:
+		default: speed = XFER_PIO_0; TA=0x09; TB=0x13; break;
+	}
+	pci_write_config_byte(dev, (drive_pci), AP|TA);
+	pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
+
+#if PDC202XX_DECODE_REGISTER_INFO
+	pci_read_config_byte(dev, (drive_pci), &AP);
+	pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+	pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+	pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
+
+	decode_registers(REG_A, AP);
+	decode_registers(REG_B, BP);
+	decode_registers(REG_C, CP);
+	decode_registers(REG_D, DP);
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+	err = ide_config_drive_speed(drive, speed);
+
+#if PDC202XX_DEBUG_DRIVE_INFO
+	printk("%s: %s drive%d 0x%08x ",
+		drive->name, ide_xfer_verbose(speed),
+		drive_number, drive_conf);
+	pci_read_config_dword(dev, drive_pci, &drive_conf);
+	printk("0x%08x\n", drive_conf);
+#endif /* PDC202XX_DEBUG_DRIVE_INFO */
+
+	return err;
+}
+
+static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio)
+{
+	(void) config_chipset_for_pio(drive, pio);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_hwif_t *hwif = HWIF(drive);
+	ide_dma_action_t dma_func = ide_dma_off_quietly;
+
+	if (id && (id->capability & 1) && hwif->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
+		}
+		dma_func = ide_dma_off_quietly;
+		if (id->field_valid & 4) {
+			if (id->dma_ultra & 0x001F) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive, 1);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if ((id->dma_mword & 0x0007) ||
+			    (id->dma_1word & 0x0007)) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive, 0);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
+			}
+		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+			if (id->eide_dma_time > 150) {
+				goto no_dma_set;
+			}
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive, 0);
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+		(void) config_chipset_for_pio(drive, 5);
+	}
+
+	return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * pdc202xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+int pdc202xx_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			return config_drive_xfer_rate(drive);
+		default:
+			break;
+	}
+	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name)
+{
+	unsigned long high_16	= dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
+	byte udma_speed_flag	= inb(high_16 + 0x001f);
+	byte primary_mode	= inb(high_16 + 0x001a);
+	byte secondary_mode	= inb(high_16 + 0x001b);
+
+	if (dev->device == PCI_DEVICE_ID_PROMISE_20262) {
+		int i = 0;
+		/*
+		 * software reset -  this is required because the bios
+		 * will set UDMA timing on if the hdd supports it. The
+		 * user may want to turn udma off. A bug in the pdc20262
+		 * is that it cannot handle a downgrade in timing from UDMA
+		 * to DMA. Disk accesses after issuing a set feature command
+		 * will result in errors. A software reset leaves the timing
+		 * registers intact, but resets the drives.
+		 */
+
+		OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f);
+		ide_delay_50ms();
+		ide_delay_50ms();
+		OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f);
+		for (i=0; i<40; i++)
+			ide_delay_50ms();
+	}
+
+	if (dev->resource[PCI_ROM_RESOURCE].start) {
+		pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+		printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
+	}
+
+	if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) {
+		byte irq = 0, irq2 = 0;
+		pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+		pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2);	/* 0xbc */
+		if (irq != irq2) {
+			pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq);	/* 0xbc */
+			printk("%s: pci-config space interrupt mirror fixed.\n", name);
+		}
+	}
+
+	printk("%s: (U)DMA Burst Bit %sABLED " \
+		"Primary %s Mode " \
+		"Secondary %s Mode.\n",
+		name,
+		(udma_speed_flag & 1) ? "EN" : "DIS",
+		(primary_mode & 1) ? "MASTER" : "PCI",
+		(secondary_mode & 1) ? "MASTER" : "PCI" );
+
+#ifdef CONFIG_PDC202XX_BURST
+	if (!(udma_speed_flag & 1)) {
+		printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1));
+		outb(udma_speed_flag|1, high_16 + 0x001f);
+		printk("%sCTIVE\n", (inb(high_16 + 0x001f) & 1) ? "A" : "INA");
+	}
+#endif /* CONFIG_PDC202XX_BURST */
+
+#ifdef CONFIG_PDC202XX_MASTER
+	if (!(primary_mode & 1)) {
+		printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ",
+			name, primary_mode, (primary_mode|1));
+		outb(primary_mode|1, high_16 + 0x001a);
+		printk("%s\n", (inb(high_16 + 0x001a) & 1) ? "MASTER" : "PCI");
+	}
+
+	if (!(secondary_mode & 1)) {
+		printk("%s: FORCING SECONDARY MODE BIT 0x%02x -> 0x%02x ",
+			name, secondary_mode, (secondary_mode|1));
+		outb(secondary_mode|1, high_16 + 0x001b);
+		printk("%s\n", (inb(high_16 + 0x001b) & 1) ? "MASTER" : "PCI");
+	}
+#endif /* CONFIG_PDC202XX_MASTER */
+
+#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
+        pdc202xx_proc = 1;
+        bmide_dev = dev;
+        pdc202xx_display_info = &pdc202xx_get_info;
+#endif /* DISPLAY_PDC202XX_TIMINGS && CONFIG_PROC_FS */
+
+	return dev->irq;
+}
+
+unsigned int __init ata66_pdc202xx (ide_hwif_t *hwif)
+{
+	unsigned short mask = (hwif->channel) ? (1<<11) : (1<<10);
+	unsigned short CIS;
+
+	pci_read_config_word(hwif->pci_dev, 0x50, &CIS);
+        return ((CIS & mask) ? 0 : 1);
+}
+
+void __init ide_init_pdc202xx (ide_hwif_t *hwif)
+{
+	hwif->tuneproc = &pdc202xx_tune_drive;
+
+	if (hwif->dma_base) {
+		hwif->dmaproc = &pdc202xx_dmaproc;
+	} else {
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/pdc4030.c linux/drivers/ide/pdc4030.c
--- v2.3.51/linux/drivers/ide/pdc4030.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/pdc4030.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,552 @@
+/*  -*- linux-c -*-
+ *  linux/drivers/ide/pdc4030.c		Version 0.90  May 27, 1999
+ *
+ *  Copyright (C) 1995-1999  Linus Torvalds & authors (see below)
+ */
+
+/*
+ *  Principal Author/Maintainer:  peterd@pnd-pc.demon.co.uk
+ *
+ *  This file provides support for the second port and cache of Promise
+ *  IDE interfaces, e.g. DC4030VL, DC4030VL-1 and DC4030VL-2.
+ *
+ *  Thanks are due to Mark Lord for advice and patiently answering stupid
+ *  questions, and all those mugs^H^H^H^Hbrave souls who've tested this,
+ *  especially Andre Hedrick.
+ *
+ *  Version 0.01	Initial version, #include'd in ide.c rather than
+ *                      compiled separately.
+ *                      Reads use Promise commands, writes as before. Drives
+ *                      on second channel are read-only.
+ *  Version 0.02        Writes working on second channel, reads on both
+ *                      channels. Writes fail under high load. Suspect
+ *			transfers of >127 sectors don't work.
+ *  Version 0.03        Brought into line with ide.c version 5.27.
+ *                      Other minor changes.
+ *  Version 0.04        Updated for ide.c version 5.30
+ *                      Changed initialization strategy
+ *  Version 0.05	Kernel integration.  -ml
+ *  Version 0.06	Ooops. Add hwgroup to direct call of ide_intr() -ml
+ *  Version 0.07	Added support for DC4030 variants
+ *			Secondary interface autodetection
+ *  Version 0.08	Renamed to pdc4030.c
+ *  Version 0.09	Obsolete - never released - did manual write request
+ *			splitting before max_sectors[major][minor] available.
+ *  Version 0.10	Updated for 2.1 series of kernels
+ *  Version 0.11	Updated for 2.3 series of kernels
+ *			Autodetection code added.
+ *
+ *  Version 0.90	Transition to BETA code. No lost/unexpected interrupts
+ */
+
+/*
+ * Once you've compiled it in, you'll have to also enable the interface
+ * setup routine from the kernel command line, as in 
+ *
+ *	'linux ide0=dc4030' or 'linux ide1=dc4030'
+ *
+ * It should now work as a second controller also ('ide1=dc4030') but only
+ * if you DON'T have BIOS V4.44, which has a bug. If you have this version
+ * and EPROM programming facilities, you need to fix 4 bytes:
+ * 	2496:	81	81
+ *	2497:	3E	3E
+ *	2498:	22	98	*
+ *	2499:	06	05	*
+ *	249A:	F0	F0
+ *	249B:	01	01
+ *	...
+ *	24A7:	81	81
+ *	24A8:	3E	3E
+ *	24A9:	22	98	*
+ *	24AA:	06	05	*
+ *	24AB:	70	70
+ *	24AC:	01	01
+ *
+ * As of January 1999, Promise Technology Inc. have finally supplied me with
+ * some technical information which has shed a glimmer of light on some of the
+ * problems I was having, especially with writes. 
+ *
+ * There are still problems with the robustness and efficiency of this driver
+ * because I still don't understand what the card is doing with interrupts.
+ */
+
+#define DEBUG_READ
+#define DEBUG_WRITE
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "pdc4030.h"
+
+/*
+ * promise_selectproc() is invoked by ide.c
+ * in preparation for access to the specified drive.
+ */
+static void promise_selectproc (ide_drive_t *drive)
+{
+	unsigned int number;
+
+	number = (HWIF(drive)->channel << 1) + drive->select.b.unit;
+	OUT_BYTE(number,IDE_FEATURE_REG);
+}
+
+/*
+ * pdc4030_cmd handles the set of vendor specific commands that are initiated
+ * by command F0. They all have the same success/failure notification -
+ * 'P' (=0x50) on success, 'p' (=0x70) on failure.
+ */
+int pdc4030_cmd(ide_drive_t *drive, byte cmd)
+{
+	unsigned long timeout, timer;
+	byte status_val;
+
+	promise_selectproc(drive);	/* redundant? */
+	OUT_BYTE(0xF3,IDE_SECTOR_REG);
+	OUT_BYTE(cmd,IDE_SELECT_REG);
+	OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG);
+	timeout = HZ * 10;
+	timeout += jiffies;
+	do {
+		if(time_after(jiffies, timeout)) {
+			return 2; /* device timed out */
+		}
+		/* This is out of delay_10ms() */
+		/* Delays at least 10ms to give interface a chance */
+		timer = jiffies + (HZ + 99)/100 + 1;
+		while (time_after(timer, jiffies));
+		status_val = IN_BYTE(IDE_SECTOR_REG);
+	} while (status_val != 0x50 && status_val != 0x70);
+
+	if(status_val == 0x50)
+		return 0; /* device returned success */
+	else
+		return 1; /* device returned failure */
+}
+
+/*
+ * pdc4030_identify sends a vendor-specific IDENTIFY command to the drive
+ */
+int pdc4030_identify(ide_drive_t *drive)
+{
+	return pdc4030_cmd(drive, PROMISE_IDENTIFY);
+}
+
+int enable_promise_support = 0;
+
+void __init init_pdc4030 (void)
+{
+	enable_promise_support = 1;
+}
+
+/*
+ * setup_pdc4030()
+ * Completes the setup of a Promise DC4030 controller card, once found.
+ */
+int __init setup_pdc4030 (ide_hwif_t *hwif)
+{
+        ide_drive_t *drive;
+	ide_hwif_t *hwif2;
+	struct dc_ident ident;
+	int i;
+	ide_startstop_t startstop;
+	
+	if (!hwif) return 0;
+
+	drive = &hwif->drives[0];
+	hwif2 = &ide_hwifs[hwif->index+1];
+	if (hwif->chipset == ide_pdc4030) /* we've already been found ! */
+		return 1;
+
+	if (IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) {
+		return 0;
+	}
+	if (IDE_CONTROL_REG)
+		OUT_BYTE(0x08,IDE_CONTROL_REG);
+	if (pdc4030_cmd(drive,PROMISE_GET_CONFIG)) {
+		return 0;
+	}
+	if (ide_wait_stat(&startstop, drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+		printk(KERN_INFO
+			"%s: Failed Promise read config!\n",hwif->name);
+		return 0;
+	}
+	ide_input_data(drive,&ident,SECTOR_WORDS);
+	if (ident.id[1] != 'P' || ident.id[0] != 'T') {
+		return 0;
+	}
+	printk(KERN_INFO "%s: Promise caching controller, ",hwif->name);
+	switch(ident.type) {
+		case 0x43:	printk("DC4030VL-2, "); break;
+		case 0x41:	printk("DC4030VL-1, "); break;
+		case 0x40:	printk("DC4030VL, "); break;
+		default:
+			printk("unknown - type 0x%02x - please report!\n"
+			       ,ident.type);
+			printk("Please e-mail the following data to "
+			       "promise@pnd-pc.demon.co.uk along with\n"
+			       "a description of your card and drives:\n");
+			for (i=0; i < 0x90; i++) {
+				printk("%02x ", ((unsigned char *)&ident)[i]);
+				if ((i & 0x0f) == 0x0f) printk("\n");
+			}
+			return 0;
+	}
+	printk("%dKB cache, ",(int)ident.cache_mem);
+	switch(ident.irq) {
+            case 0x00: hwif->irq = 14; break;
+            case 0x01: hwif->irq = 12; break;
+            default:   hwif->irq = 15; break;
+	}
+	printk("on IRQ %d\n",hwif->irq);
+
+	/*
+	 * Once found and identified, we set up the next hwif in the array
+	 * (hwif2 = ide_hwifs[hwif->index+1]) with the same io ports, irq
+	 * and other settings as the main hwif. This gives us two "mated"
+	 * hwifs pointing to the Promise card.
+	 *
+	 * We also have to shift the default values for the remaining
+	 * interfaces "up by one" to make room for the second interface on the
+	 * same set of values.
+	 */
+
+	hwif->chipset	= hwif2->chipset = ide_pdc4030;
+	hwif->mate	= hwif2;
+	hwif2->mate	= hwif;
+	hwif2->channel	= 1;
+	hwif->selectproc = hwif2->selectproc = &promise_selectproc;
+	hwif->serialized = hwif2->serialized = 1;
+
+/* Shift the remaining interfaces down by one */
+	for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
+		ide_hwif_t *h = &ide_hwifs[i];
+
+#ifdef DEBUG
+		printk(KERN_DEBUG "Shifting i/f %d values to i/f %d\n",i-1,i);
+#endif
+		ide_init_hwif_ports(&h->hw, (h-1)->io_ports[IDE_DATA_OFFSET], 0, NULL);
+		memcpy(h->io_ports, h->hw.io_ports, sizeof(h->io_ports));
+		h->noprobe = (h-1)->noprobe;
+	}
+	ide_init_hwif_ports(&hwif2->hw, hwif->io_ports[IDE_DATA_OFFSET], 0, NULL);
+	memcpy(hwif2->io_ports, hwif->hw.io_ports, sizeof(hwif2->io_ports));
+	hwif2->irq = hwif->irq;
+	hwif2->hw.irq = hwif->hw.irq = hwif->irq;
+	for (i=0; i<2 ; i++) {
+		hwif->drives[i].io_32bit = 3;
+		hwif2->drives[i].io_32bit = 3;
+		hwif->drives[i].keep_settings = 1;
+		hwif2->drives[i].keep_settings = 1;
+		if (!ident.current_tm[i].cyl)
+			hwif->drives[i].noprobe = 1;
+		if (!ident.current_tm[i+2].cyl)
+			hwif2->drives[i].noprobe = 1;
+	}
+        return 1;
+}
+
+/*
+ * detect_pdc4030()
+ * Tests for the presence of a DC4030 Promise card on this interface
+ * Returns: 1 if found, 0 if not found
+ */
+int __init detect_pdc4030(ide_hwif_t *hwif)
+{
+	ide_drive_t *drive = &hwif->drives[0];
+
+	if (IDE_DATA_REG == 0) { /* Skip test for non-existent interface */
+		return 0;
+	}
+	OUT_BYTE(0xF3, IDE_SECTOR_REG);
+	OUT_BYTE(0x14, IDE_SELECT_REG);
+	OUT_BYTE(PROMISE_EXTENDED_COMMAND, IDE_COMMAND_REG);
+	
+	ide_delay_50ms();
+
+	if (IN_BYTE(IDE_ERROR_REG) == 'P' &&
+	    IN_BYTE(IDE_NSECTOR_REG) == 'T' &&
+	    IN_BYTE(IDE_SECTOR_REG) == 'I') {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+void __init ide_probe_for_pdc4030(void)
+{
+	unsigned int	index;
+	ide_hwif_t	*hwif;
+
+	if (enable_promise_support == 0)
+		return;
+	for (index = 0; index < MAX_HWIFS; index++) {
+		hwif = &ide_hwifs[index];
+		if (hwif->chipset == ide_unknown && detect_pdc4030(hwif)) {
+			setup_pdc4030(hwif);
+		}
+	}
+}
+
+
+
+/*
+ * promise_read_intr() is the handler for disk read/multread interrupts
+ */
+static ide_startstop_t promise_read_intr (ide_drive_t *drive)
+{
+	byte stat;
+	int total_remaining;
+	unsigned int sectors_left, sectors_avail, nsect;
+	struct request *rq;
+
+	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+		return ide_error(drive, "promise_read_intr", stat);
+	}
+
+read_again:
+	do {
+		sectors_left = IN_BYTE(IDE_NSECTOR_REG);
+		IN_BYTE(IDE_SECTOR_REG);
+	} while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left);
+	rq = HWGROUP(drive)->rq;
+	sectors_avail = rq->nr_sectors - sectors_left;
+	if (!sectors_avail)
+		goto read_again;
+
+read_next:
+	rq = HWGROUP(drive)->rq;
+	nsect = rq->current_nr_sectors;
+	if (nsect > sectors_avail)
+		nsect = sectors_avail;
+	sectors_avail -= nsect;
+	ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG_READ
+	printk(KERN_DEBUG "%s:  promise_read: sectors(%ld-%ld), "
+	       "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector,
+	       rq->sector+nsect-1, (unsigned long) rq->buffer,
+	       rq->nr_sectors-nsect);
+#endif
+	rq->sector += nsect;
+	rq->buffer += nsect<<9;
+	rq->errors = 0;
+	rq->nr_sectors -= nsect;
+	total_remaining = rq->nr_sectors;
+	if ((rq->current_nr_sectors -= nsect) <= 0) {
+		ide_end_request(1, HWGROUP(drive));
+	}
+/*
+ * Now the data has been read in, do the following:
+ * 
+ * if there are still sectors left in the request, 
+ *   if we know there are still sectors available from the interface,
+ *     go back and read the next bit of the request.
+ *   else if DRQ is asserted, there are more sectors available, so
+ *     go back and find out how many, then read them in.
+ *   else if BUSY is asserted, we are going to get an interrupt, so
+ *     set the handler for the interrupt and just return
+ */
+	if (total_remaining > 0) {
+		if (sectors_avail)
+			goto read_next;
+		stat = GET_STAT();
+		if (stat & DRQ_STAT)
+			goto read_again;
+		if (stat & BUSY_STAT) {
+			ide_set_handler (drive, &promise_read_intr, WAIT_CMD, NULL);
+#ifdef DEBUG_READ
+			printk(KERN_DEBUG "%s: promise_read: waiting for"
+			       "interrupt\n", drive->name);
+#endif 
+			return ide_started;
+		}
+		printk(KERN_ERR "%s: Eeek! promise_read_intr: sectors left "
+		       "!DRQ !BUSY\n", drive->name);
+		return ide_error(drive, "promise read intr", stat);
+	}
+	return ide_stopped;
+}
+
+/*
+ * promise_complete_pollfunc()
+ * This is the polling function for waiting (nicely!) until drive stops
+ * being busy. It is invoked at the end of a write, after the previous poll
+ * has finished.
+ *
+ * Once not busy, the end request is called.
+ */
+static ide_startstop_t promise_complete_pollfunc(ide_drive_t *drive)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct request *rq = hwgroup->rq;
+	int i;
+
+	if (GET_STAT() & BUSY_STAT) {
+		if (time_before(jiffies, hwgroup->poll_timeout)) {
+			ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
+			return ide_started; /* continue polling... */
+		}
+		hwgroup->poll_timeout = 0;
+		printk(KERN_ERR "%s: completion timeout - still busy!\n",
+		       drive->name);
+		return ide_error(drive, "busy timeout", GET_STAT());
+	}
+
+	hwgroup->poll_timeout = 0;
+#ifdef DEBUG_WRITE
+	printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name);
+#endif
+	for (i = rq->nr_sectors; i > 0; ) {
+		i -= rq->current_nr_sectors;
+		ide_end_request(1, hwgroup);
+	}
+	return ide_stopped;
+}
+
+/*
+ * promise_write_pollfunc() is the handler for disk write completion polling.
+ */
+static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+	if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
+		if (time_before(jiffies, hwgroup->poll_timeout)) {
+			ide_set_handler (drive, &promise_write_pollfunc, HZ/100, NULL);
+			return ide_started; /* continue polling... */
+		}
+		hwgroup->poll_timeout = 0;
+		printk(KERN_ERR "%s: write timed-out!\n",drive->name);
+		return ide_error (drive, "write timeout", GET_STAT());
+	}
+
+	/*
+	 * Now write out last 4 sectors and poll for not BUSY
+	 */
+	ide_multwrite(drive, 4);
+	hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+	ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
+#ifdef DEBUG_WRITE
+	printk(KERN_DEBUG "%s: Done last 4 sectors - status = %02x\n",
+		drive->name, GET_STAT());
+#endif
+	return ide_started;
+}
+
+/*
+ * promise_write() transfers a block of one or more sectors of data to a
+ * drive as part of a disk write operation. All but 4 sectors are transfered
+ * in the first attempt, then the interface is polled (nicely!) for completion
+ * before the final 4 sectors are transfered. There is no interrupt generated
+ * on writes (at least on the DC4030VL-2), we just have to poll for NOT BUSY.
+ */
+static ide_startstop_t promise_write (ide_drive_t *drive)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct request *rq = &hwgroup->wrq;
+
+#ifdef DEBUG_WRITE
+	printk(KERN_DEBUG "%s: promise_write: sectors(%ld-%ld), "
+	       "buffer=0x%08x\n", drive->name, rq->sector,
+	       rq->sector + rq->nr_sectors - 1, (unsigned int)rq->buffer);
+#endif
+
+	/*
+	 * If there are more than 4 sectors to transfer, do n-4 then go into
+	 * the polling strategy as defined above.
+	 */
+	if (rq->nr_sectors > 4) {
+		if (ide_multwrite(drive, rq->nr_sectors - 4))
+			return ide_stopped;
+		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+		ide_set_handler (drive, &promise_write_pollfunc, HZ/100, NULL);
+		return ide_started;
+	} else {
+	/*
+	 * There are 4 or fewer sectors to transfer, do them all in one go
+	 * and wait for NOT BUSY.
+	 */
+		if (ide_multwrite(drive, rq->nr_sectors))
+			return ide_stopped;
+		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+		ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
+#ifdef DEBUG_WRITE
+		printk(KERN_DEBUG "%s: promise_write: <= 4 sectors, "
+			"status = %02x\n", drive->name, GET_STAT());
+#endif
+		return ide_started;
+	}
+}
+
+/*
+ * do_pdc4030_io() is called from do_rw_disk, having had the block number
+ * already set up. It issues a READ or WRITE command to the Promise
+ * controller, assuming LBA has been used to set up the block number.
+ */
+ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq)
+{
+	unsigned long timeout;
+	byte stat;
+
+	if (rq->cmd == READ) {
+		OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG);
+/*
+ * The card's behaviour is odd at this point. If the data is
+ * available, DRQ will be true, and no interrupt will be
+ * generated by the card. If this is the case, we need to call the 
+ * "interrupt" handler (promise_read_intr) directly. Otherwise, if
+ * an interrupt is going to occur, bit0 of the SELECT register will
+ * be high, so we can set the handler the just return and be interrupted.
+ * If neither of these is the case, we wait for up to 50ms (badly I'm
+ * afraid!) until one of them is.
+ */
+		timeout = jiffies + HZ/20; /* 50ms wait */
+		do {
+			stat=GET_STAT();
+			if (stat & DRQ_STAT) {
+				udelay(1);
+				return promise_read_intr(drive);
+			}
+			if (IN_BYTE(IDE_SELECT_REG) & 0x01) {
+#ifdef DEBUG_READ
+				printk(KERN_DEBUG "%s: read: waiting for "
+				                  "interrupt\n", drive->name);
+#endif
+				ide_set_handler(drive, &promise_read_intr, WAIT_CMD, NULL);
+				return ide_started;
+			}
+			udelay(1);
+		} while (time_before(jiffies, timeout));
+
+		printk(KERN_ERR "%s: reading: No DRQ and not waiting - Odd!\n",
+			drive->name);
+		return ide_stopped;
+	} else if (rq->cmd == WRITE) {
+		ide_startstop_t startstop;
+		OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
+		if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+			printk(KERN_ERR "%s: no DRQ after issuing "
+			       "PROMISE_WRITE\n", drive->name);
+			return startstop;
+	    	}
+		if (!drive->unmask)
+			__cli();	/* local CPU only */
+		HWGROUP(drive)->wrq = *rq; /* scratchpad */
+		return promise_write(drive);
+
+	} else {
+		printk("KERN_WARNING %s: bad command: %d\n",
+		       drive->name, rq->cmd);
+		ide_end_request(0, HWGROUP(drive));
+		return ide_stopped;
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/pdc4030.h linux/drivers/ide/pdc4030.h
--- v2.3.51/linux/drivers/ide/pdc4030.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/pdc4030.h	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,44 @@
+/*
+ *  linux/drivers/ide/pdc4030.h
+ *
+ *  Copyright (C) 1995-1998  Linus Torvalds & authors
+ */
+
+/*
+ * Principal author: Peter Denison <peterd@pnd-pc.demon.co.uk>
+ */
+
+#ifndef IDE_PROMISE_H
+#define IDE_PROMISE_H
+
+#define	PROMISE_EXTENDED_COMMAND	0xF0
+#define	PROMISE_READ			0xF2
+#define	PROMISE_WRITE			0xF3
+/* Extended commands - main command code = 0xf0 */
+#define	PROMISE_GET_CONFIG		0x10
+#define	PROMISE_IDENTIFY		0x20
+
+struct translation_mode {
+	u16	cyl;
+	u8	head;
+	u8	sect;
+};
+
+struct dc_ident {
+	u8	type;
+	u8	unknown1;
+	u8	hw_revision;
+	u8	firmware_major;
+	u8	firmware_minor;
+	u8	bios_address;
+	u8	irq;
+	u8	unknown2;
+	u16	cache_mem;
+	u16	unknown3;
+	u8	id[2];
+	u16	info;
+	struct translation_mode current_tm[4];
+	u8	pad[SECTOR_WORDS*4 - 32];
+};
+
+#endif IDE_PROMISE_H
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/piix.c linux/drivers/ide/piix.c
--- v2.3.51/linux/drivers/ide/piix.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/piix.c	Mon Feb 28 07:16:54 2000
@@ -0,0 +1,434 @@
+/*
+ * linux/drivers/block/piix.c		Version 0.30	Feb. 26, 2000
+ *
+ *  Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
+ *  Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
+ *  May be copied or modified under the terms of the GNU General Public License
+ *
+ *  PIO mode setting function for Intel chipsets.  
+ *  For use instead of BIOS settings.
+ *
+ * 40-41
+ * 42-43
+ * 
+ *                 41
+ *                 43
+ *
+ * | PIO 0       | c0 | 80 | 0 | 	piix_tune_drive(drive, 0);
+ * | PIO 2 | SW2 | d0 | 90 | 4 | 	piix_tune_drive(drive, 2);
+ * | PIO 3 | MW1 | e1 | a1 | 9 | 	piix_tune_drive(drive, 3);
+ * | PIO 4 | MW2 | e3 | a3 | b | 	piix_tune_drive(drive, 4);
+ * 
+ * sitre = word40 & 0x4000; primary
+ * sitre = word42 & 0x4000; secondary
+ *
+ * 44 8421|8421    hdd|hdb
+ * 
+ * 48 8421         hdd|hdc|hdb|hda udma enabled
+ *
+ *    0001         hda
+ *    0010         hdb
+ *    0100         hdc
+ *    1000         hdd
+ *
+ * 4a 84|21        hdb|hda
+ * 4b 84|21        hdd|hdc
+ *
+ *    ata-33/82371AB
+ *    ata-33/82371EB
+ *    ata-33/82801AB            ata-66/82801AA
+ *    00|00 udma 0              00|00 reserved
+ *    01|01 udma 1              01|01 udma 3
+ *    10|10 udma 2              10|10 udma 4
+ *    11|11 reserved            11|11 reserved
+ *
+ * 54 8421|8421    ata66 drive|ata66 enable
+ *
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x40, &reg40);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x42, &reg42);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x44, &reg44);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x48, &reg48);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, &reg4a);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x54, &reg54);
+ *
+ * 00:1f.1 IDE interface: Intel Corporation:
+ *                          Unknown device 2411 (rev 01) (prog-if 80 [Master])
+ *         Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop-
+ *                          ParErr- Stepping- SERR- FastB2B-
+ *         Status: Cap- 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
+ *                          <TAbort- <MAbort- >SERR- <PERR-
+ *         Latency: 0 set
+ *         Region 4: I/O ports at ffa0
+ * 00: 86 80 11 24 05 00 80 02 01 80 01 01 00 00 00 00
+ * 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 20: a1 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 40: 07 a3 03 a3 00 00 00 00 05 00 02 02 00 00 00 00
+ * 50: 00 00 00 00 11 04 00 00 00 00 00 00 00 00 00 00
+ * 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * f0: 00 00 00 00 00 00 00 00 3a 0f 00 00 00 00 00 00
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+#define PIIX_DEBUG_DRIVE_INFO		0
+
+#define DISPLAY_PIIX_TIMINGS
+
+#if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int piix_get_info(char *, char **, off_t, int);
+extern int (*piix_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int piix_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	char *p = buffer;
+	u32 bibma = bmide_dev->resource[4].start;
+        u16 reg40 = 0, psitre = 0, reg42 = 0, ssitre = 0;
+	u8  c0 = 0, c1 = 0;
+	u8  reg44 = 0, reg48 = 0, reg4a = 0, reg4b = 0, reg54 = 0;
+
+	pci_read_config_word(bmide_dev, 0x40, &reg40);
+	pci_read_config_word(bmide_dev, 0x42, &reg42);
+	pci_read_config_byte(bmide_dev, 0x44, &reg44);
+	pci_read_config_byte(bmide_dev, 0x48, &reg48);
+	pci_read_config_byte(bmide_dev, 0x4a, &reg4a);
+	pci_read_config_byte(bmide_dev, 0x4b, &reg4b);
+	pci_read_config_byte(bmide_dev, 0x54, &reg54);
+
+	psitre = (reg40 & 0x4000) ? 1 : 0;
+	ssitre = (reg42 & 0x4000) ? 1 : 0;
+
+        /*
+         * at that point bibma+0x2 et bibma+0xa are byte registers
+         * to investigate:
+         */
+	c0 = inb_p((unsigned short)bibma + 0x02);
+	c1 = inb_p((unsigned short)bibma + 0x0a);
+
+	switch(bmide_dev->device) {
+		case PCI_DEVICE_ID_INTEL_82372FB_1:
+		case PCI_DEVICE_ID_INTEL_82801AA_1:
+			p += sprintf(p, "\n                                Intel PIIX4 Ultra 66 Chipset.\n");
+			break;
+		case PCI_DEVICE_ID_INTEL_82801AB_1:
+		case PCI_DEVICE_ID_INTEL_82371AB:
+			p += sprintf(p, "\n                                Intel PIIX4 Ultra 33 Chipset.\n");
+			break;
+		case PCI_DEVICE_ID_INTEL_82371SB_1:
+			p += sprintf(p, "\n                                Intel PIIX3 Chipset.\n");
+			break;
+		case PCI_DEVICE_ID_INTEL_82371FB_1:
+		case PCI_DEVICE_ID_INTEL_82371FB_0:
+		default:
+			p += sprintf(p, "\n                                Intel PIIX Chipset.\n");
+			break;
+	}
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	p += sprintf(p, "                %sabled                         %sabled\n",
+			(c0&0x80) ? "dis" : " en",
+			(c1&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s             %s               %s\n",
+			(c0&0x20) ? "yes" : "no ",
+			(c0&0x40) ? "yes" : "no ",
+			(c1&0x20) ? "yes" : "no ",
+			(c1&0x40) ? "yes" : "no " );
+	p += sprintf(p, "UDMA enabled:   %s              %s             %s               %s\n",
+			(reg48&0x01) ? "yes" : "no ",
+			(reg48&0x02) ? "yes" : "no ",
+			(reg48&0x04) ? "yes" : "no ",
+			(reg48&0x08) ? "yes" : "no " );
+	p += sprintf(p, "UDMA enabled:   %s                %s               %s                 %s\n",
+			((reg54&0x11) && (reg4a&0x02)) ? "4" :
+			((reg54&0x11) && (reg4a&0x01)) ? "3" :
+			(reg4a&0x02) ? "2" :
+			(reg4a&0x01) ? "1" :
+			(reg4a&0x00) ? "0" : "X",
+			((reg54&0x22) && (reg4a&0x20)) ? "4" :
+			((reg54&0x22) && (reg4a&0x10)) ? "3" :
+			(reg4a&0x20) ? "2" :
+			(reg4a&0x10) ? "1" :
+			(reg4a&0x00) ? "0" : "X",
+			((reg54&0x44) && (reg4b&0x02)) ? "4" :
+			((reg54&0x44) && (reg4b&0x01)) ? "3" :
+			(reg4b&0x02) ? "2" :
+			(reg4b&0x01) ? "1" :
+			(reg4b&0x00) ? "0" : "X",
+			((reg54&0x88) && (reg4b&0x20)) ? "4" :
+			((reg54&0x88) && (reg4b&0x10)) ? "3" :
+			(reg4b&0x20) ? "2" :
+			(reg4b&0x10) ? "1" :
+			(reg4b&0x00) ? "0" : "X");
+
+	p += sprintf(p, "UDMA\n");
+	p += sprintf(p, "DMA\n");
+	p += sprintf(p, "PIO\n");
+
+/*
+ *	FIXME.... Add configuration junk data....blah blah......
+ */
+
+	return p-buffer;	 /* => must be less than 4k! */
+}
+#endif  /* defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+/*
+ *  Used to set Fifo configuration via kernel command line:
+ */
+
+byte piix_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ *
+ */
+static byte piix_dma_2_pio (byte xfer_rate) {
+	switch(xfer_rate) {
+		case XFER_UDMA_4:
+		case XFER_UDMA_3:
+		case XFER_UDMA_2:
+		case XFER_UDMA_1:
+		case XFER_UDMA_0:
+		case XFER_MW_DMA_2:
+		case XFER_PIO_4:
+			return 4;
+		case XFER_MW_DMA_1:
+		case XFER_PIO_3:
+			return 3;
+		case XFER_SW_DMA_2:
+		case XFER_PIO_2:
+			return 2;
+		case XFER_MW_DMA_0:
+		case XFER_SW_DMA_1:
+		case XFER_SW_DMA_0:
+		case XFER_PIO_1:
+		case XFER_PIO_0:
+		case XFER_PIO_SLOW:
+		default:
+			return 0;
+	}
+}
+
+/*
+ *  Based on settings done by AMI BIOS
+ *  (might be usefull if drive is not registered in CMOS for any reason).
+ */
+static void piix_tune_drive (ide_drive_t *drive, byte pio)
+{
+	unsigned long flags;
+	u16 master_data;
+	byte slave_data;
+	int is_slave		= (&HWIF(drive)->drives[1] == drive);
+	int master_port		= HWIF(drive)->index ? 0x42 : 0x40;
+	int slave_port		= 0x44;
+				 /* ISP  RTC */
+	byte timings[][2]	= { { 0, 0 },
+				    { 0, 0 },
+				    { 1, 0 },
+				    { 2, 1 },
+				    { 2, 3 }, };
+
+	pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
+	pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data);
+	if (is_slave) {
+		master_data = master_data | 0x4000;
+		if (pio > 1)
+			/* enable PPE, IE and TIME */
+			master_data = master_data | 0x0070;
+		pci_read_config_byte(HWIF(drive)->pci_dev, slave_port, &slave_data);
+		slave_data = slave_data & (HWIF(drive)->index ? 0x0f : 0xf0);
+		slave_data = slave_data | ((timings[pio][0] << 2) | (timings[pio][1]
+					   << (HWIF(drive)->index ? 4 : 0)));
+	} else {
+		master_data = master_data & 0xccf8;
+		if (pio > 1)
+			/* enable PPE, IE and TIME */
+			master_data = master_data | 0x0007;
+		master_data = master_data | (timings[pio][0] << 12) |
+			      (timings[pio][1] << 8);
+	}
+	save_flags(flags);
+	cli();
+	pci_write_config_word(HWIF(drive)->pci_dev, master_port, master_data);
+	if (is_slave)
+		pci_write_config_byte(HWIF(drive)->pci_dev, slave_port, slave_data);
+	restore_flags(flags);
+}
+
+static int piix_config_drive_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id	= drive->id;
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+
+	int			sitre;
+	short			reg4042, reg44, reg48, reg4a, reg54;
+	byte			speed;
+
+	byte maslave		= hwif->channel ? 0x42 : 0x40;
+	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+	int ultra66		= ((dev->device == PCI_DEVICE_ID_INTEL_82801AA_1) ||
+				   (dev->device == PCI_DEVICE_ID_INTEL_82372FB_1)) ? 1 : 0;
+	int ultra		= ((ultra66) ||
+				   (dev->device == PCI_DEVICE_ID_INTEL_82371AB) ||
+				   (dev->device == PCI_DEVICE_ID_INTEL_82801AB_1)) ? 1 : 0;
+	int drive_number	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+	int a_speed		= 2 << (drive_number * 4);
+	int u_flag		= 1 << drive_number;
+	int v_flag		= 0x10 << drive_number;
+	int u_speed		= 0;
+
+	pci_read_config_word(dev, maslave, &reg4042);
+	sitre = (reg4042 & 0x4000) ? 1 : 0;
+	pci_read_config_word(dev, 0x44, &reg44);
+	pci_read_config_word(dev, 0x48, &reg48);
+	pci_read_config_word(dev, 0x4a, &reg4a);
+	pci_read_config_word(dev, 0x54, &reg54);
+
+	if ((id->dma_ultra & 0x0010) && (ultra)) {
+		u_speed = 2 << (drive_number * 4);
+		speed = ((udma_66) && (ultra66)) ? XFER_UDMA_4 : XFER_UDMA_2;
+	} else if ((id->dma_ultra & 0x0008) && (ultra)) {
+		u_speed = 1 << (drive_number * 4);
+		speed = ((udma_66) && (ultra66)) ? XFER_UDMA_3 : XFER_UDMA_1;
+	} else if ((id->dma_ultra & 0x0004) && (ultra)) {
+		u_speed = 2 << (drive_number * 4);
+		speed = XFER_UDMA_2;
+	} else if ((id->dma_ultra & 0x0002) && (ultra)) {
+		u_speed = 1 << (drive_number * 4);
+		speed = XFER_UDMA_1;
+	} else if ((id->dma_ultra & 0x0001) && (ultra)) {
+		u_speed = 0 << (drive_number * 4);
+		speed = XFER_UDMA_0;
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_1word & 0x0004) {
+		speed = XFER_SW_DMA_2;
+        } else {
+		speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
+	}
+
+	if (speed >= XFER_UDMA_0) {
+		if (!(reg48 & u_flag))
+			pci_write_config_word(dev, 0x48, reg48|u_flag);
+		if (!(reg4a & u_speed)) {
+			pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+			pci_write_config_word(dev, 0x4a, reg4a|u_speed);
+		}
+		if (speed > XFER_UDMA_2) {
+			if (!(reg54 & v_flag)) {
+				pci_write_config_word(dev, 0x54, reg54|v_flag);
+			}
+		} else {
+			pci_write_config_word(dev, 0x54, reg54 & ~v_flag);
+		}
+	}
+
+	if (speed < XFER_UDMA_0) {
+		if (reg48 & u_flag)
+			pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
+		if (reg4a & a_speed)
+			pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+		if (reg54 & v_flag)
+			pci_write_config_word(dev, 0x54, reg54 & ~v_flag);
+	}
+
+	piix_tune_drive(drive, piix_dma_2_pio(speed));
+
+	(void) ide_config_drive_speed(drive, speed);
+
+#if PIIX_DEBUG_DRIVE_INFO
+	printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number);
+#endif /* PIIX_DEBUG_DRIVE_INFO */
+
+	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+}
+
+static int piix_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			 return ide_dmaproc((ide_dma_action_t) piix_config_drive_for_dma(drive), drive);
+		default :
+			break;
+	}
+	/* Other cases are done by generic IDE-DMA code. */
+	return ide_dmaproc(func, drive);
+}
+
+unsigned int __init pci_init_piix (struct pci_dev *dev, const char *name)
+{
+#if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS)
+	piix_proc = 1;
+	bmide_dev = dev;
+	piix_display_info = &piix_get_info;
+#endif /* DISPLAY_PIIX_TIMINGS && CONFIG_PROC_FS */
+	return 0;
+}
+
+/*
+ * Sheesh, someone at Intel needs to go read the ATA-4/5 T13 standards.
+ * It does not specify device detection, but channel!!!
+ * You determine later if bit 13 of word93 is set...
+ */
+unsigned int __init ata66_piix (ide_hwif_t *hwif)
+{
+	byte reg54h = 0, reg55h = 0, ata66 = 0;
+	byte mask = hwif->channel ? 0x0c : 0x03;
+
+	pci_read_config_byte(hwif->pci_dev, 0x54, &reg54h);
+	pci_read_config_byte(hwif->pci_dev, 0x55, &reg55h);
+	ata66 = (reg54h & mask) ? 1 : 0;
+
+	return ata66;
+}
+
+void __init ide_init_piix (ide_hwif_t *hwif)
+{
+	hwif->tuneproc = &piix_tune_drive;
+
+	if (hwif->dma_base) {
+#ifdef CONFIG_PIIX_TUNING
+		hwif->dmaproc = &piix_dmaproc;
+#endif /* CONFIG_PIIX_TUNING */
+		hwif->drives[0].autotune = 0;
+		hwif->drives[1].autotune = 0;
+	} else {
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+	if (!hwif->irq)
+		hwif->irq = hwif->channel ? 15 : 14;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/q40ide.c linux/drivers/ide/q40ide.c
--- v2.3.51/linux/drivers/ide/q40ide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/q40ide.c	Tue Dec 14 23:03:50 1999
@@ -0,0 +1,109 @@
+/*
+ *  linux/drivers/block/q40ide.c -- Q40 I/O port IDE Driver
+ *
+ *     original file created 12 Jul 1997 by Geert Uytterhoeven
+ *
+ *  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.
+ *
+ * RZ:
+ *  almost identical with pcide.c, maybe we can merge it later. 
+ *  Differences:
+ *       max 2 HWIFS for now
+ *       translate portaddresses to q40 native addresses (not yet...) instead rely on in/out[bw]
+ *         address translation
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/ide.h>
+
+    /*
+     *  Bases of the IDE interfaces
+     */
+
+#define PCIDE_NUM_HWIFS	2
+
+#define PCIDE_BASE1	0x1f0
+#define PCIDE_BASE2	0x170
+#define PCIDE_BASE3	0x1e8
+#define PCIDE_BASE4	0x168
+#define PCIDE_BASE5	0x1e0
+#define PCIDE_BASE6	0x160
+
+static const q40ide_ioreg_t pcide_bases[PCIDE_NUM_HWIFS] = {
+    PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4  , PCIDE_BASE5,
+    PCIDE_BASE6 */
+};
+
+
+    /*
+     *  Offsets from one of the above bases
+     */
+
+#undef HD_DATA
+#define HD_DATA  0x1f0
+
+#define PCIDE_REG(x)	((q40ide_ioreg_t)(HD_##x-PCIDE_BASE1))
+
+static const int pcide_offsets[IDE_NR_PORTS] = {
+    PCIDE_REG(DATA), PCIDE_REG(ERROR), PCIDE_REG(NSECTOR), PCIDE_REG(SECTOR),
+    PCIDE_REG(LCYL), PCIDE_REG(HCYL), PCIDE_REG(CURRENT), PCIDE_REG(STATUS),
+    PCIDE_REG(CMD)
+};
+
+int q40ide_default_irq(q40ide_ioreg_t base)
+{
+           switch (base) { 
+	            case 0x1f0: return 14;
+		    case 0x170: return 15;
+		    case 0x1e8: return 11;
+		    default:
+			return 0;
+	   }
+}
+
+void q40_ide_init_hwif_ports (q40ide_ioreg_t *p, q40ide_ioreg_t base, int *irq)
+{
+	q40ide_ioreg_t port = base;
+	int i = 8;
+
+	while (i--)
+		*p++ = port++;
+	*p++ = base + 0x206;
+	if (irq != NULL)
+		*irq = 0;
+}
+
+
+    /*
+     *  Probe for PC IDE interfaces
+     */
+
+int q40ide_probe_hwif(int index, ide_hwif_t *hwif)
+{
+    static int pcide_index[PCIDE_NUM_HWIFS] = { 0, };
+    int i;
+
+    if (!MACH_IS_Q40)
+      return 0;
+
+    for (i = 0; i < PCIDE_NUM_HWIFS; i++) {
+	if (!pcide_index[i]) {
+	  /*printk("ide%d: Q40 IDE interface\n", index);*/
+	    pcide_index[i] = index+1;
+	}
+	if (pcide_index[i] == index+1) {
+	    ide_setup_ports(hwif,(ide_ioreg_t) pcide_bases[i], pcide_offsets, 0, /*q40_ack_intr???*/ NULL);
+	    hwif->irq = ide_default_irq((ide_ioreg_t)pcide_bases[i]); /*q40_ide_irq[i];  */ /* 14 */
+	    return 1;
+	}
+    }
+    return 0;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/qd6580.c linux/drivers/ide/qd6580.c
--- v2.3.51/linux/drivers/ide/qd6580.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/qd6580.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,71 @@
+/*
+ *  linux/drivers/block/qd6580.c		Version 0.02	Feb 09, 1996
+ *
+ *  Copyright (C) 1996  Linus Torvalds & author (see below)
+ */
+
+/*
+ * QDI QD6580 EIDE controller fast support by Colten Edwards.
+ * No net access, but (maybe) can be reached at pje120@cs.usask.ca
+ */
+
+#undef REALLY_SLOW_IO           /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/*
+ * Register 0xb3 looks like:
+ *	0x4f is fast		mode3 ?
+ *	0x3f is medium		mode2 ?
+ *	0x2f is slower		mode1 ?
+ *	0x1f is slower yet	mode0 ?
+ *	0x0f ???		???
+ *
+ * Don't know whether this sets BOTH drives, or just the first drive.
+ * Don't know if there is a separate setting for the second drive.
+ *
+ * Feel free to patch this if you have one of these beasts
+ * and can work out the answers!
+ *
+ * I/O ports are 0xb0 0xb2 and 0xb3
+ *
+ * More research on qd6580 being done by willmore@cig.mot.com (David)
+ *	-- this is apparently a *dual* IDE interface
+ */
+
+static void tune_qd6580 (ide_drive_t *drive, byte pio)
+{
+	unsigned long flags;
+
+	pio = ide_get_best_pio_mode(drive, pio, 3, NULL);
+
+	save_flags(flags);	/* all CPUs */
+	cli();			/* all CPUs */
+	outb_p(0x8d,0xb0);
+	outb_p(0x0 ,0xb2);
+	outb_p(((pio+1)<<4)|0x0f,0xb3);
+	inb(0x3f6);
+	restore_flags(flags);	/* all CPUs */
+}
+
+void __init init_qd6580 (void)
+{
+	ide_hwifs[0].chipset = ide_qd6580;
+	ide_hwifs[1].chipset = ide_qd6580;
+	ide_hwifs[0].tuneproc = &tune_qd6580;
+	ide_hwifs[0].mate = &ide_hwifs[1];
+	ide_hwifs[1].mate = &ide_hwifs[0];
+	ide_hwifs[1].channel = 1;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/rapide.c linux/drivers/ide/rapide.c
--- v2.3.51/linux/drivers/ide/rapide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/rapide.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,92 @@
+/*
+ * linux/drivers/block/rapide.c
+ *
+ * Copyright (c) 1996-1998 Russell King.
+ *
+ * Changelog:
+ *  08-06-1996	RMK	Created
+ *  13-04-1998	RMK	Added manufacturer and product IDs
+ */
+
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/ide.h>
+
+#include <asm/ecard.h>
+
+static const card_ids __init rapide_cids[] = {
+	{ MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
+	{ 0xffff, 0xffff }
+};
+
+static struct expansion_card *ec[MAX_ECARDS];
+static int result[MAX_ECARDS];
+
+static inline int rapide_register(struct expansion_card *ec)
+{
+	unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
+	hw_regs_t hw;
+
+	int i;
+
+	memset(&hw, 0, sizeof(hw));
+
+	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+		hw.io_ports[i] = (ide_ioreg_t)port;
+		port += 1 << 4;
+	}
+	hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206;
+	hw.irq = ec->irq;
+
+	return ide_register_hw(&hw, NULL);
+}
+
+int __init rapide_init(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		ec[i] = NULL;
+
+	ecard_startfind();
+
+	for (i = 0; ; i++) {
+		if ((ec[i] = ecard_find(0, rapide_cids)) == NULL)
+			break;
+
+		ecard_claim(ec[i]);
+		result[i] = rapide_register(ec[i]);
+	}
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ec[i] && result[i] < 0) {
+			ecard_release(ec[i]);
+			ec[i] = NULL;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+
+int init_module (void)
+{
+	return rapide_init();
+}
+
+void cleanup_module (void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ec[i]) {
+			unsigned long port;
+			port = ecard_address(ec[i], ECARD_MEMC, 0);
+
+			ide_unregister_port(port, ec[i]->irq, 16);
+			ecard_release(ec[i]);
+			ec[i] = NULL;
+		}
+}
+#endif
+
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/rz1000.c linux/drivers/ide/rz1000.c
--- v2.3.51/linux/drivers/ide/rz1000.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/rz1000.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,96 @@
+/*
+ *  linux/drivers/block/rz1000.c		Version 0.05	December 8, 1997
+ *
+ *  Copyright (C) 1995-1998  Linus Torvalds & author (see below)
+ */
+
+/*
+ *  Principal Author:  mlord@pobox.com (Mark Lord)
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
+ *
+ *  This file provides support for disabling the buggy read-ahead
+ *  mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
+ *
+ *  Dunno if this fixes both ports, or only the primary port (?).
+ */
+
+#undef REALLY_SLOW_IO		/* most systems can safely undef this */
+
+#include <linux/config.h> /* for CONFIG_BLK_DEV_IDEPCI */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+
+void __init ide_init_rz1000 (ide_hwif_t *hwif)	/* called from ide-pci.c */
+{
+	unsigned short reg;
+	struct pci_dev *dev = hwif->pci_dev;
+
+	hwif->chipset = ide_rz1000;
+	if (!pci_read_config_word (dev, 0x40, &reg)
+	 && !pci_write_config_word(dev, 0x40, reg & 0xdfff))
+	{
+		printk("%s: disabled chipset read-ahead (buggy RZ1000/RZ1001)\n", hwif->name);
+	} else {
+		hwif->serialized = 1;
+		hwif->drives[0].no_unmask = 1;
+		hwif->drives[1].no_unmask = 1;
+		printk("%s: serialized, disabled unmasking (buggy RZ1000/RZ1001)\n", hwif->name);
+	}
+}
+
+#else
+
+static void __init init_rz1000 (struct pci_dev *dev, const char *name)
+{
+	unsigned short reg, h;
+
+	if (!pci_read_config_word (dev, PCI_COMMAND, &reg) && !(reg & PCI_COMMAND_IO)) {
+		printk("%s: buggy IDE controller disabled (BIOS)\n", name);
+		return;
+	}
+	if (!pci_read_config_word (dev, 0x40, &reg)
+	 && !pci_write_config_word(dev, 0x40, reg & 0xdfff))
+	{
+		printk("IDE: disabled chipset read-ahead (buggy %s)\n", name);
+	} else {
+		for (h = 0; h < MAX_HWIFS; ++h) {
+			ide_hwif_t *hwif = &ide_hwifs[h];
+			if ((hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0 || hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
+			 && (hwif->chipset == ide_unknown || hwif->chipset == ide_generic))
+			{
+				hwif->chipset = ide_rz1000;
+				hwif->serialized = 1;
+				hwif->drives[0].no_unmask = 1;
+				hwif->drives[1].no_unmask = 1;
+				if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
+					hwif->channel = 1;
+				printk("%s: serialized, disabled unmasking (buggy %s)\n", hwif->name, name);
+			}
+		}
+	}
+}
+
+void __init ide_probe_for_rz100x (void)	/* called from ide.c */
+{
+	struct pci_dev *dev = NULL;
+
+	while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, dev))!=NULL)
+		init_rz1000 (dev, "RZ1000");
+	while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, dev))!=NULL)
+		init_rz1000 (dev, "RZ1001");
+}
+
+#endif CONFIG_BLK_DEV_IDEPCI
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/sis5513.c linux/drivers/ide/sis5513.c
--- v2.3.51/linux/drivers/ide/sis5513.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/sis5513.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,549 @@
+/*
+ * linux/drivers/block/sis5513.c		Version 0.09	Feb. 10, 2000
+ *
+ * Copyright (C) 1999-2000	Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * Thanks to SIS Taiwan for direct support and hardware.
+ * Tested and designed on the SiS620/5513 chipset.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#define DISPLAY_SIS_TIMINGS
+#define SIS5513_DEBUG_DRIVE_INFO	0
+
+static struct pci_dev *host_dev = NULL;
+
+#define arraysize(x)			(sizeof(x)/sizeof(*(x)))
+
+#define SIS5513_FLAG_ATA_00		0x00000000
+#define SIS5513_FLAG_ATA_16		0x00000001
+#define SIS5513_FLAG_ATA_33		0x00000002
+#define SIS5513_FLAG_ATA_66		0x00000004
+#define SIS5513_FLAG_LATENCY		0x00000010
+
+static const struct {
+	const char *name;
+	unsigned short host_id;
+	unsigned int flags;
+} SiSHostChipInfo[] = {
+	{ "SiS530",	PCI_DEVICE_ID_SI_530,	SIS5513_FLAG_ATA_66, },
+	{ "SiS540",	PCI_DEVICE_ID_SI_540,	SIS5513_FLAG_ATA_66, },
+	{ "SiS620",	PCI_DEVICE_ID_SI_620,	SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, },
+	{ "SiS630",	PCI_DEVICE_ID_SI_630,	SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, },
+	{ "SiS5591",	PCI_DEVICE_ID_SI_5591,	SIS5513_FLAG_ATA_33, },
+	{ "SiS5597",	PCI_DEVICE_ID_SI_5597,	SIS5513_FLAG_ATA_33, },
+	{ "SiS5600",	PCI_DEVICE_ID_SI_5600,	SIS5513_FLAG_ATA_33, },
+	{ "SiS5511",	PCI_DEVICE_ID_SI_5511,	SIS5513_FLAG_ATA_16, },
+};
+
+#if 0
+
+static struct _pio_mode_mapping {
+	byte data_active;
+	byte recovery;
+	byte pio_mode;
+} pio_mode_mapping[] = {
+	{ 8, 12, 0 },
+	{ 6,  7, 1 },
+	{ 4,  4, 2 },
+	{ 3,  3, 3 },
+	{ 3,  1, 4 }
+};
+
+static struct _dma_mode_mapping {
+	byte data_active;
+	byte recovery;
+	byte dma_mode;
+} dma_mode_mapping[] = {
+	{ 8, 8, 0 },
+	{ 3, 2, 1 },
+	{ 3, 1, 2 }
+};
+
+static struct _udma_mode_mapping {
+	byte cycle_time;
+	char * udma_mode;
+} udma_mode_mapping[] = {
+	{ 8, "Mode 0" },
+	{ 6, "Mode 1" },
+	{ 4, "Mode 2" }, 
+	{ 3, "Mode 3" },
+	{ 2, "Mode 4" },
+	{ 0, "Undefined" }
+};
+
+static __inline__ char * find_udma_mode (byte cycle_time)
+{
+	int n;
+	
+	for (n = 0; n <= 4; n++)
+		if (udma_mode_mapping[n].cycle_time <= cycle_time)
+			return udma_mode_mapping[n].udma_mode;
+	return udma_mode_mapping[4].udma_mode;
+}
+#endif
+
+#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int sis_get_info(char *, char **, off_t, int);
+extern int (*sis_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+struct pci_dev *bmide_dev;
+
+static char *cable_type[] = {
+	"80 pins",
+	"40 pins"
+};
+
+static char *recovery_time [] ={
+	"12 PCICLK", "1 PCICLK",
+	"2 PCICLK", "3 PCICLK",
+	"4 PCICLK", "5 PCICLCK",
+	"6 PCICLK", "7 PCICLCK",
+	"8 PCICLK", "9 PCICLCK",
+	"10 PCICLK", "11 PCICLK",
+	"13 PCICLK", "14 PCICLK",
+	"15 PCICLK", "15 PCICLK"
+};
+
+static char *cycle_time [] = {
+	"Undefined", "2 CLCK",
+	"3 CLK", "4 CLK",
+	"5 CLK", "6 CLK",
+	"7 CLK", "8 CLK"
+};
+
+static char *active_time [] = {
+	"8 PCICLK", "1 PCICLCK",
+	"2 PCICLK", "2 PCICLK",
+	"4 PCICLK", "5 PCICLK",
+	"6 PCICLK", "12 PCICLK"
+};
+
+static int sis_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	int rc;
+	char *p = buffer;
+	byte reg,reg1;
+	u16 reg2, reg3;
+
+	p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+	rc = pci_read_config_byte(bmide_dev, 0x4a, &reg);
+	p += sprintf(p, "Channel Status: %s \t \t \t \t %s \n",
+		     (reg & 0x02) ? "On" : "Off",
+		     (reg & 0x04) ? "On" : "Off");
+		     
+	rc = pci_read_config_byte(bmide_dev, 0x09, &reg);
+	p += sprintf(p, "Operation Mode: %s \t \t \t %s \n",
+		     (reg & 0x01) ? "Native" : "Compatible",
+		     (reg & 0x04) ? "Native" : "Compatible");
+		     	     
+	rc = pci_read_config_byte(bmide_dev, 0x48, &reg);
+	p += sprintf(p, "Cable Type:     %s \t \t \t %s\n",
+		     (reg & 0x10) ? cable_type[1] : cable_type[0],
+		     (reg & 0x20) ? cable_type[1] : cable_type[0]);
+		     
+	rc = pci_read_config_word(bmide_dev, 0x4c, &reg2);
+	rc = pci_read_config_word(bmide_dev, 0x4e, &reg3);
+	p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n",
+		     reg2, reg3);
+
+	rc = pci_read_config_byte(bmide_dev, 0x4b, &reg);	     
+	p += sprintf(p, "Drvie 0:        Postwrite %s \t \t Postwrite %s\n",
+		     (reg & 0x10) ? "Enabled" : "Disabled",
+		     (reg & 0x40) ? "Enabled" : "Disabled");
+	p += sprintf(p, "                Prefetch  %s \t \t Prefetch  %s\n",
+		     (reg & 0x01) ? "Enabled" : "Disabled",
+		     (reg & 0x04) ? "Enabled" : "Disabled");
+		          
+	rc = pci_read_config_byte(bmide_dev, 0x41, &reg);
+	rc = pci_read_config_byte(bmide_dev, 0x45, &reg1);
+	p += sprintf(p, "                UDMA %s \t \t \t UDMA %s\n",
+		     (reg & 0x80)  ? "Enabled" : "Disabled",
+		     (reg1 & 0x80) ? "Enabled" : "Disabled");
+	p += sprintf(p, "                UDMA Cycle Time    %s \t UDMA Cycle Time    %s\n",
+		     cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]);
+	p += sprintf(p, "                Data Active Time   %s \t Data Active Time   %s\n",
+		     active_time[(reg & 0x07)], active_time[(reg &0x07)] ); 
+
+	rc = pci_read_config_byte(bmide_dev, 0x40, &reg);
+	rc = pci_read_config_byte(bmide_dev, 0x44, &reg1);
+	p += sprintf(p, "                Data Recovery Time %s \t Data Recovery Time %s\n",
+		     recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]);
+
+
+	rc = pci_read_config_byte(bmide_dev, 0x4b, &reg);	     
+	p += sprintf(p, "Drvie 1:        Postwrite %s \t \t Postwrite %s\n",
+		     (reg & 0x20) ? "Enabled" : "Disabled",
+		     (reg & 0x80) ? "Enabled" : "Disabled");
+	p += sprintf(p, "                Prefetch  %s \t \t Prefetch  %s\n",
+		     (reg & 0x02) ? "Enabled" : "Disabled",
+		     (reg & 0x08) ? "Enabled" : "Disabled");
+
+	rc = pci_read_config_byte(bmide_dev, 0x43, &reg);
+	rc = pci_read_config_byte(bmide_dev, 0x47, &reg1);
+	p += sprintf(p, "                UDMA %s \t \t \t UDMA %s\n",
+		     (reg & 0x80)  ? "Enabled" : "Disabled",
+		     (reg1 & 0x80) ? "Enabled" : "Disabled");
+	p += sprintf(p, "                UDMA Cycle Time    %s \t UDMA Cycle Time    %s\n",
+		     cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]);
+	p += sprintf(p, "                Data Active Time   %s \t Data Active Time   %s\n",
+		     active_time[(reg & 0x07)], active_time[(reg &0x07)] ); 
+
+	rc = pci_read_config_byte(bmide_dev, 0x42, &reg);
+	rc = pci_read_config_byte(bmide_dev, 0x46, &reg1);
+	p += sprintf(p, "                Data Recovery Time %s \t Data Recovery Time %s\n",
+		     recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]);
+	return p-buffer;
+}
+#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte sis_proc = 0;
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * ((id->hw_config & 0x2000) && (HWIF(drive)->udma_four))
+ */
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+	struct hd_driveid *id	= drive->id;
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+
+	byte			drive_pci, test1, test2, mask;
+	int			err;
+
+	unsigned long dma_base	= hwif->dma_base;
+	byte unit		= (drive->select.b.unit & 0x01);
+	byte speed		= 0x00, unmask = 0xE0, four_two = 0x00;
+	int drive_number	= ((hwif->channel ? 2 : 0) + unit);
+	byte udma_66		= ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+
+	if (host_dev) {
+		switch(host_dev->device) {
+			case PCI_DEVICE_ID_SI_530:
+			case PCI_DEVICE_ID_SI_540:
+			case PCI_DEVICE_ID_SI_620:
+			case PCI_DEVICE_ID_SI_630:
+				unmask   = 0xF0;
+				four_two = 0x01;
+			default:
+				break;
+		}
+	}
+
+	switch(drive_number) {
+		case 0:		drive_pci = 0x40;break;
+		case 1:		drive_pci = 0x42;break;
+		case 2:		drive_pci = 0x44;break;
+		case 3:		drive_pci = 0x46;break;
+		default:	return ide_dma_off;
+	}
+
+	pci_read_config_byte(dev, drive_pci, &test1);
+	pci_read_config_byte(dev, drive_pci|0x01, &test2);
+
+	if ((!ultra) && (test2 & 0x80)) {
+		pci_write_config_byte(dev, drive_pci|0x01, test2 & ~0x80);
+		pci_read_config_byte(dev, drive_pci|0x01, &test2);
+	}
+
+	if ((id->dma_ultra & 0x0010) && (ultra) && (udma_66) && (four_two)) {
+		if (!(test2 & 0x90)) {
+			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+			pci_write_config_byte(dev, drive_pci|0x01, test2|0x90);
+		}
+		speed = XFER_UDMA_4;
+	} else if ((id->dma_ultra & 0x0008) && (ultra) && (udma_66) && (four_two)) {
+		if (!(test2 & 0xA0)) {
+			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+			pci_write_config_byte(dev, drive_pci|0x01, test2|0xA0);
+		}
+		speed = XFER_UDMA_3;
+	} else if ((id->dma_ultra & 0x0004) && (ultra)) {
+		mask = (four_two) ? 0xB0 : 0xA0;
+		if (!(test2 & mask)) {
+			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+			pci_write_config_byte(dev, drive_pci|0x01, test2|mask);
+		}
+		speed = XFER_UDMA_2;
+	} else if ((id->dma_ultra & 0x0002) && (ultra)) {
+		mask = (four_two) ? 0xD0 : 0xC0;
+		if (!(test2 & mask)) {
+			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+			pci_write_config_byte(dev, drive_pci|0x01, test2|mask);
+		}
+		speed = XFER_UDMA_1;
+	} else if ((id->dma_ultra & 0x0001) && (ultra)) {
+		if (!(test2 & unmask)) {
+			pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+			pci_write_config_byte(dev, drive_pci|0x01, test2|unmask);
+		}
+		speed = XFER_UDMA_0;
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+	} else if (id->dma_1word & 0x0004) {
+		speed = XFER_SW_DMA_2;
+	} else if (id->dma_1word & 0x0002) {
+		speed = XFER_SW_DMA_1;
+	} else if (id->dma_1word & 0x0001) {
+		speed = XFER_SW_DMA_0;
+        } else {
+		return ((int) ide_dma_off_quietly);
+	}
+
+	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+	err = ide_config_drive_speed(drive, speed);
+
+#if SIS5513_DEBUG_DRIVE_INFO
+	printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number);
+#endif /* SIS5513_DEBUG_DRIVE_INFO */
+
+	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+}
+
+static void config_drive_art_rwp (ide_drive_t *drive)
+{
+	ide_hwif_t *hwif		= HWIF(drive);
+	struct pci_dev *dev		= hwif->pci_dev;
+
+	byte				timing, pio, drive_pci, test1, test2;
+
+	unsigned short eide_pio_timing[6] = {600, 390, 240, 180, 120, 90};
+	unsigned short xfer_pio		= drive->id->eide_pio_modes;
+	int drive_number		= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+
+	if (drive->media == ide_disk) {
+		struct pci_dev *dev	= hwif->pci_dev;
+		byte reg4bh		= 0;
+                byte rw_prefetch	= (0x11 << drive_number);
+
+		pci_read_config_byte(dev, 0x4b, &reg4bh);
+		if ((reg4bh & rw_prefetch) != rw_prefetch)
+			pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch);
+	}
+
+	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+/*
+ *               Mode 0       Mode 1     Mode 2     Mode 3     Mode 4
+ * Active time    8T (240ns)  6T (180ns) 4T (120ns) 3T  (90ns) 3T  (90ns)
+ * 0x41 2:0 bits  000          110        100        011        011
+ * Recovery time 12T (360ns)  7T (210ns) 4T (120ns) 3T  (90ns) 1T  (30ns)
+ * 0x40 3:0 bits 0000         0111       0100       0011       0001
+ * Cycle time    20T (600ns) 13T (390ns) 8T (240ns) 6T (180ns) 4T (120ns)
+ */
+
+	switch(drive_number) {
+		case 0:		drive_pci = 0x40;break;
+		case 1:		drive_pci = 0x42;break;
+		case 2:		drive_pci = 0x44;break;
+		case 3:		drive_pci = 0x46;break;
+		default:	return;
+	}
+
+	pci_read_config_byte(dev, drive_pci, &test1);
+	pci_read_config_byte(dev, drive_pci|0x01, &test2);
+
+	/*
+	 * Do a blanket clear of active and recovery timings.
+	 */
+
+	test1 &= ~0x07;
+	test2 &= ~0x0F;
+
+	switch(timing) {
+		case 4:		test1 |= 0x01;test2 |= 0x03;break;
+		case 3:		test1 |= 0x03;test2 |= 0x03;break;
+		case 2:		test1 |= 0x04;test2 |= 0x04;break;
+		case 1:		test1 |= 0x07;test2 |= 0x06;break;
+		default:	break;
+	}
+
+	pci_write_config_byte(dev, drive_pci, test1);
+	pci_write_config_byte(dev, drive_pci|0x01, test2);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+	struct hd_driveid *id		= drive->id;
+	ide_dma_action_t dma_func	= ide_dma_off_quietly;
+
+	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			return HWIF(drive)->dmaproc(ide_dma_off, drive);
+		}
+
+		if (id->field_valid & 4) {
+			if (id->dma_ultra & 0x001F) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive, 1);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if ((id->dma_mword & 0x0007) ||
+			    (id->dma_1word & 0x0007)) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive, 0);
+			}
+		} else if ((ide_dmaproc(ide_dma_good_drive, drive)) &&
+			   (id->eide_dma_time > 150)) {
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive, 0);
+		}
+	}
+	return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * sis5513_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+int sis5513_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			config_drive_art_rwp(drive);
+			return config_drive_xfer_rate(drive);
+		default:
+			break;
+	}
+	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name)
+{
+	struct pci_dev *host;
+	int i = 0;
+	byte latency = 0;
+
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
+
+	for (i = 0; i < arraysize (SiSHostChipInfo) && !host_dev; i++) {
+		host = pci_find_device (PCI_VENDOR_ID_SI,
+					SiSHostChipInfo[i].host_id,
+					NULL);
+		if (!host)
+			continue;
+
+		host_dev = host;
+		printk(SiSHostChipInfo[i].name);
+		printk("\n");
+		if (SiSHostChipInfo[i].flags & SIS5513_FLAG_LATENCY) {
+			if (latency != 0x10)
+				pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x10);
+		}
+	}
+
+	if (host_dev) {
+		byte reg52h = 0;
+
+		pci_read_config_byte(dev, 0x52, &reg52h);
+		if (!(reg52h & 0x04)) {
+			/* set IDE controller to operate in Compabitility mode obly */
+			pci_write_config_byte(dev, 0x52, reg52h|0x04);
+		}
+#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
+		sis_proc = 1;
+		bmide_dev = dev;
+		sis_display_info = &sis_get_info;
+#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */
+	}
+	return 0;
+}
+
+unsigned int __init ata66_sis5513 (ide_hwif_t *hwif)
+{
+	byte reg48h = 0, ata66 = 0;
+	byte mask = hwif->channel ? 0x20 : 0x10;
+	pci_read_config_byte(hwif->pci_dev, 0x48, &reg48h);
+
+	if (host_dev) {
+		switch(host_dev->device) {
+			case PCI_DEVICE_ID_SI_530:
+			case PCI_DEVICE_ID_SI_540:
+			case PCI_DEVICE_ID_SI_620:
+			case PCI_DEVICE_ID_SI_630:
+				ata66 = (reg48h & mask) ? 0 : 1;
+			default:
+				break;
+		}
+	}
+        return (ata66);
+}
+
+void __init ide_init_sis5513 (ide_hwif_t *hwif)
+{
+
+	hwif->irq = hwif->channel ? 15 : 14;
+
+	if (!(hwif->dma_base))
+		return;
+
+	if (host_dev) {
+		switch(host_dev->device) {
+			case PCI_DEVICE_ID_SI_530:
+			case PCI_DEVICE_ID_SI_540:
+			case PCI_DEVICE_ID_SI_620:
+			case PCI_DEVICE_ID_SI_630:
+			case PCI_DEVICE_ID_SI_5600:
+			case PCI_DEVICE_ID_SI_5597:
+			case PCI_DEVICE_ID_SI_5591:
+				hwif->autodma = 1;
+				hwif->dmaproc = &sis5513_dmaproc;
+				break;
+			default:
+				hwif->autodma = 0;
+				break;
+		}
+	}
+	return;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/sl82c105.c linux/drivers/ide/sl82c105.c
--- v2.3.51/linux/drivers/ide/sl82c105.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/sl82c105.c	Mon Feb 28 07:20:23 2000
@@ -0,0 +1,138 @@
+/*
+ * linux/drivers/block/sl82c105.c
+ *
+ * SL82C105/Winbond 553 IDE driver
+ *
+ * Maintainer unknown.
+ *
+ * Drive tuning added from Rebel.com's kernel sources
+ *  -- Russell King (15/11/98) linux@arm.linux.org.uk
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "ide_modes.h"
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+#ifdef CONFIG_ARCH_NETWINDER
+/*
+ * Convert a PIO mode and cycle time to the required on/off
+ * times for the interface.  This has protection against run-away
+ * timings.
+ */
+static unsigned int get_timing_sl82c105(ide_pio_data_t *p)
+{
+	unsigned int cmd_on;
+	unsigned int cmd_off;
+
+	cmd_on = (ide_pio_timings[p->pio_mode].active_time + 29) / 30;
+	cmd_off = (p->cycle_time - 30 * cmd_on + 29) / 30;
+
+	if (cmd_on > 32)
+		cmd_on = 32;
+	if (cmd_on == 0)
+		cmd_on = 1;
+
+	if (cmd_off > 32)
+		cmd_off = 32;
+	if (cmd_off == 0)
+		cmd_off = 1;
+
+	return (cmd_on - 1) << 8 | (cmd_off - 1) | (p->use_iordy ? 0x40 : 0x00);
+}
+
+/*
+ * We only deal with PIO mode here - DMA mode 'using_dma' is not
+ * initialised at the point that this function is called.
+ */
+static void tune_sl82c105(ide_drive_t *drive, byte pio)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	struct pci_dev *dev = hwif->pci_dev;
+	ide_pio_data_t p;
+	unsigned short drv_ctrl = 0x909;
+	unsigned int xfer_mode, reg;
+
+	reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0);
+
+	pio = ide_get_best_pio_mode(drive, pio, 5, &p);
+
+	switch (pio) {
+	default:
+	case 0:		xfer_mode = XFER_PIO_0;		break;
+	case 1:		xfer_mode = XFER_PIO_1;		break;
+	case 2:		xfer_mode = XFER_PIO_2;		break;
+	case 3:		xfer_mode = XFER_PIO_3;		break;
+	case 4:		xfer_mode = XFER_PIO_4;		break;
+	}
+
+	if (ide_config_drive_speed(drive, xfer_mode) == 0)
+		drv_ctrl = get_timing_sl82c105(&p);
+
+	pci_write_config_word(dev, reg, drv_ctrl);
+	pci_read_config_word(dev, reg, &drv_ctrl);
+
+	printk("%s: selected %s (%dns) (%04X)\n", drive->name,
+	       ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl);
+}
+#endif
+
+void ide_dmacapable_sl82c105(ide_hwif_t *hwif, unsigned long dmabase)
+{
+	unsigned char rev;
+
+	pci_read_config_byte(hwif->pci_dev, PCI_REVISION_ID, &rev);
+
+	if (rev <= 5) {
+		hwif->autodma = 0;
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+		printk("    %s: revision %d, Bus-Master DMA disabled\n",
+		       hwif->name, rev);
+	}
+	ide_setup_dma(hwif, dmabase, 8);
+}
+
+void ide_init_sl82c105(ide_hwif_t *hwif)
+{
+	struct pci_dev *dev = hwif->pci_dev;
+
+#ifdef CONFIG_ARCH_NETWINDER
+	unsigned char ctrl_stat;
+
+	pci_read_config_byte(dev, 0x40, &ctrl_stat);
+	pci_write_config_byte(dev, 0x40, ctrl_stat | 0x33);
+
+	hwif->tuneproc = tune_sl82c105;
+#else
+	unsigned short t16;
+	unsigned int t32;
+	pci_read_config_word(dev, PCI_COMMAND, &t16);
+	printk("SL82C105 command word: %x\n",t16);
+        t16 |= PCI_COMMAND_IO;
+        pci_write_config_word(dev, PCI_COMMAND, t16);
+	/* IDE timing */
+	pci_read_config_dword(dev, 0x44, &t32);
+	printk("IDE timing: %08x, resetting to PIO0 timing\n",t32);
+	pci_write_config_dword(dev, 0x44, 0x03e4);
+#ifndef CONFIG_MBX
+	pci_read_config_dword(dev, 0x40, &t32);
+	printk("IDE control/status register: %08x\n",t32);
+	pci_write_config_dword(dev, 0x40, 0x10ff08a1);
+#endif /* CONFIG_MBX */
+#endif
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/trm290.c linux/drivers/ide/trm290.c
--- v2.3.51/linux/drivers/ide/trm290.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/trm290.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,282 @@
+/*
+ *  linux/drivers/block/trm290.c		Version 1.01	December 5, 1997
+ *
+ *  Copyright (c) 1997-1998  Mark Lord
+ *  May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for the bus-master IDE DMA function
+ * of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards,
+ * including a "Precision Instruments" board.  The TRM290 pre-dates
+ * the sff-8038 standard (ide-dma.c) by a few months, and differs
+ * significantly enough to warrant separate routines for some functions,
+ * while re-using others from ide-dma.c.
+ *
+ * EXPERIMENTAL!  It works for me (a sample of one).
+ *
+ * Works reliably for me in DMA mode (READs only),
+ * DMA WRITEs are disabled by default (see #define below);
+ *
+ * DMA is not enabled automatically for this chipset,
+ * but can be turned on manually (with "hdparm -d1") at run time.
+ *
+ * I need volunteers with "spare" drives for further testing
+ * and development, and maybe to help figure out the peculiarities.
+ * Even knowing the registers (below), some things behave strangely.
+ */
+
+#define TRM290_NO_DMA_WRITES	/* DMA writes seem unreliable sometimes */
+
+/*
+ * TRM-290 PCI-IDE2 Bus Master Chip
+ * ================================
+ * The configuration registers are addressed in normal I/O port space
+ * and are used as follows:
+ *
+ * trm290_base depends on jumper settings, and is probed for by ide-dma.c
+ *
+ * trm290_base+2 when WRITTEN: chiptest register (byte, write-only)
+ *	bit7 must always be written as "1"
+ *	bits6-2 undefined
+ *	bit1 1=legacy_compatible_mode, 0=native_pci_mode
+ *	bit0 1=test_mode, 0=normal(default)
+ *
+ * trm290_base+2 when READ: status register (byte, read-only)
+ *	bits7-2 undefined
+ *	bit1 channel0 busmaster interrupt status 0=none, 1=asserted
+ *	bit0 channel0 interrupt status 0=none, 1=asserted
+ *
+ * trm290_base+3 Interrupt mask register
+ *	bits7-5 undefined
+ *	bit4 legacy_header: 1=present, 0=absent
+ *	bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only)
+ *	bit2 channel1 interrupt status 0=none, 1=asserted (read only)
+ *	bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default)
+ *	bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default)
+ *
+ * trm290_base+1 "CPR" Config Pointer Register (byte)
+ *	bit7 1=autoincrement CPR bits 2-0 after each access of CDR
+ *	bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state
+ *	bit5 0=enabled master burst access (default), 1=disable  (write only)
+ *	bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast
+ *	bit3 0=primary IDE channel, 1=secondary IDE channel
+ *	bits2-0 register index for accesses through CDR port
+ *
+ * trm290_base+0 "CDR" Config Data Register (word)
+ *	two sets of seven config registers,
+ *	selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6),
+ *	each index defined below:
+ *
+ * Index-0 Base address register for command block (word)
+ *	defaults: 0x1f0 for primary, 0x170 for secondary
+ *
+ * Index-1 general config register (byte)
+ *	bit7 1=DMA enable, 0=DMA disable
+ *	bit6 1=activate IDE_RESET, 0=no action (default)
+ *	bit5 1=enable IORDY, 0=disable IORDY (default)
+ *	bit4 0=16-bit data port(default), 1=8-bit (XT) data port
+ *	bit3 interrupt polarity: 1=active_low, 0=active_high(default)
+ *	bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only)
+ *	bit1 bus_master_mode(?): 1=enable, 0=disable(default)
+ *	bit0 enable_io_ports: 1=enable(default), 0=disable
+ *
+ * Index-2 read-ahead counter preload bits 0-7 (byte, write only)
+ *	bits7-0 bits7-0 of readahead count
+ *
+ * Index-3 read-ahead config register (byte, write only)
+ *	bit7 1=enable_readahead, 0=disable_readahead(default)
+ *	bit6 1=clear_FIFO, 0=no_action
+ *	bit5 undefined
+ *	bit4 mode4 timing control: 1=enable, 0=disable(default)
+ *	bit3 undefined
+ *	bit2 undefined
+ *	bits1-0 bits9-8 of read-ahead count
+ *
+ * Index-4 base address register for control block (word)
+ *	defaults: 0x3f6 for primary, 0x376 for secondary
+ *
+ * Index-5 data port timings (shared by both drives) (byte)
+ *	standard PCI "clk" (clock) counts, default value = 0xf5
+ *
+ *	bits7-6 setup time:  00=1clk, 01=2clk, 10=3clk, 11=4clk
+ *	bits5-3 hold time:	000=1clk, 001=2clk, 010=3clk,
+ *				011=4clk, 100=5clk, 101=6clk,
+ *				110=8clk, 111=12clk
+ *	bits2-0 active time:	000=2clk, 001=3clk, 010=4clk,
+ *				011=5clk, 100=6clk, 101=8clk,
+ *				110=12clk, 111=16clk
+ *
+ * Index-6 command/control port timings (shared by both drives) (byte)
+ *	same layout as Index-5, default value = 0xde
+ *
+ * Suggested CDR programming for PIO mode0 (600ns):
+ *	0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde	; primary
+ *	0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde	; secondary
+ *
+ * Suggested CDR programming for PIO mode3 (180ns):
+ *	0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde	; primary
+ *	0x0170,0x21,0xff,0x80,0x0376,0x09,0xde	; secondary
+ *
+ * Suggested CDR programming for PIO mode4 (120ns):
+ *	0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde	; primary
+ *	0x0170,0x21,0xff,0x80,0x0376,0x00,0xde	; secondary
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned int reg;
+	unsigned long flags;
+
+	/* select PIO or DMA */
+	reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82);
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+
+	if (reg != hwif->select_data) {
+		hwif->select_data = reg;
+		outb(0x51|(hwif->channel<<3), hwif->config_data+1);	/* set PIO/DMA */
+		outw(reg & 0xff, hwif->config_data);
+	}
+
+	/* enable IRQ if not probing */
+	if (drive->present) {
+		reg = inw(hwif->config_data+3) & 0x13;
+		reg &= ~(1 << hwif->channel);
+		outw(reg, hwif->config_data+3);
+	}
+
+	__restore_flags(flags);	/* local CPU only */
+}
+
+static void trm290_selectproc (ide_drive_t *drive)
+{
+	trm290_prepare_drive(drive, drive->using_dma);
+}
+
+static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned int count, reading = 2, writing = 0;
+
+	switch (func) {
+		case ide_dma_write:
+			reading = 0;
+			writing = 1;
+#ifdef TRM290_NO_DMA_WRITES
+			break;	/* always use PIO for writes */
+#endif
+		case ide_dma_read:
+			if (!(count = ide_build_dmatable(drive, func)))
+				break;		/* try PIO instead of DMA */
+			trm290_prepare_drive(drive, 1);	/* select DMA xfer */
+			outl(hwif->dmatable_dma|reading|writing, hwif->dma_base);
+			drive->waiting_for_dma = 1;
+			outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */
+			if (drive->media != ide_disk)
+				return 0;
+			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
+			OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+			return 0;
+		case ide_dma_begin:
+			return 0;
+		case ide_dma_end:
+			drive->waiting_for_dma = 0;
+			ide_destroy_dmatable(drive);		/* purge DMA mappings */
+			return (inw(hwif->dma_base+2) != 0x00ff);
+		case ide_dma_test_irq:
+			return (inw(hwif->dma_base+2) == 0x00ff);
+		default:
+			return ide_dmaproc(func, drive);
+	}
+	trm290_prepare_drive(drive, 0);	/* select PIO xfer */
+	return 1;
+}
+
+/*
+ * Invoked from ide-dma.c at boot time.
+ */
+void __init ide_init_trm290 (ide_hwif_t *hwif)
+{
+	unsigned int cfgbase = 0;
+	unsigned long flags;
+	byte reg;
+	struct pci_dev *dev = hwif->pci_dev;
+
+	hwif->chipset = ide_trm290;
+	cfgbase = dev->resource[4].start;
+	if ((dev->class & 5) && cfgbase)
+	{
+		hwif->config_data = cfgbase & PCI_BASE_ADDRESS_IO_MASK;
+		printk("TRM290: chip config base at 0x%04lx\n", hwif->config_data);
+	} else {
+		hwif->config_data = 0x3df0;
+		printk("TRM290: using default config base at 0x%04lx\n", hwif->config_data);
+	}
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	/* put config reg into first byte of hwif->select_data */
+	outb(0x51|(hwif->channel<<3), hwif->config_data+1);
+	hwif->select_data = 0x21;			/* select PIO as default */
+	outb(hwif->select_data, hwif->config_data);
+	reg = inb(hwif->config_data+3);			/* get IRQ info */
+	reg = (reg & 0x10) | 0x03;			/* mask IRQs for both ports */
+	outb(reg, hwif->config_data+3);
+	__restore_flags(flags);	/* local CPU only */
+
+	if ((reg & 0x10))
+		hwif->irq = hwif->channel ? 15 : 14;	/* legacy mode */
+	else if (!hwif->irq && hwif->mate && hwif->mate->irq)
+		hwif->irq = hwif->mate->irq;		/* sharing IRQ with mate */
+	ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3);
+	hwif->dmaproc = &trm290_dmaproc;
+	hwif->selectproc = &trm290_selectproc;
+	hwif->autodma = 0;				/* play it safe for now */
+#if 1
+	{
+		/*
+		 * My trm290-based card doesn't seem to work with all possible values
+		 * for the control basereg, so this kludge ensures that we use only
+		 * values that are known to work.  Ugh.		-ml
+		 */
+		unsigned short old, compat = hwif->channel ? 0x374 : 0x3f4;
+		static unsigned short next_offset = 0;
+
+		outb(0x54|(hwif->channel<<3), hwif->config_data+1);
+		old = inw(hwif->config_data) & ~1;
+		if (old != compat && inb(old+2) == 0xff) {
+			compat += (next_offset += 0x400);	/* leave lower 10 bits untouched */
+#if 1
+			if (ide_check_region(compat + 2, 1))
+				printk("Aieee %s: ide_check_region failure at 0x%04x\n", hwif->name, (compat + 2));
+			/*
+			 * The region check is not needed; however.........
+			 * Since this is the checked in ide-probe.c,
+			 * this is only an assignment.
+			 */
+#endif
+			hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2;
+			outw(compat|1, hwif->config_data);
+			printk("%s: control basereg workaround: old=0x%04x, new=0x%04x\n", hwif->name, old, inw(hwif->config_data) & ~1);
+		}
+	}
+#endif
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/umc8672.c linux/drivers/ide/umc8672.c
--- v2.3.51/linux/drivers/ide/umc8672.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/umc8672.c	Sat Feb 26 20:32:14 2000
@@ -0,0 +1,159 @@
+/*
+ *  linux/drivers/block/umc8672.c		Version 0.05	Jul 31, 1996
+ *
+ *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
+ */
+
+/*
+ *  Principal Author/Maintainer:  PODIEN@hml2.atlas.de (Wolfram Podien)
+ *
+ *  This file provides support for the advanced features
+ *  of the UMC 8672 IDE interface.
+ *
+ *  Version 0.01	Initial version, hacked out of ide.c,
+ *			and #include'd rather than compiled separately.
+ *			This will get cleaned up in a subsequent release.
+ *
+ *  Version 0.02	now configs/compiles separate from ide.c  -ml
+ *  Version 0.03	enhanced auto-tune, fix display bug
+ *  Version 0.05	replace sti() with restore_flags()  -ml
+ *			add detection of possible race condition  -ml
+ */
+
+/*
+ * VLB Controller Support from 
+ * Wolfram Podien
+ * Rohoefe 3
+ * D28832 Achim
+ * Germany
+ *
+ * To enable UMC8672 support there must a lilo line like
+ * append="ide0=umc8672"...
+ * To set the speed according to the abilities of the hardware there must be a
+ * line like
+ * #define UMC_DRIVE0 11
+ * in the beginning of the driver, which sets the speed of drive 0 to 11 (there
+ * are some lines present). 0 - 11 are allowed speed values. These values are
+ * the results from the DOS speed test program supplied from UMC. 11 is the 
+ * highest speed (about PIO mode 3)
+ */
+#define REALLY_SLOW_IO		/* some systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/*
+ * Default speeds.  These can be changed with "auto-tune" and/or hdparm.
+ */
+#define UMC_DRIVE0      1              /* DOS measured drive speeds */
+#define UMC_DRIVE1      1              /* 0 to 11 allowed */
+#define UMC_DRIVE2      1              /* 11 = Fastest Speed */
+#define UMC_DRIVE3      1              /* In case of crash reduce speed */
+
+static byte current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
+static const byte pio_to_umc [5] = {0,3,7,10,11};	/* rough guesses */
+
+/*       0    1    2    3    4    5    6    7    8    9    10   11      */
+static const byte speedtab [3][12] = {
+	{0xf, 0xb, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+	{0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+	{0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}};
+
+static void out_umc (char port,char wert)
+{
+	outb_p (port,0x108);
+	outb_p (wert,0x109);
+}
+
+static inline byte in_umc (char port)
+{
+	outb_p (port,0x108);
+	return inb_p (0x109);
+}
+
+static void umc_set_speeds (byte speeds[])
+{
+	int i, tmp;
+
+	outb_p (0x5A,0x108); /* enable umc */
+
+	out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
+	out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
+	tmp = 0;
+	for (i = 3; i >= 0; i--)
+	{
+		tmp = (tmp << 2) | speedtab[1][speeds[i]];
+	}
+	out_umc (0xdc,tmp);
+	for (i = 0;i < 4; i++)
+	{
+		out_umc (0xd0+i,speedtab[2][speeds[i]]);
+		out_umc (0xd8+i,speedtab[2][speeds[i]]);
+	}
+	outb_p (0xa5,0x108); /* disable umc */
+
+	printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
+		speeds[0], speeds[1], speeds[2], speeds[3]);
+}
+
+static void tune_umc (ide_drive_t *drive, byte pio)
+{
+	unsigned long flags;
+	ide_hwgroup_t *hwgroup = ide_hwifs[HWIF(drive)->index^1].hwgroup;
+
+	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+	printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", drive->name, pio, pio_to_umc[pio]);
+	save_flags(flags);	/* all CPUs */
+	cli();			/* all CPUs */
+	if (hwgroup && hwgroup->handler != NULL) {
+		printk("umc8672: other interface is busy: exiting tune_umc()\n");
+	} else {
+		current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
+		umc_set_speeds (current_speeds);
+	}
+	restore_flags(flags);	/* all CPUs */
+}
+
+void __init init_umc8672 (void)	/* called from ide.c */
+{
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	if (check_region(0x108, 2)) {
+		__restore_flags(flags);
+		printk("\numc8672: PORTS 0x108-0x109 ALREADY IN USE\n");
+		return;
+	}
+	outb_p (0x5A,0x108); /* enable umc */
+	if (in_umc (0xd5) != 0xa0)
+	{
+		__restore_flags(flags);	/* local CPU only */
+		printk ("umc8672: not found\n");
+		return;  
+	}
+	outb_p (0xa5,0x108); /* disable umc */
+
+	umc_set_speeds (current_speeds);
+	__restore_flags(flags);	/* local CPU only */
+
+	request_region(0x108, 2, "umc8672");
+	ide_hwifs[0].chipset = ide_umc8672;
+	ide_hwifs[1].chipset = ide_umc8672;
+	ide_hwifs[0].tuneproc = &tune_umc;
+	ide_hwifs[1].tuneproc = &tune_umc;
+	ide_hwifs[0].mate = &ide_hwifs[1];
+	ide_hwifs[1].mate = &ide_hwifs[0];
+	ide_hwifs[1].channel = 1;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/ide/via82cxxx.c linux/drivers/ide/via82cxxx.c
--- v2.3.51/linux/drivers/ide/via82cxxx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/ide/via82cxxx.c	Wed Mar  8 11:40:25 2000
@@ -0,0 +1,834 @@
+/*
+ * linux/drivers/block/via82cxxx.c	Version 0.07	Feb. 10, 2000
+ *
+ *  Copyright (C) 1998-99	Michel Aubry, Maintainer
+ *  Copyright (C) 1999		Jeff Garzik, MVP4 Support
+ *					(jgarzik@mandrakesoft.com)
+ *  Copyright (C) 1998-2000	Andre Hedrick (andre@suse.com)
+ *  May be copied or modified under the terms of the GNU General Public License
+ *
+ *  The VIA MVP-4 is reported OK with UDMA.
+ *  The VIA MVP-3 is reported OK with UDMA.
+ *  The TX Pro III is also reported OK with UDMA.
+ *
+ *  VIA chips also have a single FIFO, with the same 64 bytes deep
+ *  buffer (16 levels of 4 bytes each).
+ *
+ *  However, VIA chips can have the buffer split either 8:8 levels,
+ *  16:0 levels or 0:16 levels between both channels. One could think
+ *  of using this feature, as even if no level of FIFO is given to a
+ *  given channel, one can for instance always reach ATAPI drives through
+ *  it, or, if one channel is unused, configuration defaults to
+ *  an even split FIFO levels.
+ *  
+ *  This feature is available only through a kernel command line :
+ *		"splitfifo=Chan,Thr0,Thr1" or "splitfifo=Chan".
+ *		where:  Chan =1,2,3 or 4 and Thrx = 1,2,3,or 4.
+ *
+ *  If Chan == 1:
+ *	gives all the fifo to channel 0,
+ *	sets its threshold to Thr0/4,
+ *	and disables any dma access to channel 1.
+ *
+ *  If chan == 2:
+ *	gives all the fifo to channel 1,
+ *	sets its threshold to Thr1/4,
+ *	and disables any dma access to channel 0.
+ *
+ *  If chan == 3 or 4:
+ *	shares evenly fifo between channels,
+ *	gives channel 0 a threshold of Thr0/4,
+ *	and channel 1 a threshold of Thr1/4.
+ *
+ *  Note that by default (if no command line is provided) and if a channel
+ *  has been disabled in Bios, all the fifo is given to the active channel,
+ *  and its threshold is set to 3/4.
+ *
+ *  VT82c586B
+ *
+ *    Offset 4B-48 - Drive Timing Control
+ *             | pio0 | pio1 | pio2 | pio3 | pio4
+ *    25.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20 
+ *    33.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ *    37.5 MHz | 0xA9 | 0x76 | 0x76 | 0x32 | 0x21
+ *
+ *    Offset 53-50 - UltraDMA Extended Timing Control
+ *      UDMA   |  NO  |   0  |   1  |   2
+ *             | 0x03 | 0x62 | 0x61 | 0x60
+ *
+ *  VT82c596B & VT82c686A
+ *
+ *    Offset 4B-48 - Drive Timing Control
+ *             | pio0 | pio1 | pio2 | pio3 | pio4
+ *    25.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ *    33.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ *    37.5 MHz | 0xDB | 0x87 | 0x87 | 0x42 | 0x31
+ *    41.5 MHz | 0xFE | 0xA8 | 0xA8 | 0x53 | 0x32
+ *
+ *    Offset 53-50 - UltraDMA Extended Timing Control
+ *      UDMA   |  NO  |   0  |   1  |   2
+ *    33.0 MHz | 0x03 | 0xE2 | 0xE1 | 0xE0
+ *    37.5 MHz | 0x03 | 0xE2 | 0xE2 | 0xE1   (1)
+ *
+ *    Offset 53-50 - UltraDMA Extended Timing Control
+ *      UDMA   |  NO  |   0  |   1  |   2  |   3  |   4
+ *    33.0 MHz |  (2) | 0xE6 | 0xE4 | 0xE2 | 0xE1 | 0xE0
+ *    37.5 MHz |  (2) | 0xE6 | 0xE6 | 0xE4 | 0xE2 | 0xE1   (1)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+static struct pci_dev *host_dev = NULL;
+static struct pci_dev *isa_dev = NULL;
+
+static const struct {
+	const char *name;
+	unsigned short host_id;
+} ApolloHostChipInfo[] = {
+	{ "VT 82C585 Apollo VP1/VPX",	PCI_DEVICE_ID_VIA_82C585, },
+	{ "VT 82C595 Apollo VP2",	PCI_DEVICE_ID_VIA_82C595, },
+	{ "VT 82C597 Apollo VP3",	PCI_DEVICE_ID_VIA_82C597_0, },
+	{ "VT 82C598 Apollo MVP3",	PCI_DEVICE_ID_VIA_82C598_0, },
+	{ "VT 82C680 Apollo P6",	PCI_DEVICE_ID_VIA_82C680, },
+	{ "VT 82C691 Apollo Pro",	PCI_DEVICE_ID_VIA_82C691, },
+	{ "VT 82C693 Apollo Pro Plus",	PCI_DEVICE_ID_VIA_82C693, },
+	{ "Apollo MVP4",		PCI_DEVICE_ID_VIA_8501_0, },
+	{ "VT 8371",			PCI_DEVICE_ID_VIA_8371_0, },
+	{ "VT 8601",			PCI_DEVICE_ID_VIA_8601_0, },
+};
+
+#define NUM_APOLLO_ISA_CHIP_DEVICES	2
+#define VIA_FLAG_CHECK_REV		0x00000001
+#define VIA_FLAG_ATA_66			0x00000002
+
+static const struct {
+	unsigned short host_id;
+	unsigned short isa_id;
+	unsigned int flags;
+} ApolloISAChipInfo[] = {
+	{ PCI_DEVICE_ID_VIA_82C585,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
+	{ PCI_DEVICE_ID_VIA_82C595,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
+	{ PCI_DEVICE_ID_VIA_82C597_0,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
+	{ PCI_DEVICE_ID_VIA_82C598_0,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
+	{ PCI_DEVICE_ID_VIA_82C598_0,	PCI_DEVICE_ID_VIA_82C596,	0 },
+	{ PCI_DEVICE_ID_VIA_82C680,	PCI_DEVICE_ID_VIA_82C586_1,	VIA_FLAG_CHECK_REV },
+	{ PCI_DEVICE_ID_VIA_82C691,	PCI_DEVICE_ID_VIA_82C596,	0 },
+	{ PCI_DEVICE_ID_VIA_82C693,	PCI_DEVICE_ID_VIA_82C596,	0 },
+	{ PCI_DEVICE_ID_VIA_8501_0,	PCI_DEVICE_ID_VIA_82C686,	VIA_FLAG_ATA_66 },
+	{ PCI_DEVICE_ID_VIA_8371_0,	PCI_DEVICE_ID_VIA_82C686,	VIA_FLAG_ATA_66 },
+	{ PCI_DEVICE_ID_VIA_8601_0,	PCI_DEVICE_ID_VIA_8231,		VIA_FLAG_ATA_66 },
+};
+
+#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
+
+#define DISPLAY_VIA_TIMINGS
+
+#if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static char *FIFO_str[] = {
+	" 1 ",
+	"3/4",
+	"1/2",
+	"1/4"
+};
+
+static char *control3_str[] = {
+	"No limitation",
+	"64",
+	"128",
+	"192"
+};
+
+static int via_get_info(char *, char **, off_t, int);
+extern int (*via_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+static struct pci_dev *bmide_dev;
+
+static char * print_apollo_drive_config (char *buf, struct pci_dev *dev)
+{
+	int rc;
+	unsigned int time;
+	byte tm;
+	char *p = buf;  
+ 
+	/* Drive Timing Control */
+	rc = pci_read_config_dword(dev, 0x48, &time);
+	p += sprintf(p, "Act Pls Width:  %02d          %02d           %02d          %02d\n",
+			((time & 0xf0000000)>>28) + 1,
+			((time & 0xf00000)>>20) + 1,
+			((time & 0xf000)>>12) + 1,
+			((time & 0xf0)>>4) + 1 );
+	p += sprintf(p, "Recovery Time:  %02d          %02d           %02d          %02d\n",
+			((time & 0x0f000000)>>24) + 1,
+			((time & 0x0f0000)>>16) + 1,
+			((time & 0x0f00)>>8) + 1,
+			(time & 0x0f) + 1 );
+ 
+	/* Address Setup Time */
+	rc = pci_read_config_byte(dev, 0x4C, &tm);
+	p += sprintf(p, "Add. Setup T.:  %01dT          %01dT           %01dT          %01dT\n",
+			((tm & 0xc0)>>6) + 1,
+			((tm & 0x30)>>4) + 1,
+			((tm & 0x0c)>>2) + 1,
+			(tm & 0x03) + 1 );
+ 
+	/* UltraDMA33 Extended Timing Control */
+ 	rc = pci_read_config_dword(dev, 0x50, &time);
+ 	p += sprintf(p, "------------------UDMA-Timing-Control------------------------\n");
+ 	p += sprintf(p, "Enable Meth.:    %01d           %01d            %01d           %01d\n",
+			(time & 0x80000000)	? 1 : 0,
+			(time & 0x800000)	? 1 : 0,
+			(time & 0x8000)		? 1 : 0,
+			(time & 0x80)		? 1 : 0 );
+ 	p += sprintf(p, "Enable:         %s         %s          %s         %s\n",
+			(time & 0x40000000)	? "yes" : "no ",
+			(time & 0x400000)	? "yes" : "no ",
+			(time & 0x4000)		? "yes" : "no ",
+			(time & 0x40)		? "yes" : "no " );
+ 	p += sprintf(p, "Transfer Mode: %s         %s          %s         %s\n",
+			(time & 0x20000000)	? "PIO" : "DMA",
+			(time & 0x200000)	? "PIO" : "DMA",
+			(time & 0x2000)		? "PIO" : "DMA",
+			(time & 0x20)		? "PIO" : "DMA" );
+ 	p += sprintf(p, "Cycle Time:     %01dT          %01dT           %01dT          %01dT\n",
+			((time & 0x03000000)>>24) + 2,
+			((time & 0x030000)>>16) + 2,
+			((time & 0x0300)>>8) + 2,
+			(time & 0x03) + 2 );
+ 
+ 	return (char *)p;
+}
+
+static char * print_apollo_ide_config (char *buf, struct pci_dev *dev)
+{
+	byte time, tmp; 
+	unsigned short size0, size1;
+	int rc;
+	char *p = buf;  
+ 
+	rc = pci_read_config_byte(dev, 0x41, &time);
+	p += sprintf(p, "Prefetch Buffer :      %s                     %s\n",
+		(time & 128)	? "on " : "off",
+		(time & 32)	? "on " : "off" );
+	p += sprintf(p, "Post Write Buffer:     %s                     %s\n",
+		(time & 64)	? "on " : "off",
+		(time & 16)	? "on " : "off" );
+ 
+	/* FIFO configuration */
+	rc = pci_read_config_byte(dev, 0x43, &time);
+	tmp = ((time & 0x20)>>2) + ((time & 0x40)>>3);
+	p += sprintf(p, "FIFO Conf/Chan. :      %02d                      %02d\n",
+			16 - tmp, tmp);
+	tmp = (time & 0x0F)>>2;
+	p += sprintf(p, "Threshold Prim. :      %s                     %s\n",
+			FIFO_str[tmp],
+			FIFO_str[time & 0x03] );
+ 
+	/* chipset Control3 */
+	rc = pci_read_config_byte(dev, 0x46, &time);
+	p += sprintf(p, "Read DMA FIFO flush:   %s                     %s\n",
+			(time & 0x80)	? "on " : "off",
+			(time & 0x40)	? "on " : "off" );
+	p += sprintf(p, "End Sect. FIFO flush:  %s                     %s\n",
+			(time & 0x20)	? "on " : "off",
+			(time & 0x10)	? "on " : "off" );
+	p += sprintf(p, "Max DRDY Pulse Width:  %s %s\n",
+			control3_str[(time & 0x03)],
+			(time & 0x03) ? "PCI clocks" : "" );
+ 
+	/* Primary and Secondary sector sizes */
+	rc = pci_read_config_word(dev, 0x60, &size0);
+	rc = pci_read_config_word(dev, 0x68, &size1);
+	p += sprintf(p, "Bytes Per Sector:      %03d                     %03d\n",
+			size0 & 0xfff,
+			size1 & 0xfff );
+
+	return (char *)p;
+}
+
+static char * print_apollo_chipset_control1 (char *buf, struct pci_dev *dev)
+{
+	byte t;
+	int rc;
+	char *p = buf;  
+	unsigned short c;
+	byte l, l_max;   
+ 
+	rc = pci_read_config_word(dev, 0x04, &c);
+	rc = pci_read_config_byte(dev, 0x44, &t);
+	rc = pci_read_config_byte(dev, 0x0d, &l);
+	rc = pci_read_config_byte(dev, 0x3f, &l_max);
+
+	p += sprintf(p, "Command register = 0x%x\n", c);
+	p += sprintf(p, "Master Read  Cycle IRDY %d Wait State\n",
+			(t & 64) >>6 );
+	p += sprintf(p, "Master Write Cycle IRDY %d Wait State\n",
+			(t & 32) >> 5 );
+	p += sprintf(p, "FIFO Output Data 1/2 Clock Advance: %s\n",
+			(t & 16) ? "on " : "off" );
+	p += sprintf(p, "Bus Master IDE Status Register Read Retry: %s\n",
+			(t & 8) ? "on " : "off" );
+	p += sprintf(p, "Latency timer = %d (max. = %d)\n",
+			l, l_max);
+
+	return (char *)p;
+}
+ 
+static char * print_apollo_chipset_control2 (char *buf, struct pci_dev *dev)
+{
+	byte t;
+	int rc;
+	char *p = buf;  
+	rc = pci_read_config_byte(dev, 0x45, &t);
+	p += sprintf(p, "Interrupt Steering Swap: %s\n",
+			(t & 64) ? "on ":"off" );
+
+	return (char *)p;
+}
+ 
+static char * print_apollo_chipset_control3 (char *buf, struct pci_dev *dev,
+						unsigned short n)
+{
+	/*
+	 * at that point we can be sure that register 0x20 of the
+	 * chipset contains the right address...
+	 */
+	unsigned int bibma;
+	int rc;
+	byte c0, c1;    
+	char *p = buf; 
+ 
+	rc = pci_read_config_dword(dev, 0x20, &bibma);
+	bibma = (bibma & 0xfff0) ;
+ 
+	/*
+	 * at that point bibma+0x2 et bibma+0xa are byte registers
+	 * to investigate:
+	 */
+	c0 = inb((unsigned short)bibma + 0x02);
+	c1 = inb((unsigned short)bibma + 0x0a);
+ 
+	if (n == 0) {
+        /*p = sprintf(p,"--------------------Primary IDE------------Secondary IDE-----");*/
+		p += sprintf(p, "both channels togth:   %s                     %s\n",
+				(c0&0x80) ? "no" : "yes",
+				(c1&0x80) ? "no" : "yes" );
+	} else {
+        /*p = sprintf(p,"--------------drive0------drive1-------drive0------drive1----");*/
+		p += sprintf(p, "DMA enabled:    %s         %s          %s         %s\n",
+				(c0&0x20) ? "yes" : "no ",
+				(c0&0x40) ? "yes" : "no ",
+				(c1&0x20) ? "yes" : "no ",
+				(c1&0x40) ? "yes" : "no " );
+	}
+ 
+	return (char *)p;
+}
+
+static int via_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+	/*
+	 * print what /proc/via displays,
+	 * if required from DISPLAY_APOLLO_TIMINGS
+	 */
+	char *p = buffer;
+	/* Parameter of chipset : */
+
+	/* Miscellaneous control 1 */
+	p = print_apollo_chipset_control1(buffer, bmide_dev);
+
+	/* Miscellaneous control 2 */
+	p = print_apollo_chipset_control2(p, bmide_dev);
+	/* Parameters of drives: */
+
+	/* Header */
+	p += sprintf(p, "------------------Primary IDE------------Secondary IDE-----\n");
+	p = print_apollo_chipset_control3(p, bmide_dev, 0);
+	p = print_apollo_ide_config(p, bmide_dev);
+	p += sprintf(p, "--------------drive0------drive1-------drive0------drive1----\n");
+	p = print_apollo_chipset_control3(p, bmide_dev, 1);
+	p = print_apollo_drive_config(p, bmide_dev);
+ 
+	return p-buffer;	/* hoping it is less than 4K... */
+}
+
+#endif  /* defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+/*
+ *  Used to set Fifo configuration via kernel command line:
+ */
+
+byte via_proc = 0;
+byte fifoconfig = 0;
+static byte newfifo = 0;
+
+/* Used to just intialize once Fifo configuration */
+static short int done = 0;
+
+/*
+ *  Set VIA Chipset Timings for (U)DMA modes enabled.
+ *
+ *  VIA Apollo chipset has complete support for
+ *  setting up the timing parameters.
+ */
+static void set_via_timings (ide_hwif_t *hwif)
+{
+	struct pci_dev  *dev = hwif->pci_dev;
+	byte post  = hwif->channel ? 0x30 : 0xc0;
+	byte flush = hwif->channel ? 0x50 : 0xa0;
+	int mask = hwif->channel ? ((newfifo & 0x60) ? 0 : 1) :
+				   (((newfifo & 0x60) == 0x60) ? 1 : 0);
+	byte via_config = 0;
+	int rc = 0, errors = 0;
+
+	printk("%s: VIA Bus-Master ", hwif->name);
+
+	/*
+	 * setting IDE read prefetch buffer and IDE post write buffer.
+	 * (This feature allows prefetched reads and post writes).
+	 */
+	if ((rc = pci_read_config_byte(dev, 0x41, &via_config))) 
+		errors++;
+
+	if (mask) {
+		if ((rc = pci_write_config_byte(dev, 0x41, via_config & ~post)))
+			errors++;
+	} else {
+		if ((rc = pci_write_config_byte(dev, 0x41, via_config | post)))
+			errors++;
+	}
+
+	/*
+	 * setting Channel read and End-of-sector FIFO flush.
+	 * (This feature ensures that FIFO flush is enabled:
+	 *  - for read DMA when interrupt asserts the given channel.
+	 *  - at the end of each sector for the given channel.)
+	 */
+	if ((rc = pci_read_config_byte(dev, 0x46, &via_config)))
+		errors++;
+
+	if (mask) {
+		if ((rc = pci_write_config_byte(dev, 0x46, via_config & ~flush)))
+			errors++;
+	} else {
+		if ((rc = pci_write_config_byte(dev, 0x46, via_config | flush)))
+			errors++;
+	}
+
+	if (!hwif->dma_base)
+		printk("Config %s. No DMA Enabled\n",
+			errors ? "ERROR":"Success");
+	else
+		printk("(U)DMA Timing Config %s\n",
+			errors ? "ERROR" : "Success");
+}
+
+/*
+ *  Sets VIA 82cxxx FIFO configuration:
+ *  This chipsets gets a splitable fifo. This can be driven either by command
+ *  line option (eg "splitfifo=2,2,3" which asks this driver to switch all the 
+ *  16 fifo levels to the second drive, and give it a threshold of 3 for (u)dma 
+ *  triggering.
+ */
+
+static int via_set_fifoconfig(ide_hwif_t *hwif)
+{
+	byte fifo;
+	unsigned int timings;
+	struct pci_dev  *dev = hwif->pci_dev;
+
+	/* read port configuration */
+	if (pci_read_config_dword(dev, 0x40, &timings))
+		return 1;
+   
+	/* first read actual fifo config: */
+	if (pci_read_config_byte(dev, 0x43, &fifo))
+		return 1;
+
+	/* keep 4 and 7 bit as they seem to differ between chipsets flavors... */
+	newfifo = fifo & 0x90;
+
+	if (fifoconfig) {
+		/* we received a config request from kernel command line: */
+		newfifo |= fifoconfig & 0x6f;
+	} else {
+		/* If ever just one channel is unused, allocate all fifo levels to it
+		 * and give it a 3/4 threshold for (u)dma transfers.
+		 * Otherwise, share it evenly between channels:
+		 */
+		if ((timings & 3) == 2) {
+			/* only primary channel is  enabled
+			 * 16 buf. to prim. chan. thresh=3/4
+			 */
+			newfifo |= 0x06;
+		} else if ((timings & 3) == 1) {
+			/* only secondary channel is enabled!
+			 * 16 buffers to sec. ch. thresh=3/4
+			 */
+			newfifo |= 0x69;
+		} else {
+			/* fifo evenly distributed: */
+			newfifo |= 0x2a;
+		}
+	}
+
+	/* write resulting configuration to chipset: */
+	if (pci_write_config_byte(dev, 0x43, newfifo))
+		return 1;
+
+	/* and then reread it to get the actual one */
+	if (pci_read_config_byte(dev, 0x43, &newfifo))
+		return 1;
+
+	/* print a kernel report: */
+	printk("Split FIFO Configuration: %s Primary buffers, threshold = %s\n",
+		((newfifo & 0x60) == 0x60)	? " 0" :
+		((newfifo & 0x60)		? " 8" : "16"),
+		!(newfifo & 0x0c)		? "1" :
+		(!(newfifo & 0x08)		? "3/4" :
+		(newfifo & 0x04)		? "1/4" : "1/2"));
+
+	printk("                          %s Second. buffers, threshold = %s\n",
+		((newfifo & 0x60) == 0x60)	? "16" :
+		((newfifo & 0x60)		? " 8" : " 0"),
+		!(newfifo & 0x03)		? "1" :
+		(!(newfifo & 0x02)		? "3/4" :
+		(newfifo & 0x01)		? "1/4" : "1/2"));
+	return 0;
+}
+
+#ifdef CONFIG_VIA82CXXX_TUNING
+
+static int via82cxxx_tune_chipset (ide_drive_t *drive, byte speed)
+{
+	struct hd_driveid *id	= drive->id;
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct pci_dev *dev	= hwif->pci_dev;
+	unsigned long dma_base	= hwif->dma_base;
+	byte unit		= (drive->select.b.unit & 0x01);
+	int drive_number	= ((hwif->channel ? 2 : 0) + unit);
+
+	byte ata2_pci		= 0x00;
+	byte ata3_pci		= 0x00;
+	byte timing		= 0x00;
+	byte ultra		= 0x00;
+	int			err;
+
+	int bus_speed		= ide_system_bus_speed();
+
+	switch(drive_number) {
+		case 0: ata2_pci = 0x48; ata3_pci = 0x50; break;
+		case 1: ata2_pci = 0x49; ata3_pci = 0x51; break;
+		case 2: ata2_pci = 0x4a; ata3_pci = 0x52; break;
+		case 3: ata2_pci = 0x4b; ata3_pci = 0x53; break;
+		default:
+			return err;
+	}
+
+	pci_read_config_byte(dev, ata2_pci, &timing);
+	pci_read_config_byte(dev, ata3_pci, &ultra);
+
+	switch(speed) {
+		case XFER_UDMA_4:
+		case XFER_UDMA_3:
+		case XFER_UDMA_2:
+		case XFER_UDMA_1:
+		case XFER_UDMA_0:
+		case XFER_MW_DMA_2:
+		case XFER_MW_DMA_1:
+		case XFER_MW_DMA_0:
+		case XFER_SW_DMA_2:
+		case XFER_SW_DMA_1:
+		case XFER_SW_DMA_0:
+		case XFER_PIO_4:
+		case XFER_PIO_3:
+		case XFER_PIO_2:
+		case XFER_PIO_1:
+		case XFER_PIO_0:
+		case XFER_PIO_SLOW:
+		default:
+			break;
+	}
+
+	pci_write_config_byte(dev, ata2_pci, timing);
+	pci_write_config_byte(dev, ata3_pci, ultra);
+
+	err = ide_config_drive_speed(drive, speed);
+
+	return(err);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id	= drive->id;
+	byte speed		= 0x00;
+	int  rval;
+
+	if ((id->dma_ultra & 0x0010) && (HWIF(drive)->udma_four)) {
+		speed = XFER_UDMA_4;
+	} else if ((id->dma_ultra & 0x0008) && (HWIF(drive)->udma_four)) {
+		speed = XFER_UDMA_3;
+	} else if (id->dma_ultra & 0x0004) {
+		speed = XFER_UDMA_2;
+	} else if (id->dma_ultra & 0x0002) {
+		speed = XFER_UDMA_1;
+	} else if (id->dma_ultra & 0x0001) {
+		speed = XFER_UDMA_0;
+	} else if (id->dma_mword & 0x0004) {
+		speed = XFER_MW_DMA_2;
+	} else if (id->dma_mword & 0x0002) {
+		speed = XFER_MW_DMA_1;
+	} else if (id->dma_mword & 0x0001) {
+		speed = XFER_MW_DMA_0;
+	} else if (id->dma_1word & 0x0004) {
+		speed = XFER_SW_DMA_2;
+	} else if (id->dma_1word & 0x0002) {
+		speed = XFER_SW_DMA_1;
+	} else if (id->dma_1word & 0x0001) {
+		speed = XFER_SW_DMA_0;
+	} else {
+		return ((int) ide_dma_off_quietly);
+	}
+
+	(void) via82cxxx_tune_chipset(drive, speed);
+
+	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+			((id->dma_mword >> 8) & 7) ? ide_dma_on :
+			((id->dma_1word >> 8) & 7) ? ide_dma_on :
+						     ide_dma_off_quietly);
+	return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+	unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+	unsigned short xfer_pio = drive->id->eide_pio_modes;
+	byte			timing, speed, pio;
+
+	pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+	if (xfer_pio> 4)
+		xfer_pio = 0;
+
+	if (drive->id->eide_pio_iordy > 0) {
+		for (xfer_pio = 5;
+			xfer_pio>0 &&
+			drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+			xfer_pio--);
+	} else {
+		xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+			   (drive->id->eide_pio_modes & 2) ? 0x04 :
+			   (drive->id->eide_pio_modes & 1) ? 0x03 :
+			   (drive->id->tPIO & 2) ? 0x02 :
+			   (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+	}
+
+	timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+	switch(timing) {
+		case 4: speed = XFER_PIO_4;break;
+		case 3: speed = XFER_PIO_3;break;
+		case 2: speed = XFER_PIO_2;break;
+		case 1: speed = XFER_PIO_1;break;
+		default:
+			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;                        break;
+	}
+	(void) via82cxxx_tune_chipset(drive, speed);
+}
+
+static void via82cxxx_tune_drive (ide_drive_t *drive, byte pio)
+{
+	byte speed;
+	switch(pio) {
+		case 4:		speed = XFER_PIO_4;break;
+		case 3:		speed = XFER_PIO_3;break;
+		case 2:		speed = XFER_PIO_2;break;
+		case 1:		speed = XFER_PIO_1;break;
+		default:	speed = XFER_PIO_0;break;
+	}
+	(void) via82cxxx_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_dma_action_t dma_func = ide_dma_on;
+
+	if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+			dma_func = ide_dma_off;
+			goto fast_ata_pio;
+		}
+		dma_func = ide_dma_off_quietly;
+		if (id->field_valid & 4) {
+			if (id->dma_ultra & 0x001F) {
+				/* Force if Capable UltraDMA */
+				dma_func = config_chipset_for_dma(drive);
+				if ((id->field_valid & 2) &&
+				    (dma_func != ide_dma_on))
+					goto try_dma_modes;
+			}
+		} else if (id->field_valid & 2) {
+try_dma_modes:
+			if ((id->dma_mword & 0x0007) ||
+			    (id->dma_1word & 0x0007)) {
+				/* Force if Capable regular DMA modes */
+				dma_func = config_chipset_for_dma(drive);
+				if (dma_func != ide_dma_on)
+					goto no_dma_set;
+			}
+		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+			if (id->eide_dma_time > 150) {
+				goto no_dma_set;
+			}
+			/* Consult the list of known "good" drives */
+			dma_func = config_chipset_for_dma(drive);
+			if (dma_func != ide_dma_on)
+				goto no_dma_set;
+		} else {
+			goto fast_ata_pio;
+		}
+	} else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+		dma_func = ide_dma_off_quietly;
+no_dma_set:
+		config_chipset_for_pio(drive);
+	}
+	return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+int via82cxxx_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			return config_drive_xfer_rate(drive);
+		default:
+			break;
+	}
+	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+}
+#endif /* CONFIG_VIA82CXXX_TUNING */
+
+unsigned int __init pci_init_via82cxxx (struct pci_dev *dev, const char *name)
+{
+	struct pci_dev *host;
+	struct pci_dev *isa;
+	int i, j, ata33, ata66;
+
+	byte revision = 0;
+
+	for (i = 0; i < arraysize (ApolloHostChipInfo) && !host_dev; i++) {
+		host = pci_find_device (PCI_VENDOR_ID_VIA,
+					ApolloHostChipInfo[i].host_id,
+					NULL);
+		if (!host)
+			continue;
+
+		host_dev = host;
+		printk(ApolloHostChipInfo[i].name);
+
+		for (j = 0; j < arraysize (ApolloISAChipInfo) && !isa_dev; j++) {
+			if (ApolloISAChipInfo[j].host_id !=
+			    ApolloHostChipInfo[i].host_id)
+				continue;
+
+			isa = pci_find_device (PCI_VENDOR_ID_VIA,
+					ApolloISAChipInfo[j].isa_id,
+					NULL);
+			if (!isa)
+				continue;
+
+			isa_dev = isa;
+
+			ata33 = 1;
+			ata66 = 0;
+
+			if (ApolloISAChipInfo[j].flags & VIA_FLAG_CHECK_REV) {
+				pci_read_config_byte(isa_dev, 0x0d, &revision);
+				ata33 = (revision >= 0x20) ? 1 : 0;
+			} else if (ApolloISAChipInfo[j].flags & VIA_FLAG_ATA_66) {
+				ata33 = 0;
+				ata66 = 1;
+			}
+
+			if (ata33 | ata66)
+				printk(" Chipset Core ATA-%s", ata66 ? "66" : "33");
+		}
+		printk("\n");
+	}
+
+#if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS)
+	via_proc = 1;
+	bmide_dev = dev;
+	via_display_info = &via_get_info;
+#endif /* DISPLAY_VIA_TIMINGS &&  CONFIG_PROC_FS*/
+
+	return 0;
+}
+
+unsigned int __init ata66_via82cxxx (ide_hwif_t *hwif)
+{
+	/* (Jeff Garzik) FIXME!!! for MVP4 */
+	return 0;
+}
+
+void __init ide_init_via82cxxx (ide_hwif_t *hwif)
+{
+	set_via_timings(hwif);
+
+#ifdef CONFIG_VIA82CXXX_TUNING
+	hwif->tuneproc = &via82cxxx_tune_drive;
+	if (hwif->dma_base) {
+		hwif->dmaproc = &via82cxxx_dmaproc;
+	} else {
+		hwif->autodma = 0;
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+#endif /* CONFIG_VIA82CXXX_TUNING */
+}
+
+/*
+ *  ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long)
+ *  checks if channel "channel" of if hwif is dma
+ *  capable or not, according to kernel command line,
+ *  and the new fifo settings.
+ *  It calls "ide_setup_dma" on capable mainboards, and
+ *  bypasses the setup if not capable.
+ */
+
+void ide_dmacapable_via82cxxx (ide_hwif_t *hwif, unsigned long dmabase)
+{
+	if (!done) {
+		via_set_fifoconfig(hwif);
+		done = 1;
+	}
+
+	/*
+	 * check if any fifo is available for requested port:
+	 */
+	if (((hwif->channel == 0) && ((newfifo & 0x60) == 0x60)) ||
+	    ((hwif->channel == 1) && ((newfifo & 0x60) == 0x00))) {
+		printk("    %s: VP_IDE Bus-Master DMA disabled (FIFO setting)\n", hwif->name);
+	} else {
+		ide_setup_dma(hwif, dmabase, 8);
+	}
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/net/3c527.c linux/drivers/net/3c527.c
--- v2.3.51/linux/drivers/net/3c527.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/3c527.c	Sun Mar 12 19:18:55 2000
@@ -2,6 +2,7 @@
  *
  *	(c) Copyright 1998 Red Hat Software Inc
  *	Written by Alan Cox.
+ *	Further debugging by Carl Drougge.
  *
  *	Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
  *	(for the MCA stuff) written by Wim Dumon.
@@ -15,7 +16,7 @@
  */
 
 static const char *version =
-	"3c527.c:v0.07 2000/01/18 Alan Cox (alan@redhat.com)\n";
+	"3c527.c:v0.08 2000/02/22 Alan Cox (alan@redhat.com)\n";
 
 /**
  * DOC: Traps for the unwary
@@ -122,6 +123,7 @@
 	u32 base;
 	u16 rx_halted;
 	u16 tx_halted;
+	u16 rx_pending;
 	u16 exec_pending;
 	u16 mc_reload_wait;	/* a multicast load request is pending */
 	atomic_t tx_count;		/* buffers left */
@@ -451,6 +453,9 @@
 	
 	printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
 		dev->name, lp->rx_len, lp->tx_len, lp->base);
+		
+	if(lp->tx_len > TX_RING_MAX)
+		lp->tx_len = TX_RING_MAX;
 	
 	dev->open		= mc32_open;
 	dev->stop		= mc32_close;
@@ -462,6 +467,7 @@
 	
 	lp->rx_halted		= 1;
 	lp->tx_halted		= 1;
+	lp->rx_pending		= 0;
 
 	/* Fill in the fields of the device structure with ethernet values. */
 	ether_setup(dev);
@@ -652,6 +658,7 @@
 	mc32_ring_poll(dev);	
 	
 	lp->rx_halted=0;
+	lp->rx_pending=0;
 }
 
 /**
@@ -944,6 +951,7 @@
 static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int ioaddr = dev->base_addr;
 	unsigned long flags;
 		
 	u16 tx_head;
@@ -967,7 +975,16 @@
 	lp->tx_skb[lp->tx_skb_end] = skb;
 	lp->tx_skb_end++;
 	lp->tx_skb_end&=(TX_RING_MAX-1);
+
+	/* TX suspend - shouldnt be needed but apparently is.
+	   This is a research item ... */
+		   
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	lp->tx_box->mbox=0;
+	outb(3, ioaddr+HOST_CMD);
 	
+	/* Transmit now stopped */
+
 	/* P is the last sending/sent buffer as a pointer */
 	p=(struct skb_header *)bus_to_virt(lp->base+tx_head);
 	
@@ -990,7 +1007,9 @@
 	p->status	= 0;
 	p->control	&= ~(1<<6);
 	
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
 	lp->tx_box->mbox=0;
+	outb(5, ioaddr+HOST_CMD);		/* Restart TX */
 	restore_flags(flags);
 	
 	netif_wake_queue(dev);
@@ -1096,11 +1115,16 @@
 		base = p->next;
 	}
 	while(x++<48);
+
+	/*
+	 *	Restart ring processing
+	 */	
 	
 	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
 	lp->rx_box->mbox=0;
 	lp->rx_box->data[0] = top;
 	outb(1<<3, ioaddr+HOST_CMD);	
+	lp->rx_halted=0;
 }
 
 
@@ -1123,7 +1147,6 @@
 	struct net_device *dev = dev_id;
 	struct mc32_local *lp;
 	int ioaddr, status, boguscount = 0;
-	int rx_event = 0;
 	
 	if (dev == NULL) {
 		printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
@@ -1182,7 +1205,15 @@
 			case 0:
 				break;
 			case 2:	/* RX */
-				rx_event=1;
+				lp->rx_pending=1;
+				if(!lp->rx_halted)
+				{
+					/*
+					 *	Halt ring receive
+					 */
+					while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+					outb(3<<3, ioaddr+HOST_CMD);
+				}
 				break;
 			case 3:
 			case 4:
@@ -1195,9 +1226,10 @@
 				break;
 			case 6:
 				/* Out of RX buffers stat */
-				/* Must restart */
 				lp->net_stats.rx_dropped++;
-				rx_event = 1; 	/* To restart */
+				lp->rx_pending=1;
+				/* Must restart */
+				lp->rx_halted=1;
 				break;
 			default:
 				printk("%s: strange rx ack %d\n", 
@@ -1231,11 +1263,17 @@
 	}
 	
 	/*
-	 *	Process and restart the receive ring.
+	 *	Process and restart the receive ring. This has some state
+	 *	as we must halt the ring to process it and halting the ring
+	 *	might not occur in the same IRQ handling loop as we issue
+	 *	the halt.
 	 */
 	 
-	if(rx_event)
+	if(lp->rx_pending && lp->rx_halted)
+	{
 		mc32_rx_ring(dev);
+		lp->rx_pending = 0;
+	}
 	return;
 }
 
diff -u --recursive --new-file v2.3.51/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.3.51/linux/drivers/net/3c59x.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/3c59x.c	Mon Mar 13 09:50:16 2000
@@ -1914,7 +1914,7 @@
 		data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
 		return 0;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		EL3WINDOW(4);
 		mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.3.51/linux/drivers/net/Config.in	Tue Mar  7 14:32:25 2000
+++ linux/drivers/net/Config.in	Mon Mar 13 09:35:06 2000
@@ -199,11 +199,13 @@
 fi
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
-   if [ "$CONFIG_HIPPI" = "y" ]; then
-      tristate '  Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
-      if [ "$CONFIG_ROADRUNNER" != "n" ]; then
-	 bool '    Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+   if [ "$CONFIG_INET" = "y" ]; then
+      bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
+      if [ "$CONFIG_HIPPI" = "y" ]; then
+         tristate '  Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
+         if [ "$CONFIG_ROADRUNNER" != "n" ]; then
+   	    bool '    Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+         fi
       fi
    fi
 fi
diff -u --recursive --new-file v2.3.51/linux/drivers/net/aironet4500.h linux/drivers/net/aironet4500.h
--- v2.3.51/linux/drivers/net/aironet4500.h	Fri Mar 10 16:40:42 2000
+++ linux/drivers/net/aironet4500.h	Sun Mar 12 19:11:17 2000
@@ -454,7 +454,7 @@
 };
 
 
-extern  void
+extern  __inline__ void
 awc_fid_queue_init(struct awc_fid_queue * queue){
 
 	unsigned long flags;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/appletalk/Config.in linux/drivers/net/appletalk/Config.in
--- v2.3.51/linux/drivers/net/appletalk/Config.in	Tue Mar  7 14:32:26 2000
+++ linux/drivers/net/appletalk/Config.in	Sat Mar 11 11:27:14 2000
@@ -3,18 +3,21 @@
 #
 
 if [ "$CONFIG_ATALK" != "n" ]; then
-  mainmenu_option next_comment
-  comment 'Appletalk devices'
-  dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK
-  dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK
-  if [ "$CONFIG_COPS" != "n" ]; then
-     bool '  Dayna firmware support' CONFIG_COPS_DAYNA
-     bool '  Tangent firmware support' CONFIG_COPS_TANGENT
-  fi
-  dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK
-  if [ "$CONFIG_IPDDP" != "n" ]; then
-     bool '  IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
-     bool '  Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
-  fi
-  endmenu
+   mainmenu_option next_comment
+   comment 'Appletalk devices'
+   bool 'Appletalk interfaces support' CONFIG_APPLETALK
+   if [ "$CONFIG_APPLETALK" != "n" ]; then
+      dep_tristate '  Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_APPLETALK
+      dep_tristate '  COPS LocalTalk PC support' CONFIG_COPS $CONFIG_APPLETALK
+      if [ "$CONFIG_COPS" != "n" ]; then
+	 bool '    Dayna firmware support' CONFIG_COPS_DAYNA
+	 bool '    Tangent firmware support' CONFIG_COPS_TANGENT
+      fi
+      dep_tristate '  Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_APPLETALK
+      if [ "$CONFIG_IPDDP" != "n" ]; then
+	 bool '    IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
+	 bool '    Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
+      fi
+   fi
+   endmenu
 fi
diff -u --recursive --new-file v2.3.51/linux/drivers/net/bsd_comp.c linux/drivers/net/bsd_comp.c
--- v2.3.51/linux/drivers/net/bsd_comp.c	Wed Mar 10 16:51:35 1999
+++ linux/drivers/net/bsd_comp.c	Sun Mar 12 21:12:37 2000
@@ -39,7 +39,7 @@
 /*
  * This version is for use with contiguous buffers on Linux-derived systems.
  *
- *  ==FILEVERSION 970607==
+ *  ==FILEVERSION 20000226==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -58,32 +58,9 @@
 #endif
 
 #include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
+#include <linux/init.h>
 #include <linux/malloc.h>
 #include <linux/vmalloc.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h>	/* used in new tty drivers */
-#include <linux/signal.h>	/* used in new tty drivers */
-
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/byteorder.h>
-
-#include <linux/if.h>
-
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
 
 #include <linux/ppp_defs.h>
 
@@ -1177,17 +1154,18 @@
  * Module support routines
  *************************************************************/
 
-int
-init_module(void)
+int bsdcomp_init(void)
 {
-	int answer = ppp_register_compressor (&ppp_bsd_compress);
+	int answer = ppp_register_compressor(&ppp_bsd_compress);
 	if (answer == 0)
-		printk (KERN_INFO "PPP BSD Compression module registered\n");
+		printk(KERN_INFO "PPP BSD Compression module registered\n");
 	return answer;
 }
 
-void
-cleanup_module(void)
+void bsdcomp_cleanup(void)
 {
-	ppp_unregister_compressor (&ppp_bsd_compress);
+	ppp_unregister_compressor(&ppp_bsd_compress);
 }
+
+module_init(bsdcomp_init);
+module_exit(bsdcomp_cleanup);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- v2.3.51/linux/drivers/net/dgrs.c	Thu Feb 10 17:11:10 2000
+++ linux/drivers/net/dgrs.c	Sun Mar 12 19:18:55 2000
@@ -852,6 +852,8 @@
 				return -EFAULT;
 			return (0);
 		case DGRS_SETFILTER:
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
 			if (ioc.port > privN->bcomm->bc_nports)
 				return -EINVAL;
 			if (ioc.filter >= NFILTERS)
@@ -1188,8 +1190,11 @@
 
 	priv->intrcnt = 0;
 	for (i = jiffies + 2*HZ + HZ/2; time_after(i, jiffies); )
+	{
+		barrier();		/* gcc 2.95 needs this */
 		if (priv->intrcnt >= 2)
 			break;
+	}
 	if (priv->intrcnt < 2)
 	{
 		printk("%s: Not interrupting on IRQ %d (%d)\n",
diff -u --recursive --new-file v2.3.51/linux/drivers/net/dmfe.c linux/drivers/net/dmfe.c
--- v2.3.51/linux/drivers/net/dmfe.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/dmfe.c	Sun Mar 12 19:27:22 2000
@@ -1,15 +1,26 @@
 /*
-   dmfe.c: Version 1.26
+   dmfe.c: Version 1.28 01/18/2000
 
-   A Davicom DM9102 fast ethernet driver for Linux. 
+   A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. 
+   Copyright (C) 1997  Sten Wang
+
+   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.
 
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, version 1.
 
    Compiler command:
    "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall 
-		-Wstrict-prototypes -O6 -c dmfe.c"
+   -Wstrict-prototypes -O6 -c dmfe.c"
+   OR
+   "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall 
+   -Wstrict-prototypes -O6 -c dmfe.c"
 
    The following steps teach you how to active DM9102 board:
    1. Used the upper compiler command to compile dmfe.c
@@ -25,7 +36,7 @@
    "route add -net 172.22.3.0 eth0"
    5. Well done. Your DM9102 adapter actived now.
 
-   Author: Sten Wang, E-mail: sten_wang@davicom.com.tw
+   Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
 
    Date:   10/28,1998
 
@@ -44,6 +55,9 @@
    Check and fix on 64bit and big endian boxes.
    Sort out the PCI latency.
    
+   (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
+     
+   Cleaned up for kernel merge by Alan Cox (alan@redhat.com)
  */
 
 #include <linux/module.h>
@@ -60,38 +74,41 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/version.h>
-
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 #include <linux/delay.h>
+
 #include <asm/processor.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
 
 
 /* Board/System/Debug information/definition ---------------- */
+#define PCI_DM9132_ID   0x91321282	/* Davicom DM9132 ID */
 #define PCI_DM9102_ID   0x91021282	/* Davicom DM9102 ID */
 #define PCI_DM9100_ID   0x91001282	/* Davicom DM9100 ID */
 
 #define DMFE_SUCC       0
 #define DM9102_IO_SIZE  0x80
-#define TX_FREE_DESC_CNT 0x1	/* Tx packet count */
+#define DM9102A_IO_SIZE 0x100
+#define TX_FREE_DESC_CNT 0xc	/* Tx packet count */
+#define TX_MAX_SEND_CNT 0x1	/* Maximum tx packet per time */
 #define TX_DESC_CNT     0x10	/* Allocated Tx descriptors */
 #define RX_DESC_CNT     0x10	/* Allocated Rx descriptors */
 #define DESC_ALL_CNT    TX_DESC_CNT+RX_DESC_CNT
 #define TX_BUF_ALLOC    0x600
 #define RX_ALLOC_SIZE   0x620
 #define DM910X_RESET    1
-#define CR6_DEFAULT     0x002c0000	/* SF, MII, HD */
+#define CR6_DEFAULT     0x00280000	/* SF, HD */
 #define CR7_DEFAULT     0x1a2cd
 #define CR15_DEFAULT    0x06	/* TxJabber RxWatchdog */
 #define TDES0_ERR_MASK  0x4302	/* TXJT, LC, EC, FUE */
 #define MAX_PACKET_SIZE 1514
 #define DMFE_MAX_MULTICAST 14
-#define RX_MAX_TRAFFIC 0x5000
+#define RX_MAX_TRAFFIC  0x14000
 #define MAX_CHECK_PACKET 0x8000
 
 #define DMFE_10MHF      0
@@ -100,8 +117,8 @@
 #define DMFE_100MFD     5
 #define DMFE_AUTO       8
 
-#define DMFE_TIMER_WUT  jiffies+HZ*1	/* timer wakeup time : 1 second */
-#define DMFE_TX_TIMEOUT HZ*2	/* tx packet time-out time */
+#define DMFE_TIMER_WUT  jiffies+(HZ*2)/2	/* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT HZ*1.5	/* tx packet time-out time 1.5 s" */
 
 #define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule)
 
@@ -109,7 +126,7 @@
 
 #define DELAY_1US udelay(1)	/* udelay scale 1 usec */
 
-#define SHOW_MEDIA_TYPE(mode) printk("\n<WARN> Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+#define SHOW_MEDIA_TYPE(mode) printk(KERN_WARNING "dmfe: Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
 
 
 /* CR9 definition: SROM/MII */
@@ -125,6 +142,9 @@
 
 #define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;
 
+#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE
+
+
 /* Structure/enum declaration ------------------------------- */
 struct tx_desc {
 	u32 tdes0, tdes1, tdes2, tdes3;
@@ -149,13 +169,14 @@
 
 	struct pci_dev *net_dev;	/* PCI device */
 
-	u32 ioaddr;		/* I/O base address */
+	unsigned long ioaddr;		/* I/O base address */
+	u32 cr0_data;
 	u32 cr5_data;
 	u32 cr6_data;
 	u32 cr7_data;
 	u32 cr15_data;
-
-/* descriptor pointer */
+	
+	/* descriptor pointer */
 	unsigned char *buf_pool_ptr;	/* Tx buffer pool memory */
 	unsigned char *buf_pool_start;	/* Tx buffer pool align dword */
 	unsigned char *desc_pool_ptr;	/* descriptor pool memory */
@@ -166,9 +187,12 @@
 	struct rx_desc *rx_insert_ptr;
 	struct rx_desc *rx_ready_ptr;	/* packet come pointer */
 	u32 tx_packet_cnt;	/* transmitted packet count */
+	u32 tx_queue_cnt;	/* wait to send packet count */
 	u32 rx_avail_cnt;	/* available rx descriptor count */
 	u32 interval_rx_cnt;	/* rx packet count a callback time */
 
+	u16 phy_id2;		/* Phyxcer ID2 */
+
 	u8 media_mode;		/* user specify media mode */
 	u8 op_mode;		/* real work media mode */
 	u8 phy_addr;
@@ -190,14 +214,14 @@
 
 enum dmfe_CR6_bits {
 	CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, CR6_FDM = 0x200,
-	CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000
+	CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000,
+	CR6_NO_PURGE = 0x20000000
 };
 
 /* Global variable declaration ----------------------------- */
-
 static int dmfe_debug = 0;
 static unsigned char dmfe_media_mode = 8;
-static struct net_device *dmfe_root_dev = NULL;		/* First device */
+static struct net_device *dmfe_root_dev = NULL;	/* First device */
 static u32 dmfe_cr6_user_set = 0;
 
 /* For module input parameter */
@@ -206,7 +230,7 @@
 static unsigned char mode = 8;
 static u8 chkmode = 1;
 
-unsigned long CrcTable[256] =
+static unsigned long CrcTable[256] =
 {
 	0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
 	0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
@@ -275,7 +299,7 @@
 };
 
 /* function declaration ------------------------------------- */
-static int dmfe_reg_board(void);
+static int dmfe_probe(void);
 static int dmfe_open(struct net_device *);
 static int dmfe_start_xmit(struct sk_buff *, struct net_device *);
 static int dmfe_stop(struct net_device *);
@@ -288,11 +312,11 @@
 static void allocated_rx_buffer(struct dmfe_board_info *);
 static void update_cr6(u32, u32);
 static void send_filter_frame(struct net_device *, int);
-static u16 phy_read(u32, u8, u8);
-static void phy_write(u32, u8, u8, u16);
+static void dm9132_id_table(struct net_device *, int);
+static u16 phy_read(u32, u8, u8, u32);
+static void phy_write(u32, u8, u8, u16, u32);
 static void phy_write_1bit(u32, u32);
 static u16 phy_read_1bit(u32);
-static void parser_ctrl_info(struct dmfe_board_info *);
 static void dmfe_sense_speed(struct dmfe_board_info *);
 static void dmfe_process_mode(struct dmfe_board_info *);
 static void dmfe_timer(unsigned long);
@@ -301,7 +325,7 @@
 static void dmfe_dynamic_reset(struct net_device *);
 static void dmfe_free_rxbuffer(struct dmfe_board_info *);
 static void dmfe_init_dm910x(struct net_device *);
-static unsigned long cal_CRC(unsigned char *, unsigned int);
+static unsigned long cal_CRC(unsigned char *, unsigned int, u8);
 
 /* DM910X network board routine ---------------------------- */
 
@@ -309,9 +333,9 @@
  *	Search DM910X board, allocate space and register it
  */
  
-static int __init dmfe_reg_board(void)
+static int __init dmfe_probe(void)
 {
-	u32 pci_iobase;
+	unsigned long pci_iobase;
 	u16 dm9102_count = 0;
 	u8 pci_irqline;
 	static int index = 0;	/* For multiple call */
@@ -320,7 +344,7 @@
 	struct pci_dev *net_dev = NULL;
 	struct net_device *dev;
 
-	DMFE_DBUG(0, "dmfe_reg_board()", 0);
+	DMFE_DBUG(0, "dmfe_probe()", 0);
 
 	if (!pci_present())
 		return -ENODEV;
@@ -329,20 +353,18 @@
 	while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev)))
 	{
 		u32 pci_id;
+		u32 dev_rev;
 
 		index++;
 		if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC)
 			continue;
 
-		if (pci_id != PCI_DM9102_ID)
+		if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID))
 			continue;
 
 		pci_iobase = net_dev->resource[0].start;
 		pci_irqline = net_dev->irq;
 				
-		if (check_region(pci_iobase, DM9102_IO_SIZE))	/* IO range check */
-			continue;
-
 		/* Enable Master/IO access, Disable memory access */
 		
 		pci_enable_device (net_dev);
@@ -355,7 +377,19 @@
 		   
 		pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80);
 
-		/* IO range and interrupt check */
+		/* Read Chip revesion */
+		pci_read_config_dword(net_dev, PCI_REVISION_ID, &dev_rev);
+		
+		/* IO range check */
+		if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) {
+			printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev));
+  			continue;
+		}
+		/* Interrupt check */
+		if (pci_irqline == 0) {
+			printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline);
+			continue;
+		}
 
 		/* Found DM9102 card and PCI resource allocated OK */
 		dm9102_count++;	/* Found a DM9102 card */
@@ -373,7 +407,7 @@
 
 		db->chip_id = pci_id;	/* keep Chip vandor/Device ID */
 		db->ioaddr = pci_iobase;
-		pci_read_config_dword(net_dev, 8, &db->chip_revesion);
+		db->chip_revesion = dev_rev;
 
 		db->net_dev = net_dev;
 
@@ -386,7 +420,7 @@
 		dev->set_multicast_list = &dmfe_set_filter_mode;
 		dev->do_ioctl = &dmfe_do_ioctl;
 
-		request_region(pci_iobase, DM9102_IO_SIZE, dev->name);
+		request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name);
 
 		/* read 64 word srom data */
 		for (i = 0; i < 64; i++)
@@ -422,20 +456,17 @@
 	db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA);
 	if (db->desc_pool_ptr == NULL)
 		return -ENOMEM;
-
 	if ((u32) db->desc_pool_ptr & 0x1f)
 		db->first_tx_desc = (struct tx_desc *) (((u32) db->desc_pool_ptr & ~0x1f) + 0x20);
 	else
 		db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
 
 	/* Allocated Tx buffer memory */
-	
 	db->buf_pool_ptr = kmalloc(TX_BUF_ALLOC * TX_DESC_CNT + 4, GFP_KERNEL | GFP_DMA);
 	if (db->buf_pool_ptr == NULL) {
 		kfree(db->desc_pool_ptr);
 		return -ENOMEM;
 	}
-	
 	if ((u32) db->buf_pool_ptr & 0x3)
 		db->buf_pool_start = (char *) (((u32) db->buf_pool_ptr & ~0x3) + 0x4);
 	else
@@ -444,16 +475,21 @@
 	/* system variable init */
 	db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
 	db->tx_packet_cnt = 0;
+	db->tx_queue_cnt = 0;
 	db->rx_avail_cnt = 0;
 	db->link_failed = 0;
 	db->wait_reset = 0;
 	db->in_reset_state = 0;
 	db->rx_error_cnt = 0;
 
-	if (chkmode && (db->chip_revesion < 0x02000030)) {
-		db->dm910x_chk_mode = 1;	/* Enter the check mode */
-	} else {
+	if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) {
+		//db->cr6_data &= ~CR6_SFT;         /* Used Tx threshold */
+		//db->cr6_data |= CR6_NO_PURGE;     /* No purge if rx unavailable */
+		db->cr0_data = 0xc00000;	/* TX/RX desc burst mode */
 		db->dm910x_chk_mode = 4;	/* Enter the normal mode */
+	} else {
+		db->cr0_data = 0;
+		db->dm910x_chk_mode = 1;	/* Enter the check mode */
 	}
 
 	/* Initilize DM910X board */
@@ -474,14 +510,12 @@
 	return 0;
 }
 
-/*
- *	Initialize DM910X board
- *	Reset DM910X board
- *	Initialize TX/Rx descriptor chain structure
- *	Send the set-up frame
- *	Enable Tx/Rx machine
+/* Initilize DM910X board
+   Reset DM910X board
+   Initilize TX/Rx descriptor chain structure
+   Send the set-up frame
+   Enable Tx/Rx machine
  */
- 
 static void dmfe_init_dm910x(struct net_device *dev)
 {
 	struct dmfe_board_info *db = dev->priv;
@@ -490,16 +524,18 @@
 	DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
 
 	/* Reset DM910x board : need 32 PCI clock to complete */
-	outl(DM910X_RESET, ioaddr + DCR0);
+	outl(DM910X_RESET, ioaddr + DCR0);	/* RESET MAC */
 	DELAY_5US;
-	outl(0, ioaddr + DCR0);
+	outl(db->cr0_data, ioaddr + DCR0);
 
 	outl(0x180, ioaddr + DCR12);	/* Let bit 7 output port */
-	outl(0x80, ioaddr + DCR12);	/* Reset DM9102 phyxcer */
+	outl(0x80, ioaddr + DCR12);	/* RESET DM9102 phyxcer */
 	outl(0x0, ioaddr + DCR12);	/* Clear RESET signal */
 
-	/* Parser control information: Phy addr */
-	parser_ctrl_info(db);
+	/* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
+	db->phy_addr = 1;
+
+	/* Media Mode Check */
 	db->media_mode = dmfe_media_mode;
 	if (db->media_mode & DMFE_AUTO)
 		dmfe_sense_speed(db);
@@ -514,7 +550,10 @@
 	update_cr6(db->cr6_data, ioaddr);
 
 	/* Send setup frame */
-	send_filter_frame(dev, 0);
+	if (db->chip_id == PCI_DM9132_ID)
+		dm9132_id_table(dev, dev->mc_count);	/* DM9132 */
+	else
+		send_filter_frame(dev, dev->mc_count);	/* DM9102/DM9102A */
 
 	/* Init CR5/CR7, interrupt active bit */
 	outl(0xffffffff, ioaddr + DCR5);	/* clear all CR5 status */
@@ -533,10 +572,9 @@
 
 
 /*
- *	Hardware start transmission.
- *	Send a packet to media from the upper layer.
+   Hardware start transmission.
+   Send a packet to media from the upper layer.
  */
- 
 static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct dmfe_board_info *db = dev->priv;
@@ -561,25 +599,24 @@
 	txptr = db->tx_insert_ptr;
 	memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
 	txptr->tdes1 = 0xe1000000 | skb->len;
-	txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
 
 	/* Point to next transmit free descriptor */
 	db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
 
-	/* transmit counter increase 1 */
-	db->tx_packet_cnt++;
-	db->stats.tx_packets++;
-
-	/* issue Tx polling command */
-	outl(0x1, dev->base_addr + DCR1);
+	/* Transmit Packet Process */
+	if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
+		txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
+		db->tx_packet_cnt++;	/* Ready to send count */
+		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling comand */
+	} else {
+		db->tx_queue_cnt++;	/* queue the tx packet */
+		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling comand */
+	}
 
 	/* Tx resource check */
 	if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
 		netif_wake_queue(dev);
 
-	/* Set transmit time stamp */
-	dev->trans_start = jiffies;	/* saved the time stamp */
-
 	/* free this SKB */
 	dev_kfree_skb(skb);
 	return 0;
@@ -622,8 +659,8 @@
 }
 
 /*
- *	DM9102 insterrupt handler
- *	receive the packet to upper layer, free the transmitted packet
+   DM9102 insterrupt handler
+   receive the packet to upper layer, free the transmitted packet
  */
 
 static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -637,7 +674,6 @@
 		DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0);
 		return;
 	}
-
 	/* A real interrupt coming */
 	db = (struct dmfe_board_info *) dev->priv;
 	ioaddr = dev->base_addr;
@@ -667,15 +703,35 @@
 		/* printk("tdes0=%x\n", txptr->tdes0); */
 		if (txptr->tdes0 & 0x80000000)
 			break;
+		db->stats.tx_packets++;
+		
 		if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) {
 			/* printk("tdes0=%x\n", txptr->tdes0); */
 			db->stats.tx_errors++;
 		}
+		/* Transmit statistic counter */
+		if (txptr->tdes0 != 0x7fffffff) {
+ 			/* printk("tdes0=%x\n", txptr->tdes0); */
+ 			db->stats.collisions += (txptr->tdes0 >> 3) & 0xf;
+			db->stats.tx_bytes += txptr->tdes1 & 0x7ff;
+			if (txptr->tdes0 & TDES0_ERR_MASK)
+				db->stats.tx_errors++;
+		}
 		txptr = (struct tx_desc *) txptr->next_tx_desc;
 		db->tx_packet_cnt--;
 	}
+	/* Update TX remove pointer to next */
 	db->tx_remove_ptr = (struct tx_desc *) txptr;
 
+	/* Send the Tx packet in queue */
+	if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) {
+		txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
+		db->tx_packet_cnt++;	/* Ready to send count */
+		outl(0x1, ioaddr + DCR1);	/* Issue Tx polling command */
+		dev->trans_start = jiffies;	/* saved the time stamp */
+		db->tx_queue_cnt--;
+ 	}
+	/* Resource available check */
 	if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
 		netif_wake_queue(dev);
 
@@ -695,7 +751,6 @@
 	}
 
 	/* Restore CR7 to enable interrupt mask */
-	
 	if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
 		db->cr7_data = 0x1a28d;
 	else
@@ -704,9 +759,8 @@
 }
 
 /*
- *	Receive the come packet and pass to upper layer
+   Receive the come packet and pass to upper layer
  */
- 
 static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
 {
 	struct rx_desc *rxptr;
@@ -727,11 +781,11 @@
 			/* reused this SKB */
 			DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
 			dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
-			db->rx_error_cnt++;
+			/* db->rx_error_cnt++; */
 		} else {
+			/* A packet with First/Last flag */
 			rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4;	/* skip CRC */
 
-			/* A packet with First/Last flag */
 			if (rxptr->rdes0 & 0x8000) {	/* error summary bit check */
 				/* This is a error packet */
 				/* printk("rdes0 error : %x \n", rxptr->rdes0); */
@@ -748,7 +802,7 @@
 				skb = (struct sk_buff *) rxptr->rx_skb_ptr;
 
 				/* Received Packet CRC check need or not */
-				if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen) != (*(unsigned long *) (skb->tail + rxlen)))) {
+				if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen, 1) != (*(unsigned long *) (skb->tail + rxlen)))) {
 					/* Found a error received packet */
 					dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
 					db->dm910x_chk_mode = 3;
@@ -758,11 +812,12 @@
 					skb_put(skb, rxlen);
 					skb->protocol = eth_type_trans(skb, dev);
 					netif_rx(skb);	/* Send to upper layer */
-					/* skb->ip_summed = CHECKSUM_UNNECESSARY; */
 					dev->last_rx = jiffies;
 					db->stats.rx_packets++;
+					db->stats.rx_bytes += rxlen;
 				}
 			} else {
+				/* Reuse SKB buffer when the packet is error */
 				DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
 				dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
 			}
@@ -772,12 +827,12 @@
 	}
 
 	db->rx_ready_ptr = rxptr;
+
 }
 
 /*
- *	Get statistics from driver.
+   Get statistics from driver.
  */
- 
 static struct enet_statistics *dmfe_get_stats(struct net_device *dev)
 {
 	struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
@@ -787,9 +842,8 @@
 }
 
 /*
- *	Set DM910X multicast address
+   Set DM910X multicast address
  */
- 
 static void dmfe_set_filter_mode(struct net_device *dev)
 {
 	struct dmfe_board_info *db = dev->priv;
@@ -809,13 +863,15 @@
 		return;
 	}
 	DMFE_DBUG(0, "Set multicast address", dev->mc_count);
-	send_filter_frame(dev, dev->mc_count);
+	if (db->chip_id == PCI_DM9132_ID)
+		dm9132_id_table(dev, dev->mc_count);	/* DM9132 */
+	else
+		send_filter_frame(dev, dev->mc_count);	/* DM9102/DM9102A */
 }
 
 /*
- *	Process the upper socket ioctl command
+   Process the upper socket ioctl command
  */
- 
 static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
 	DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
@@ -823,10 +879,9 @@
 }
 
 /*
- *	A periodic timer routine
- *	Dynamic media sense, allocated Rx buffer...
+   A periodic timer routine
+   Dynamic media sense, allocated Rx buffer...
  */
- 
 static void dmfe_timer(unsigned long data)
 {
 	u32 tmp_cr8;
@@ -861,18 +916,26 @@
 
 	if (db->wait_reset | (db->tx_packet_cnt &&
 			      ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
-		/* printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start); */
+		/*
+		   printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
+		 */
 		DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
 		dmfe_dynamic_reset(dev);
 		db->timer.expires = DMFE_TIMER_WUT;
 		add_timer(&db->timer);
 		return;
 	}
-	db->rx_error_cnt = 0;	/* Clear previous counter */
+	db->rx_error_cnt = 0;	/* Clear previos counter */
 
 	/* Link status check, Dynamic media type change */
-	tmp_cr12 = inb(db->ioaddr + DCR12);
-	if (db->chip_revesion == 0x02000030) {
+	if (db->chip_id == PCI_DM9132_ID)
+		tmp_cr12 = inb(db->ioaddr + DCR9 + 3);	/* DM9132 */
+	else
+		tmp_cr12 = inb(db->ioaddr + DCR12);	/* DM9102/DM9102A */
+
+	if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) ||
+	    ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) {
+		/* DM9102A Chip */
 		if (tmp_cr12 & 2)
 			tmp_cr12 = 0x0;		/* Link failed */
 		else
@@ -882,10 +945,26 @@
 		/* Link Failed */
 		DMFE_DBUG(0, "Link Failed", tmp_cr12);
 		db->link_failed = 1;
-		phy_write(db->ioaddr, db->phy_addr, 0, 0x8000);		/* reset Phy controller */
+		phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id);	/* reset Phy */
+
+		/* 10/100M link failed, used 1M Home-Net */
+		db->cr6_data |= 0x00040000;	/* CR6 bit18 = 1, select Home-Net */
+		db->cr6_data &= ~0x00000200;	/* CR6 bit9 =0, half duplex mode */
+		update_cr6(db->cr6_data, db->ioaddr);
+
+		/* For DM9801 : PHY ID1 0181h, PHY ID2 B900h */
+		db->phy_id2 = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id);
+		if (db->phy_id2 == 0xb900)
+			phy_write(db->ioaddr, db->phy_addr, 25, 0x7e08, db->chip_id);
 	} else if ((tmp_cr12 & 0x3) && db->link_failed) {
 		DMFE_DBUG(0, "Link link OK", tmp_cr12);
 		db->link_failed = 0;
+
+		/* CR6 bit18=0, select 10/100M */
+		db->cr6_data &= ~0x00040000;
+		update_cr6(db->cr6_data, db->ioaddr);
+
+		/* Auto Sense Speed */
 		if (db->media_mode & DMFE_AUTO)
 			dmfe_sense_speed(db);
 		dmfe_process_mode(db);
@@ -902,13 +981,12 @@
 }
 
 /*
- *	Dynamic reset the DM910X board
- *	Stop DM910X board
- *	Free Tx/Rx allocated memory
- *	Reset DM910X board
- *	Re-initilize DM910X board
+   Dynamic reset the DM910X board
+   Stop DM910X board
+   Free Tx/Rx allocated memory
+   Reset DM910X board
+   Re-initilize DM910X board
  */
- 
 static void dmfe_dynamic_reset(struct net_device *dev)
 {
 	struct dmfe_board_info *db = dev->priv;
@@ -929,6 +1007,7 @@
 
 	/* system variable init */
 	db->tx_packet_cnt = 0;
+	db->tx_queue_cnt = 0;
 	db->rx_avail_cnt = 0;
 	db->link_failed = 0;
 	db->wait_reset = 0;
@@ -945,9 +1024,8 @@
 }
 
 /*
- *	Free all allocated rx buffer 
+   free all allocated rx buffer 
  */
-
 static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
 {
 	DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0);
@@ -961,9 +1039,8 @@
 }
 
 /*
- *	Reused the SK buffer
+   Reused the SK buffer
  */
- 
 static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
 {
 	struct rx_desc *rxptr = db->rx_insert_ptr;
@@ -979,10 +1056,9 @@
 }
 
 /*
- *	Initialize transmit/Receive descriptor 
- *	Using Chain structure, and allocated Tx/Rx buffer
+   Initialize transmit/Receive descriptor 
+   Using Chain structure, and allocated Tx/Rx buffer
  */
- 
 static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
 {
 	struct tx_desc *tmp_tx;
@@ -1033,10 +1109,9 @@
 }
 
 /*
- *	Update CR6 vaule
- *	Firstly stop DM910X , then written value and start
+   Update CR6 vaule
+   Firstly stop DM910X , then written value and start
  */
- 
 static void update_cr6(u32 cr6_data, u32 ioaddr)
 {
 	u32 cr6_tmp;
@@ -1049,11 +1124,50 @@
 	/* printk("CR6 update %x ", cr6_tmp); */
 }
 
-/*
- *	Send a setup frame
- *	This setup frame initilize DM910X addres filter mode
+/* Send a setup frame for DM9132
+   This setup frame initilize DM910X addres filter mode
+ */
+static void dm9132_id_table(struct net_device *dev, int mc_cnt)
+{
+	struct dev_mc_list *mcptr;
+	u16 *addrptr;
+	u32 ioaddr = dev->base_addr + 0xc0;	/* ID Table */
+	u32 hash_val;
+	u16 i, hash_table[4];
+
+	DMFE_DBUG(0, "dm9132_id_table()", 0);
+
+	/* Node address */
+	addrptr = (u16 *) dev->dev_addr;
+	outw(addrptr[0], ioaddr);
+	ioaddr += 4;
+	outw(addrptr[1], ioaddr);
+	ioaddr += 4;
+	outw(addrptr[2], ioaddr);
+	ioaddr += 4;
+
+	/* Clear Hash Table */
+	for (i = 0; i < 4; i++)
+		hash_table[i] = 0x0;
+
+	/* broadcast address */
+	hash_table[3] = 0x8000;
+
+	/* the multicast address in Hash Table : 64 bits */
+	for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+		hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
+		hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
+	}
+
+	/* Write the hash table to MAC MD table */
+	for (i = 0; i < 4; i++, ioaddr += 4) {
+		outw(hash_table[i], ioaddr);
+	}
+}
+
+/* Send a setup frame for DM9102/DM9102A
+   This setup frame initilize DM910X addres filter mode
  */
- 
 static void send_filter_frame(struct net_device *dev, int mc_cnt)
 {
 	struct dmfe_board_info *db = dev->priv;
@@ -1068,17 +1182,17 @@
 	txptr = db->tx_insert_ptr;
 	suptr = (u32 *) txptr->tx_buf_ptr;
 
-	/* broadcast address */
-	*suptr++ = 0xffff;
-	*suptr++ = 0xffff;
-	*suptr++ = 0xffff;
-
 	/* Node address */
 	addrptr = (u16 *) dev->dev_addr;
 	*suptr++ = addrptr[0];
 	*suptr++ = addrptr[1];
 	*suptr++ = addrptr[2];
 
+	/* broadcast address */
+	*suptr++ = 0xffff;
+	*suptr++ = 0xffff;
+	*suptr++ = 0xffff;
+
 	/* fit the multicast address */
 	for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
 		addrptr = (u16 *) mcptr->dmi_addr;
@@ -1092,19 +1206,21 @@
 		*suptr++ = 0xffff;
 		*suptr++ = 0xffff;
 	}
-
 	/* prepare the setup frame */
-	db->tx_packet_cnt++;
-	netif_stop_queue(dev);
-	txptr->tdes1 = 0x890000c0;
-	txptr->tdes0 = 0x80000000;
 	db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
-
-	update_cr6(db->cr6_data | 0x2000, dev->base_addr);
-	outl(0x1, dev->base_addr + DCR1);
-	update_cr6(db->cr6_data, dev->base_addr);
-	dev->trans_start = jiffies;
-
+	txptr->tdes1 = 0x890000c0;
+	/* Resource Check and Send the setup packet */
+	if (!db->tx_packet_cnt) {
+		/* Resource Empty */
+		db->tx_packet_cnt++;
+		txptr->tdes0 = 0x80000000;
+		update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling command */
+		update_cr6(db->cr6_data, dev->base_addr);
+	} else {
+		/* Put into TX queue */
+		db->tx_queue_cnt++;
+	}
 }
 
 /*
@@ -1132,9 +1248,8 @@
 }
 
 /*
- *	Read one word data from the serial ROM
+   Read one word data from the serial ROM
  */
- 
 static u16 read_srom_word(long ioaddr, int offset)
 {
 	int i;
@@ -1170,35 +1285,6 @@
 }
 
 /*
- *	Parser Control media block to get Phy address
- */
- 
-static void parser_ctrl_info(struct dmfe_board_info *db)
-{
-	int i;
-	char *sdata = db->srom;
-	unsigned char count;
-
-	/* point to info leaf0 */
-	count = *(sdata + 33);
-
-	/* Point to First media block */
-	sdata += 34;
-	for (i = 0; i < count; i++) {
-		if (*(sdata + 1) == 1) {
-			db->phy_addr = *(sdata + 2);
-			break;
-		}
-		sdata += ((unsigned char) *(sdata) & 0x7f) + 1;
-	}
-
-	if (i >= count) {
-		printk("Can't found Control Block\n");
-		db->phy_addr = 1;
-	}
-}
-
-/*
  *	Auto sense the media mode
  */
  
@@ -1209,13 +1295,16 @@
 
 	for (i = 1000; i; i--) {
 		DELAY_5US;
-		phy_mode = phy_read(db->ioaddr, db->phy_addr, 1);
+		phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
 		if ((phy_mode & 0x24) == 0x24)
 			break;
 	}
 
 	if (i) {
-		phy_mode = phy_read(db->ioaddr, db->phy_addr, 17) & 0xf000;
+		if (db->chip_id == PCI_DM9132_ID)	/* DM9132 */
+			phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000;
+		else		/* DM9102/DM9102A */
+			phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000;
 		/* printk("Phy_mode %x ",phy_mode); */
 		switch (phy_mode) {
 		case 0x1000:
@@ -1231,23 +1320,22 @@
 			db->op_mode = DMFE_100MFD;
 			break;
 		default:
-			db->op_mode = DMFE_100MHF;
-			DMFE_DBUG(1, "Media Type error, phy reg17", phy_mode);
+			db->op_mode = DMFE_10MHF;
+			DMFE_DBUG(0, "Media Type error, phy reg17", phy_mode);
 			break;
 		}
 	} else {
-		db->op_mode = DMFE_100MHF;
+		db->op_mode = DMFE_10MHF;
 		DMFE_DBUG(0, "Link Failed :", phy_mode);
 	}
 }
 
 /*
- *	Process op-mode
- *	AUTO mode : PHY controller in Auto-negotiation Mode
- *	Force mode: PHY controller in force mode with HUB
- *	N-way force capability with SWITCH
+   Process op-mode
+   AUTO mode : PHY controller in Auto-negotiation Mode
+   Force mode: PHY controller in force mode with HUB
+   N-way force capability with SWITCH
  */
- 
 static void dmfe_process_mode(struct dmfe_board_info *db)
 {
 	u16 phy_reg;
@@ -1259,11 +1347,11 @@
 
 	if (!(db->media_mode & DMFE_AUTO)) {	/* Force Mode Check */
 		/* User force the media type */
-		phy_reg = phy_read(db->ioaddr, db->phy_addr, 5);
+		phy_reg = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id);
 		/* printk("Nway phy_reg5 %x ",phy_reg); */
 		if (phy_reg & 0x1) {
 			/* parter own the N-Way capability */
-			phy_reg = phy_read(db->ioaddr, db->phy_addr, 4) & ~0x1e0;
+			phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x1e0;
 			switch (db->op_mode) {
 			case DMFE_10MHF:
 				phy_reg |= 0x20;
@@ -1278,7 +1366,7 @@
 				phy_reg |= 0x100;
 				break;
 			}
-			phy_write(db->ioaddr, db->phy_addr, 4, phy_reg);
+			phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
 		} else {
 			/* parter without the N-Way capability */
 			switch (db->op_mode) {
@@ -1295,95 +1383,109 @@
 				phy_reg = 0x2100;
 				break;
 			}
-			phy_write(db->ioaddr, db->phy_addr, 0, phy_reg);
+			phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
 		}
 	}
 }
 
 /*
- *	Write a word to Phy register
+   Write a word to Phy register
  */
- 
-static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data)
+static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id)
 {
 	u16 i;
-	u32 ioaddr = iobase + DCR9;
+	u32 ioaddr;
 
-	/* Send 33 synchronization clock to Phy controller */
-	for (i = 0; i < 35; i++)
-		phy_write_1bit(ioaddr, PHY_DATA_1);
+	if (chip_id == PCI_DM9132_ID) {
+		ioaddr = iobase + 0x80 + offset * 4;
+		outw(phy_data, ioaddr);
+	} else {
+		/* DM9102/DM9102A Chip */
+		ioaddr = iobase + DCR9;
 
-	/* Send start command(01) to Phy */
-	phy_write_1bit(ioaddr, PHY_DATA_0);
-	phy_write_1bit(ioaddr, PHY_DATA_1);
+		/* Send 33 synchronization clock to Phy controller */
+		for (i = 0; i < 35; i++)
+			phy_write_1bit(ioaddr, PHY_DATA_1);
 
-	/* Send write command(01) to Phy */
-	phy_write_1bit(ioaddr, PHY_DATA_0);
-	phy_write_1bit(ioaddr, PHY_DATA_1);
+		/* Send start command(01) to Phy */
+		phy_write_1bit(ioaddr, PHY_DATA_0);
+		phy_write_1bit(ioaddr, PHY_DATA_1);
 
-	/* Send Phy addres */
-	for (i = 0x10; i > 0; i = i >> 1)
-		phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+		/* Send write command(01) to Phy */
+		phy_write_1bit(ioaddr, PHY_DATA_0);
+		phy_write_1bit(ioaddr, PHY_DATA_1);
 
-	/* Send register addres */
-	for (i = 0x10; i > 0; i = i >> 1)
-		phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+		/* Send Phy addres */
+		for (i = 0x10; i > 0; i = i >> 1)
+			phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+
+		/* Send register addres */
+		for (i = 0x10; i > 0; i = i >> 1)
+			phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
 
-	/* written trasnition */
-	phy_write_1bit(ioaddr, PHY_DATA_1);
-	phy_write_1bit(ioaddr, PHY_DATA_0);
+		/* written trasnition */
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+		phy_write_1bit(ioaddr, PHY_DATA_0);
 
-	/* Write a word data to PHY controller */
-	for (i = 0x8000; i > 0; i >>= 1)
-		phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+		/* Write a word data to PHY controller */
+		for (i = 0x8000; i > 0; i >>= 1)
+			phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+	}
 }
 
 /*
- *	Read a word data from phy register
+   Read a word data from phy register
  */
- 
-static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset)
+static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset, u32 chip_id)
 {
 	int i;
 	u16 phy_data;
-	u32 ioaddr = iobase + DCR9;
+	u32 ioaddr;
 
-	/* Send 33 synchronization clock to Phy controller */
-	for (i = 0; i < 35; i++)
+	if (chip_id == PCI_DM9132_ID) {
+		/* DM9132 Chip */
+		ioaddr = iobase + 0x80 + offset * 4;
+		phy_data = inw(ioaddr);
+	} else {
+		/* DM9102/DM9102A Chip */
+
+		ioaddr = iobase + DCR9;
+		/* Send 33 synchronization clock to Phy controller */
+		for (i = 0; i < 35; i++)
+			phy_write_1bit(ioaddr, PHY_DATA_1);
+
+		/* Send start command(01) to Phy */
+		phy_write_1bit(ioaddr, PHY_DATA_0);
 		phy_write_1bit(ioaddr, PHY_DATA_1);
 
-	/* Send start command(01) to Phy */
-	phy_write_1bit(ioaddr, PHY_DATA_0);
-	phy_write_1bit(ioaddr, PHY_DATA_1);
-
-	/* Send read command(10) to Phy */
-	phy_write_1bit(ioaddr, PHY_DATA_1);
-	phy_write_1bit(ioaddr, PHY_DATA_0);
-
-	/* Send Phy addres */
-	for (i = 0x10; i > 0; i = i >> 1)
-		phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
-
-	/* Send register addres */
-	for (i = 0x10; i > 0; i = i >> 1)
-		phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
-
-	/* Skip transition state */
-	phy_read_1bit(ioaddr);
-
-	/* read 16bit data */
-	for (phy_data = 0, i = 0; i < 16; i++) {
-		phy_data <<= 1;
-		phy_data |= phy_read_1bit(ioaddr);
+		/* Send read command(10) to Phy */
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+		phy_write_1bit(ioaddr, PHY_DATA_0);
+
+		/* Send Phy addres */
+		for (i = 0x10; i > 0; i = i >> 1)
+			phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+
+		/* Send register addres */
+		for (i = 0x10; i > 0; i = i >> 1)
+			phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+
+		/* Skip transition state */
+		phy_read_1bit(ioaddr);
+
+		/* read 16bit data */
+		for (phy_data = 0, i = 0; i < 16; i++) {
+			phy_data <<= 1;
+			phy_data |= phy_read_1bit(ioaddr);
+		}
 	}
 
 	return phy_data;
 }
 
 /*
- *	Write one bit data to Phy Controller
+   Write one bit data to Phy Controller
  */
- 
 static void phy_write_1bit(u32 ioaddr, u32 phy_data)
 {
 	outl(phy_data, ioaddr);	/* MII Clock Low */
@@ -1395,9 +1497,8 @@
 }
 
 /*
- *	Read one bit phy data from PHY controller
+   Read one bit phy data from PHY controller
  */
- 
 static u16 phy_read_1bit(u32 ioaddr)
 {
 	u16 phy_data;
@@ -1412,10 +1513,11 @@
 }
 
 /*
- *	Calculate the CRC valude of the Rx packet
+   Calculate the CRC valude of the Rx packet
+   flag = 1 : return the reverse CRC (for the received packet CRC)
+   0 : return the normal CRC (for Hash Table index)
  */
- 
-static unsigned long cal_CRC(unsigned char *Data, unsigned int Len)
+unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag)
 {
 	unsigned long Crc = 0xffffffff;
 
@@ -1423,8 +1525,10 @@
 		Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
 	}
 
-	return ~Crc;
-
+	if (flag)
+		return ~Crc;
+	else
+		return Crc;
 }
 
 
@@ -1461,7 +1565,7 @@
 		break;
 	}
 
-	return dmfe_reg_board();	/* search board and register */
+	return dmfe_probe();	/* search board and register */
 }
 
 /*
@@ -1473,14 +1577,16 @@
 static void __exit dmfe_cleanup_module(void)
 {
 	struct net_device *next_dev;
+	struct dmfe_board_info *db;
 
 	DMFE_DBUG(0, "clean_module()", 0);
 
 	while (dmfe_root_dev) {
-		next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev;
+		db = dmfe_root_dev->priv;
+		next_dev = db->next_dev;
 		unregister_netdev(dmfe_root_dev);
-		release_region(dmfe_root_dev->base_addr, DM9102_IO_SIZE);
-		kfree(dmfe_root_dev->priv);	/* free board information */
+		release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion));
+		kfree(db);	/* free board information */
 		kfree(dmfe_root_dev);	/* free device structure */
 		dmfe_root_dev = next_dev;
 	}
diff -u --recursive --new-file v2.3.51/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v2.3.51/linux/drivers/net/eexpress.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/eexpress.c	Sun Mar 12 19:18:55 2000
@@ -712,6 +712,7 @@
 			ack_cmd |= SCB_RUstart;
 			scb_wrrfa(dev, lp->rx_buf_start);
 			lp->rx_ptr = lp->rx_buf_start;
+			lp->started |= STARTED_RU;
 		}
 		ack_cmd |= SCB_CUstart | 0x2000;
 	}
diff -u --recursive --new-file v2.3.51/linux/drivers/net/epic100.c linux/drivers/net/epic100.c
--- v2.3.51/linux/drivers/net/epic100.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/epic100.c	Mon Mar 13 09:50:16 2000
@@ -1071,7 +1071,7 @@
 		}
 		return 0;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		if (! netif_running(dev)) {
 			outl(0x0200, ioaddr + GENCTL);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/eql.c linux/drivers/net/eql.c
--- v2.3.51/linux/drivers/net/eql.c	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/eql.c	Mon Mar 13 09:50:16 2000
@@ -42,7 +42,7 @@
  * reformatted.
  *
  * Revision 3.12  1995/03/22  21:07:51  anarchy
- * Added suser() checks on configuration.
+ * Added capable() checks on configuration.
  * Moved header file.
  *
  * Revision 3.11  1995/01/19  23:14:31  guru
@@ -1030,6 +1030,8 @@
 
 void cleanup_module(void)
 {
+	kfree(((equalizer_t *)dev_eql.priv)->stats );
+	kfree(dev_eql.priv);
 	unregister_netdev(&dev_eql);
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c
--- v2.3.51/linux/drivers/net/ewrk3.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/net/ewrk3.c	Mon Mar 13 09:50:16 2000
@@ -1836,7 +1836,7 @@
 			status = -EFAULT;
 		break;
 	case EWRK3_SET_TX_CUT_THRU:	/* Set TX cut through mode */
-		if (suser()) {
+		if (capable(CAP_NET_ADMIN)) {
 			lp->txc = 1;
 		} else {
 			status = -EPERM;
@@ -1844,7 +1844,7 @@
 
 		break;
 	case EWRK3_CLR_TX_CUT_THRU:	/* Clear TX cut through mode */
-		if (suser()) {
+		if (capable(CAP_NET_ADMIN)) {
 			lp->txc = 0;
 		} else {
 			status = -EPERM;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/baycom_epp.c linux/drivers/net/hamradio/baycom_epp.c
--- v2.3.51/linux/drivers/net/hamradio/baycom_epp.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/hamradio/baycom_epp.c	Mon Mar 13 09:50:16 2000
@@ -1254,7 +1254,7 @@
 		break;
 
 	case HDLCDRVCTL_SETCHANNELPAR:
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
 		bc->ch_params.tx_delay = hi.data.cp.tx_delay;
 		bc->ch_params.tx_tail = hi.data.cp.tx_tail;
@@ -1275,7 +1275,7 @@
 		break;
 
 	case HDLCDRVCTL_SETMODEMPAR:
-		if ((!suser()) || netif_running(dev))
+		if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
 			return -EACCES;
 		dev->base_addr = hi.data.mp.iobase;
 		dev->irq = /*hi.data.mp.irq*/0;
@@ -1299,6 +1299,8 @@
 		break;		
 
 	case HDLCDRVCTL_CALIBRATE:
+		if (!capable(CAP_SYS_RAWIO))
+			return -EACCES;
 		bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8;
 		return 0;
 
@@ -1314,7 +1316,7 @@
 		break;
 
 	case HDLCDRVCTL_SETMODE:
-		if (!suser() || netif_running(dev))
+		if (!capable(CAP_NET_ADMIN) || netif_running(dev))
 			return -EACCES;
 		hi.data.modename[sizeof(hi.data.modename)-1] = '\0';
 		return baycom_setmode(bc, hi.data.modename);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/baycom_par.c linux/drivers/net/hamradio/baycom_par.c
--- v2.3.51/linux/drivers/net/hamradio/baycom_par.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/hamradio/baycom_par.c	Mon Mar 13 09:50:16 2000
@@ -445,7 +445,7 @@
 		return 0;
 
 	case HDLCDRVCTL_SETMODE:
-		if (netif_running(dev) || !suser())
+		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
 			return -EACCES;
 		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
 		return baycom_setmode(bc, hi->data.modename);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/baycom_ser_fdx.c linux/drivers/net/hamradio/baycom_ser_fdx.c
--- v2.3.51/linux/drivers/net/hamradio/baycom_ser_fdx.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/hamradio/baycom_ser_fdx.c	Mon Mar 13 09:50:16 2000
@@ -555,7 +555,7 @@
 		return 0;
 
 	case HDLCDRVCTL_SETMODE:
-		if (netif_running(dev) || !suser())
+		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
 			return -EACCES;
 		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
 		return baycom_setmode(bc, hi->data.modename);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/baycom_ser_hdx.c linux/drivers/net/hamradio/baycom_ser_hdx.c
--- v2.3.51/linux/drivers/net/hamradio/baycom_ser_hdx.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/hamradio/baycom_ser_hdx.c	Mon Mar 13 09:50:16 2000
@@ -598,7 +598,7 @@
 		return 0;
 
 	case HDLCDRVCTL_SETMODE:
-		if (netif_running(dev) || !suser())
+		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
 			return -EACCES;
 		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
 		return baycom_setmode(bc, hi->data.modename);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/bpqether.c linux/drivers/net/hamradio/bpqether.c
--- v2.3.51/linux/drivers/net/hamradio/bpqether.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/hamradio/bpqether.c	Mon Mar 13 09:50:16 2000
@@ -366,7 +366,7 @@
 	struct bpqdev *bpq = dev->priv;
 	struct bpq_req req;
 
-	if (!suser())
+	if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
 
 	if (bpq == NULL)		/* woops! */
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/hdlcdrv.c linux/drivers/net/hamradio/hdlcdrv.c
--- v2.3.51/linux/drivers/net/hamradio/hdlcdrv.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/hamradio/hdlcdrv.c	Mon Mar 13 09:50:16 2000
@@ -635,7 +635,7 @@
 		break;
 
 	case HDLCDRVCTL_SETCHANNELPAR:
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
 		s->ch_params.tx_delay = bi.data.cp.tx_delay;
 		s->ch_params.tx_tail = bi.data.cp.tx_tail;
@@ -656,7 +656,7 @@
 		break;
 
 	case HDLCDRVCTL_SETMODEMPAR:
-		if ((!suser()) || netif_running(dev))
+		if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
 			return -EACCES;
 		dev->base_addr = bi.data.mp.iobase;
 		dev->irq = bi.data.mp.irq;
@@ -684,6 +684,8 @@
 		break;		
 
 	case HDLCDRVCTL_CALIBRATE:
+		if(!capable(CAP_SYS_RAWIO))
+			return -EPERM;
 		s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
 		return 0;
 
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/pi2.c linux/drivers/net/hamradio/pi2.c
--- v2.3.51/linux/drivers/net/hamradio/pi2.c	Wed Aug 18 11:38:50 1999
+++ linux/drivers/net/hamradio/pi2.c	Mon Mar 13 09:50:16 2000
@@ -1580,7 +1580,7 @@
     switch (rq.cmd) {
     case SIOCSPIPARAM:
 
-	if (!suser())
+	if (!capable(CAP_NET_ADMIN))
 	    return -EPERM;
 	save_flags(flags);
 	cli();
@@ -1597,7 +1597,7 @@
 
     case SIOCSPIDMA:
 
-	if (!suser())
+	if (!capable(CAP_SYS_RAWIO))
 	    return -EPERM;
 	ret = 0;
 	if (dev->base_addr & 2) {   /* if A channel */
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/pt.c linux/drivers/net/hamradio/pt.c
--- v2.3.51/linux/drivers/net/hamradio/pt.c	Wed Aug 18 11:38:50 1999
+++ linux/drivers/net/hamradio/pt.c	Mon Mar 13 09:50:16 2000
@@ -998,7 +998,7 @@
     switch (rq.cmd) {
     case SIOCSPIPARAM:
 
-	if (!suser())
+	if (!capable(CAP_NET_ADMIN))
 	    return -EPERM;
 	save_flags(flags);
 	cli();
@@ -1015,7 +1015,7 @@
 
     case SIOCSPIDMA:
 
-	if (!suser())
+	if (!capable(CAP_SYS_RAWIO))
 	    return -EPERM;
 	ret = 0;
 	if (dev->base_addr & CHANA) {   /* if A channel */
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/scc.c linux/drivers/net/hamradio/scc.c
--- v2.3.51/linux/drivers/net/hamradio/scc.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/net/hamradio/scc.c	Mon Mar 13 09:50:16 2000
@@ -1791,7 +1791,7 @@
 		{
 			int found = 1;
 
-			if (!suser()) return -EPERM;
+			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
 			if (!arg) return -EFAULT;
 
 			if (Nchips >= SCC_MAXCHIPS) 
@@ -1887,7 +1887,7 @@
 		
 		if (cmd == SIOCSCCINI)
 		{
-			if (!suser())
+			if (!capable(CAP_SYS_RAWIO))
 				return -EPERM;
 				
 			if (Nchips == 0)
@@ -1904,7 +1904,7 @@
 	{
 		if (cmd == SIOCSCCCHANINI)
 		{
-			if (!suser()) return -EPERM;
+			if (!capable(CAP_NET_ADMIN)) return -EPERM;
 			if (!arg) return -EINVAL;
 			
 			scc->stat.bufsize   = SCC_BUFSIZE;
@@ -1957,7 +1957,7 @@
 			return -ENOIOCTLCMD;
 
 		case SIOCSCCSMEM:
-			if (!suser()) return -EPERM;
+			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
 			if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
 				return -EINVAL;
 			scc->stat.bufsize   = memcfg.bufsize;
@@ -1977,13 +1977,13 @@
 			return 0;
 		
 		case SIOCSCCSKISS:
-			if (!suser()) return -EPERM;
+			if (!capable(CAP_NET_ADMIN)) return -EPERM;
 			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
 				return -EINVAL;
 			return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
 		
 		case SIOCSCCCAL:
-			if (!suser()) return -EPERM;
+			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
 			if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0)
 				return -EINVAL;
 
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/soundmodem/sm.c linux/drivers/net/hamradio/soundmodem/sm.c
--- v2.3.51/linux/drivers/net/hamradio/soundmodem/sm.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/hamradio/soundmodem/sm.c	Mon Mar 13 09:50:16 2000
@@ -509,7 +509,7 @@
 		return 0;
 
 	case HDLCDRVCTL_SETMODE:
-		if (netif_running(dev) || !suser())
+		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
 			return -EACCES;
 		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
 		return sethw(dev, sm, hi->data.modename);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/soundmodem/sm_sbc.c linux/drivers/net/hamradio/soundmodem/sm_sbc.c
--- v2.3.51/linux/drivers/net/hamradio/soundmodem/sm_sbc.c	Wed Aug 18 11:38:50 1999
+++ linux/drivers/net/hamradio/soundmodem/sm_sbc.c	Mon Mar 13 09:50:16 2000
@@ -1,4 +1,4 @@
-/*****************************************************************************/
+
 
 /*
  *	sm_sbc.c  -- soundcard radio modem driver soundblaster hardware driver
@@ -576,7 +576,7 @@
 		return i;
 		
 	case SMCTL_SETMIXER:
-		if (!suser())
+		if (!capable(CAP_SYS_RAWIO))
 			return -EACCES;
 		switch (SCSTATE->revhi) {
 		case 2:
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/soundmodem/sm_wss.c linux/drivers/net/hamradio/soundmodem/sm_wss.c
--- v2.3.51/linux/drivers/net/hamradio/soundmodem/sm_wss.c	Wed Aug 18 11:38:51 1999
+++ linux/drivers/net/hamradio/soundmodem/sm_wss.c	Mon Mar 13 09:50:16 2000
@@ -637,7 +637,7 @@
 		return i;
 
 	case SMCTL_SETMIXER:
-		if (!suser())
+		if (!capable(CAP_SYS_RAWIO))
 			return -EACCES;
 		if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || 
 		     !SCSTATE->crystal) &&
diff -u --recursive --new-file v2.3.51/linux/drivers/net/hamradio/yam.c linux/drivers/net/hamradio/yam.c
--- v2.3.51/linux/drivers/net/hamradio/yam.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/hamradio/yam.c	Mon Mar 13 09:50:16 2000
@@ -962,7 +962,7 @@
 	if (yp == NULL || yp->magic != YAM_MAGIC)
 		return -EINVAL;
 
-	if (!suser())
+	if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
 
 	if (cmd != SIOCDEVPRIVATE)
@@ -977,6 +977,8 @@
 		if (netif_running(dev))
 			return -EINVAL;		/* Cannot change this parameter when up */
 		ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_ATOMIC);
+		if(ym==NULL)
+			return -ENOBUFS;
 		ym->bitrate = 9600;
 		if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs)))
 			 return -EFAULT;
@@ -987,6 +989,8 @@
 		break;
 
 	case SIOCYAMSCFG:
+		if (!capable(CAP_SYS_RAWIO))
+			return -EPERM;
 		if (copy_from_user(&yi, ifr->ifr_data, sizeof(struct yamdrv_ioctl_cfg)))
 			 return -EFAULT;
 
diff -u --recursive --new-file v2.3.51/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v2.3.51/linux/drivers/net/lance.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/lance.c	Sun Mar 12 19:18:56 2000
@@ -14,22 +14,8 @@
 	Center of Excellence in Space Data and Information Sciences
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-	Fixing alignment problem with 1.3.* kernel and some minor changes
-	by Andrey V. Savochkin, 1996.
-
-	Problems or questions may be send to Donald Becker (see above) or to
-	Andrey Savochkin -- saw@shade.msu.ru or
-		Laboratory of Computation Methods, 
-		Department of Mathematics and Mechanics,
-		Moscow State University,
-		Leninskye Gory, Moscow 119899
-
-	But I should to inform you that I'm not an expert in the LANCE card
-	and it may occurs that you will receive no answer on your mail
-	to Donald Becker. I didn't receive any answer on all my letters
-	to him. Who knows why... But may be you are more lucky?  ;->
-                                                          SAW
-
+	Andrey V. Savochkin:
+	- alignment problem with 1.3.* kernel and some minor changes.
 	Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
 	- added support for Linux/Alpha, but removed most of it, because
         it worked only for the PCI chip. 
@@ -785,11 +771,19 @@
 */
 
 static void 
-lance_purge_tx_ring(struct net_device *dev)
+lance_purge_ring(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *)dev->priv;
 	int i;
 
+	/* Free all the skbuffs in the Rx and Tx queues. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = lp->rx_skbuff[i];
+		lp->rx_skbuff[i] = 0;
+		lp->rx_ring[i].base = 0;		/* Not owned by LANCE chip. */
+		if (skb)
+			dev_kfree_skb(skb);
+	}
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		if (lp->tx_skbuff[i]) {
 			dev_kfree_skb(lp->tx_skbuff[i]);
@@ -850,7 +844,7 @@
 
 	if (must_reinit ||
 		(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
-		lance_purge_tx_ring(dev);
+		lance_purge_ring(dev);
 		lance_init_ring(dev, GFP_ATOMIC);
 	}
 	outw(0x0000,    dev->base_addr + LANCE_ADDR);
@@ -869,7 +863,7 @@
 	outw (0x0004, ioaddr + LANCE_DATA);
 	lp->stats.tx_errors++;
 #ifndef final_version
-	{
+	if (lance_debug > 3) {
 		int i;
 		printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
 		  lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
@@ -1192,19 +1186,7 @@
 	}
 	free_irq(dev->irq, dev);
 
-	/* Free all the skbuffs in the Rx and Tx queues. */
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		struct sk_buff *skb = lp->rx_skbuff[i];
-		lp->rx_skbuff[i] = 0;
-		lp->rx_ring[i].base = 0;		/* Not owned by LANCE chip. */
-		if (skb)
-			dev_kfree_skb(skb);
-	}
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		if (lp->tx_skbuff[i])
-			dev_kfree_skb(lp->tx_skbuff[i]);
-		lp->tx_skbuff[i] = 0;
-	}
+	lance_purge_ring(dev);
 
 	MOD_DEC_USE_COUNT;
 	return 0;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/pcmcia/3c574_cs.c linux/drivers/net/pcmcia/3c574_cs.c
--- v2.3.51/linux/drivers/net/pcmcia/3c574_cs.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/pcmcia/3c574_cs.c	Mon Mar 13 11:09:10 2000
@@ -920,8 +920,6 @@
 		  "status %4.4x.\n", dev->name, (long)skb->len,
 		  inw(ioaddr + EL3_STATUS));
 
-	netif_stop_queue (dev);
-	
 	outw(skb->len, ioaddr + TX_FIFO);
 	outw(0, ioaddr + TX_FIFO);
 	outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
@@ -929,13 +927,13 @@
 	dev->trans_start = jiffies;
 
 	/* TxFree appears only in Window 1, not offset 0x1c. */
-	if (inw(ioaddr + TxFree) > 1536) {
-		netif_start_queue (dev);
-	} else
+	if (inw(ioaddr + TxFree) <= 1536) {
+		netif_stop_queue (dev);
 		/* Interrupt us when the FIFO has room for max-sized packet. 
 		   The threshold is in units of dwords. */
 		outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
-
+	}
+	
 	dev_kfree_skb (skb);
 	pop_tx_status(dev);
 
@@ -976,8 +974,6 @@
 			/* There's room in the FIFO for a full-sized packet. */
 			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
 			netif_wake_queue (dev);
-		} else {
-			netif_stop_queue (dev);
 		}
 
 		if (status & TxComplete)
diff -u --recursive --new-file v2.3.51/linux/drivers/net/pcmcia/Config.in linux/drivers/net/pcmcia/Config.in
--- v2.3.51/linux/drivers/net/pcmcia/Config.in	Fri Mar 10 16:40:42 2000
+++ linux/drivers/net/pcmcia/Config.in	Sun Mar 12 19:24:42 2000
@@ -18,7 +18,6 @@
 
    if [ "$CONFIG_CARDBUS" = "y" ]; then
       dep_tristate '  3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 m
-      dep_tristate '  DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP m
    fi
 
    bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
diff -u --recursive --new-file v2.3.51/linux/drivers/net/pcmcia/Makefile linux/drivers/net/pcmcia/Makefile
--- v2.3.51/linux/drivers/net/pcmcia/Makefile	Wed Feb 16 17:03:52 2000
+++ linux/drivers/net/pcmcia/Makefile	Sun Mar 12 19:24:42 2000
@@ -20,7 +20,6 @@
 export-objs	:= ray_cs.o
 
 CFLAGS_3c575_cb.o = -DCARDBUS -DMODULE
-CFLAGS_tulip_cb.o = -DCARDBUS -DMODULE
 
 # 16-bit client drivers
 obj-$(CONFIG_PCMCIA_3C589)	+= 3c589_cs.o
@@ -40,7 +39,6 @@
 
 # Cardbus client drivers
 obj-$(CONFIG_PCMCIA_3C575)	+= 3c575_cb.o
-obj-$(CONFIG_PCMCIA_TULIP)	+= tulip_cb.o
 
 O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
 OX_OBJS		:= $(filter     $(export-objs), $(obj-y))
diff -u --recursive --new-file v2.3.51/linux/drivers/net/pcmcia/ray_cs.c linux/drivers/net/pcmcia/ray_cs.c
--- v2.3.51/linux/drivers/net/pcmcia/ray_cs.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/pcmcia/ray_cs.c	Mon Mar 13 09:50:16 2000
@@ -1417,7 +1417,7 @@
 #define SIOCGIPFRAMING	SIOCDEVPRIVATE + 1	/* Get framing mode */
 #define SIOCGIPCOUNTRY	SIOCDEVPRIVATE + 3	/* Get country code */
     case SIOCSIPFRAMING:
-      if(!suser())	/* For private IOCTLs, we need to check permissions */
+      if(!capable(CAP_NET_ADMIN))	/* For private IOCTLs, we need to check permissions */
 	{
 	  err = -EPERM;
 	  break;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/pcmcia/tulip_cb.c linux/drivers/net/pcmcia/tulip_cb.c
--- v2.3.51/linux/drivers/net/pcmcia/tulip_cb.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/pcmcia/tulip_cb.c	Wed Dec 31 16:00:00 1969
@@ -1,3150 +0,0 @@
-/* tulip.c: A DEC 21040-family ethernet driver for Linux. */
-/*
-	Written/copyright 1994-1999 by Donald Becker.
-
-	This software may be used and distributed according to the terms
-	of the GNU Public License, incorporated herein by reference.
-
-	This driver is for the Digital "Tulip" Ethernet adapter interface.
-	It should work with most DEC 21*4*-based chips/ethercards, as well as
-	with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX.
-
-	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
-	Center of Excellence in Space Data and Information Sciences
-	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
-
-	Support and updates available at
-	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
-*/
-
-#define SMP_CHECK
-static const char version[] = "tulip.c:v0.91 4/14/99 becker@cesdis.gsfc.nasa.gov (modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford)\n";
-
-/* A few user-configurable values. */
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 25;
-
-#define MAX_UNITS 8
-/* Used to pass the full-duplex flag, etc. */
-static int full_duplex[MAX_UNITS] = {0, };
-static int options[MAX_UNITS] = {0, };
-static int mtu[MAX_UNITS] = {0, };			/* Jumbo MTU for interfaces. */
-
-/*  The possible media types that can be set in options[] are: */
-static const char * const medianame[] = {
-	"10baseT", "10base2", "AUI", "100baseTx",
-	"10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
-	"100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
-	"10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
-};
-
-/* Keep the ring sizes a power of two for efficiency.
-   Making the Tx ring too large decreases the effectiveness of channel
-   bonding and packet priority.
-   There are no ill effects from too-large receive rings. */
-#define TX_RING_SIZE	16
-#define RX_RING_SIZE	32
-
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#ifdef __alpha__
-static int rx_copybreak = 1518;
-#else
-static int rx_copybreak = 100;
-#endif
-
-/*
-  Set the bus performance register.
-	Typical: Set 16 longword cache alignment, no burst limit.
-	Cache alignment bits 15:14	     Burst length 13:8
-		0000	No alignment  0x00000000 unlimited		0800 8 longwords
-		4000	8  longwords		0100 1 longword		1000 16 longwords
-		8000	16 longwords		0200 2 longwords	2000 32 longwords
-		C000	32  longwords		0400 4 longwords
-	Warning: many older 486 systems are broken and require setting 0x00A04800
-	   8 longword cache alignment, 8 longword burst.
-	ToDo: Non-Intel setting could be better.
-*/
-
-#if defined(__alpha__)
-static int csr0 = 0x01A00000 | 0xE000;
-#elif defined(__powerpc__)
-static int csr0 = 0x01B00000 | 0x8000;
-#elif defined(__sparc__)
-static int csr0 = 0x01B00080 | 0x8000;
-#elif defined(__i386__)
-static int csr0 = 0x01A00000 | 0x8000;
-#else
-#warning Processor architecture undefined!
-static int csr0 = 0x00A00000 | 0x4800;
-#endif
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  (4*HZ)
-#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
-/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
-   to support a pre-NWay full-duplex signaling mechanism using short frames.
-   No one knows what it should be, but if left at its default value some
-   10base2(!) packets trigger a full-duplex-request interrupt. */
-#define FULL_DUPLEX_MAGIC	0x6969
-
-#if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
-#warning  You must compile this file with the correct options!
-#warning  See the last lines of the source file.
-#error You must compile this driver with "-O".
-#endif
-
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <asm/processor.h>		/* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/unaligned.h>
-
-/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
-   This is only in the support-all-kernels source code. */
-
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(reverse_probe, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(csr0, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-
-#define RUN_AT(x) (jiffies + (x))
-
-#define tulip_debug debug
-#ifdef TULIP_DEBUG
-static int tulip_debug = TULIP_DEBUG;
-#else
-static int tulip_debug = 1;
-#endif
-
-/*
-				Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the DECchip "Tulip", Digital's
-single-chip ethernet controllers for PCI.  Supported members of the family
-are the 21040, 21041, 21140, 21140A, 21142, and 21143.  Similar work-alike
-chips from Lite-On, Macronics, ASIX, Compex and other listed below are also
-supported. 
-
-These chips are used on at least 140 unique PCI board designs.  The great
-number of chips and board designs supported is the reason for the
-driver size and complexity.  Almost of the increasing complexity is in the
-board configuration and media selection code.  There is very little
-increasing in the operational critical path length.
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board.  The system BIOS preferably should assign the
-PCI INTA signal to an otherwise unused system IRQ line.
-
-Some boards have EEPROMs tables with default media entry.  The factory default
-is usually "autoselect".  This should only be overridden when using
-transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!)
-for forcing full-duplex when used with old link partners that do not do
-autonegotiation. 
-
-III. Driver operation
-
-IIIa. Ring buffers
-
-The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
-This driver uses statically allocated rings of Rx and Tx descriptors, set at
-compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs
-for the Rx ring buffers at open() time and passes the skb->data field to the
-Tulip as receive data buffers.  When an incoming frame is less than
-RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
-copied to the new skbuff.  When the incoming frame is larger, the skbuff is
-passed directly up the protocol stack and replaced by a newly allocated
-skbuff.
-
-The RX_COPYBREAK value is chosen to trade-off the memory wasted by
-using a full-sized skbuff for small frames vs. the copying costs of larger
-frames.  For small frames the copying cost is negligible (esp. considering
-that we are pre-loading the cache with immediately useful header
-information).  For large frames the copying cost is non-trivial, and the
-larger copy might flush the cache of useful data.  A subtle aspect of this
-choice is that the Tulip only receives into longword aligned buffers, thus
-the IP header at offset 14 isn't longword aligned for further processing.
-Copied frames are put into the new skbuff at an offset of "+2", thus copying
-has the beneficial effect of aligning the IP header and preloading the
-cache.
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control.  One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag.  The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'tp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.)	 After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero.	 Iff the 'tp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-IV. Notes
-
-Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board.
-Greg LaPolla at Linksys provided PNIC and other Linksys boards.
-Znyx provided a four-port card for testing.
-
-IVb. References
-
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
-http://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM")
-http://www.national.com/pf/DP/DP83840A.html
-http://www.asix.com.tw/pmac.htm
-http://www.admtek.com.tw/
-
-IVc. Errata
-
-The old DEC databooks were light on details.
-The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written.  Hmmm, now how is that possible?
-
-The DEC SROM format is very badly designed not precisely defined, leading to
-part of the media selection junkheap below.  Some boards do not have EEPROM
-media tables and need to be patched up.  Worse, other boards use the DEC
-design kit media table when it isn't correct for their board.
-
-We cannot use MII interrupts because there is no defined GPIO pin to attach
-them.  The MII transceiver status is polled using an kernel timer.
-
-*/
-
-/* This table use during operation for capabilities and media timer. */
-
-static void tulip_timer(unsigned long data);
-static void t21142_timer(unsigned long data);
-static void mxic_timer(unsigned long data);
-static void pnic_timer(unsigned long data);
-static void comet_timer(unsigned long data);
-
-enum tbl_flag {
-	HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
-	HAS_ACPI=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
-	HAS_NWAY143=0x40,			/* Uses 21143-like internal NWay. */
-};
-static struct tulip_chip_table {
-	char *chip_name;
-	int io_size;
-	int valid_intrs;			/* CSR7 interrupt enable settings */
-	int flags;
-	void (*media_timer)(unsigned long data);
-} tulip_tbl[] = {
-  { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
-  { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
-  { "Digital DS21140 Tulip", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
-  { "Digital DS21143 Tulip", 128, 0x0801fbff,
-	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143, t21142_timer },
-  { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
-	HAS_MII, pnic_timer },
-  { "Macronix 98713 PMAC", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
-  { "Macronix 98715 PMAC", 256, 0x0001ebef,
-	HAS_MEDIA_TABLE, mxic_timer },
-  { "Macronix 98725 PMAC", 256, 0x0001ebef,
-	HAS_MEDIA_TABLE, mxic_timer },
-  { "ASIX AX88140", 128, 0x0001fbff,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer },
-  { "Lite-On PNIC-II", 256, 0x0001ebef,
-	HAS_MII | HAS_NWAY143, pnic_timer },
-  { "ADMtek Comet", 256, 0x0001abef,
-	MC_HASH_ONLY, comet_timer },
-  { "Compex 9881 PMAC", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
-  { "Xircom Cardbus Adapter (DEC 21143 compatible mode)", 128, 0x0801fbff,
-       HAS_MII | HAS_ACPI, tulip_timer }, 
-  {0},
-};
-/* This matches the table above.  Note 21142 == 21143. */
-enum chips {
-	DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
-	LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881,
-	X3201_3,
-};
-
-/* A full-duplex map for media types. */
-enum MediaIs {
-	MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
-	MediaIs100=16};
-static const char media_cap[] =
-{0,0,0,16,  3,19,16,24,  27,4,7,5, 0,20,23,20 };
-static u8 t21040_csr13[] = {2,0x0C,8,4,  4,0,0,0, 0,0,0,0, 4,0,0,0};
-/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
-static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
-static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
-static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
-static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
-static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
-static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
-/* Offsets to the Command and Status Registers, "CSRs".  All accesses
-   must be longword instructions and quadword aligned. */
-enum tulip_offsets {
-	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
-	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
-	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
-
-/* The bits in the CSR5 status registers, mostly interrupt sources. */
-enum status_bits {
-	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
-	NormalIntr=0x10000, AbnormalIntr=0x8000,
-	RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
-	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
-};
-
-/* The Tulip Rx and Tx buffer descriptors. */
-struct tulip_rx_desc {
-	s32 status;
-	s32 length;
-	u32 buffer1, buffer2;
-};
-
-struct tulip_tx_desc {
-	s32 status;
-	s32 length;
-	u32 buffer1, buffer2;				/* We use only buffer 1.  */
-};
-
-enum desc_status_bits {
-	DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
-};
-
-/* Ring-wrap flag in length field, use for last ring entry.
-	0x01000000 means chain on buffer2 address,
-	0x02000000 means use the ring start address in CSR2/3.
-   Note: Some work-alike chips do not function correctly in chained mode.
-   The ASIX chip works only in chained mode.
-   Thus we indicates ring mode, but always write the 'next' field for
-   chained mode as well.
-*/
-#define DESC_RING_WRAP 0x02000000
-
-#ifdef CARDBUS
-#define EEPROM_ADDRLEN (chip_rev == 65 ? 8 : 6)
-#else
-#define EEPROM_ADDRLEN 6
-#endif
-#define EEPROM_SIZE 128 	/* 2 << EEPROM_ADDRLEN */
-
-struct medialeaf {
-	u8 type;
-	u8 media;
-	unsigned char *leafdata;
-};
-
-struct mediatable {
-	u16 defaultmedia;
-	u8 leafcount, csr12dir;				/* General purpose pin directions. */
-	unsigned has_mii:1, has_nonmii:1, has_reset:6;
-	u32 csr15dir, csr15val;				/* 21143 NWay setting. */
-	struct medialeaf mleaf[0];
-};
-
-struct mediainfo {
-	struct mediainfo *next;
-	int info_type;
-	int index;
-	unsigned char *info;
-};
-
-struct tulip_private {
-	char devname[8];			/* Used only for kernel debugging. */
-	const char *product_name;
-	struct tulip_rx_desc rx_ring[RX_RING_SIZE];
-	struct tulip_tx_desc tx_ring[TX_RING_SIZE];
-	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
-	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-#ifdef CARDBUS
-	/* The X3201-3 requires double word aligned tx bufs */
-	struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];
-#endif
-	/* The addresses of receive-in-place skbuffs. */
-	struct sk_buff* rx_skbuff[RX_RING_SIZE];
-	char *rx_buffs;				/* Address of temporary Rx buffers. */
-	u8 setup_buf[96*sizeof(u16) + 7];
-	u16 *setup_frame;		/* Pseudo-Tx frame to init address table. */
-	int chip_id;
-	int revision;
-	struct net_device_stats stats;
-	struct timer_list timer;	/* Media selection timer. */
-	int interrupt;				/* In-interrupt flag. */
-	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
-	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
-	unsigned int tx_full:1;				/* The Tx queue is full. */
-	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int full_duplex_lock:1;
-	unsigned int fake_addr:1;			/* Multiport board faked address. */
-	unsigned int default_port:4;		/* Last dev->if_port value. */
-	unsigned int media2:4;				/* Secondary monitored media port. */
-	unsigned int medialock:1;			/* Don't sense media type. */
-	unsigned int mediasense:1;			/* Media sensing in progress. */
-	unsigned int nway:1, nwayset:1;		/* 21143 internal NWay. */
-	unsigned int open:1;
-	unsigned int csr0;					/* CSR0 setting. */
-	unsigned int csr6;					/* Current CSR6 control settings. */
-	unsigned char eeprom[EEPROM_SIZE];	/* Serial EEPROM contents. */
-	u16 to_advertise;					/* NWay capabilities advertised.  */
-	u16 lpar;							/* 21143 Link partner ability. */
-	u16 advertising[4];
-	signed char phys[4], mii_cnt;		/* MII device addresses. */
-	struct mediatable *mtable;
-	int cur_index;						/* Current media index. */
-	int saved_if_port;
-	struct pci_dev *pdev;
-	spinlock_t lock;
-	int pad0, pad1;						/* Used for 8-byte alignment */
-};
-
-static void parse_eeprom(struct net_device *dev);
-static int read_eeprom(long ioaddr, int location, int addr_len);
-static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
-static void select_media(struct net_device *dev, int startup);
-static void tulip_up(struct net_device *dev);
-static void tulip_down(struct net_device *dev);
-static int tulip_open(struct net_device *dev);
-static void tulip_timer(unsigned long data);
-static void t21142_start_nway(struct net_device *dev);
-static void tulip_tx_timeout(struct net_device *dev);
-static void tulip_init_ring(struct net_device *dev);
-static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static int tulip_rx(struct net_device *dev);
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int tulip_close(struct net_device *dev);
-static struct net_device_stats *tulip_get_stats(struct net_device *dev);
-#ifdef HAVE_PRIVATE_IOCTL
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-#endif
-static void set_rx_mode(struct net_device *dev);
-
-/* The Xircom cards are picky about when certain bits in CSR6 can be
-   manipulated.  Keith Owens <kaos@ocs.com.au>. */
-
-static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx)
-{
-	const int strict_bits = 0x0060e202;
-    int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
-    long flags;
-    save_flags(flags);
-    cli();
-    if (chip_idx != X3201_3) {
-		outl(newcsr6, ioaddr + CSR6);
-		restore_flags(flags);
-		return;
-    }
-    newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */
-			   /* read 0 on the Xircom cards */
-    newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */
-    currcsr6 = inl(ioaddr + CSR6);
-    if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||
-	((currcsr6 & ~0x2002) == 0)) {
-		outl(newcsr6, ioaddr + CSR6);	/* safe */
-		restore_flags(flags);
-		return;
-    }
-    /* make sure the transmitter and receiver are stopped first */
-    currcsr6 &= ~0x2002;
-    while (1) {
-		csr5 = inl(ioaddr + CSR5);
-		if (csr5 == 0xffffffff)
-			break;  /* cannot read csr5, card removed? */
-		csr5_22_20 = csr5 & 0x700000;
-		csr5_19_17 = csr5 & 0x0e0000;
-		if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &&
-			(csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
-			break;  /* both are stopped or suspended */
-		if (!--attempts) {
-			printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts,"
-				   "csr5=0x%08x\n", csr5);
-			outl(newcsr6, ioaddr + CSR6);  /* unsafe but do it anyway */
-			restore_flags(flags);
-			return;
-		}
-		outl(currcsr6, ioaddr + CSR6);
-		udelay(1);
-    }
-    /* now it is safe to change csr6 */
-    outl(newcsr6, ioaddr + CSR6);
-    restore_flags(flags);
-}
-
-static struct net_device *tulip_probe1(struct pci_dev *pdev,
-				       struct net_device *dev, long ioaddr, int irq,
-				       int chip_idx, int board_idx)
-{
-	static int did_version = 0;			/* Already printed version info. */
-	struct tulip_private *tp;
-	/* See note below on the multiport cards. */
-	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
-	static int last_irq = 0;
-	static int multiport_cnt = 0;		/* For four-port boards w/one EEPROM */
-	u8 chip_rev;
-	int i;
-	unsigned short sum;
-
-	if (tulip_debug > 0  &&  did_version++ == 0)
-		printk(KERN_INFO "%s", version);
-
-	dev = init_etherdev(dev, 0);
-
-	pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev);
-	/* Bring the 21143 out of sleep mode.
-	   Caution: Snooze mode does not work with some boards! */
-	if (tulip_tbl[chip_idx].flags & HAS_ACPI)
-		pci_write_config_dword(pdev, 0x40, 0x00000000);
-
-	printk(KERN_INFO "%s: %s rev %d at %#3lx,",
-		   dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
-
-	/* Stop the chip's Tx and Rx processes. */
-	outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx);
-	/* Clear the missed-packet counter. */
-	(volatile int)inl(ioaddr + CSR8);
-
-	if (chip_idx == DC21041) {
-		if (inl(ioaddr + CSR9) & 0x8000) {
-			printk(" 21040 compatible mode,");
-			chip_idx = DC21040;
-		} else {
-			printk(" 21041 mode,");
-		}
-	}
-
-	/* The station address ROM is read byte serially.  The register must
-	   be polled, waiting for the value to be read bit serially from the
-	   EEPROM.
-	   */
-	sum = 0;
-	if (chip_idx == DC21040) {
-		outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */
-		for (i = 0; i < 6; i++) {
-			int value, boguscnt = 100000;
-			do
-				value = inl(ioaddr + CSR9);
-			while (value < 0  && --boguscnt > 0);
-			dev->dev_addr[i] = value;
-			sum += value & 0xff;
-		}
-	} else if (chip_idx == LC82C168) {
-		for (i = 0; i < 3; i++) {
-			int value, boguscnt = 100000;
-			outl(0x600 | i, ioaddr + 0x98);
-			do
-				value = inl(ioaddr + CSR9);
-			while (value < 0  && --boguscnt > 0);
-			put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i);
-			sum += value & 0xffff;
-		}
-	} else if (chip_idx == COMET) {
-		/* No need to read the EEPROM. */
-		put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr);
-		put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4));
-		for (i = 0; i < 6; i ++)
-			sum += dev->dev_addr[i];
-	} else if (chip_idx == X3201_3) {
-		/* Xircom has its address stored in the CIS
-		 * we access it through the boot rom interface for now
-		 * this might not work, as the CIS is not parsed but I
-		 * (danilo) use the offset I found on my card's CIS !!!
-		 * 
-		 * Doug Ledford: I changed this routine around so that it
-		 * walks the CIS memory space, parsing the config items, and
-		 * finds the proper lan_node_id tuple and uses the data
-		 * stored there.
-		 */
-		unsigned char j, tuple, link, data_id, data_count;
-		outl(1<<12, ioaddr + CSR9); /* enable boot rom access */
-		for (i = 0x100; i < 0x1f7; i += link+2) {
-			outl(i, ioaddr + CSR10);
-			tuple = inl(ioaddr + CSR9) & 0xff;
-			outl(i + 1, ioaddr + CSR10);
-			link = inl(ioaddr + CSR9) & 0xff;
-			outl(i + 2, ioaddr + CSR10);
-			data_id = inl(ioaddr + CSR9) & 0xff;
-			outl(i + 3, ioaddr + CSR10);
-			data_count = inl(ioaddr + CSR9) & 0xff;
-			if ( (tuple == 0x22) &&
-				 (data_id == 0x04) && (data_count == 0x06) ) {
-				/*
-				 * This is it.  We have the data we want.
-				 */
-				for (j = 0; j < 6; j++) {
-					outl(i + j + 4, ioaddr + CSR10);
-					dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff;
-				}
-				break;
-			} else if (link == 0) {
-				break;
-			}
-		}
-		sum = 1; // to make check below fail!
-	} else {	/* Must be a new chip, with a serial EEPROM interface. */
-		/* We read the whole EEPROM, and sort it out later.  DEC has a
-		   specification _Digital Semiconductor 21X4 Serial ROM Format_
-		   but early vendor boards just put the address in the first six
-		   EEPROM locations. */
-		unsigned char ee_data[EEPROM_SIZE];
-		int sa_offset = 0;
-
-		for (i = 0; i < sizeof(ee_data)/2; i++)
-			((u16 *)ee_data)[i] =
-				le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
-
-		/* Detect the simple EEPROM format by the duplicated station addr. */
-		for (i = 0; i < 8; i ++)
-			if (ee_data[i] != ee_data[16+i])
-				sa_offset = 20;
-		if (ee_data[0] == 0xff  &&  ee_data[1] == 0xff &&  ee_data[2] == 0) {
-			sa_offset = 2;		/* Grrr, damn Matrox boards. */
-			multiport_cnt = 4;
-		}
-		for (i = 0; i < 6; i ++) {
-			dev->dev_addr[i] = ee_data[i + sa_offset];
-			sum += ee_data[i + sa_offset];
-		}
-	}
-	/* Lite-On boards have the address byte-swapped. */
-	if ((dev->dev_addr[0] == 0xA0  ||  dev->dev_addr[0] == 0xC0)
-		&&  dev->dev_addr[1] == 0x00)
-		for (i = 0; i < 6; i+=2) {
-			char tmp = dev->dev_addr[i];
-			dev->dev_addr[i] = dev->dev_addr[i+1];
-			dev->dev_addr[i+1] = tmp;
-		}
-	/* On the Zynx 315 Etherarray and other multiport boards only the
-	   first Tulip has an EEPROM.
-	   The addresses of the subsequent ports are derived from the first.
-	   Many PCI BIOSes also incorrectly report the IRQ line, so we correct
-	   that here as well. */
-	if (sum == 0  || sum == 6*0xff) {
-		printk(" EEPROM not present,");
-		for (i = 0; i < 5; i++)
-			dev->dev_addr[i] = last_phys_addr[i];
-		dev->dev_addr[i] = last_phys_addr[i] + 1;
-#if defined(__i386__)		/* Patch up x86 BIOS bug. */
-		if (last_irq)
-			irq = last_irq;
-#endif
-	}
-
-	for (i = 0; i < 6; i++)
-		printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]);
-	printk(", IRQ %d.\n", irq);
-	last_irq = irq;
-
-	/* We do a request_region() only to register /proc/ioports info. */
-	/* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
-	request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name);
-
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-
-	/* Make certain the data structures are quadword aligned. */
-	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
-	memset(tp, 0, sizeof(*tp));
-	dev->priv = tp;
-
-	tp->lock = SPIN_LOCK_UNLOCKED;
-	tp->pdev = pdev;
-	tp->chip_id = chip_idx;
-	tp->revision = chip_rev;
-	tp->csr0 = csr0;
-	tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7);
-
-	/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
-	   And the ASIX must have a burst limit or horrible things happen. */
-	if ( (chip_idx == DC21143  &&  chip_rev == 65) ||
-	     (chip_idx == X3201_3) )
-		tp->csr0 &= ~0x01000000;
-	else if (chip_idx == AX88140)
-		tp->csr0 |= 0x2000;
-
-#ifdef TULIP_FULL_DUPLEX
-	tp->full_duplex = 1;
-	tp->full_duplex_lock = 1;
-#endif
-#ifdef TULIP_DEFAULT_MEDIA
-	tp->default_port = TULIP_DEFAULT_MEDIA;
-#endif
-#ifdef TULIP_NO_MEDIA_SWITCH
-	tp->medialock = 1;
-#endif
-
-	/* The lower four bits are the media type. */
-	if (board_idx >= 0  &&  board_idx < MAX_UNITS) {
-		tp->default_port = options[board_idx] & 15;
-		if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0)
-			tp->full_duplex = 1;
-		if (mtu[board_idx] > 0)
-			dev->mtu = mtu[board_idx];
-	}
-	if (dev->mem_start)
-		tp->default_port = dev->mem_start;
-	if (tp->default_port) {
-		tp->medialock = 1;
-		if (media_cap[tp->default_port] & MediaAlwaysFD)
-			tp->full_duplex = 1;
-	}
-	if (tp->full_duplex)
-		tp->full_duplex_lock = 1;
-
-	/* This is logically part of probe1(), but too complex to write inline. */
-	if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE)
-		parse_eeprom(dev);
-
-	if (media_cap[tp->default_port] & MediaIsMII) {
-		u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 };
-		tp->to_advertise = media2advert[tp->default_port - 9];
-	} else
-		tp->to_advertise = 0x03e1;
-
-	if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) ||
-		(tp->mtable  &&  tp->mtable->has_mii) ||
-		( ! tp->mtable  &&  (tulip_tbl[chip_idx].flags & HAS_MII))) {
-		int phy, phy_idx;
-		if (tp->mtable  &&  tp->mtable->has_mii) {
-			for (i = 0; i < tp->mtable->leafcount; i++)
-				if (tp->mtable->mleaf[i].media == 11) {
-					tp->cur_index = i;
-					tp->saved_if_port = dev->if_port;
-					select_media(dev, 1);
-					dev->if_port = tp->saved_if_port;
-					break;
-				}
-		}
-		/* Find the connected MII xcvrs.
-		   Doing this in open() would allow detecting external xcvrs later,
-		   but takes much time. */
-		for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
-			 phy++) {
-			int mii_status = mdio_read(dev, phy, 1);
-			if ((mii_status & 0x8301) == 0x8001 ||
-				((mii_status & 0x8000) == 0  && (mii_status & 0x7800) != 0)) {
-				int mii_reg0 = mdio_read(dev, phy, 0);
-				int mii_advert = mdio_read(dev, phy, 4);
-				int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
-				tp->phys[phy_idx] = phy;
-				tp->advertising[phy_idx++] = reg4;
-				printk(KERN_INFO "%s:  MII transceiver #%d "
-					   "config %4.4x status %4.4x advertising %4.4x.\n",
-					   dev->name, phy, mii_reg0, mii_status, mii_advert);
-				/* Fixup for DLink with miswired PHY. */
-				if (mii_advert != reg4) {
-					printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d,"
-						   " previously advertising %4.4x.\n",
-						   dev->name, reg4, phy, mii_advert);
-					mdio_write(dev, phy, 4, reg4);
-				}
-				/* Enable autonegotiation: some boards default to off. */
-				mdio_write(dev, phy, 0, mii_reg0 |
-						   (tp->full_duplex ? 0x1100 : 0x1000) |
-						   (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
-			}
-		}
-		tp->mii_cnt = phy_idx;
-		if (tp->mtable  &&  tp->mtable->has_mii  &&  phy_idx == 0) {
-			printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
-				   dev->name);
-			tp->phys[0] = 1;
-		}
-	}
-
-	/* The Tulip-specific entries in the device structure. */
-	dev->open = &tulip_open;
-	dev->hard_start_xmit = &tulip_start_xmit;
-	dev->stop = &tulip_close;
-	dev->get_stats = &tulip_get_stats;
-#ifdef HAVE_PRIVATE_IOCTL
-	dev->do_ioctl = &private_ioctl;
-#endif
-#ifdef HAVE_MULTICAST
-	dev->set_multicast_list = &set_rx_mode;
-#endif
-	dev->tx_timeout = tulip_tx_timeout;
-	dev->watchdog_timeo = TX_TIMEOUT;
-
-	/* Reset the xcvr interface and turn on heartbeat. */
-	switch (chip_idx) {
-	case DC21041:
-		outl(0x00000000, ioaddr + CSR13);
-		outl(0xFFFFFFFF, ioaddr + CSR14);
-		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
-		outl_CSR6(inl(ioaddr + CSR6) | 0x0200, ioaddr, chip_idx);
-		outl(0x0000EF05, ioaddr + CSR13);
-		break;
-	case DC21040:
-		outl(0x00000000, ioaddr + CSR13);
-		outl(0x00000004, ioaddr + CSR13);
-		break;
-	case DC21140: default:
-		if (tp->mtable)
-			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
-		break;
-	case DC21142:
-	case PNIC2:
-		if (tp->mii_cnt  ||  media_cap[dev->if_port] & MediaIsMII) {
-			outl_CSR6(0x82020000, ioaddr, chip_idx);
-			outl(0x0000, ioaddr + CSR13);
-			outl(0x0000, ioaddr + CSR14);
-			outl_CSR6(0x820E0000, ioaddr, chip_idx);
-		} else {
-			outl_CSR6(0x82420200, ioaddr, chip_idx);
-			outl(0x0001, ioaddr + CSR13);
-			outl(0x0003FFFF, ioaddr + CSR14);
-			outl(0x0008, ioaddr + CSR15);
-			outl(0x0001, ioaddr + CSR13);
-			outl(0x1301, ioaddr + CSR12); /* Start NWay. */
-		}
-		break;
-	case X3201_3:
-		outl(0x0008, ioaddr + CSR15);
-		udelay(5);  /* The delays are Xircom recommended to give the
-					 * chipset time to reset the actual hardware
-					 * on the PCMCIA card
-					 */
-		outl(0xa8050000, ioaddr + CSR15);
-		udelay(5);
-		outl(0xa00f0000, ioaddr + CSR15);
-		udelay(5);
-		outl_CSR6(0x32000200, ioaddr, chip_idx);
-		break;
-	case LC82C168:
-		if ( ! tp->mii_cnt) {
-			outl_CSR6(0x00420000, ioaddr, chip_idx);
-			outl(0x30, ioaddr + CSR12);
-			outl(0x0001F078, ioaddr + 0xB8);
-			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
-		}
-		break;
-	case MX98713: case COMPEX9881:
-		outl_CSR6(0x00000000, ioaddr, chip_idx);
-		outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
-		outl(0x00000001, ioaddr + CSR13);
-		break;
-	case MX98715: case MX98725:
-		outl_CSR6(0x01a80000, ioaddr, chip_idx);
-		outl(0xFFFFFFFF, ioaddr + CSR14);
-		outl(0x00001000, ioaddr + CSR12);
-		break;
-	case COMET:
-		/* No initialization necessary. */
-		break;
-	}
-
-	return dev;
-}
-
-/* Serial EEPROM section. */
-/* The main routine to parse the very complicated SROM structure.
-   Search www.digital.com for "21X4 SROM" to get details.
-   This code is very complex, and will require changes to support
-   additional cards, so I'll be verbose about what is going on.
-   */
-
-/* Known cards that have old-style EEPROMs. */
-static struct fixups {
-  char *name;
-  unsigned char addr0, addr1, addr2;
-  u16 newtable[32];				/* Max length below. */
-} eeprom_fixups[] = {
-  {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
-						  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
-  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
-							   0x0000, 0x009E, /* 10baseT */
-							   0x0903, 0x006D, /* 100baseTx */ }},
-  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f,
-								 0x0107, 0x8021, /* 100baseFx */
-								 0x0108, 0x8021, /* 100baseFx-FD */
-								 0x0103, 0x006D, /* 100baseTx */ }},
-  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
-							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
-							   0x0000, 0x009E, /* 10baseT */
-							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
-  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
-							0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
-							0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
-							0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
-   }},
-  {0, 0, 0, 0, {}}};
-
-static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
- "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
-
-#if defined(__i386__)			/* AKA get_unaligned() */
-#define get_u16(ptr) (*(u16 *)(ptr))
-#else
-#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
-#endif
-
-static void parse_eeprom(struct net_device *dev)
-{
-	/* The last media info list parsed, for multiport boards.  */
-	static struct mediatable *last_mediatable = NULL;
-	static unsigned char *last_ee_data = NULL;
-	static int controller_index = 0;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	unsigned char *ee_data = tp->eeprom;
-	int i;
-#ifdef CARDBUS
-	int chip_rev = tp->revision;
-#endif
-
-	tp->mtable = 0;
-	for (i = 0; i < EEPROM_SIZE/2; i++)
-		((u16 *)ee_data)[i] =
-			le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
-
-	/* Detect an old-style (SA only) EEPROM layout:
-	   memcmp(eedata, eedata+16, 8). */
-	for (i = 0; i < 8; i ++)
-		if (ee_data[i] != ee_data[16+i])
-			break;
-	if (i >= 8) {
-		if (ee_data[0] == 0xff) {
-			if (last_mediatable) {
-				controller_index++;
-				printk(KERN_INFO "%s:  Controller %d of multiport board.\n",
-					   dev->name, controller_index);
-				tp->mtable = last_mediatable;
-				ee_data = last_ee_data;
-				goto subsequent_board;
-			} else
-				printk(KERN_INFO "%s:  Missing EEPROM, this interface may "
-					   "not work correctly!\n",
-			   dev->name);
-			return;
-		}
-	  /* Do a fix-up based on the vendor half of the station address prefix. */
-	  for (i = 0; eeprom_fixups[i].name; i++) {
-		if (dev->dev_addr[0] == eeprom_fixups[i].addr0
-			&&  dev->dev_addr[1] == eeprom_fixups[i].addr1
-			&&  dev->dev_addr[2] == eeprom_fixups[i].addr2) {
-		  if (dev->dev_addr[2] == 0xE8  &&  ee_data[0x1a] == 0x55)
-			  i++;			/* An Accton EN1207, not an outlaw Maxtech. */
-		  memcpy(ee_data + 26, eeprom_fixups[i].newtable,
-				 sizeof(eeprom_fixups[i].newtable));
-		  printk(KERN_INFO "%s: Old format EEPROM on '%s' board.  Using"
-				 " substitute media control info.\n",
-				 dev->name, eeprom_fixups[i].name);
-		  break;
-		}
-	  }
-	  if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
-		  printk(KERN_INFO "%s: Old style EEPROM with no media selection "
-				 "information.\n",
-			   dev->name);
-		return;
-	  }
-	}
-
-	controller_index = 0;
-	if (ee_data[19] > 1) {		/* Multiport board. */
-		last_ee_data = ee_data;
-	}
-subsequent_board:
-
-	if (ee_data[27] == 0) {		/* No valid media table. */
-	} else if (tp->chip_id == DC21041) {
-		unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
-		short media;
-		int count;
-
-		media = get_u16(p);
-		p += 2;
-		count = *p++;
-
-		printk(KERN_INFO "%s:21041 Media information at %d, default media "
-			   "%4.4x (%s).\n", dev->name, ee_data[27], media,
-			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
-		for (i = 0; i < count; i++) {
-			unsigned char media_code = *p++;
-			u16 csrvals[3];
-			int idx;
-			for (idx = 0; idx < 3; idx++) {
-				csrvals[idx] = get_u16(p);
-				p += 2;
-			}
-			if (media_code & 0x40) {
-				printk(KERN_INFO "%s:  21041 media %2.2x (%s),"
-					   " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
-					   dev->name, media_code & 15, medianame[media_code & 15],
-					   csrvals[0], csrvals[1], csrvals[2]);
-			} else
-				printk(KERN_INFO "%s:  21041 media #%d, %s.\n",
-					   dev->name, media_code & 15, medianame[media_code & 15]);
-		}
-	} else {
-		unsigned char *p = (void *)ee_data + ee_data[27];
-		unsigned char csr12dir = 0;
-		int count;
-		struct mediatable *mtable;
-		u16 media = get_u16(p);
-
-		p += 2;
-		if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
-			csr12dir = *p++;
-		count = *p++;
-		mtable = (struct mediatable *)
-			kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
-					GFP_KERNEL);
-		if (mtable == NULL)
-			return;				/* Horrible, impossible failure. */
-		last_mediatable = tp->mtable = mtable;
-		mtable->defaultmedia = media;
-		mtable->leafcount = count;
-		mtable->csr12dir = csr12dir;
-		mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
-		mtable->csr15dir = mtable->csr15val = 0;
-
-		printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name,
-			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
-		for (i = 0; i < count; i++) {
-			struct medialeaf *leaf = &mtable->mleaf[i];
-
-			if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
-				leaf->type = 0;
-				leaf->media = p[0] & 0x3f;
-				leaf->leafdata = p;
-				if ((p[2] & 0x61) == 0x01)	/* Bogus, but Znyx boards do it. */
-					mtable->has_mii = 1;
-				p += 4;
-			} else {
-				leaf->type = p[1];
-				if (p[1] == 0x05) {
-					mtable->has_reset = i;
-					leaf->media = p[2] & 0x0f;
-				} else if (p[1] & 1) {
-					mtable->has_mii = 1;
-					leaf->media = 11;
-				} else {
-					mtable->has_nonmii = 1;
-					leaf->media = p[2] & 0x0f;
-					if (p[1] == 2) {
-						if (leaf->media == 0) {
-							mtable->csr15dir = get_unaligned((u16*)&p[3])<<16;
-							mtable->csr15val = get_unaligned((u16*)&p[5])<<16;
-						} else if (leaf->media == 0x40) {
-							u32 base15 = get_unaligned((u16*)&p[7]);
-							mtable->csr15dir =
-								(get_unaligned((u16*)&p[9])<<16) + base15;
-							mtable->csr15val =
-								(get_unaligned((u16*)&p[11])<<16) + base15;
-						}
-					}
-				}
-				leaf->leafdata = p + 2;
-				p += (p[0] & 0x3f) + 1;
-			}
-			if (tulip_debug > 1  &&  leaf->media == 11) {
-				unsigned char *bp = leaf->leafdata;
-				printk(KERN_INFO "%s:  MII interface PHY %d, setup/reset "
-					   "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
-					   dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
-					   bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
-			}
-			printk(KERN_INFO "%s:  Index #%d - Media %s (#%d) described "
-				   "by a %s (%d) block.\n",
-				   dev->name, i, medianame[leaf->media], leaf->media,
-				   block_name[leaf->type], leaf->type);
-		}
-	}
-}
-/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
-
-/*  EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
-#define EE_CS			0x01	/* EEPROM chip select. */
-#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
-#define EE_WRITE_0		0x01
-#define EE_WRITE_1		0x05
-#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
-#define EE_ENB			(0x4800 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
-   Even at 33Mhz current PCI implementations don't overrun the EEPROM clock.
-   We add a bus turn-around to insure that this remains true. */
-#define eeprom_delay()	inl(ee_addr)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD	(5 << addr_len)
-#define EE_READ_CMD		(6 << addr_len)
-#define EE_ERASE_CMD	(7 << addr_len)
-
-static int read_eeprom(long ioaddr, int location, int addr_len)
-{
-	int i;
-	unsigned short retval = 0;
-	long ee_addr = ioaddr + CSR9;
-	int read_cmd = location | EE_READ_CMD;
-
-	outl(EE_ENB & ~EE_CS, ee_addr);
-	outl(EE_ENB, ee_addr);
-
-	/* Shift the read command bits out. */
-	for (i = 4 + addr_len; i >= 0; i--) {
-		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
-		outl(EE_ENB | dataval, ee_addr);
-		eeprom_delay();
-		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay();
-	}
-	outl(EE_ENB, ee_addr);
-
-	for (i = 16; i > 0; i--) {
-		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay();
-		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
-		outl(EE_ENB, ee_addr);
-		eeprom_delay();
-	}
-
-	/* Terminate the EEPROM access. */
-	outl(EE_ENB & ~EE_CS, ee_addr);
-	return retval;
-}
-
-/* MII transceiver control section.
-   Read and write the MII registers using software-generated serial
-   MDIO protocol.  See the MII specifications or DP83840A data sheet
-   for details. */
-
-/* The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
-   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
-   "overclocking" issues or future 66Mhz PCI. */
-#define mdio_delay() inl(mdio_addr)
-
-/* Read and write the MII registers using software-generated serial
-   MDIO protocol.  It is just different enough from the EEPROM protocol
-   to not share code.  The maxium data clock rate is 2.5 Mhz. */
-#define MDIO_SHIFT_CLK	0x10000
-#define MDIO_DATA_WRITE0 0x00000
-#define MDIO_DATA_WRITE1 0x20000
-#define MDIO_ENB		0x00000		/* Ignore the 0x02000 databook setting. */
-#define MDIO_ENB_IN		0x40000
-#define MDIO_DATA_READ	0x80000
-
-static int mdio_read(struct net_device *dev, int phy_id, int location)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int i;
-	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
-	int retval = 0;
-	long ioaddr = dev->base_addr;
-	long mdio_addr = ioaddr + CSR9;
-
-	if (tp->chip_id == LC82C168) {
-		int i = 1000;
-		outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
-		inl(ioaddr + 0xA0);
-		inl(ioaddr + 0xA0);
-		while (--i > 0)
-			if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
-				return retval & 0xffff;
-		return 0xffff;
-	}
-
-	if (tp->chip_id == COMET) {
-		if (phy_id == 1) {
-			if (location < 7)
-				return inl(ioaddr + 0xB4 + (location<<2));
-			else if (location == 17)
-				return inl(ioaddr + 0xD0);
-			else if (location >= 29 && location <= 31)
-				return inl(ioaddr + 0xD4 + ((location-29)<<2));
-		}
-		return 0xffff;
-	}
-
-	/* Establish sync by sending at least 32 logic ones. */
-	for (i = 32; i >= 0; i--) {
-		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
-		mdio_delay();
-		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
-		mdio_delay();
-	}
-	/* Shift the read command bits out. */
-	for (i = 15; i >= 0; i--) {
-		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
-
-		outl(MDIO_ENB | dataval, mdio_addr);
-		mdio_delay();
-		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
-		mdio_delay();
-	}
-	/* Read the two transition, 16 data, and wire-idle bits. */
-	for (i = 19; i > 0; i--) {
-		outl(MDIO_ENB_IN, mdio_addr);
-		mdio_delay();
-		retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
-		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
-		mdio_delay();
-	}
-	return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int i;
-	int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
-	long ioaddr = dev->base_addr;
-	long mdio_addr = ioaddr + CSR9;
-
-	if (tp->chip_id == LC82C168) {
-		int i = 1000;
-		outl(cmd, ioaddr + 0xA0);
-		do
-			if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
-				break;
-		while (--i > 0);
-		return;
-	}
-
-	if (tp->chip_id == COMET) {
-		if (phy_id != 1)
-			return;
-		if (location < 7)
-			outl(value, ioaddr + 0xB4 + (location<<2));
-		else if (location == 17)
-			outl(value, ioaddr + 0xD0);
-		else if (location >= 29 && location <= 31)
-			outl(value, ioaddr + 0xD4 + ((location-29)<<2));
-		return;
-	}
-
-	/* Establish sync by sending 32 logic ones. */
-	for (i = 32; i >= 0; i--) {
-		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
-		mdio_delay();
-		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
-		mdio_delay();
-	}
-	/* Shift the command bits out. */
-	for (i = 31; i >= 0; i--) {
-		int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
-		outl(MDIO_ENB | dataval, mdio_addr);
-		mdio_delay();
-		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
-		mdio_delay();
-	}
-	/* Clear out extra bits. */
-	for (i = 2; i > 0; i--) {
-		outl(MDIO_ENB_IN, mdio_addr);
-		mdio_delay();
-		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
-		mdio_delay();
-	}
-	return;
-}
-
-static void
-tulip_up(struct net_device *dev)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	int i;
-
-	/* On some chip revs we must set the MII/SYM port before the reset!? */
-	if (tp->mii_cnt  ||  (tp->mtable  &&  tp->mtable->has_mii))
-		outl_CSR6(0x00040000, ioaddr, tp->chip_id);
-
-	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
-	outl(0x00000001, ioaddr + CSR0);
-
-	/* Deassert reset. */
-	outl(tp->csr0, ioaddr + CSR0);
-	udelay(2);
-
-	if (tulip_tbl[tp->chip_id].flags & HAS_ACPI)
-		pci_write_config_dword(tp->pdev, 0x40, 0x00000000);
-
-	/* Clear the tx ring */
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		tp->tx_skbuff[i] = 0;
-		tp->tx_ring[i].status = 0x00000000;
-	}
-
-	if (tulip_debug > 1)
-		printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
-
-	if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
-		u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
-		u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4)));
-		if (tp->chip_id == AX88140) {
-			outl(0, ioaddr + CSR13);
-			outl(addr_low,  ioaddr + CSR14);
-			outl(1, ioaddr + CSR13);
-			outl(addr_high, ioaddr + CSR14);
-		} else if (tp->chip_id == COMET) {
-			outl(addr_low,  ioaddr + 0xA4);
-			outl(addr_high, ioaddr + 0xA8);
-			outl(0, ioaddr + 0xAC);
-			outl(0, ioaddr + 0xB0);
-		}
-	} else if (tp->chip_id != X3201_3) {
-		/* This is set_rx_mode(), but without starting the transmitter. */
-		u16 *eaddrs = (u16 *)dev->dev_addr;
-		u16 *setup_frm = &tp->setup_frame[15*6];
-
-		/* 21140 bug: you must add the broadcast address. */
-		memset(tp->setup_frame, 0xff, 96*sizeof(u16));
-		/* Fill the final entry of the table with our physical address. */
-		*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-		*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-		*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-		/* Put the setup frame on the Tx list. */
-		tp->tx_ring[0].length = 0x08000000 | 192;
-		tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
-		tp->tx_ring[0].status = DescOwned;
-
-		tp->cur_tx++;
-	} else { /* X3201_3 */
-		u16 *eaddrs = (u16 *)dev->dev_addr;
-		u16 *setup_frm = &tp->setup_frame[0*6];
-		
-		/* fill the table with the broadcast address */
-		memset(tp->setup_frame, 0xff, 96*sizeof(u16));
-		/* re-fill the first 14 table entries with our address */
-		for(i=0; i<14; i++) {
-			*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-			*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-			*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-		}
-
-		/* Put the setup frame on the Tx list. */
-		tp->tx_ring[0].length = 0x08000000 | 192;
-		/* Lie about the address of our setup frame to make the */
-		/* chip happy */
-		tp->tx_ring[0].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
-		tp->tx_ring[0].status = DescOwned;
-
-		tp->cur_tx++;
-	}
-	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
-	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
-
-	tp->saved_if_port = dev->if_port;
-	if (dev->if_port == 0)
-		dev->if_port = tp->default_port;
-	if (tp->chip_id == DC21041  &&  dev->if_port > 4)
-		/* Invalid: Select initial TP, autosense, autonegotiate.  */
-		dev->if_port = 4;
-
-	/* Allow selecting a default media. */
-	i = 0;
-	if (tp->mtable == NULL)
-		goto media_picked;
-	if (dev->if_port) {
-		int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 :
-			(dev->if_port == 12 ? 0 : dev->if_port);
-		for (i = 0; i < tp->mtable->leafcount; i++)
-			if (tp->mtable->mleaf[i].media == looking_for) {
-				printk(KERN_INFO "%s: Using user-specified media %s.\n",
-					   dev->name, medianame[dev->if_port]);
-				goto media_picked;
-			}
-	}
-	if ((tp->mtable->defaultmedia & 0x0800) == 0) {
-		int looking_for = tp->mtable->defaultmedia & 15;
-		for (i = 0; i < tp->mtable->leafcount; i++)
-			if (tp->mtable->mleaf[i].media == looking_for) {
-				printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
-					   dev->name, medianame[looking_for]);
-				goto media_picked;
-			}
-	}
-	/* Start sensing first non-full-duplex media. */
-	for (i = tp->mtable->leafcount - 1;
-		 (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
-		;
-media_picked:
-
-	tp->csr6 = 0;
-	tp->cur_index = i;
-	if (dev->if_port == 0  &&  tp->chip_id == DC21142) {
-		if (tp->mii_cnt) {
-			select_media(dev, 1);
-			if (tulip_debug > 1)
-				printk(KERN_INFO "%s: Using MII transceiver %d, status "
-					   "%4.4x.\n",
-					   dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1));
-			outl_CSR6(0x82020000, ioaddr, tp->chip_id);
-			tp->csr6 = 0x820E0000;
-			dev->if_port = 11;
-			outl(0x0000, ioaddr + CSR13);
-			outl(0x0000, ioaddr + CSR14);
-		} else
-			t21142_start_nway(dev);
-	} else if ((tp->chip_id == LC82C168  || tp->chip_id == PNIC2)
-			   &&  tp->mii_cnt && ! tp->medialock) {
-		dev->if_port = 11;
-		tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
-		outl(0x0001, ioaddr + CSR15);
-	} else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881)
-			   && ! tp->medialock) {
-		dev->if_port = 0;
-		tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
-		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
-	} else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) {
-		/* Provided by BOLO, Macronix - 12/10/1998. */
-		dev->if_port = 0;
-		tp->csr6 = 0x01880200;
-		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
-		outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
-	} else if (tp->chip_id == DC21143  &&
-			   media_cap[dev->if_port] & MediaIsMII) {
-		/* We must reset the media CSRs when we force-select MII mode. */
-		outl(0x0000, ioaddr + CSR13);
-		outl(0x0000, ioaddr + CSR14);
-		outl(0x0008, ioaddr + CSR15);
-	} else if (tp->chip_id == X3201_3) {
-		outl(0x0008, ioaddr + CSR15);
-		udelay(5);
-		outl(0xa8050000, ioaddr + CSR15);
-		udelay(5);
-		outl(0xa00f0000, ioaddr + CSR15); 
-		udelay(5);
-		tp->csr6  = 0x32400000;
-	} else if (tp->chip_id == COMET) {
-		dev->if_port = 0;
-		tp->csr6 = 0x00040000;
-	} else
-		select_media(dev, 1);
-
-	/* Start the chip's Tx to process setup frame. */
-	outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
-	outl_CSR6(tp->csr6 | 0x2000, ioaddr, tp->chip_id);
-
-	/* Enable interrupts by setting the interrupt mask. */
-	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
-	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
-	outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-	outl(0, ioaddr + CSR2);		/* Rx poll demand */
-	
-	netif_start_queue (dev);
-
-	if (tulip_debug > 2) {
-		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
-			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
-			   inl(ioaddr + CSR6));
-	}
-	/* Set the timer to switch to check for link beat and perhaps switch
-	   to an alternate media type. */
-	init_timer(&tp->timer);
-	tp->timer.expires = RUN_AT(5*HZ);
-	tp->timer.data = (unsigned long)dev;
-	tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
-	add_timer(&tp->timer);
-}
-
-static int
-tulip_open(struct net_device *dev)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
-		return -EAGAIN;
-
-	tulip_init_ring(dev);
-
-	tulip_up(dev);
-	tp->open = 1;
-	MOD_INC_USE_COUNT;
-
-	return 0;
-}
-
-/* Set up the transceiver control registers for the selected media type. */
-static void select_media(struct net_device *dev, int startup)
-{
-	long ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	struct mediatable *mtable = tp->mtable;
-	u32 new_csr6;
-	int i;
-
-	if (mtable) {
-		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
-		unsigned char *p = mleaf->leafdata;
-		switch (mleaf->type) {
-		case 0:					/* 21140 non-MII xcvr. */
-			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver"
-					   " with control setting %2.2x.\n",
-					   dev->name, p[1]);
-			dev->if_port = p[0];
-			if (startup)
-				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
-			outl(p[1], ioaddr + CSR12);
-			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
-			break;
-		case 2: case 4: {
-			u16 setup[5];
-			u32 csr13val, csr14val, csr15dir, csr15val;
-			for (i = 0; i < 5; i++)
-				setup[i] = get_u16(&p[i*2 + 1]);
-
-			dev->if_port = p[0] & 15;
-			if (media_cap[dev->if_port] & MediaAlwaysFD)
-				tp->full_duplex = 1;
-
-			if (startup && mtable->has_reset) {
-				struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
-				unsigned char *rst = rleaf->leafdata;
-				if (tulip_debug > 1)
-					printk(KERN_DEBUG "%s: Resetting the transceiver.\n",
-						   dev->name);
-				for (i = 0; i < rst[0]; i++)
-					outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
-			}
-			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control "
-					   "%4.4x/%4.4x.\n",
-					   dev->name, medianame[dev->if_port], setup[0], setup[1]);
-			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
-				csr13val = setup[0];
-				csr14val = setup[1];
-				csr15dir = (setup[3]<<16) | setup[2];
-				csr15val = (setup[4]<<16) | setup[2];
-				outl(0, ioaddr + CSR13);
-				outl(csr14val, ioaddr + CSR14);
-				outl(csr15dir, ioaddr + CSR15);	/* Direction */
-				outl(csr15val, ioaddr + CSR15);	/* Data */
-				outl(csr13val, ioaddr + CSR13);
-			} else {
-				csr13val = 1;
-				csr14val = 0x0003FF7F;
-				csr15dir = (setup[0]<<16) | 0x0008;
-				csr15val = (setup[1]<<16) | 0x0008;
-				if (dev->if_port <= 4)
-					csr14val = t21142_csr14[dev->if_port];
-				if (startup) {
-					outl(0, ioaddr + CSR13);
-					outl(csr14val, ioaddr + CSR14);
-				}
-				outl(csr15dir, ioaddr + CSR15);	/* Direction */
-				outl(csr15val, ioaddr + CSR15);	/* Data */
-				if (startup) outl(csr13val, ioaddr + CSR13);
-			}
-			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s:  Setting CSR15 to %8.8x/%8.8x.\n",
-					   dev->name, csr15dir, csr15val);
-			if (mleaf->type == 4)
-				new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
-			else
-				new_csr6 = 0x82420000;
-			break;
-		}
-		case 1: case 3: {
-			int phy_num = p[0];
-			int init_length = p[1];
-			u16 *misc_info;
-			u16 to_advertise;
-
-			dev->if_port = 11;
-			new_csr6 = 0x020E0000;
-			if (mleaf->type == 3) {	/* 21142 */
-				u16 *init_sequence = (u16*)(p+2);
-				u16 *reset_sequence = &((u16*)(p+3))[init_length];
-				int reset_length = p[2 + init_length*2];
-				misc_info = reset_sequence + reset_length;
-				if (startup)
-					for (i = 0; i < reset_length; i++)
-						outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
-				for (i = 0; i < init_length; i++)
-					outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
-			} else {
-				u8 *init_sequence = p + 2;
-				u8 *reset_sequence = p + 3 + init_length;
-				int reset_length = p[2 + init_length];
-				misc_info = (u16*)(reset_sequence + reset_length);
-				if (startup) {
-					outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
-					for (i = 0; i < reset_length; i++)
-						outl(reset_sequence[i], ioaddr + CSR12);
-				}
-				for (i = 0; i < init_length; i++)
-					outl(init_sequence[i], ioaddr + CSR12);
-			}
-			to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1;
-			tp->advertising[phy_num] = to_advertise;
-			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d (%d).\n",
-					   dev->name, to_advertise, phy_num, tp->phys[phy_num]);
-			/* Bogus: put in by a committee?  */
-			mdio_write(dev, tp->phys[phy_num], 4, to_advertise);
-			break;
-		}
-		default:
-			printk(KERN_DEBUG "%s:  Invalid media table selection %d.\n",
-					   dev->name, mleaf->type);
-			new_csr6 = 0x020E0000;
-		}
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
-				   dev->name, medianame[dev->if_port],
-				   inl(ioaddr + CSR12) & 0xff);
-	} else if (tp->chip_id == DC21041) {
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
-				   dev->name, medianame[dev->if_port & 15],
-				   inl(ioaddr + CSR12) & 0xffff);
-		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
-		outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
-		outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
-		outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
-		new_csr6 = 0x80020000;
-	} else if (tp->chip_id == LC82C168 || tp->chip_id == PNIC2) {
-		if (startup && ! tp->medialock)
-			dev->if_port = tp->mii_cnt ? 11 : 0;
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x,"
-				   " media %s.\n",
-				   dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
-				   medianame[dev->if_port]);
-		if (tp->mii_cnt) {
-			new_csr6 = 0x810C0000;
-			outl(0x0001, ioaddr + CSR15);
-			outl(0x0201B07A, ioaddr + 0xB8);
-		} else if (startup) {
-			/* Start with 10mbps to do autonegotiation. */
-			outl(0x32, ioaddr + CSR12);
-			new_csr6 = 0x00420000;
-			outl(0x0001B078, ioaddr + 0xB8);
-			outl(0x0201B078, ioaddr + 0xB8);
-		} else if (dev->if_port == 3  ||  dev->if_port == 5) {
-			outl(0x33, ioaddr + CSR12);
-			new_csr6 = 0x01860000;
-			if (startup)
-				outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */
-			else
-				outl(0x1F868, ioaddr + 0xB8);
-		} else {
-			outl(0x32, ioaddr + CSR12);
-			new_csr6 = 0x00420000;
-			outl(0x1F078, ioaddr + 0xB8);
-		}
-	} else if (tp->chip_id == DC21040) {					/* 21040 */
-		/* Turn on the xcvr interface. */
-		int csr12 = inl(ioaddr + CSR12);
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
-				   dev->name, medianame[dev->if_port], csr12);
-		if (media_cap[dev->if_port] & MediaAlwaysFD)
-			tp->full_duplex = 1;
-		new_csr6 = 0x20000;
-		/* Set the full duplux match frame. */
-		outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
-		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
-		if (t21040_csr13[dev->if_port] & 8) {
-			outl(0x0705, ioaddr + CSR14);
-			outl(0x0006, ioaddr + CSR15);
-		} else {
-			outl(0xffff, ioaddr + CSR14);
-			outl(0x0000, ioaddr + CSR15);
-		}
-		outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13);
-	} else if (tp->chip_id == X3201_3) {					/* Xircom */
-		if (tp->default_port == 0)
-			dev->if_port = tp->mii_cnt ? 11 : 3;
-/* Someone is on crack, the Xircom only does MII, no Fx */
-/*		if (media_cap[dev->if_port] & MediaIsMII) {
-			new_csr6 = 0x020E0000;
-		} else if (media_cap[dev->if_port] & MediaIsFx) {
-			new_csr6 = 0x028600000;
-		} else
-			new_csr6 = 0x038600000;*/
-		new_csr6 = 0x324c0000;
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: Xircom CardBus Adapter: "
-				   "%s transceiver, CSR12 %2.2x.\n",
-				   dev->name, medianame[dev->if_port],
-				   inl(ioaddr + CSR12));
-	} else {					/* Unknown chip type with no media table. */
-		if (tp->default_port == 0)
-			dev->if_port = tp->mii_cnt ? 11 : 3;
-		if (media_cap[dev->if_port] & MediaIsMII) {
-			new_csr6 = 0x020E0000;
-		} else if (media_cap[dev->if_port] & MediaIsFx) {
-			new_csr6 = 0x028600000;
-		} else
-			new_csr6 = 0x038600000;
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: No media description table, assuming "
-				   "%s transceiver, CSR12 %2.2x.\n",
-				   dev->name, medianame[dev->if_port],
-				   inl(ioaddr + CSR12));
-	}
-
-	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
-	return;
-}
-
-/*
-  Check the MII negotiated duplex, and change the CSR6 setting if
-  required.
-  Return 0 if everything is OK.
-  Return < 0 if the transceiver is missing or has no link beat.
-  */
-static int check_duplex(struct net_device *dev)
-{
-	long ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int mii_reg1, mii_reg5, negotiated, duplex;
-
-	if (tp->full_duplex_lock)
-		return 0;
-	mii_reg1 = mdio_read(dev, tp->phys[0], 1);
-	mii_reg5 = mdio_read(dev, tp->phys[0], 5);
-	if (tulip_debug > 1)
-		printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
-			   "%4.4x.\n", dev->name, mii_reg1, mii_reg5);
-	if (mii_reg1 == 0xffff)
-		return -2;
-	if ((mii_reg1 & 0x0004) == 0) {
-		int new_reg1 = mdio_read(dev, tp->phys[0], 1);
-		if ((new_reg1 & 0x0004) == 0) {
-			if (tulip_debug  > 1)
-				printk(KERN_INFO "%s: No link beat on the MII interface,"
-					   " status %4.4x.\n", dev->name, new_reg1);
-			return -1;
-		}
-	}
-	negotiated = mii_reg5 & tp->advertising[0];
-	duplex = ((negotiated & 0x0300) == 0x0100
-			  || (negotiated & 0x00C0) == 0x0040);
-	/* 100baseTx-FD  or  10T-FD, but not 100-HD */
-	if (tp->full_duplex != duplex) {
-		tp->full_duplex = duplex;
-		if (tp->full_duplex) tp->csr6 |= 0x0200;
-		else				 tp->csr6 &= ~0x0200;
-		outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-		if (tulip_debug > 0)
-			printk(KERN_INFO "%s: Setting %s-duplex based on MII"
-				   "#%d link partner capability of %4.4x.\n",
-				   dev->name, tp->full_duplex ? "full" : "half",
-				   tp->phys[0], mii_reg5);
-	}
-	return 0;
-}
-
-static void tulip_timer(unsigned long data)
-{
-	struct net_device *dev = (struct net_device *)data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	u32 csr12 = inl(ioaddr + CSR12);
-	int next_tick = 2*HZ;
-
-	if (tulip_debug > 2) {
-		printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
-			   "SIA %8.8x %8.8x %8.8x %8.8x.\n",
-			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
-			   csr12, inl(ioaddr + CSR13),
-			   inl(ioaddr + CSR14), inl(ioaddr + CSR15));
-	}
-	switch (tp->chip_id) {
-	case DC21040:
-		if (!tp->medialock  &&  csr12 & 0x0002) { /* Network error */
-			printk(KERN_INFO "%s: No link beat found.\n",
-				   dev->name);
-			dev->if_port = (dev->if_port == 2 ? 0 : 2);
-			select_media(dev, 0);
-			dev->trans_start = jiffies;
-		}
-		break;
-	case DC21041:
-		if (tulip_debug > 2)
-			printk(KERN_DEBUG "%s: 21041 media tick  CSR12 %8.8x.\n",
-				   dev->name, csr12);
-		switch (dev->if_port) {
-		case 0: case 3: case 4:
-		  if (csr12 & 0x0004) { /*LnkFail */
-			/* 10baseT is dead.  Check for activity on alternate port. */
-			tp->mediasense = 1;
-			if (csr12 & 0x0200)
-				dev->if_port = 2;
-			else
-				dev->if_port = 1;
-			printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
-				   dev->name, medianame[dev->if_port]);
-			outl(0, ioaddr + CSR13); /* Reset */
-			outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
-			outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
-			outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
-			next_tick = 10*HZ;			/* 2.4 sec. */
-		  } else
-			next_tick = 30*HZ;
-		  break;
-		case 1:					/* 10base2 */
-		case 2:					/* AUI */
-			if (csr12 & 0x0100) {
-				next_tick = (30*HZ);			/* 30 sec. */
-				tp->mediasense = 0;
-			} else if ((csr12 & 0x0004) == 0) {
-				printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n",
-					   dev->name);
-				dev->if_port = 0;
-				select_media(dev, 0);
-				next_tick = (24*HZ)/10;				/* 2.4 sec. */
-			} else if (tp->mediasense || (csr12 & 0x0002)) {
-				dev->if_port = 3 - dev->if_port; /* Swap ports. */
-				select_media(dev, 0);
-				next_tick = 20*HZ;
-			} else {
-				next_tick = 20*HZ;
-			}
-			break;
-		}
-		break;
-	case DC21140:  case DC21142: case MX98713: case COMPEX9881: default: {
-		struct medialeaf *mleaf;
-		unsigned char *p;
-		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
-			/* Not much that can be done.
-			   Assume this a generic MII or SYM transceiver. */
-			next_tick = 60*HZ;
-			if (tulip_debug > 2)
-				printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x "
-					   "CSR12 0x%2.2x.\n",
-					   dev->name, inl(ioaddr + CSR6), csr12 & 0xff);
-			break;
-		}
-		mleaf = &tp->mtable->mleaf[tp->cur_index];
-		p = mleaf->leafdata;
-		switch (mleaf->type) {
-		case 0: case 4: {
-			/* Type 0 serial or 4 SYM transceiver.  Check the link beat bit. */
-			int offset = mleaf->type == 4 ? 5 : 2;
-			s8 bitnum = p[offset];
-			if (p[offset+1] & 0x80) {
-				if (tulip_debug > 1)
-					printk(KERN_DEBUG"%s: Transceiver monitor tick "
-						   "CSR12=%#2.2x, no media sense.\n",
-						   dev->name, csr12);
-				if (mleaf->type == 4) {
-					if (mleaf->media == 3 && (csr12 & 0x02))
-						goto select_next_media;
-				}
-				break;
-			}
-			if (tulip_debug > 2)
-				printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x"
-					   " bit %d is %d, expecting %d.\n",
-					   dev->name, csr12, (bitnum >> 1) & 7,
-					   (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
-					   (bitnum >= 0));
-			/* Check that the specified bit has the proper value. */
-			if ((bitnum < 0) !=
-				((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
-				if (tulip_debug > 1)
-					printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
-						   medianame[mleaf->media]);
-				if ((p[2] & 0x61) == 0x01)	/* Bogus Znyx board. */
-					goto actually_mii;
-				break;
-			}
-			if (tp->medialock)
-				break;
-	  select_next_media:
-			if (--tp->cur_index < 0) {
-				/* We start again, but should instead look for default. */
-				tp->cur_index = tp->mtable->leafcount - 1;
-			}
-			dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
-			if (media_cap[dev->if_port] & MediaIsFD)
-				goto select_next_media; /* Skip FD entries. */
-			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s: No link beat on media %s,"
-					   " trying transceiver type %s.\n",
-					   dev->name, medianame[mleaf->media & 15],
-					   medianame[tp->mtable->mleaf[tp->cur_index].media]);
-			select_media(dev, 0);
-			/* Restart the transmit process. */
-			outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-			outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-			next_tick = (24*HZ)/10;
-			break;
-		}
-		case 1:  case 3:		/* 21140, 21142 MII */
-		actually_mii:
-			check_duplex(dev);
-			next_tick = 60*HZ;
-			break;
-		case 2:					/* 21142 serial block has no link beat. */
-		default:
-			break;
-		}
-	}
-	break;
-	}
-	tp->timer.expires = RUN_AT(next_tick);
-	add_timer(&tp->timer);
-}
-
-/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
-   of available transceivers.  */
-static void t21142_timer(unsigned long data)
-{
-	struct net_device *dev = (struct net_device *)data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	int csr12 = inl(ioaddr + CSR12);
-	int next_tick = 60*HZ;
-	int new_csr6 = 0;
-
-	if ((tulip_debug > 2) && !(media_cap[dev->if_port] & MediaIsMII))
-		printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
-			   dev->name, csr12, medianame[dev->if_port]);
-	if (media_cap[dev->if_port] & MediaIsMII) {
-		check_duplex(dev);
-		next_tick = 60*HZ;
-	} else if (tp->nwayset) {
-		/* Don't screw up a negotiated session! */
-		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n",
-				   dev->name, medianame[dev->if_port], csr12);
-	} else if (tp->medialock) {
-			;
-	} else if (dev->if_port == 3) {
-		if (csr12 & 2) {	/* No 100mbps link beat, revert to 10mbps. */
-			if (tulip_debug > 1)
-				printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, "
-					   "trying NWay.\n", dev->name, csr12);
-			t21142_start_nway(dev);
-			next_tick = 3*HZ;
-		}
-	} else if (((csr12 & 0x7000) != 0x5000)
-			   && tp->chip_id != X3201_3) {
-		/* Negotiation failed.  Search media types. */
-		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n",
-				   dev->name, csr12);
-		if (!(csr12 & 4)) {		/* 10mbps link beat good. */
-			new_csr6 = 0x82420000;
-			dev->if_port = 0;
-			outl(0, ioaddr + CSR13);
-			outl(0x0003FFFF, ioaddr + CSR14);
-			outw(t21142_csr15[dev->if_port], ioaddr + CSR15);
-			outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
-		} else {
-			/* Select 100mbps port to check for link beat. */
-			new_csr6 = 0x83860000;
-			dev->if_port = 3;
-			outl(0, ioaddr + CSR13);
-			outl(0x0003FF7F, ioaddr + CSR14);
-			outw(8, ioaddr + CSR15);
-			outl(1, ioaddr + CSR13);
-		}
-		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: Testing new 21143 media %s.\n",
-				   dev->name, medianame[dev->if_port]);
-		if (new_csr6 != (tp->csr6 & ~0x00D5)) {
-			tp->csr6 &= 0x00D5;
-			tp->csr6 |= new_csr6;
-			outl(0x0301, ioaddr + CSR12);
-			outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-			outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-		}
-		next_tick = 3*HZ;
-	}
-	if (tp->cur_tx - tp->dirty_tx > 0  &&
-		jiffies - dev->trans_start > TX_TIMEOUT) {
-		printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n",
-			   dev->name, tp->cur_tx, tp->dirty_tx);
-		tulip_tx_timeout(dev);
-	}
-
-	tp->timer.expires = RUN_AT(next_tick);
-	add_timer(&tp->timer);
-}
-
-static void t21142_start_nway(struct net_device *dev)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	int csr14 = ((tp->to_advertise & 0x0180) << 9)  |
-		((tp->to_advertise&0x0020)<<1) | 0xffbf;
-
-	dev->if_port = 0;
-	tp->nway = tp->mediasense = 1;
-	tp->nwayset = tp->lpar = 0;
-	if (debug > 1)
-		printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n",
-			   dev->name, csr14);
-	outl(0x0001, ioaddr + CSR13);
-	outl(csr14, ioaddr + CSR14);
-	tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
-	outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
-	if (tp->mtable  &&  tp->mtable->csr15dir) {
-		outl(tp->mtable->csr15dir, ioaddr + CSR15);
-		outl(tp->mtable->csr15val, ioaddr + CSR15);
-	} else
-		outw(0x0008, ioaddr + CSR15);
-	outl(0x1301, ioaddr + CSR12); 		/* Trigger NWAY. */
-}
-
-static void t21142_lnk_change(struct net_device *dev, int csr5)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	int csr12 = inl(ioaddr + CSR12);
-
-	if (tulip_debug > 1)
-		printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
-			   "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
-
-	/* If NWay finished and we have a negotiated partner capability. */
-	if (tp->nway  &&  !tp->nwayset  &&  (csr12 & 0x7000) == 0x5000) {
-		int setup_done = 0;
-		tp->lpar = csr12 >> 16;
-		tp->nwayset = 1;
-		if (csr12 & 0x01000000)			dev->if_port = 5;
-		else if (csr12 & 0x00800000)	dev->if_port = 3;
-		else if (csr12 & 0x00400000)	dev->if_port = 4;
-		else if (csr12 & 0x00200000)	dev->if_port = 0;
-		else {
-			tp->nwayset = 0;
-			if ( ! (csr12 & 2))			dev->if_port = 3;
-			else if ( ! (csr12 & 4))	dev->if_port = 0;
-		}
-		tp->full_duplex = (media_cap[tp->default_port] & MediaAlwaysFD) ? 1:0;
-
-		if (tulip_debug > 1) {
-			if (tp->nwayset)
-				printk(KERN_INFO "%s: Switching to %s based on link partner "
-					   "advertisement %4.4x.\n",
-					   dev->name, medianame[dev->if_port], tp->lpar);
-			else
-				printk(KERN_INFO "%s: Switching to %s based on link beat "
-					   "status of %4.4x.\n",
-					   dev->name, medianame[dev->if_port], csr12);
-		}
-
-		if (tp->mtable) {
-			int i;
-			for (i = 0; i < tp->mtable->leafcount; i++)
-				if (tp->mtable->mleaf[i].media == dev->if_port) {
-					tp->cur_index = i;
-					select_media(dev, 0);
-					setup_done = 1;
-					break;
-				}
-		}
-		if ( ! setup_done) {
-			tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000;
-			if (tp->full_duplex)
-				tp->csr6 |= 0x0200;
-			outw(0x0000, ioaddr + CSR13);
-			outw(0x0000, ioaddr + CSR14);
-		}
-		outl_CSR6(tp->csr6 | 0x0000, ioaddr, tp->chip_id);
-		if (debug > 2)
-			printk(KERN_DEBUG "%s:  Restarting Tx and Rx, CSR5 is %8.8x.\n",
-				   dev->name, inl(ioaddr + CSR5));
-		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-	} else if ((tp->nwayset  &&  (csr5 & 0x08000000)
-				&& (dev->if_port == 3  ||  dev->if_port == 5)
-				&& (csr12 & 2) == 2) ||
-			   (tp->nway && (csr5 & (TPLnkFail)))) {
-		/* Link blew? Maybe restart NWay. */
-		del_timer(&tp->timer);
-		t21142_start_nway(dev);
-		tp->timer.expires = RUN_AT(3*HZ);
-		add_timer(&tp->timer);
-	} else if (dev->if_port == 3  ||  dev->if_port == 5) {
-		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: 21143 %s link beat %s.\n",
-				   dev->name, medianame[dev->if_port],
-				   (csr12 & 2) ? "failed" : "good");
-		if ((csr12 & 2)  &&  ! tp->medialock) {
-			del_timer(&tp->timer);
-			t21142_start_nway(dev);
-			tp->timer.expires = RUN_AT(3*HZ);
-			add_timer(&tp->timer);
-		}
-	} else if (dev->if_port == 0  ||  dev->if_port == 4) {
-		if ((csr12 & 4) == 0)
-			printk(KERN_INFO"%s: 21143 10baseT link beat good.\n",
-				   dev->name);
-	} else if (!(csr12 & 4)) {		/* 10mbps link beat good. */
-		if (tulip_debug)
-			printk(KERN_INFO"%s: 21143 10mbps sensed media.\n",
-				   dev->name);
-		dev->if_port = 0;
-	} else if (tp->nwayset) {
-		if (tulip_debug)
-			printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n",
-				   dev->name, medianame[dev->if_port], tp->csr6);
-	} else {		/* 100mbps link beat good. */
-		if (tulip_debug)
-			printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n",
-				   dev->name);
-		dev->if_port = 3;
-		tp->csr6 = 0x83860000;
-		outl(0x0003FF7F, ioaddr + CSR14);
-		outl(0x0301, ioaddr + CSR12);
-		outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-	}
-}
-
-static void mxic_timer(unsigned long data)
-{
-	struct net_device *dev = (struct net_device *)data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	int next_tick = 60*HZ;
-
-	if (tulip_debug > 3) {
-		printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name,
-			   inl(ioaddr + CSR12));
-	}
-	if (next_tick) {
-		tp->timer.expires = RUN_AT(next_tick);
-		add_timer(&tp->timer);
-	}
-}
-
-static void pnic_timer(unsigned long data)
-{
-	struct net_device *dev = (struct net_device *)data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	int csr12 = inl(ioaddr + CSR12);
-	int next_tick = 60*HZ;
-	int new_csr6 = tp->csr6 & ~0x40C40200;
-
-	if (media_cap[dev->if_port] & MediaIsMII) {
-		int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0];
-
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: PNIC negotiated capability %8.8x, "
-				   "CSR5 %8.8x.\n",
-				   dev->name, negotiated, inl(ioaddr + CSR5));
-
-		if (negotiated & 0x0380) 				/* 10 vs 100mbps */
-			new_csr6 |= 0x810E0000;
-		else
-			new_csr6 |= 0x814E0000;
-		if (((negotiated & 0x0300) == 0x0100)			/* Duplex */
-			|| (negotiated & 0x00C0) == 0x0040
-			|| tp->full_duplex_lock) {
-			tp->full_duplex = 1;
-			new_csr6 |= 0x0200;
-		}
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: PNIC MII PHY status %4.4x, Link "
-				   "partner report %4.4x, csr6 %8.8x/%8.8x.\n",
-			   dev->name, mdio_read(dev, tp->phys[0], 1), negotiated,
-				   tp->csr6, inl(ioaddr + CSR6));
-	} else {
-		int phy_reg = inl(ioaddr + 0xB8);
-		int csr5 = inl(ioaddr + CSR5);
-
-		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: PNIC PHY status %8.8x, CSR5 %8.8x.\n",
-				   dev->name, phy_reg, csr5);
-
-		if (phy_reg & 0x04000000) {	/* Remote link fault */
-			/*outl(0x0201F078, ioaddr + 0xB8);*/
-			next_tick = 3*HZ;
-		}
-		if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
-			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
-					   "CSR5 %8.8x, PHY %3.3x.\n",
-					   dev->name, medianame[dev->if_port], csr12,
-					   inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
-			if (tp->medialock) {
-			} else if (dev->if_port == 0) {
-				dev->if_port = 3;
-				outl(0x33, ioaddr + CSR12);
-				new_csr6 = 0x01860000;
-				outl(0x1F868, ioaddr + 0xB8);
-			} else {
-				dev->if_port = 0;
-				outl(0x32, ioaddr + CSR12);
-				new_csr6 = 0x00420000;
-				outl(0x1F078, ioaddr + 0xB8);
-			}
-			new_csr6 |= (tp->csr6 & 0xfdff);
-			next_tick = 3*HZ;
-		} else
-			new_csr6 = tp->csr6;
-		if (tp->full_duplex_lock  ||  (phy_reg & 0x30000000) != 0) {
-			tp->full_duplex = 1;
-			new_csr6 |= 0x00000200;
-		}
-	}
-	if (tp->csr6 != new_csr6) {
-		tp->csr6 = new_csr6;
-		outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);	/* Restart Tx */
-		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-		dev->trans_start = jiffies;
-		if (tulip_debug > 1)
-			printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, "
-				   "CSR6 %8.8x.\n",
-				   dev->name, tp->full_duplex ? "full" : "half", new_csr6);
-	}
-	tp->timer.expires = RUN_AT(next_tick);
-	add_timer(&tp->timer);
-}
-
-static void comet_timer(unsigned long data)
-{
-	struct net_device *dev = (struct net_device *)data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	int next_tick = 60*HZ;
-
-	if (tulip_debug > 1)
-		printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability "
-			   "%4.4x.\n",
-			   dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8));
-	tp->timer.expires = RUN_AT(next_tick);
-	add_timer(&tp->timer);
-}
-
-static void tulip_tx_timeout(struct net_device *dev)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-
-	if (media_cap[dev->if_port] & MediaIsMII) {
-		/* Do nothing -- the media monitor should handle this. */
-		if (tulip_debug > 1)
-			printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
-				   dev->name);
-	} else if (tp->chip_id == DC21040) {
-		if ( !tp->medialock  &&  inl(ioaddr + CSR12) & 0x0002) {
-			dev->if_port = (dev->if_port == 2 ? 0 : 2);
-			printk(KERN_INFO "%s: transmit timed out, switching to "
-				   "%s.\n",
-				   dev->name, medianame[dev->if_port]);
-			select_media(dev, 0);
-		}
-		dev->trans_start = jiffies;
-		return;
-	} else if (tp->chip_id == DC21041) {
-		int csr12 = inl(ioaddr + CSR12);
-
-		printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, "
-			   "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
-			   dev->name, inl(ioaddr + CSR5), csr12,
-			   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
-		tp->mediasense = 1;
-		if ( ! tp->medialock) {
-			if (dev->if_port == 1 || dev->if_port == 2)
-				if (csr12 & 0x0004) {
-					dev->if_port = 2 - dev->if_port;
-				} else
-					dev->if_port = 0;
-			else
-				dev->if_port = 1;
-			select_media(dev, 0);
-		}
-	} else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
-			   || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) {
-		printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
-			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
-			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
-			   inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
-		if ( ! tp->medialock  &&  tp->mtable) {
-			do
-				--tp->cur_index;
-			while (tp->cur_index >= 0
-				   && (media_cap[tp->mtable->mleaf[tp->cur_index].media]
-					   & MediaIsFD));
-			if (--tp->cur_index < 0) {
-				/* We start again, but should instead look for default. */
-				tp->cur_index = tp->mtable->leafcount - 1;
-			}
-			select_media(dev, 0);
-			printk(KERN_WARNING "%s: transmit timed out, switching to %s "
-				   "media.\n", dev->name, medianame[dev->if_port]);
-		}
-	} else {
-		printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 "
-			   "%8.8x, resetting...\n",
-			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
-		dev->if_port = 0;
-	}
-
-#if defined(way_too_many_messages)
-	if (tulip_debug > 3) {
-		int i;
-		for (i = 0; i < RX_RING_SIZE; i++) {
-			u8 *buf = (u8 *)(tp->rx_ring[i].buffer1);
-			int j;
-			printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x  "
-				   "%2.2x %2.2x %2.2x.\n",
-				   i, (unsigned int)tp->rx_ring[i].status,
-				   (unsigned int)tp->rx_ring[i].length,
-				   (unsigned int)tp->rx_ring[i].buffer1,
-				   (unsigned int)tp->rx_ring[i].buffer2,
-				   buf[0], buf[1], buf[2]);
-			for (j = 0; buf[j] != 0xee && j < 1600; j++)
-				if (j < 100) printk(" %2.2x", buf[j]);
-			printk(" j=%d.\n", j);
-		}
-		printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)tp->rx_ring);
-		for (i = 0; i < RX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
-		printk("\n" KERN_DEBUG "  Tx ring %8.8x: ", (int)tp->tx_ring);
-		for (i = 0; i < TX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
-		printk("\n");
-	}
-#endif
-
-	/* Stop and restart the chip's Tx processes . */
-	outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-	outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-	/* Trigger an immediate transmit demand. */
-	outl(0, ioaddr + CSR1);
-
-	dev->trans_start = jiffies;
-	netif_wake_queue (dev);
-	tp->stats.tx_errors++;
-}
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void tulip_init_ring(struct net_device *dev)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int i;
-
-	tp->tx_full = 0;
-	tp->cur_rx = tp->cur_tx = 0;
-	tp->dirty_rx = tp->dirty_tx = 0;
-
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = 0x00000000;
-		tp->rx_ring[i].length = PKT_BUF_SZ;
-		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
-		tp->rx_skbuff[i] = NULL;
-	}
-	/* Mark the last entry as wrapping the ring. */
-	tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP;
-	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
-
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		/* Note the receive buffer must be longword aligned.
-		   dev_alloc_skb() provides 16 byte alignment.  But do *not*
-		   use skb_reserve() to align the IP header! */
-		struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
-		tp->rx_skbuff[i] = skb;
-		if (skb == NULL)
-			break;
-		skb->dev = dev;			/* Mark as being used by this device. */
-		tp->rx_ring[i].status = DescOwned;	/* Owned by Tulip chip */
-		tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
-	}
-	tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
-
-	/* The Tx buffer descriptor is filled in as needed, but we
-	   do need to clear the ownership bit. */
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		tp->tx_skbuff[i] = 0;
-		tp->tx_ring[i].status = 0x00000000;
-		tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
-#ifdef CARDBUS
-		if (tp->chip_id == X3201_3)
-			tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ);
-#endif CARDBUS
-	}
-	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
-}
-
-static int
-tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int entry;
-	u32 flag;
-
-	/* Caution: the write order is important here, set the base address
-	   with the "ownership" bits last. */
-
-	/* Calculate the next Tx descriptor entry. */
-	entry = tp->cur_tx % TX_RING_SIZE;
-
-	tp->tx_skbuff[entry] = skb;
-#ifdef CARDBUS
-	if (tp->chip_id == X3201_3) {
-		memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
-		tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
-	} else
-#endif
-		tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
-
-	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
-		flag = 0x60000000; /* No interrupt */
-	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
-		flag = 0xe0000000; /* Tx-done intr. */
-	} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
-		flag = 0x60000000; /* No Tx-done intr. */
-	} else {
-		/* Leave room for set_rx_mode() to fill entries. */
-		flag = 0xe0000000; /* Tx-done intr. */
-		tp->tx_full = 1;
-	}
-	if (entry == TX_RING_SIZE-1)
-		flag |= 0xe0000000 | DESC_RING_WRAP;
-
-	tp->tx_ring[entry].length = skb->len | flag;
-	tp->tx_ring[entry].status = DescOwned;	/* Pass ownership to the chip. */
-	tp->cur_tx++;
-	if (tp->tx_full)
-		netif_stop_queue (dev);
-	else
-		netif_wake_queue (dev);
-
-	/* Trigger an immediate transmit demand. */
-	outl(0, dev->base_addr + CSR1);
-
-	dev->trans_start = jiffies;
-
-	return 0;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
-   after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
-{
-	struct net_device *dev = (struct net_device *)dev_instance;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	int csr5, work_budget = max_interrupt_work;
-
-	spin_lock (&tp->lock);
-
-	do {
-		csr5 = inl(ioaddr + CSR5);
-		/* Acknowledge all of the current interrupt sources ASAP. */
-		outl(csr5 & 0x0001ffff, ioaddr + CSR5);
-
-		if (tulip_debug > 4)
-			printk(KERN_DEBUG "%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
-				   dev->name, csr5, inl(dev->base_addr + CSR5));
-
-		if (csr5 == 0xffffffff)
-			break;	/* all bits set, assume PCMCIA card removed */
-
-		if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
-			break;
-
-		if (csr5 & (RxIntr | RxNoBuf))
-			work_budget -= tulip_rx(dev);
-
-		if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
-			unsigned int dirty_tx;
-
-			for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
-				 dirty_tx++) {
-				int entry = dirty_tx % TX_RING_SIZE;
-				int status = tp->tx_ring[entry].status;
-
-				if (status < 0)
-					break;			/* It still hasn't been Txed */
-				/* Check for Rx filter setup frames. */
-				if (tp->tx_skbuff[entry] == NULL)
-				  continue;
-
-				if (status & 0x8000) {
-					/* There was an major error, log it. */
-#ifndef final_version
-					if (tulip_debug > 1)
-						printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
-							   dev->name, status);
-#endif
-					tp->stats.tx_errors++;
-					if (status & 0x4104) tp->stats.tx_aborted_errors++;
-					if (status & 0x0C00) tp->stats.tx_carrier_errors++;
-					if (status & 0x0200) tp->stats.tx_window_errors++;
-					if (status & 0x0002) tp->stats.tx_fifo_errors++;
-					if ((status & 0x0080) && tp->full_duplex == 0)
-						tp->stats.tx_heartbeat_errors++;
-#ifdef ETHER_STATS
-					if (status & 0x0100) tp->stats.collisions16++;
-#endif
-				} else {
-#ifdef ETHER_STATS
-					if (status & 0x0001) tp->stats.tx_deferred++;
-#endif
-					tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
-					tp->stats.collisions += (status >> 3) & 15;
-					tp->stats.tx_packets++;
-				}
-
-				/* Free the original skb. */
-				dev_kfree_skb_irq(tp->tx_skbuff[entry]);
-				tp->tx_skbuff[entry] = 0;
-			}
-
-#ifndef final_version
-			if (tp->cur_tx - dirty_tx > TX_RING_SIZE) {
-				printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-					   dev->name, dirty_tx, tp->cur_tx, tp->tx_full);
-				dirty_tx += TX_RING_SIZE;
-			}
-#endif
-
-			if (tp->tx_full && 
-			    tp->cur_tx - dirty_tx  < TX_RING_SIZE - 2)
-				/* The ring is no longer full */
-				tp->tx_full = 0;
-			
-			if (tp->tx_full)
-				netif_stop_queue (dev);
-			else
-				netif_wake_queue (dev);
-
-			tp->dirty_tx = dirty_tx;
-			if (csr5 & TxDied) {
-				if (tulip_debug > 2)
-					printk(KERN_WARNING "%s: The transmitter stopped."
-						   "  CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
-						   dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
-				outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-			}
-		}
-
-		/* Log errors. */
-		if (csr5 & AbnormalIntr) {	/* Abnormal error summary bit. */
-			if (csr5 == 0xffffffff)
-				break;
-			if (csr5 & TxJabber) tp->stats.tx_errors++;
-			if (csr5 & TxFIFOUnderflow) {
-				if ((tp->csr6 & 0xC000) != 0xC000)
-					tp->csr6 += 0x4000;	/* Bump up the Tx threshold */
-				else
-					tp->csr6 |= 0x00200000;  /* Store-n-forward. */
-				/* Restart the transmit process. */
-				outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-			}
-			if (csr5 & RxDied) {		/* Missed a Rx frame. */
-				tp->stats.rx_errors++;
-				tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-			}
-			if (csr5 & TimerInt) {
-				if (tulip_debug > 2)
-					printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
-						   dev->name, csr5);
-				outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
-			}
-			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
-				if ( tp->chip_id == DC21142)
-					t21142_lnk_change(dev, csr5);
-			}
-			/* Clear all error sources, included undocumented ones! */
-			outl(0x0800f7ba, ioaddr + CSR5);
-		}
-		if (--work_budget < 0) {
-			if (tulip_debug > 1)
-				printk(KERN_WARNING "%s: Too much work during an interrupt, "
-					   "csr5=0x%8.8x.\n", dev->name, csr5);
-			/* Acknowledge all interrupt sources. */
-			outl(0x8001ffff, ioaddr + CSR5);
-#ifdef notdef
-			/* Clear all but standard interrupt sources. */
-			outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
-#endif
-			break;
-		}
-	} while (1);
-
-	if (tulip_debug > 3)
-		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
-			   dev->name, inl(ioaddr + CSR5));
-
-	spin_unlock (&tp->lock);
-}
-
-static int
-tulip_rx(struct net_device *dev)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int entry = tp->cur_rx % RX_RING_SIZE;
-	int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
-	int work_done = 0;
-
-	if (tulip_debug > 4)
-		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
-			   tp->rx_ring[entry].status);
-	/* If we own the next entry, it's a new packet. Send it up. */
-	while (tp->rx_ring[entry].status >= 0) {
-		s32 status = tp->rx_ring[entry].status;
-
-		if (tulip_debug > 5)
-			printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
-				   tp->rx_ring[entry].status);
-		if (--rx_work_limit < 0)
-			break;
-		if ((status & 0x38008300) != 0x0300) {
-			if ((status & 0x38000300) != 0x0300) {
-				/* Ingore earlier buffers. */
-				if ((status & 0xffff) != 0x7fff) {
-					if (tulip_debug > 1)
-						printk(KERN_WARNING "%s: Oversized Ethernet frame "
-							   "spanned multiple buffers, status %8.8x!\n",
-							   dev->name, status);
-					tp->stats.rx_length_errors++;
-				}
-			} else if (status & RxDescFatalErr) {
-				/* There was a fatal error. */
-				if (tulip_debug > 2)
-					printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
-						   dev->name, status);
-				tp->stats.rx_errors++; /* end of a packet.*/
-				if (status & 0x0890) tp->stats.rx_length_errors++;
-				if (status & 0x0004) tp->stats.rx_frame_errors++;
-				if (status & 0x0002) tp->stats.rx_crc_errors++;
-				if (status & 0x0001) tp->stats.rx_fifo_errors++;
-			}
-		} else {
-			/* Omit the four octet CRC from the length. */
-			short pkt_len = ((status >> 16) & 0x7ff) - 4;
-			struct sk_buff *skb;
-
-#ifndef final_version
-			if (pkt_len > 1518) {
-				printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n",
-					   dev->name, pkt_len, pkt_len);
-				pkt_len = 1518;
-				tp->stats.rx_length_errors++;
-			}
-#endif
-			/* Check if the packet is long enough to accept without copying
-			   to a minimally-sized skbuff. */
-			if (pkt_len < rx_copybreak
-				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
-				skb->dev = dev;
-				skb_reserve(skb, 2);	/* 16 byte align the IP header */
-#if ! defined(__alpha__)
-				eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
-								 pkt_len, 0);
-				skb_put(skb, pkt_len);
-#else
-				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
-#endif
-				work_done++;
-			} else { 	/* Pass up the skb already on the Rx ring. */
-				char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
-				tp->rx_skbuff[entry] = NULL;
-#ifndef final_version
-				if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
-					printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
-						   "do not match in tulip_rx: %p vs. %p / %p.\n",
-						   dev->name, bus_to_virt(tp->rx_ring[entry].buffer1),
-						   skb->head, temp);
-#endif
-			}
-			skb->protocol = eth_type_trans(skb, dev);
-			netif_rx(skb);
-			dev->last_rx = jiffies;
-			tp->stats.rx_packets++;
-			tp->stats.rx_bytes += pkt_len;
-		}
-		entry = (++tp->cur_rx) % RX_RING_SIZE;
-	}
-
-	/* Refill the Rx ring buffers. */
-	for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
-		entry = tp->dirty_rx % RX_RING_SIZE;
-		if (tp->rx_skbuff[entry] == NULL) {
-			struct sk_buff *skb;
-			skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
-			if (skb == NULL)
-				break;
-			skb->dev = dev;			/* Mark as being used by this device. */
-			tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
-			work_done++;
-		}
-		tp->rx_ring[entry].status = DescOwned;
-	}
-
-	return work_done;
-}
-
-static void
-tulip_down(struct net_device *dev)
-{
-	long ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-	/* Disable interrupts by clearing the interrupt mask. */
-	outl(0x00000000, ioaddr + CSR7);
-	/* Stop the chip's Tx and Rx processes. */
-	outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, tp->chip_id);
-	/* 21040 -- Leave the card in 10baseT state. */
-	if (tp->chip_id == DC21040)
-		outl(0x00000004, ioaddr + CSR13);
-
-	if (inl(ioaddr + CSR6) != 0xffffffff)
-		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-
-	dev->if_port = tp->saved_if_port;
-}
-
-static int
-tulip_close(struct net_device *dev)
-{
-	long ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int i;
-
-	if (tulip_debug > 1)
-		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
-			   dev->name, inl(ioaddr + CSR5));
-
-	netif_stop_queue(dev);
-
-	if (netif_device_present(dev))
-		tulip_down(dev);
-
-	del_timer(&tp->timer);
-
-	free_irq(dev->irq, dev);
-
-	/* Free all the skbuffs in the Rx queue. */
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		struct sk_buff *skb = tp->rx_skbuff[i];
-		tp->rx_skbuff[i] = 0;
-		tp->rx_ring[i].status = 0;		/* Not owned by Tulip chip. */
-		tp->rx_ring[i].length = 0;
-		tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
-		if (skb) {
-			dev_kfree_skb(skb);
-		}
-	}
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		if (tp->tx_skbuff[i])
-			dev_kfree_skb(tp->tx_skbuff[i]);
-		tp->tx_skbuff[i] = 0;
-	}
-
-	MOD_DEC_USE_COUNT;
-	tp->open = 0;
-	return 0;
-}
-
-static struct net_device_stats *tulip_get_stats(struct net_device *dev)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-
-	if (netif_device_present(dev))
-		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-
-	return &tp->stats;
-}
-
-#ifdef HAVE_PRIVATE_IOCTL
-/* Provide ioctl() calls to examine the MII xcvr state. */
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	u16 *data = (u16 *)&rq->ifr_data;
-	int phy = tp->phys[0] & 0x1f;
-	long flags;
-
-	switch(cmd) {
-	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
-		if (tp->mii_cnt)
-			data[0] = phy;
-		else if (tp->chip_id == DC21142)	/* 21142 pseudo-MII */
-			data[0] = 32;
-		else if (tp->chip_id == PNIC2)
-			data[0] = 32;
-		else if (tp->chip_id == COMET)
-			data[0] = 1;
-		else
-			return -ENODEV;
-		return 0;
-	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
-		if (data[0] == 32  &&
-			(tp->chip_id == DC21142 || tp->chip_id == PNIC2)) {
-			int csr12 = inl(ioaddr + CSR12);
-			int csr14 = inl(ioaddr + CSR14);
-			switch (data[1]) {
-			case 0: {
-				data[3] = (csr14<<5) & 0x1000;
-				break; }
-			case 1:
-				data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0)
-					+ (csr12&0x06 ? 0x04 : 0);
-				break;
-			case 4: {
-				data[3] = ((csr14>>9)&0x0380) +
-					((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1;
-				break;
-			}
-			case 5: data[3] = csr12 >> 16; break;
-			default: data[3] = 0; break;
-			}
-		} else {
-			save_flags(flags);
-			cli();
-			data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
-			restore_flags(flags);
-		}
-		return 0;
-	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-#if defined(CAP_NET_ADMIN)
-		if (!capable(CAP_NET_ADMIN))
-			return -EPERM;
-#else
-		if (!suser())
-			return -EPERM;
-#endif
-		if (data[0] == 32  &&  tp->chip_id == DC21142) {
-			if (data[1] == 5)
-				tp->to_advertise = data[2];
-		} else {
-			save_flags(flags);
-			cli();
-			mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
-			restore_flags(flags);
-		}
-		return 0;
-	default:
-		return -EOPNOTSUPP;
-	}
-
-	return -EOPNOTSUPP;
-}
-#endif  /* HAVE_PRIVATE_IOCTL */
-
-/* Set or clear the multicast filter for this adaptor.
-   Note that we only use exclusion around actually queueing the
-   new frame, not around filling tp->setup_frame.  This is non-deterministic
-   when re-entered but still correct. */
-
-/* The little-endian AUTODIN32 ethernet CRC calculation.
-   N.B. Do not use for bulk data, use a table-based routine instead.
-   This is common code and should be moved to net/core/crc.c */
-static unsigned const ethernet_polynomial_le = 0xedb88320U;
-static inline u32 ether_crc_le(int length, unsigned char *data)
-{
-	u32 crc = 0xffffffff;	/* Initial value. */
-	while(--length >= 0) {
-		unsigned char current_octet = *data++;
-		int bit;
-		for (bit = 8; --bit >= 0; current_octet >>= 1) {
-			if ((crc ^ current_octet) & 1) {
-				crc >>= 1;
-				crc ^= ethernet_polynomial_le;
-			} else
-				crc >>= 1;
-		}
-	}
-	return crc;
-}
-static unsigned const ethernet_polynomial = 0x04c11db7U;
-static inline u32 ether_crc(int length, unsigned char *data)
-{
-    int crc = -1;
-
-    while(--length >= 0) {
-		unsigned char current_octet = *data++;
-		int bit;
-		for (bit = 0; bit < 8; bit++, current_octet >>= 1)
-			crc = (crc << 1) ^
-				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
-    }
-    return crc;
-}
-
-static void set_rx_mode(struct net_device *dev)
-{
-	long ioaddr = dev->base_addr;
-	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-	tp->csr6 &= ~0x00D5;
-	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
-		tp->csr6 |= 0x00C0;
-		csr6 |= 0x00C0;
-		/* Unconditionally log net taps. */
-		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
-	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
-		/* Too many to filter well -- accept all multicasts. */
-		tp->csr6 |= 0x0080;
-		csr6 |= 0x0080;
-	} else	if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
-		/* Some work-alikes have only a 64-entry hash filter table. */
-		/* Should verify correctness on big-endian/__powerpc__ */
-		struct dev_mc_list *mclist;
-		int i;
-		u32 mc_filter[2];		 /* Multicast hash filter */
-		if (dev->mc_count > 64) {		/* Arbitrary non-effective limit. */
-			tp->csr6 |= 0x0080;
-			csr6 |= 0x0080;
-		} else {
-			mc_filter[1] = mc_filter[0] = 0;
-			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-				 i++, mclist = mclist->next)
-				set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter);
-			if (tp->chip_id == AX88140) {
-				outl(2, ioaddr + CSR13);
-				outl(mc_filter[0], ioaddr + CSR14);
-				outl(3, ioaddr + CSR13);
-				outl(mc_filter[1], ioaddr + CSR14);
-			} else if (tp->chip_id == COMET) { /* Has a simple hash filter. */
-				outl(mc_filter[0], ioaddr + 0xAC);
-				outl(mc_filter[1], ioaddr + 0xB0);
-			}
-		}
-	} else {
-		u16 *eaddrs, *setup_frm = tp->setup_frame;
-		struct dev_mc_list *mclist;
-		u32 tx_flags = 0x08000000 | 192;
-		int i;
-
-		/* Note that only the low-address shortword of setup_frame is valid!
-		   The values are doubled for big-endian architectures. */
-		if ((dev->mc_count > 14) || ((dev->mc_count > 6) && (tp->chip_id == X3201_3))) { /* Must use a multicast hash table. */
-			u16 hash_table[32];
-			tx_flags = 0x08400000 | 192;		/* Use hash filter. */
-			memset(hash_table, 0, sizeof(hash_table));
-			set_bit(255, hash_table); 			/* Broadcast entry */
-			/* This should work on big-endian machines as well. */
-			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-				 i++, mclist = mclist->next)
-				set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
-						hash_table);
-			for (i = 0; i < 32; i++) {
-				*setup_frm++ = hash_table[i];
-				*setup_frm++ = hash_table[i];
-			}
-			setup_frm = &tp->setup_frame[13*6];
-		} else if(tp->chip_id != X3201_3) {
-			/* We have <= 14 addresses so we can use the wonderful
-			   16 address perfect filtering of the Tulip. */
-			for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
-				 i++, mclist = mclist->next) {
-				eaddrs = (u16 *)mclist->dmi_addr;
-				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-			}
-			/* Fill the unused entries with the broadcast address. */
-			memset(setup_frm, 0xff, (15-i)*12);
-			setup_frm = &tp->setup_frame[15*6];
-		} else {
-			/* fill the first two table entries with our address */
-			eaddrs = (u16 *)dev->dev_addr;
-			for(i=0; i<2; i++) {
-				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-			}
-			/* Double fill each entry to accomodate chips that */
-			/* don't like to parse these correctly */
-			for (i=0, mclist=dev->mc_list; i<dev->mc_count;
-				 i++, mclist=mclist->next) {
-				eaddrs = (u16 *)mclist->dmi_addr;
-				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-			}
-			i=((i+1)*2);
-			/* Fill the unused entries with the broadcast address. */
-			memset(setup_frm, 0xff, (15-i)*12);
-			setup_frm = &tp->setup_frame[15*6];
-		}
-
-		/* Fill the final entry with our physical address. */
-		eaddrs = (u16 *)dev->dev_addr;
-		*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-		*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-		*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-		/* Now add this frame to the Tx list. */
-		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
-			/* Same setup recently queued, we need not add it. */
-		} else {
-			unsigned long flags;
-			unsigned int entry, dummy = -1;
-
-			save_flags(flags); cli();
-			entry = tp->cur_tx++ % TX_RING_SIZE;
-
-			if (entry != 0) {
-				/* Avoid a chip errata by prefixing a dummy entry. */
-				tp->tx_skbuff[entry] = 0;
-				tp->tx_ring[entry].length =
-					(entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0;
-				tp->tx_ring[entry].buffer1 = 0;
-				/* race with chip, set DescOwned later */
-				dummy = entry;
-				entry = tp->cur_tx++ % TX_RING_SIZE;
-			}
-
-			tp->tx_skbuff[entry] = 0;
-			/* Put the setup frame on the Tx list. */
-			if (entry == TX_RING_SIZE-1)
-				tx_flags |= DESC_RING_WRAP;		/* Wrap ring. */
-			tp->tx_ring[entry].length = tx_flags;
-			if(tp->chip_id == X3201_3)
-				tp->tx_ring[entry].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
-			else
-				tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
-			tp->tx_ring[entry].status = DescOwned;
-			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
-				tp->tx_full = 1;
-				netif_stop_queue (dev);
-			}
-			if (dummy >= 0)
-				tp->tx_ring[dummy].status = DescOwned;
-			restore_flags(flags);
-			/* Trigger an immediate transmit demand. */
-			outl(0, ioaddr + CSR1);
-		}
-	}
-	outl_CSR6(csr6 | 0x0000, ioaddr, tp->chip_id);
-}
-
-static const struct pci_device_id tulip_pci_table[] __devinitdata = {
-  { 0x1011, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21040 },
-  { 0x1011, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21041 },
-  { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
-  { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21142 },
-  { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 },
-  { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 },
-  { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
-  { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },
-  { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 },
-  { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 },
-  { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
-  { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
-  { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 },
-  {0},
-};
-
-MODULE_DEVICE_TABLE(pci, tulip_pci_table);
-
-static int __devinit tulip_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
-	struct net_device *dev;
-	static int board_idx = 0;
-
-	printk(KERN_INFO "tulip_attach(%s)\n", pdev->slot_name);
-
-	pci_enable_device (pdev);
-	pci_set_master (pdev);
-	dev = tulip_probe1(pdev, NULL,
-			   pci_resource_start (pdev, 0), pdev->irq,
-			   id->driver_data, board_idx++);
-	if (dev) {
-		pdev->driver_data = dev;
-		return 0;
-	}
-	return -ENODEV;
-}
-
-static void tulip_suspend(struct pci_dev *pdev)
-{
-	struct net_device *dev = pdev->driver_data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	printk(KERN_INFO "tulip_suspend(%s)\n", dev->name);
-	if (tp->open) tulip_down(dev);
-}
-
-static void tulip_resume(struct pci_dev *pdev)
-{
-	struct net_device *dev = pdev->driver_data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	printk(KERN_INFO "tulip_resume(%s)\n", dev->name);
-	if (tp->open) tulip_up(dev);
-}
-
-static void __devexit tulip_remove(struct pci_dev *pdev)
-{
-	struct net_device *dev = pdev->driver_data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-	printk(KERN_INFO "tulip_detach(%s)\n", dev->name);
-	unregister_netdev(dev);
-	kfree(dev);
-	kfree(tp);
-}
-
-static struct pci_driver tulip_ops = {
-	name:		"tulip_cb",
-	id_table:	tulip_pci_table,
-	probe:		tulip_pci_probe,
-	remove:		tulip_remove,
-	suspend:	tulip_suspend,
-	resume:		tulip_resume
-};
-
-static int __init tulip_init(void)
-{
-	pci_register_driver(&tulip_ops);
-	return 0;
-}
-
-static __exit void tulip_exit(void)
-{
-	pci_unregister_driver(&tulip_ops);
-}
-
-module_init(tulip_init)
-module_exit(tulip_exit)
-
-
-/*
- * Local variables:
- *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/"
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 4
- * End:
- */
diff -u --recursive --new-file v2.3.51/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- v2.3.51/linux/drivers/net/pcnet32.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/pcnet32.c	Mon Mar 13 09:50:16 2000
@@ -1445,7 +1445,7 @@
 	    lp->a.write_bcr (ioaddr, 33, phyaddr);
 	    return 0;
 	 case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
-	    if (!suser())
+	    if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
 	    lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
 	    lp->a.write_bcr (ioaddr, 34, data[2]);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.3.51/linux/drivers/net/plip.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/plip.c	Sun Mar 12 19:18:55 2000
@@ -583,6 +583,61 @@
 	return OK;
 }
 
+/*
+ *	Determine the packet's protocol ID. The rule here is that we 
+ *	assume 802.3 if the type field is short enough to be a length.
+ *	This is normal practice and works for any 'now in use' protocol.
+ *
+ *	PLIP is ethernet ish but the daddr might not be valid if unicast.
+ *	PLIP fortunately has no bus architecture (its Point-to-point).
+ *
+ *	We can't fix the daddr thing as that quirk (more bug) is embedded
+ *	in far too many old systems not all even running Linux.
+ */
+ 
+static unsigned short plip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ethhdr *eth;
+	unsigned char *rawp;
+	
+	skb->mac.raw=skb->data;
+	skb_pull(skb,dev->hard_header_len);
+	eth= skb->mac.ethernet;
+	
+	if(*eth->h_dest&1)
+	{
+		if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+			skb->pkt_type=PACKET_BROADCAST;
+		else
+			skb->pkt_type=PACKET_MULTICAST;
+	}
+	
+	/*
+	 *	This ALLMULTI check should be redundant by 1.4
+	 *	so don't forget to remove it.
+	 */
+	 
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+		
+	rawp = skb->data;
+	
+	/*
+	 *	This is a magic hack to spot IPX packets. Older Novell breaks
+	 *	the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *	layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *	won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *)rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+		
+	/*
+	 *	Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
+}
+
+
 /* PLIP_RECEIVE_PACKET --- receive a packet */
 static int
 plip_receive_packet(struct net_device *dev, struct net_local *nl,
@@ -669,7 +724,7 @@
 
 	case PLIP_PK_DONE:
 		/* Inform the upper layer for the arrival of a packet. */
-		rcv->skb->protocol=eth_type_trans(rcv->skb, dev);
+		rcv->skb->protocol=plip_type_trans(rcv->skb, dev);
 		netif_rx(rcv->skb);
 		nl->enet_stats.rx_bytes += rcv->length.h;
 		nl->enet_stats.rx_packets++;
@@ -1227,6 +1282,8 @@
 		pc->nibble  = nl->nibble;
 		break;
 	case PLIP_SET_TIMEOUT:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		nl->trigger = pc->trigger;
 		nl->nibble  = pc->nibble;
 		break;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/ppp_async.c linux/drivers/net/ppp_async.c
--- v2.3.51/linux/drivers/net/ppp_async.c	Thu Nov 11 20:11:41 1999
+++ linux/drivers/net/ppp_async.c	Tue Mar 14 09:46:22 2000
@@ -17,11 +17,9 @@
  * PPP driver, written by Michael Callahan and Al Longyear, and
  * subsequently hacked by Paul Mackerras.
  *
- * ==FILEVERSION 990806==
+ * ==FILEVERSION 20000227==
  */
 
-/* $Id: ppp_async.c,v 1.3 1999/09/02 05:30:10 paulus Exp $ */
-
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
@@ -31,9 +29,18 @@
 #include <linux/ppp_defs.h>
 #include <linux/if_ppp.h>
 #include <linux/ppp_channel.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
 #include <asm/uaccess.h>
 
-#define PPP_VERSION	"2.4.0"
+#ifndef spin_trylock_bh
+#define spin_trylock_bh(lock)	({ int __r; local_bh_disable();	\
+				   __r = spin_trylock(lock);	\
+				   if (!__r) local_bh_enable();	\
+				   __r; })
+#endif
+
+#define PPP_VERSION	"2.4.1"
 
 #define OBUFSIZE	256
 
@@ -44,7 +51,9 @@
 	unsigned int	state;
 	unsigned int	rbits;
 	int		mru;
-	unsigned long	busy;
+	spinlock_t	xmit_lock;
+	spinlock_t	recv_lock;
+	unsigned long	xmit_flags;
 	u32		xaccm[8];
 	u32		raccm;
 	unsigned int	bytes_sent;
@@ -55,24 +64,18 @@
 	u16		tfcs;
 	unsigned char	*optr;
 	unsigned char	*olim;
-	struct sk_buff_head xq;
 	unsigned long	last_xmit;
 
 	struct sk_buff	*rpkt;
-	struct sk_buff_head rq;
-	wait_queue_head_t rwait;
+	int		lcp_fcs;
 
 	struct ppp_channel chan;	/* interface to generic ppp layer */
-	int		connected;
-	int	index;
 	unsigned char	obuf[OBUFSIZE];
 };
 
-/* Bit numbers in busy */
-#define XMIT_BUSY	0
-#define RECV_BUSY	1
-#define XMIT_WAKEUP	2
-#define XMIT_FULL	3
+/* Bit numbers in xmit_flags */
+#define XMIT_WAKEUP	0
+#define XMIT_FULL	1
 
 /* State bits */
 #define SC_TOSS		0x20000000
@@ -81,8 +84,6 @@
 /* Bits in rbits */
 #define SC_RCV_BITS	(SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
 
-#define PPPASYNC_MAX_RQLEN	32	/* arbitrary */
-
 static int flag_time = HZ;
 MODULE_PARM(flag_time, "i");
 
@@ -95,57 +96,17 @@
 static void ppp_async_flush_output(struct asyncppp *ap);
 static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
 			    char *flags, int count);
+static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd,
+			   unsigned long arg);
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+			   int len, int inbound);
 
 struct ppp_channel_ops async_ops = {
-	ppp_async_send
+	ppp_async_send,
+	ppp_async_ioctl
 };
 
 /*
- * Routines for locking and unlocking the transmit and receive paths.
- */
-static inline void
-lock_path(struct asyncppp *ap, int bit)
-{
-	do {
-		while (test_bit(bit, &ap->busy))
-		       mb();
-	} while (test_and_set_bit(bit, &ap->busy));
-	mb();
-}
-
-static inline int
-trylock_path(struct asyncppp *ap, int bit)
-{
-	if (test_and_set_bit(bit, &ap->busy))
-		return 0;
-	mb();
-	return 1;
-}
-
-static inline void
-unlock_path(struct asyncppp *ap, int bit)
-{
-	mb();
-	clear_bit(bit, &ap->busy);
-}
-
-#define lock_xmit_path(ap)	lock_path(ap, XMIT_BUSY)
-#define trylock_xmit_path(ap)	trylock_path(ap, XMIT_BUSY)
-#define unlock_xmit_path(ap)	unlock_path(ap, XMIT_BUSY)
-#define lock_recv_path(ap)	lock_path(ap, RECV_BUSY)
-#define trylock_recv_path(ap)	trylock_path(ap, RECV_BUSY)
-#define unlock_recv_path(ap)	unlock_path(ap, RECV_BUSY)
-
-static inline void
-flush_skb_queue(struct sk_buff_head *q)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(q)) != 0)
-		kfree_skb(skb);
-}
-
-/*
  * Routines implementing the PPP line discipline.
  */
 
@@ -153,256 +114,113 @@
  * Called when a tty is put into PPP line discipline.
  */
 static int
-ppp_async_open(struct tty_struct *tty)
+ppp_asynctty_open(struct tty_struct *tty)
 {
 	struct asyncppp *ap;
+	int err;
 
 	ap = kmalloc(sizeof(*ap), GFP_KERNEL);
 	if (ap == 0)
 		return -ENOMEM;
 
-	MOD_INC_USE_COUNT;
-
 	/* initialize the asyncppp structure */
 	memset(ap, 0, sizeof(*ap));
 	ap->tty = tty;
 	ap->mru = PPP_MRU;
+	spin_lock_init(&ap->xmit_lock);
+	spin_lock_init(&ap->recv_lock);
 	ap->xaccm[0] = ~0U;
 	ap->xaccm[3] = 0x60000000U;
 	ap->raccm = ~0U;
 	ap->optr = ap->obuf;
 	ap->olim = ap->obuf;
-	skb_queue_head_init(&ap->xq);
-	skb_queue_head_init(&ap->rq);
-	init_waitqueue_head(&ap->rwait);
+	ap->lcp_fcs = -1;
+
+	ap->chan.private = ap;
+	ap->chan.ops = &async_ops;
+	ap->chan.mtu = PPP_MRU;
+	err = ppp_register_channel(&ap->chan);
+	if (err) {
+		kfree(ap);
+		return err;
+	}
 
 	tty->disc_data = ap;
 
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
 /*
  * Called when the tty is put into another line discipline
- * (or it hangs up).
+ * or it hangs up.
+ * We assume that while we are in this routine, the tty layer
+ * won't call any of the other line discipline entries for the
+ * same tty.
  */
 static void
-ppp_async_close(struct tty_struct *tty)
+ppp_asynctty_close(struct tty_struct *tty)
 {
 	struct asyncppp *ap = tty->disc_data;
 
 	if (ap == 0)
 		return;
 	tty->disc_data = 0;
-	lock_xmit_path(ap);
-	lock_recv_path(ap);
+	ppp_unregister_channel(&ap->chan);
 	if (ap->rpkt != 0)
 		kfree_skb(ap->rpkt);
-	flush_skb_queue(&ap->rq);
 	if (ap->tpkt != 0)
 		kfree_skb(ap->tpkt);
-	flush_skb_queue(&ap->xq);
-	if (ap->connected)
-		ppp_unregister_channel(&ap->chan);
 	kfree(ap);
 	MOD_DEC_USE_COUNT;
 }
 
 /*
- * Read a PPP frame.  pppd can use this to negotiate over the
- * channel before it joins it to a bundle.
+ * Read does nothing.
  */
 static ssize_t
-ppp_async_read(struct tty_struct *tty, struct file *file,
-	       unsigned char *buf, size_t count)
+ppp_asynctty_read(struct tty_struct *tty, struct file *file,
+		  unsigned char *buf, size_t count)
 {
+	/* For now, do the same as the old 2.3.x code useta */
 	struct asyncppp *ap = tty->disc_data;
-	DECLARE_WAITQUEUE(wait, current);
-	ssize_t ret;
-	struct sk_buff *skb = 0;
 
-	ret = -ENXIO;
 	if (ap == 0)
-		goto out;		/* should never happen */
-
-	add_wait_queue(&ap->rwait, &wait);
-	current->state = TASK_INTERRUPTIBLE;
-	for (;;) {
-		ret = -EAGAIN;
-		skb = skb_dequeue(&ap->rq);
-		if (skb)
-			break;
-		if (file->f_flags & O_NONBLOCK)
-			break;
-		ret = -ERESTARTSYS;
-		if (signal_pending(current))
-			break;
-		schedule();
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&ap->rwait, &wait);
-
-	if (skb == 0)
-		goto out;
-
-	ret = -EOVERFLOW;
-	if (skb->len > count)
-		goto outf;
-	ret = -EFAULT;
-	if (copy_to_user(buf, skb->data, skb->len))
-		goto outf;
-	ret = skb->len;
-
- outf:
-	kfree_skb(skb);
- out:
-	return ret;
+		return -ENXIO;
+	return ppp_channel_read(&ap->chan, file, buf, count);
 }
 
 /*
- * Write a ppp frame.  pppd can use this to send frames over
- * this particular channel.
+ * Write on the tty does nothing, the packets all come in
+ * from the ppp generic stuff.
  */
 static ssize_t
-ppp_async_write(struct tty_struct *tty, struct file *file,
-		const unsigned char *buf, size_t count)
+ppp_asynctty_write(struct tty_struct *tty, struct file *file,
+		   const unsigned char *buf, size_t count)
 {
+	/* For now, do the same as the old 2.3.x code useta */
 	struct asyncppp *ap = tty->disc_data;
-	struct sk_buff *skb;
-	ssize_t ret;
 
-	ret = -ENXIO;
 	if (ap == 0)
-		goto out;	/* should never happen */
-
-	ret = -ENOMEM;
-	skb = alloc_skb(count + 2, GFP_KERNEL);
-	if (skb == 0)
-		goto out;
-	skb_reserve(skb, 2);
-	ret = -EFAULT;
-	if (copy_from_user(skb_put(skb, count), buf, count)) {
-		kfree_skb(skb);
-		goto out;
-	}
-
-	skb_queue_tail(&ap->xq, skb);
-	ppp_async_push(ap);
-
-	ret = count;
-
- out:
-	return ret;
+		return -ENXIO;
+	return ppp_channel_write(&ap->chan, buf, count);
 }
 
 static int
-ppp_async_ioctl(struct tty_struct *tty, struct file *file,
-		unsigned int cmd, unsigned long arg)
+ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
+		   unsigned int cmd, unsigned long arg)
 {
 	struct asyncppp *ap = tty->disc_data;
 	int err, val;
-	u32 accm[8];
-	struct sk_buff *skb;
-
-	err = -ENXIO;
-	if (ap == 0)
-		goto out;	/* should never happen */
-	err = -EPERM;
-	if (!capable(CAP_NET_ADMIN))
-		goto out;
 
 	err = -EFAULT;
 	switch (cmd) {
-	case PPPIOCGFLAGS:
-		val = ap->flags | ap->rbits;
-		if (put_user(val, (int *) arg))
-			break;
-		err = 0;
-		break;
-	case PPPIOCSFLAGS:
-		if (get_user(val, (int *) arg))
-			break;
-		ap->flags = val & ~SC_RCV_BITS;
-		ap->rbits = val & SC_RCV_BITS;
-		err = 0;
-		break;
-
-	case PPPIOCGASYNCMAP:
-		if (put_user(ap->xaccm[0], (u32 *) arg))
-			break;
-		err = 0;
-		break;
-	case PPPIOCSASYNCMAP:
-		if (get_user(ap->xaccm[0], (u32 *) arg))
-			break;
-		err = 0;
-		break;
-
-	case PPPIOCGRASYNCMAP:
-		if (put_user(ap->raccm, (u32 *) arg))
-			break;
-		err = 0;
-		break;
-	case PPPIOCSRASYNCMAP:
-		if (get_user(ap->raccm, (u32 *) arg))
-			break;
-		err = 0;
-		break;
-
-	case PPPIOCGXASYNCMAP:
-		if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
-			break;
-		err = 0;
-		break;
-	case PPPIOCSXASYNCMAP:
-		if (copy_from_user(accm, (void *) arg, sizeof(accm)))
-			break;
-		accm[2] &= ~0x40000000U;	/* can't escape 0x5e */
-		accm[3] |= 0x60000000U;		/* must escape 0x7d, 0x7e */
-		memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
-		err = 0;
-		break;
-
-	case PPPIOCGMRU:
-		if (put_user(ap->mru, (int *) arg))
-			break;
-		err = 0;
-		break;
-	case PPPIOCSMRU:
-		if (get_user(val, (int *) arg))
-			break;
-		if (val < PPP_MRU)
-			val = PPP_MRU;
-		ap->mru = val;
-		err = 0;
-		break;
-
-	case PPPIOCATTACH:
-		if (get_user(val, (int *) arg))
-			break;
-		err = -EALREADY;
-		if (ap->connected)
-			break;
-		ap->chan.private = ap;
-		ap->chan.ops = &async_ops;
-		err = ppp_register_channel(&ap->chan, val);
-		if (err != 0)
-			break;
-		ap->connected = 1;
-		ap->index = val;
-		break;
-	case PPPIOCDETACH:
-		err = -ENXIO;
-		if (!ap->connected)
-			break;
-		ppp_unregister_channel(&ap->chan);
-		ap->connected = 0;
-		err = 0;
-		break;
 	case PPPIOCGUNIT:
 		err = -ENXIO;
-		if (!ap->connected)
+		if (ap == 0)
 			break;
-		if (put_user(ap->index, (int *) arg))
+		err = -EFAULT;
+		if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
 			break;
 		err = 0;
 		break;
@@ -414,8 +232,6 @@
 
 	case TCFLSH:
 		/* flush our buffers and the serial port's buffer */
-		if (arg == TCIFLUSH || arg == TCIOFLUSH)
-			flush_skb_queue(&ap->rq);
 		if (arg == TCIOFLUSH || arg == TCOFLUSH)
 			ppp_async_flush_output(ap);
 		err = n_tty_ioctl(tty, file, cmd, arg);
@@ -423,68 +239,86 @@
 
 	case FIONREAD:
 		val = 0;
-		if ((skb = skb_peek(&ap->rq)) != 0)
-			val = skb->len;
 		if (put_user(val, (int *) arg))
 			break;
 		err = 0;
 		break;
 
+/*
+ * For now, do the same as the old 2.3 driver useta
+ */
+	case PPPIOCGFLAGS:
+	case PPPIOCSFLAGS:
+	case PPPIOCGASYNCMAP:
+	case PPPIOCSASYNCMAP:
+	case PPPIOCGRASYNCMAP:
+	case PPPIOCSRASYNCMAP:
+	case PPPIOCGXASYNCMAP:
+	case PPPIOCSXASYNCMAP:
+	case PPPIOCGMRU:
+	case PPPIOCSMRU:
+		err = ppp_async_ioctl(&ap->chan, cmd, arg);
+		break;
+
+	case PPPIOCATTACH:
+		err = ppp_channel_ioctl(&ap->chan, cmd, arg);
+		break;
+
 	default:
 		err = -ENOIOCTLCMD;
 	}
- out:
+
 	return err;
 }
 
 static unsigned int
-ppp_async_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
+ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
 {
-	struct asyncppp *ap = tty->disc_data;
 	unsigned int mask;
+	struct asyncppp *ap = tty->disc_data;
 
-	if (ap == 0)
-		return 0;	/* should never happen */
-	poll_wait(file, &ap->rwait, wait);
 	mask = POLLOUT | POLLWRNORM;
-	if (skb_peek(&ap->rq))
-		mask |= POLLIN | POLLRDNORM;
+/*
+ * For now, do the same as the old 2.3 driver useta
+ */
+	if (ap != 0)
+		mask |= ppp_channel_poll(&ap->chan, file, wait);
 	if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file))
 		mask |= POLLHUP;
 	return mask;
 }
 
 static int
-ppp_async_room(struct tty_struct *tty)
+ppp_asynctty_room(struct tty_struct *tty)
 {
 	return 65535;
 }
 
 static void
-ppp_async_receive(struct tty_struct *tty, const unsigned char *buf,
+ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
 		  char *flags, int count)
 {
 	struct asyncppp *ap = tty->disc_data;
 
 	if (ap == 0)
 		return;
-	trylock_recv_path(ap);
+	spin_lock_bh(&ap->recv_lock);
 	ppp_async_input(ap, buf, flags, count);
-	unlock_recv_path(ap);
+	spin_unlock_bh(&ap->recv_lock);
 	if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
 	    && tty->driver.unthrottle)
 		tty->driver.unthrottle(tty);
 }
 
 static void
-ppp_async_wakeup(struct tty_struct *tty)
+ppp_asynctty_wakeup(struct tty_struct *tty)
 {
 	struct asyncppp *ap = tty->disc_data;
 
 	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
 	if (ap == 0)
 		return;
-	if (ppp_async_push(ap) && ap->connected)
+	if (ppp_async_push(ap))
 		ppp_output_wakeup(&ap->chan);
 }
 
@@ -492,15 +326,15 @@
 static struct tty_ldisc ppp_ldisc = {
 	magic:	TTY_LDISC_MAGIC,
 	name:	"ppp",
-	open:	ppp_async_open,
-	close:	ppp_async_close,
-	read:	ppp_async_read,
-	write:	ppp_async_write,
-	ioctl:	ppp_async_ioctl,
-	poll:	ppp_async_poll,
-	receive_room: ppp_async_room,
-	receive_buf: ppp_async_receive,
-	write_wakeup: ppp_async_wakeup,
+	open:	ppp_asynctty_open,
+	close:	ppp_asynctty_close,
+	read:	ppp_asynctty_read,
+	write:	ppp_asynctty_write,
+	ioctl:	ppp_asynctty_ioctl,
+	poll:	ppp_asynctty_poll,
+	receive_room: ppp_asynctty_room,
+	receive_buf: ppp_asynctty_receive,
+	write_wakeup: ppp_asynctty_wakeup,
 };
 
 int
@@ -516,6 +350,91 @@
 }
 
 /*
+ * The following routines provide the PPP channel interface.
+ */
+static int
+ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+{
+	struct asyncppp *ap = chan->private;
+	int err, val;
+	u32 accm[8];
+
+	err = -EFAULT;
+	switch (cmd) {
+	case PPPIOCGFLAGS:
+		val = ap->flags | ap->rbits;
+		if (put_user(val, (int *) arg))
+			break;
+		err = 0;
+		break;
+	case PPPIOCSFLAGS:
+		if (get_user(val, (int *) arg))
+			break;
+		ap->flags = val & ~SC_RCV_BITS;
+		spin_lock_bh(&ap->recv_lock);
+		ap->rbits = val & SC_RCV_BITS;
+		spin_unlock_bh(&ap->recv_lock);
+		err = 0;
+		break;
+
+	case PPPIOCGASYNCMAP:
+		if (put_user(ap->xaccm[0], (u32 *) arg))
+			break;
+		err = 0;
+		break;
+	case PPPIOCSASYNCMAP:
+		if (get_user(ap->xaccm[0], (u32 *) arg))
+			break;
+		err = 0;
+		break;
+
+	case PPPIOCGRASYNCMAP:
+		if (put_user(ap->raccm, (u32 *) arg))
+			break;
+		err = 0;
+		break;
+	case PPPIOCSRASYNCMAP:
+		if (get_user(ap->raccm, (u32 *) arg))
+			break;
+		err = 0;
+		break;
+
+	case PPPIOCGXASYNCMAP:
+		if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
+			break;
+		err = 0;
+		break;
+	case PPPIOCSXASYNCMAP:
+		if (copy_from_user(accm, (void *) arg, sizeof(accm)))
+			break;
+		accm[2] &= ~0x40000000U;	/* can't escape 0x5e */
+		accm[3] |= 0x60000000U;		/* must escape 0x7d, 0x7e */
+		memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+		err = 0;
+		break;
+
+	case PPPIOCGMRU:
+		if (put_user(ap->mru, (int *) arg))
+			break;
+		err = 0;
+		break;
+	case PPPIOCSMRU:
+		if (get_user(val, (int *) arg))
+			break;
+		if (val < PPP_MRU)
+			val = PPP_MRU;
+		ap->mru = val;
+		err = 0;
+		break;
+
+	default:
+		err = -ENOTTY;
+	}
+
+	return err;
+}
+
+/*
  * Procedures for encapsulation and framing.
  */
 
@@ -597,6 +516,9 @@
 	islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
 
 	if (i == 0) {
+		if (islcp)
+			async_lcp_peek(ap, data, count, 0);
+
 		/*
 		 * Start of a new packet - insert the leading FLAG
 		 * character if necessary.
@@ -675,7 +597,7 @@
 
 	ppp_async_push(ap);
 
-	if (test_and_set_bit(XMIT_FULL, &ap->busy))
+	if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
 		return 0;	/* already full */
 	ap->tpkt = skb;
 	ap->tpkt_pos = 0;
@@ -694,12 +616,11 @@
 	struct tty_struct *tty = ap->tty;
 	int tty_stuffed = 0;
 
-	if (!trylock_xmit_path(ap)) {
-		set_bit(XMIT_WAKEUP, &ap->busy);
+	set_bit(XMIT_WAKEUP, &ap->xmit_flags);
+	if (!spin_trylock_bh(&ap->xmit_lock))
 		return 0;
-	}
 	for (;;) {
-		if (test_and_clear_bit(XMIT_WAKEUP, &ap->busy))
+		if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
 			tty_stuffed = 0;
 		if (!tty_stuffed && ap->optr < ap->olim) {
 			avail = ap->olim - ap->optr;
@@ -715,22 +636,17 @@
 		if (ap->optr == ap->olim && ap->tpkt != 0) {
 			if (ppp_async_encode(ap)) {
 				/* finished processing ap->tpkt */
-				struct sk_buff *skb = skb_dequeue(&ap->xq);
-				if (skb != 0) {
-					ap->tpkt = skb;
-				} else {
-					clear_bit(XMIT_FULL, &ap->busy);
-					done = 1;
-				}
+				clear_bit(XMIT_FULL, &ap->xmit_flags);
+				done = 1;
 			}
 			continue;
 		}
 		/* haven't made any progress */
-		unlock_xmit_path(ap);
-		if (!(test_bit(XMIT_WAKEUP, &ap->busy)
+		spin_unlock_bh(&ap->xmit_lock);
+		if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)
 		      || (!tty_stuffed && ap->tpkt != 0)))
 			break;
-		if (!trylock_xmit_path(ap))
+		if (!spin_trylock_bh(&ap->xmit_lock))
 			break;
 	}
 	return done;
@@ -739,11 +655,11 @@
 	if (ap->tpkt != 0) {
 		kfree_skb(ap->tpkt);
 		ap->tpkt = 0;
-		clear_bit(XMIT_FULL, &ap->busy);
+		clear_bit(XMIT_FULL, &ap->xmit_flags);
 		done = 1;
 	}
 	ap->optr = ap->olim;
-	unlock_xmit_path(ap);
+	spin_unlock_bh(&ap->xmit_lock);
 	return done;
 }
 
@@ -756,17 +672,16 @@
 {
 	int done = 0;
 
-	flush_skb_queue(&ap->xq);
-	lock_xmit_path(ap);
+	spin_lock_bh(&ap->xmit_lock);
 	ap->optr = ap->olim;
 	if (ap->tpkt != NULL) {
 		kfree_skb(ap->tpkt);
 		ap->tpkt = 0;
-		clear_bit(XMIT_FULL, &ap->busy);
+		clear_bit(XMIT_FULL, &ap->xmit_flags);
 		done = 1;
 	}
-	unlock_xmit_path(ap);
-	if (done && ap->connected)
+	spin_unlock_bh(&ap->xmit_lock);
+	if (done)
 		ppp_output_wakeup(&ap->chan);
 }
 
@@ -795,7 +710,7 @@
 {
 	struct sk_buff *skb;
 	unsigned char *p;
-	unsigned int len, fcs;
+	unsigned int len, fcs, proto;
 	int code = 0;
 
 	skb = ap->rpkt;
@@ -827,37 +742,32 @@
 			goto err;
 		p = skb_pull(skb, 2);
 	}
-	if (p[0] & 1) {
+	proto = p[0];
+	if (proto & 1) {
 		/* protocol is compressed */
 		skb_push(skb, 1)[0] = 0;
-	} else if (skb->len < 2)
-		goto err;
-
-	/* all OK, give it to the generic layer or queue it */
-	if (ap->connected) {
-		ppp_input(&ap->chan, skb);
 	} else {
-		skb_queue_tail(&ap->rq, skb);
-		/* drop old frames if queue too long */
-		while (ap->rq.qlen > PPPASYNC_MAX_RQLEN
-		       && (skb = skb_dequeue(&ap->rq)) != 0)
-			kfree(skb);
-		wake_up_interruptible(&ap->rwait);
+		if (skb->len < 2)
+			goto err;
+		proto = (proto << 8) + p[1];
+		if (proto == PPP_LCP)
+			async_lcp_peek(ap, p, skb->len, 1);
 	}
+
+	/* all OK, give it to the generic layer */
+	ppp_input(&ap->chan, skb);
 	return;
 
  err:
 	kfree_skb(skb);
-	if (ap->connected)
-		ppp_input_error(&ap->chan, code);
+	ppp_input_error(&ap->chan, code);
 }
 
 static inline void
 input_error(struct asyncppp *ap, int code)
 {
 	ap->state |= SC_TOSS;
-	if (ap->connected)
-		ppp_input_error(&ap->chan, code);
+	ppp_input_error(&ap->chan, code);
 }
 
 /* called when the tty driver has data for us. */
@@ -950,17 +860,96 @@
 	input_error(ap, 0);
 }
 
-#ifdef MODULE
-int
-init_module(void)
-{
-	return ppp_async_init();
+/*
+ * We look at LCP frames going past so that we can notice
+ * and react to the LCP configure-ack from the peer.
+ * In the situation where the peer has been sent a configure-ack
+ * already, LCP is up once it has sent its configure-ack
+ * so the immediately following packet can be sent with the
+ * configured LCP options.  This allows us to process the following
+ * packet correctly without pppd needing to respond quickly.
+ *
+ * We only respond to the received configure-ack if we have just
+ * sent a configure-request, and the configure-ack contains the
+ * same data (this is checked using a 16-bit crc of the data).
+ */
+#define CONFREQ		1	/* LCP code field values */
+#define CONFACK		2
+#define LCP_MRU		1	/* LCP option numbers */
+#define LCP_ASYNCMAP	2
+
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+			   int len, int inbound)
+{
+	int dlen, fcs, i, code;
+	u32 val;
+
+	data += 2;		/* skip protocol bytes */
+	len -= 2;
+	if (len < 4)		/* 4 = code, ID, length */
+		return;
+	code = data[0];
+	if (code != CONFACK && code != CONFREQ)
+		return;
+	dlen = (data[2] << 8) + data[3];
+	if (len < dlen)
+		return;		/* packet got truncated or length is bogus */
+
+	if (code == (inbound? CONFACK: CONFREQ)) {
+		/*
+		 * sent confreq or received confack:
+		 * calculate the crc of the data from the ID field on.
+		 */
+		fcs = PPP_INITFCS;
+		for (i = 1; i < dlen; ++i)
+			fcs = PPP_FCS(fcs, data[i]);
+
+		if (!inbound) {
+			/* outbound confreq - remember the crc for later */
+			ap->lcp_fcs = fcs;
+			return;
+		}
+
+		/* received confack, check the crc */
+		fcs ^= ap->lcp_fcs;
+		ap->lcp_fcs = -1;
+		if (fcs != 0)
+			return;
+	} else if (inbound)
+		return;	/* not interested in received confreq */
+
+	/* process the options in the confack */
+	data += 4;
+	dlen -= 4;
+	/* data[0] is code, data[1] is length */
+	while (dlen >= 2 && dlen >= data[1]) {
+		switch (data[0]) {
+		case LCP_MRU:
+			val = (data[2] << 8) + data[3];
+			if (inbound)
+				ap->mru = val;
+			else
+				ap->chan.mtu = val;
+			break;
+		case LCP_ASYNCMAP:
+			val = (data[2] << 24) + (data[3] << 16)
+				+ (data[4] << 8) + data[5];
+			if (inbound)
+				ap->raccm = val;
+			else
+				ap->xaccm[0] = val;
+			break;
+		}
+		dlen -= data[1];
+		data += data[1];
+	}
 }
 
-void
-cleanup_module(void)
+void __exit ppp_async_cleanup(void)
 {
 	if (tty_register_ldisc(N_PPP, NULL) != 0)
 		printk(KERN_ERR "failed to unregister PPP line discipline\n");
 }
-#endif /* MODULE */
+
+module_init(ppp_async_init);
+module_exit(ppp_async_cleanup);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/ppp_deflate.c linux/drivers/net/ppp_deflate.c
--- v2.3.51/linux/drivers/net/ppp_deflate.c	Wed Mar 10 16:51:35 1999
+++ linux/drivers/net/ppp_deflate.c	Sun Mar 12 21:12:37 2000
@@ -32,26 +32,9 @@
  */
 
 #include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/vmalloc.h>
-#include <linux/errno.h>
-#include <linux/string.h>	/* used in new tty drivers */
-#include <linux/signal.h>	/* used in new tty drivers */
-
-#include <asm/system.h>
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
+#include <linux/init.h>
 
 #include <linux/ppp_defs.h>
 #include <linux/ppp-comp.h>
@@ -656,31 +639,21 @@
 	z_comp_stats,		/* decomp_stat */
 };
 
-#ifdef MODULE
-/*************************************************************
- * Module support routines
- *************************************************************/
-
-int
-init_module(void)
+int deflate_init(void)
 {  
-        int answer = ppp_register_compressor (&ppp_deflate);
+        int answer = ppp_register_compressor(&ppp_deflate);
         if (answer == 0)
-                printk (KERN_INFO
-			"PPP Deflate Compression module registered\n");
+                printk(KERN_INFO
+		       "PPP Deflate Compression module registered\n");
 	ppp_register_compressor(&ppp_deflate_draft);
         return answer;
 }
      
-void
-cleanup_module(void)
+void deflate_cleanup(void)
 {
-	if (MOD_IN_USE)
-		printk (KERN_INFO
-			"Deflate Compression module busy, remove delayed\n");
-	else {
-	        ppp_unregister_compressor (&ppp_deflate);
-	        ppp_unregister_compressor (&ppp_deflate_draft);
-	}
+	ppp_unregister_compressor(&ppp_deflate);
+	ppp_unregister_compressor(&ppp_deflate_draft);
 }
-#endif
+
+module_init(deflate_init);
+module_exit(deflate_cleanup);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/ppp_generic.c linux/drivers/net/ppp_generic.c
--- v2.3.51/linux/drivers/net/ppp_generic.c	Fri Mar 10 16:40:43 2000
+++ linux/drivers/net/ppp_generic.c	Sun Mar 12 21:12:37 2000
@@ -1,7 +1,7 @@
 /*
  * Generic PPP layer for Linux.
  *
- * Copyright 1999 Paul Mackerras.
+ * Copyright 1999-2000 Paul Mackerras.
  *
  *  This program is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License
@@ -19,11 +19,9 @@
  * PPP driver, written by Michael Callahan and Al Longyear, and
  * subsequently hacked by Paul Mackerras.
  *
- * ==FILEVERSION 990915==
+ * ==FILEVERSION 20000313==
  */
 
-/* $Id: ppp_generic.c,v 1.5 1999/09/15 11:21:48 paulus Exp $ */
-
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -44,16 +42,9 @@
 #include <linux/tcp.h>
 #include <linux/spinlock.h>
 #include <net/slhc_vj.h>
+#include <asm/atomic.h>
 
-#define PPP_VERSION	"2.4.0"
-
-EXPORT_SYMBOL(ppp_register_channel);
-EXPORT_SYMBOL(ppp_unregister_channel);
-EXPORT_SYMBOL(ppp_input);
-EXPORT_SYMBOL(ppp_input_error);
-EXPORT_SYMBOL(ppp_output_wakeup);
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
+#define PPP_VERSION	"2.4.1"
 
 /*
  * Network protocols we support.
@@ -64,32 +55,56 @@
 #define NP_AT	3		/* Appletalk protocol */
 #define NUM_NP	4		/* Number of NPs. */
 
+#define MPHDRLEN	4	/* multilink protocol header length */
+#define MPHDRLEN_SSN	2	/* ditto with short sequence numbers */
+#define MIN_FRAG_SIZE	64
+
+/*
+ * An instance of /dev/ppp can be associated with either a ppp
+ * interface unit or a ppp channel.  In both cases, file->private_data
+ * points to one of these.
+ */
+struct ppp_file {
+	enum {
+		INTERFACE=1, CHANNEL
+	}		kind;
+	struct sk_buff_head xq;		/* pppd transmit queue */
+	struct sk_buff_head rq;		/* receive queue for pppd */
+	wait_queue_head_t rwait;	/* for poll on reading /dev/ppp */
+	atomic_t	refcnt;		/* # refs (incl /dev/ppp attached) */
+	int		hdrlen;		/* space to leave for headers */
+	struct list_head list;		/* link in all_* list */
+	int		index;		/* interface unit / channel number */
+};
+
+#define PF_TO_X(pf, X)	((X *)((char *)(pf)-(unsigned long)(&((X *)0)->file)))
+
+#define PF_TO_PPP(pf)		PF_TO_X(pf, struct ppp)
+#define PF_TO_CHANNEL(pf)	PF_TO_X(pf, struct channel)
+
+#define ROUNDUP(n, x)		(((n) + (x) - 1) / (x))
+
 /*
  * Data structure describing one ppp unit.
  * A ppp unit corresponds to a ppp network interface device
  * and represents a multilink bundle.
- * It may have 0 or more ppp channels connected to it.
+ * It can have 0 or more ppp channels connected to it.
  */
 struct ppp {
-	struct list_head list;		/* link in list of ppp units */
-	int		index;		/* interface unit number */
+	struct ppp_file	file;		/* stuff for read/write/poll */
 	char		name[16];	/* unit name */
-	int		refcnt;		/* # open /dev/ppp attached */
-	unsigned long	busy;		/* lock and other bits */
 	struct list_head channels;	/* list of attached channels */
 	int		n_channels;	/* how many channels are attached */
+	spinlock_t	rlock;		/* lock for receive side */
+	spinlock_t	wlock;		/* lock for transmit side */
 	int		mru;		/* max receive unit */
 	unsigned int	flags;		/* control bits */
 	unsigned int	xstate;		/* transmit state bits */
 	unsigned int	rstate;		/* receive state bits */
 	int		debug;		/* debug flags */
 	struct slcompress *vj;		/* state for VJ header compression */
-	struct sk_buff_head xq;		/* pppd transmit queue */
-	struct sk_buff_head rq;		/* receive queue for pppd */
-	wait_queue_head_t rwait;	/* for poll on reading /dev/ppp */
 	enum NPmode	npmode[NUM_NP];	/* what to do with each net proto */
 	struct sk_buff	*xmit_pending;	/* a packet ready to go out */
-	struct sk_buff_head recv_pending;/* pending input packets */
 	struct compressor *xcomp;	/* transmit packet compressor */
 	void		*xc_state;	/* its internal state */
 	struct compressor *rcomp;	/* receive decompressor */
@@ -97,57 +112,121 @@
 	unsigned long	last_xmit;	/* jiffies when last pkt sent */
 	unsigned long	last_recv;	/* jiffies when last pkt rcvd */
 	struct net_device *dev;		/* network interface device */
+#ifdef CONFIG_PPP_MULTILINK
+	int		nxchan;		/* next channel to send something on */
+	u32		nxseq;		/* next sequence number to send */
+	int		mrru;		/* MP: max reconst. receive unit */
+	u32		nextseq;	/* MP: seq no of next packet */
+	u32		minseq;		/* MP: min of most recent seqnos */
+	struct sk_buff_head mrq;	/* MP: receive reconstruction queue */
+#endif /* CONFIG_PPP_MULTILINK */
 	struct net_device_stats stats;	/* statistics */
 };
 
-static LIST_HEAD(all_ppp_units);
-static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+/*
+ * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
+ * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ.
+ * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
+ * Bits in xstate: SC_COMP_RUN
+ */
+#define SC_FLAG_BITS	(SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
+			 |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ)
 
 /*
  * Private data structure for each channel.
- * Ultimately this will have multilink stuff etc. in it.
+ * This includes the data structure used for multilink.
  */
 struct channel {
-	struct list_head list;		/* link in list of channels per unit */
+	struct ppp_file	file;		/* stuff for read/write/poll */
 	struct ppp_channel *chan;	/* public channel data structure */
-	int		blocked;	/* if channel refused last packet */
+	spinlock_t	downl;		/* protects `chan', file.xq dequeue */
 	struct ppp	*ppp;		/* ppp unit we're connected to */
+	struct list_head clist;		/* link in list of channels per unit */
+	rwlock_t	upl;		/* protects `ppp' and `ulist' */
+#ifdef CONFIG_PPP_MULTILINK
+	u8		avail;		/* flag used in multilink stuff */
+	u8		had_frag;	/* >= 1 fragments have been sent */
+	u32		lastseq;	/* MP: last sequence # received */
+#endif /* CONFIG_PPP_MULTILINK */
 };
 
-/* Bit numbers in busy */
-#define XMIT_BUSY	0
-#define RECV_BUSY	1
-#define XMIT_WAKEUP	2
+/*
+ * SMP locking issues:
+ * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels
+ * list and the ppp.n_channels field, you need to take both locks
+ * before you modify them.
+ * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock ->
+ * channel.downl.
+ */
 
 /*
- * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC.
- * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
- * Bits in xstate: SC_COMP_RUN
+ * all_ppp_lock protects the all_ppp_units.
+ * It also ensures that finding a ppp unit in the all_ppp_units list
+ * and updating its file.refcnt field is atomic.
  */
-#define SC_FLAG_BITS	(SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC)
+static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(all_ppp_units);
+
+/*
+ * all_channels_lock protects all_channels and last_channel_index,
+ * and the atomicity of find a channel and updating its file.refcnt
+ * field.
+ */
+static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(all_channels);
+static int last_channel_index;
 
 /* Get the PPP protocol number from a skb */
 #define PPP_PROTO(skb)	(((skb)->data[0] << 8) + (skb)->data[1])
 
-/* We limit the length of ppp->rq to this (arbitrary) value */
+/* We limit the length of ppp->file.rq to this (arbitrary) value */
 #define PPP_MAX_RQLEN	32
 
+/* Multilink header bits. */
+#define B	0x80		/* this fragment begins a packet */
+#define E	0x40		/* this fragment ends a packet */
+
+/* Compare multilink sequence numbers (assumed to be 32 bits wide) */
+#define seq_before(a, b)	((s32)((a) - (b)) < 0)
+#define seq_after(a, b)		((s32)((a) - (b)) > 0)
+
 /* Prototypes. */
-static void ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh);
+static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
+			     char *buf, size_t count);
+static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
+			      size_t count);
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+				unsigned int cmd, unsigned long arg);
+static void ppp_xmit_process(struct ppp *ppp, int wakeup);
 static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
 static void ppp_push(struct ppp *ppp);
-static void ppp_recv_unlock(struct ppp *ppp);
-static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_channel_push(struct channel *pch);
+static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
+			      struct channel *pch);
+static void ppp_receive_error(struct ppp *ppp);
+static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
 static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
 					    struct sk_buff *skb);
+#ifdef CONFIG_PPP_MULTILINK
+static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
+				struct channel *pch);
+static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
+#endif /* CONFIG_PPP_MULTILINK */
 static int ppp_set_compress(struct ppp *ppp, unsigned long arg);
 static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
 static void ppp_ccp_closed(struct ppp *ppp);
 static struct compressor *find_compressor(int type);
 static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
-static struct ppp *ppp_create_unit(int unit, int *retp);
-static void ppp_release_unit(struct ppp *ppp);
+static struct ppp *ppp_create_interface(int unit, int *retp);
+static void init_ppp_file(struct ppp_file *pf, int kind);
+static void ppp_destroy_interface(struct ppp *ppp);
 static struct ppp *ppp_find_unit(int unit);
+static struct channel *ppp_find_channel(int unit);
+static int ppp_connect_channel(struct channel *pch, int unit);
+static int ppp_disconnect_channel(struct channel *pch);
+static void ppp_destroy_channel(struct channel *pch);
 
 /* Translates a PPP protocol number to a NP index (NP == network protocol) */
 static inline int proto_to_npindex(int proto)
@@ -199,69 +278,24 @@
 };
 
 /*
- * Routines for locking and unlocking the transmit and receive paths
- * of each unit.
- *
- * On the transmit side, we have threads of control coming into the
- * driver from (at least) three places: the core net code, write calls
- * on /dev/ppp from pppd, and wakeup calls from channels.  There is
- * possible concurrency even on UP systems (between mainline and
- * BH processing).  The XMIT_BUSY bit in ppp->busy serializes the
- * transmit-side processing for each ppp unit.
+ * Locking shorthand.
  */
-static inline void
-lock_path(struct ppp *ppp, int bit)
-{
-	int timeout = 1000000;
-
-	do {
-		while (test_bit(bit, &ppp->busy)) {
-		       mb();
-		       if (--timeout == 0) {
-			       printk(KERN_ERR "lock_path timeout ppp=%p bit=%x\n", ppp, bit);
-			       return;
-		       }
-		}
-	} while (test_and_set_bit(bit, &ppp->busy));
-	mb();
-}
-
-static inline int
-trylock_path(struct ppp *ppp, int bit)
-{
-	if (test_and_set_bit(bit, &ppp->busy))
-		return 0;
-	mb();
-	return 1;
-}
-
-static inline void
-unlock_path(struct ppp *ppp, int bit)
-{
-	mb();
-	clear_bit(bit, &ppp->busy);
-}
+#define ppp_xmit_lock(ppp)	spin_lock_bh(&(ppp)->wlock)
+#define ppp_xmit_unlock(ppp)	spin_unlock_bh(&(ppp)->wlock)
+#define ppp_recv_lock(ppp)	spin_lock_bh(&(ppp)->rlock)
+#define ppp_recv_unlock(ppp)	spin_unlock_bh(&(ppp)->rlock)
+#define ppp_lock(ppp)		do { ppp_xmit_lock(ppp); \
+				     ppp_recv_lock(ppp); } while (0)
+#define ppp_unlock(ppp)		do { ppp_recv_unlock(ppp); \
+				     ppp_xmit_unlock(ppp); } while (0)
 
-#define lock_xmit_path(ppp)	lock_path(ppp, XMIT_BUSY)
-#define trylock_xmit_path(ppp)	trylock_path(ppp, XMIT_BUSY)
-#define unlock_xmit_path(ppp)	unlock_path(ppp, XMIT_BUSY)
-#define lock_recv_path(ppp)	lock_path(ppp, RECV_BUSY)
-#define trylock_recv_path(ppp)	trylock_path(ppp, RECV_BUSY)
-#define unlock_recv_path(ppp)	unlock_path(ppp, RECV_BUSY)
-
-static inline void
-free_skbs(struct sk_buff_head *head)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(head)) != 0)
-		kfree_skb(skb);
-}
 
 /*
  * /dev/ppp device routines.
  * The /dev/ppp device is used by pppd to control the ppp unit.
  * It supports the read, write, ioctl and poll functions.
+ * Open instances of /dev/ppp can be in one of three states:
+ * unattached, attached to a ppp unit, or attached to a ppp channel.
  */
 static int ppp_open(struct inode *inode, struct file *file)
 {
@@ -276,11 +310,20 @@
 
 static int ppp_release(struct inode *inode, struct file *file)
 {
-	struct ppp *ppp = (struct ppp *) file->private_data;
+	struct ppp_file *pf = (struct ppp_file *) file->private_data;
 
-	if (ppp != 0) {
+	if (pf != 0) {
 		file->private_data = 0;
-		ppp_release_unit(ppp);
+		if (atomic_dec_and_test(&pf->refcnt)) {
+			switch (pf->kind) {
+			case INTERFACE:
+				ppp_destroy_interface(PF_TO_PPP(pf));
+				break;
+			case CHANNEL:
+				ppp_destroy_channel(PF_TO_CHANNEL(pf));
+				break;
+			}
+		}
 	}
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -289,20 +332,27 @@
 static ssize_t ppp_read(struct file *file, char *buf,
 			size_t count, loff_t *ppos)
 {
-	struct ppp *ppp = (struct ppp *) file->private_data;
+	struct ppp_file *pf = (struct ppp_file *) file->private_data;
+
+	return ppp_file_read(pf, file, buf, count);
+}
+
+static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
+			     char *buf, size_t count)
+{
 	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	struct sk_buff *skb = 0;
 
 	ret = -ENXIO;
-	if (ppp == 0)
+	if (pf == 0)
 		goto out;		/* not currently attached */
 
-	add_wait_queue(&ppp->rwait, &wait);
+	add_wait_queue(&pf->rwait, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 	for (;;) {
 		ret = -EAGAIN;
-		skb = skb_dequeue(&ppp->rq);
+		skb = skb_dequeue(&pf->rq);
 		if (skb)
 			break;
 		if (file->f_flags & O_NONBLOCK)
@@ -313,7 +363,7 @@
 		schedule();
 	}
 	current->state = TASK_RUNNING;
-	remove_wait_queue(&ppp->rwait, &wait);
+	remove_wait_queue(&pf->rwait, &wait);
 
 	if (skb == 0)
 		goto out;
@@ -335,32 +385,42 @@
 static ssize_t ppp_write(struct file *file, const char *buf,
 			 size_t count, loff_t *ppos)
 {
-	struct ppp *ppp = (struct ppp *) file->private_data;
+	struct ppp_file *pf = (struct ppp_file *) file->private_data;
+
+	return ppp_file_write(pf, buf, count);
+}
+
+static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
+			      size_t count)
+{
 	struct sk_buff *skb;
 	ssize_t ret;
-	int extra;
 
 	ret = -ENXIO;
-	if (ppp == 0)
+	if (pf == 0)
 		goto out;
 
 	ret = -ENOMEM;
-	extra = PPP_HDRLEN - 2;
-	if (ppp->dev && ppp->dev->hard_header_len > PPP_HDRLEN)
-		extra = ppp->dev->hard_header_len - 2;
-	skb = alloc_skb(count + extra, GFP_KERNEL);
+	skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
 	if (skb == 0)
 		goto out;
-	skb_reserve(skb, extra);
+	skb_reserve(skb, pf->hdrlen);
 	ret = -EFAULT;
 	if (copy_from_user(skb_put(skb, count), buf, count)) {
 		kfree_skb(skb);
 		goto out;
 	}
 
-	skb_queue_tail(&ppp->xq, skb);
-	if (trylock_xmit_path(ppp))
-		ppp_xmit_unlock(ppp, 1);
+	skb_queue_tail(&pf->xq, skb);
+
+	switch (pf->kind) {
+	case INTERFACE:
+		ppp_xmit_process(PF_TO_PPP(pf), 0);
+		break;
+	case CHANNEL:
+		ppp_channel_push(PF_TO_CHANNEL(pf));
+		break;
+	}
 
 	ret = count;
 
@@ -370,68 +430,82 @@
 
 static unsigned int ppp_poll(struct file *file, poll_table *wait)
 {
-	struct ppp *ppp = (struct ppp *) file->private_data;
+	struct ppp_file *pf = (struct ppp_file *) file->private_data;
 	unsigned int mask;
 
-	if (ppp == 0)
+	if (pf == 0)
 		return 0;
-	poll_wait(file, &ppp->rwait, wait);
+	poll_wait(file, &pf->rwait, wait);
 	mask = POLLOUT | POLLWRNORM;
-	if (skb_peek(&ppp->rq) != 0)
+	if (skb_peek(&pf->rq) != 0)
 		mask |= POLLIN | POLLRDNORM;
+	if (pf->kind == CHANNEL) {
+		struct channel *pch = PF_TO_CHANNEL(pf);
+		if (pch->chan == 0)
+			mask |= POLLHUP;
+	}
 	return mask;
 }
 
 static int ppp_ioctl(struct inode *inode, struct file *file,
 		     unsigned int cmd, unsigned long arg)
 {
-	struct ppp *ppp = (struct ppp *) file->private_data;
-	int err, val, val2, i;
+	struct ppp_file *pf = (struct ppp_file *) file->private_data;
+	struct ppp *ppp;
+	int err = -EFAULT, val, val2, i;
 	struct ppp_idle idle;
 	struct npioctl npi;
+	int unit;
+	struct slcompress *vj;
 
-	if (cmd == PPPIOCNEWUNIT) {
-		/* Create a new ppp unit */
-		int unit, ret;
+	if (pf == 0)
+		return ppp_unattached_ioctl(pf, file, cmd, arg);
 
-		if (ppp != 0)
-			return -EINVAL;
-		if (get_user(unit, (int *) arg))
-			return -EFAULT;
-		ppp = ppp_create_unit(unit, &ret);
-		if (ppp == 0)
-			return ret;
-		file->private_data = ppp;
-		if (put_user(ppp->index, (int *) arg))
-			return -EFAULT;
-		return 0;
+	if (pf->kind == CHANNEL) {
+		struct channel *pch = PF_TO_CHANNEL(pf);
+		struct ppp_channel *chan;
+
+		switch (cmd) {
+		case PPPIOCCONNECT:
+			if (get_user(unit, (int *) arg))
+				break;
+			err = ppp_connect_channel(pch, unit);
+			break;
+
+		case PPPIOCDISCONN:
+			err = ppp_disconnect_channel(pch);
+			break;
+
+		case PPPIOCDETACH:
+			file->private_data = 0;
+			if (atomic_dec_and_test(&pf->refcnt))
+				ppp_destroy_channel(pch);
+			err = 0;
+			break;
+
+		default:
+			spin_lock_bh(&pch->downl);
+			chan = pch->chan;
+			err = -ENOTTY;
+			if (chan->ops->ioctl)
+				err = chan->ops->ioctl(chan, cmd, arg);
+			spin_unlock_bh(&pch->downl);
+		}
+		return err;
 	}
-	if (cmd == PPPIOCATTACH) {
-		/* Attach to an existing ppp unit */
-		int unit;
 
-		if (ppp != 0)
-			return -EINVAL;
-		if (get_user(unit, (int *) arg))
-			return -EFAULT;
-		spin_lock(&all_ppp_lock);
-		ppp = ppp_find_unit(unit);
-		if (ppp != 0)
-			++ppp->refcnt;
-		spin_unlock(&all_ppp_lock);
-		if (ppp == 0)
-			return -ENXIO;
-		file->private_data = ppp;
-		return 0;
+	if (pf->kind != INTERFACE) {
+		/* can't happen */
+		printk(KERN_ERR "PPP: not interface or channel??\n");
+		return -EINVAL;
 	}
 
-	if (ppp == 0)
-		return -ENXIO;
-	err = -EFAULT;
+	ppp = PF_TO_PPP(pf);
 	switch (cmd) {
 	case PPPIOCDETACH:
 		file->private_data = 0;
-		ppp_release_unit(ppp);
+		if (atomic_dec_and_test(&pf->refcnt))
+			ppp_destroy_interface(ppp);
 		err = 0;
 		break;
 
@@ -445,9 +519,11 @@
 	case PPPIOCSFLAGS:
 		if (get_user(val, (int *) arg))
 			break;
+		ppp_lock(ppp);
 		if (ppp->flags & ~val & SC_CCP_OPEN)
 			ppp_ccp_closed(ppp);
 		ppp->flags = val & SC_FLAG_BITS;
+		ppp_unlock(ppp);
 		err = 0;
 		break;
 
@@ -463,7 +539,7 @@
 		break;
 
 	case PPPIOCGUNIT:
-		if (put_user(ppp->index, (int *) arg))
+		if (put_user(ppp->file.index, (int *) arg))
 			break;
 		err = 0;
 		break;
@@ -497,18 +573,17 @@
 			val2 = val >> 16;
 			val &= 0xffff;
 		}
-		lock_xmit_path(ppp);
-		lock_recv_path(ppp);
-		if (ppp->vj != 0)
-			slhc_free(ppp->vj);
-		ppp->vj = slhc_init(val2+1, val+1);
-		ppp_recv_unlock(ppp);
-		ppp_xmit_unlock(ppp, 1);
-		err = -ENOMEM;
-		if (ppp->vj == 0) {
+		vj = slhc_init(val2+1, val+1);
+		if (vj == 0) {
 			printk(KERN_ERR "PPP: no memory (VJ compressor)\n");
+			err = -ENOMEM;
 			break;
 		}
+		ppp_lock(ppp);
+		if (ppp->vj != 0)
+			slhc_free(ppp->vj);
+		ppp->vj = vj;
+		ppp_unlock(ppp);
 		err = 0;
 		break;
 
@@ -533,6 +608,76 @@
 		err = 0;
 		break;
 
+#ifdef CONFIG_PPP_MULTILINK
+	case PPPIOCSMRRU:
+		if (get_user(val, (int *) arg))
+			break;
+		ppp_recv_lock(ppp);
+		ppp->mrru = val;
+		ppp_recv_unlock(ppp);
+		err = 0;
+		break;
+#endif /* CONFIG_PPP_MULTILINK */
+
+	default:
+		err = -ENOTTY;
+	}
+	return err;
+}
+
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	int unit, err = -EFAULT;
+	struct ppp *ppp;
+	struct channel *chan;
+
+	switch (cmd) {
+	case PPPIOCNEWUNIT:
+		/* Create a new ppp unit */
+		if (get_user(unit, (int *) arg))
+			break;
+		ppp = ppp_create_interface(unit, &err);
+		if (ppp == 0)
+			break;
+		file->private_data = &ppp->file;
+		err = -EFAULT;
+		if (put_user(ppp->file.index, (int *) arg))
+			break;
+		err = 0;
+		break;
+
+	case PPPIOCATTACH:
+		/* Attach to an existing ppp unit */
+		if (get_user(unit, (int *) arg))
+			break;
+		spin_lock(&all_ppp_lock);
+		ppp = ppp_find_unit(unit);
+		if (ppp != 0)
+			atomic_inc(&ppp->file.refcnt);
+		spin_unlock(&all_ppp_lock);
+		err = -ENXIO;
+		if (ppp == 0)
+			break;
+		file->private_data = &ppp->file;
+		err = 0;
+		break;
+
+	case PPPIOCATTCHAN:
+		if (get_user(unit, (int *) arg))
+			break;
+		spin_lock_bh(&all_channels_lock);
+		chan = ppp_find_channel(unit);
+		if (chan != 0)
+			atomic_inc(&chan->file.refcnt);
+		spin_unlock_bh(&all_channels_lock);
+		err = -ENXIO;
+		if (chan == 0)
+			break;
+		file->private_data = &chan->file;
+		err = 0;
+		break;
+
 	default:
 		err = -ENOTTY;
 	}
@@ -557,37 +702,17 @@
 int __init ppp_init(void)
 {
 	int err;
-#ifndef MODULE
-#ifdef CONFIG_PPP_DEFLATE
-	extern struct compressor ppp_deflate, ppp_deflate_draft;
-#endif
-	extern int ppp_async_init(void);
-	extern int ppp_sync_init(void);
-#endif
 
 	printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
 	err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
 	if (err)
 		printk(KERN_ERR "failed to register PPP device (%d)\n", err);
-	devfs_handle = devfs_register (NULL, "ppp", 0, DEVFS_FL_NONE,
-				       PPP_MAJOR, 0,
-				       S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
-				       &ppp_device_fops, NULL);
-#ifndef MODULE
-#ifdef CONFIG_PPP_ASYNC
-	ppp_async_init();
-#endif
-#ifdef CONFIG_PPP_SYNC_TTY
-	ppp_sync_init();
-#endif
-#ifdef CONFIG_PPP_DEFLATE
-	if (ppp_register_compressor(&ppp_deflate) == 0)
-		printk(KERN_INFO "PPP Deflate compression module registered\n");
-	ppp_register_compressor(&ppp_deflate_draft);
-#endif
-#endif /* MODULE */
+	devfs_handle = devfs_register(NULL, "ppp", 0, DEVFS_FL_NONE,
+				      PPP_MAJOR, 0,
+				      S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+				      &ppp_device_fops, NULL);
 
-	return -ENODEV;
+	return 0;
 }
 
 /*
@@ -636,9 +761,8 @@
 	pp[1] = proto;
 
 	netif_stop_queue(dev);
-	skb_queue_tail(&ppp->xq, skb);
-	if (trylock_xmit_path(ppp))
-		ppp_xmit_unlock(ppp, 0);
+	skb_queue_tail(&ppp->file.xq, skb);
+	ppp_xmit_process(ppp, 0);
 	return 0;
 
  outf:
@@ -719,38 +843,26 @@
  */
 
 /*
- * Called to unlock the transmit side of the ppp unit,
- * making sure that any work queued up gets done.
+ * Called to do any work queued up on the transmit side
+ * that can now be done.
  */
 static void
-ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh)
+ppp_xmit_process(struct ppp *ppp, int wakeup)
 {
 	struct sk_buff *skb;
 
-	for (;;) {
-		/* Do whatever work is waiting to be done. */
-		if (test_and_clear_bit(XMIT_WAKEUP, &ppp->busy))
-			ppp_push(ppp);
-		/* If there's no work left to do, tell the core net
-		   code that we can accept some more. */
-		while (ppp->xmit_pending == 0
-		       && (skb = skb_dequeue(&ppp->xq)) != 0)
-			ppp_send_frame(ppp, skb);
-		if (ppp->xmit_pending == 0 && skb_peek(&ppp->xq) == 0)
-			netif_wake_queue(ppp->dev);
-
-		/* Now unlock the transmit path, let others in. */
-		unlock_xmit_path(ppp);
-		/* Check whether any work was queued up
-		   between our last check and the unlock. */
-		if (!(test_bit(XMIT_WAKEUP, &ppp->busy)
-		      || (ppp->xmit_pending == 0 && skb_peek(&ppp->xq))))
-			break;
-		/* If so, lock again and do the work.  If we can't get
-		   the lock, someone else has it and they'll do the work. */
-		if (!trylock_xmit_path(ppp))
-			break;
-	}
+	ppp_xmit_lock(ppp);
+	if (wakeup)
+		ppp_push(ppp);
+	while (ppp->xmit_pending == 0
+	       && (skb = skb_dequeue(&ppp->file.xq)) != 0)
+		ppp_send_frame(ppp, skb);
+	/* If there's no work left to do, tell the core net
+	   code that we can accept some more. */
+	if (ppp->xmit_pending == 0 && skb_peek(&ppp->file.xq) == 0
+	    && ppp->dev != 0)
+		netif_wake_queue(ppp->dev);
+	ppp_xmit_unlock(ppp);
 }
 
 /*
@@ -847,10 +959,10 @@
 	 * queue it up for pppd to receive.
 	 */
 	if (ppp->flags & SC_LOOP_TRAFFIC) {
-		if (ppp->rq.qlen > PPP_MAX_RQLEN)
+		if (ppp->file.rq.qlen > PPP_MAX_RQLEN)
 			goto drop;
-		skb_queue_tail(&ppp->rq, skb);
-		wake_up_interruptible(&ppp->rwait);
+		skb_queue_tail(&ppp->file.rq, skb);
+		wake_up_interruptible(&ppp->file.rwait);
 		return;
 	}
 
@@ -871,7 +983,7 @@
 ppp_push(struct ppp *ppp)
 {
 	struct list_head *list;
-	struct channel *chan;
+	struct channel *pch;
 	struct sk_buff *skb = ppp->xmit_pending;
 
 	if (skb == 0)
@@ -885,40 +997,222 @@
 		return;
 	}
 
-	/* If we are doing multilink, decide which channel gets the
-	   packet, and/or fragment the packet over several links. */
-	/* XXX for now, just take the first channel */
-	list = list->next;
-	chan = list_entry(list, struct channel, list);
+	if ((ppp->flags & SC_MULTILINK) == 0) {
+		/* not doing multilink: send it down the first channel */
+		list = list->next;
+		pch = list_entry(list, struct channel, clist);
+
+		spin_lock_bh(&pch->downl);
+		if (skb_queue_len(&pch->file.xq) == 0
+		    && pch->chan->ops->start_xmit(pch->chan, skb))
+			ppp->xmit_pending = 0;
+		spin_unlock_bh(&pch->downl);
+		return;
+	}
+
+#ifdef CONFIG_PPP_MULTILINK
+	/* Multilink: fragment the packet over as many links
+	   as can take the packet at the moment. */
+	if (!ppp_mp_explode(ppp, skb))
+		return;
+#endif /* CONFIG_PPP_MULTILINK */
 
-	if (chan->chan->ops->start_xmit(chan->chan, skb)) {
-		ppp->xmit_pending = 0;
-		chan->blocked = 0;
-	} else
-		chan->blocked = 1;
+	ppp->xmit_pending = 0;
+	kfree_skb(skb);
+}
+
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Divide a packet to be transmitted into fragments and
+ * send them out the individual links.
+ */
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
+{
+	int nch, len, fragsize;
+	int i, bits, hdrlen;
+	unsigned char *p, *q;
+	struct list_head *list;
+	struct channel *pch;
+	struct sk_buff *frag;
+	struct ppp_channel *chan;
+
+	nch = 0;
+	list = &ppp->channels;
+	while ((list = list->next) != &ppp->channels) {
+		pch = list_entry(list, struct channel, clist);
+		nch += pch->avail = (skb_queue_len(&pch->file.xq) == 0);
+		/*
+		 * If a channel hasn't had a fragment yet, it has to get
+		 * one before we send any fragments on later channels.
+		 * If it can't take a fragment now, don't give any
+		 * to subsequent channels.
+		 */
+		if (!pch->had_frag && !pch->avail) {
+			while ((list = list->next) != &ppp->channels) {
+				pch = list_entry(list, struct channel, clist);
+				pch->avail = 0;
+			}
+			break;
+		}
+	}
+	if (nch == 0)
+		return 0;	/* can't take now, leave it in xmit_pending */
+
+	/* Do protocol field compression (XXX this should be optional) */
+	p = skb->data;
+	len = skb->len;
+	if (*p == 0) {
+		++p;
+		--len;
+	}
+
+	/* decide on fragment size */
+	fragsize = len;
+	if (nch > 1) {
+		int maxch = ROUNDUP(len, MIN_FRAG_SIZE);
+		if (nch > maxch)
+			nch = maxch;
+		fragsize = ROUNDUP(fragsize, nch);
+	}
+
+	/* skip to the channel after the one we last used
+	   and start at that one */
+	for (i = 0; i < ppp->nxchan; ++i) {
+		list = list->next;
+		if (list == &ppp->channels) {
+			i = 0;
+			break;
+		}
+	}
+
+	/* create a fragment for each channel */
+	bits = B;
+	hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
+	/* XXX gotta do A/C and prot compression here */
+	do {
+		list = list->next;
+		if (list == &ppp->channels) {
+			i = 0;
+			continue;
+		}
+		pch = list_entry(list, struct channel, clist);
+		++i;
+		if (!pch->avail)
+			continue;
+		if (fragsize >= len) {
+			fragsize = len;
+			bits |= E;
+		}
+		frag = alloc_skb(fragsize + hdrlen, GFP_ATOMIC);
+		if (frag != 0) {
+			q = skb_put(frag, fragsize + hdrlen);
+			/* make the MP header */
+			if (ppp->flags & SC_MP_XSHORTSEQ) {
+				q[0] = bits + ((ppp->nxseq >> 8) & 0xf);
+				q[1] = ppp->nxseq;
+			} else {
+				q[0] = bits;
+				q[1] = ppp->nxseq >> 16;
+				q[2] = ppp->nxseq >> 8;
+				q[3] = ppp->nxseq;
+			}
+
+			/* copy the data in */
+			memcpy(q + hdrlen, p, fragsize);
+
+			/* try to send it down the channel */
+			spin_lock_bh(&pch->downl);
+			chan = pch->chan;
+			if (chan != 0) {
+				if (!chan->ops->start_xmit(chan, frag))
+					skb_queue_tail(&pch->file.xq, frag);
+			} else {
+				/* channel got unregistered, too bad */
+				kfree_skb(skb);
+			}
+			spin_unlock_bh(&pch->downl);
+		}
+		p += fragsize;
+		len -= fragsize;
+		++ppp->nxseq;
+		bits = 0;
+	} while (len > 0);
+	ppp->nxchan = i;
+
+	return 1;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
+/*
+ * Try to send data out on a channel.
+ */
+static void
+ppp_channel_push(struct channel *pch)
+{
+	struct sk_buff *skb;
+
+	spin_lock_bh(&pch->downl);
+	if (pch->chan != 0) {
+		while (skb_queue_len(&pch->file.xq) > 0) {
+			skb = skb_dequeue(&pch->file.xq);
+			if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
+				/* put the packet back and try again later */
+				skb_queue_head(&pch->file.xq, skb);
+				break;
+			}
+		}
+	} else {
+		/* channel got deregistered */
+		skb_queue_purge(&pch->file.xq);
+	}
+	spin_unlock_bh(&pch->downl);
 }
 
 /*
  * Receive-side routines.
  */
+
+/* misuse a few fields of the skb for MP reconstruction */
+#define sequence	priority
+#define BEbits		cb[0]
+
 static inline void
-ppp_do_recv(struct ppp *ppp, struct sk_buff *skb)
+ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 {
-	skb_queue_tail(&ppp->recv_pending, skb);
-	if (trylock_recv_path(ppp))
-		ppp_recv_unlock(ppp);
+	ppp_recv_lock(ppp);
+	/* ppp->dev == 0 means interface is closing down */
+	if (ppp->dev != 0)
+		ppp_receive_frame(ppp, skb, pch);
+	else
+		kfree_skb(skb);
+	ppp_recv_unlock(ppp);
 }
 
 void
 ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
 {
 	struct channel *pch = chan->ppp;
+	int proto;
 
 	if (pch == 0 || skb->len == 0) {
 		kfree_skb(skb);
 		return;
 	}
-	ppp_do_recv(pch->ppp, skb);
+
+	proto = PPP_PROTO(skb);
+	read_lock_bh(&pch->upl);
+	if (pch->ppp == 0 || proto == PPP_LCP || proto == 0x80fb) {
+		/* put it on the channel queue */
+		skb_queue_tail(&pch->file.rq, skb);
+		/* drop old frames if queue too long */
+		while (pch->file.rq.qlen > PPP_MAX_RQLEN
+		       && (skb = skb_dequeue(&pch->file.rq)) != 0)
+			kfree_skb(skb);
+		wake_up_interruptible(&pch->file.rwait);
+	} else {
+		ppp_do_recv(pch->ppp, skb, pch);
+	}
+	read_unlock_bh(&pch->upl);
 }
 
 /* Put a 0-length skb in the receive queue as an error indication */
@@ -930,47 +1224,64 @@
 
 	if (pch == 0)
 		return;
-	skb = alloc_skb(0, GFP_ATOMIC);
-	if (skb == 0)
-		return;
-	skb->len = 0;		/* probably unnecessary */
-	skb->cb[0] = code;
-	ppp_do_recv(pch->ppp, skb);
+
+	read_lock_bh(&pch->upl);
+	if (pch->ppp != 0) {
+		skb = alloc_skb(0, GFP_ATOMIC);
+		if (skb != 0) {
+			skb->len = 0;		/* probably unnecessary */
+			skb->cb[0] = code;
+			ppp_do_recv(pch->ppp, skb, pch);
+		}
+	}
+	read_unlock_bh(&pch->upl);
 }
 
+/*
+ * We come in here to process a received frame.
+ * The receive side of the ppp unit is locked.
+ */
 static void
-ppp_recv_unlock(struct ppp *ppp)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 {
-	struct sk_buff *skb;
-
-	for (;;) {
-		while ((skb = skb_dequeue(&ppp->recv_pending)) != 0)
-			ppp_receive_frame(ppp, skb);
-		unlock_recv_path(ppp);
-		if (skb_peek(&ppp->recv_pending) == 0)
-			break;
-		if (!trylock_recv_path(ppp))
-			break;
+	if (skb->len >= 2) {
+#ifdef CONFIG_PPP_MULTILINK
+		/* XXX do channel-level decompression here */
+		if (PPP_PROTO(skb) == PPP_MP)
+			ppp_receive_mp_frame(ppp, skb, pch);
+		else
+#endif /* CONFIG_PPP_MULTILINK */
+			ppp_receive_nonmp_frame(ppp, skb);
+		return;
 	}
+
+	if (skb->len > 0)
+		/* note: a 0-length skb is used as an error indication */
+		++ppp->stats.rx_length_errors;
+
+	kfree_skb(skb);
+	ppp_receive_error(ppp);
+}
+
+static void
+ppp_receive_error(struct ppp *ppp)
+{
+	++ppp->stats.rx_errors;
+	if (ppp->vj != 0)
+		slhc_toss(ppp->vj);
 }
 
 static void
-ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
+ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
 {
 	struct sk_buff *ns;
 	int proto, len, npi;
 
-	if (skb->len == 0) {
-		/* XXX should do something with code in skb->cb[0] */
-		goto err;	/* error indication */
-	}
-
-	if (skb->len < 2) {
-		++ppp->stats.rx_length_errors;
-		goto err;
-	}
-
-	/* Decompress the frame, if compressed. */
+	/*
+	 * Decompress the frame, if compressed.
+	 * Note that some decompressors need to see uncompressed frames
+	 * that come in as well as compressed frames.
+	 */
 	if (ppp->rc_state != 0 && (ppp->rstate & SC_DECOMP_RUN)
 	    && (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
 		skb = ppp_decompress_frame(ppp, skb);
@@ -995,7 +1306,12 @@
 		}
 		len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
 		if (len <= 0) {
-			printk(KERN_ERR "PPP: VJ decompression error\n");
+			int i;
+			printk(KERN_DEBUG "PPP: VJ decompression error\n");
+			printk(KERN_DEBUG "PPP: len = %d data =", skb->len);
+			for (i = 0; i < 16 && i < skb->len; ++i)
+				printk(" %.2x", skb->data[i]);
+			printk("\n");
 			goto err;
 		}
 		len += 2;
@@ -1027,15 +1343,13 @@
 	npi = proto_to_npindex(proto);
 	if (npi < 0) {
 		/* control or unknown frame - pass it to pppd */
-		skb_queue_tail(&ppp->rq, skb);
+		skb_queue_tail(&ppp->file.rq, skb);
 		/* limit queue length by dropping old frames */
-		while (ppp->rq.qlen > PPP_MAX_RQLEN) {
-			skb = skb_dequeue(&ppp->rq);
-			if (skb)
-				kfree_skb(skb);
-		}
+		while (ppp->file.rq.qlen > PPP_MAX_RQLEN
+		       && (skb = skb_dequeue(&ppp->file.rq)) != 0)
+			kfree_skb(skb);
 		/* wake up any process polling or blocking on read */
-		wake_up_interruptible(&ppp->rwait);
+		wake_up_interruptible(&ppp->file.rwait);
 
 	} else {
 		/* network protocol frame - give it to the kernel */
@@ -1054,10 +1368,8 @@
 	return;
 
  err:
-	++ppp->stats.rx_errors;
-	if (ppp->vj != 0)
-		slhc_toss(ppp->vj);
 	kfree_skb(skb);
+	ppp_receive_error(ppp);
 }
 
 static struct sk_buff *
@@ -1070,7 +1382,7 @@
 	if (proto == PPP_COMP) {
 		ns = dev_alloc_skb(ppp->mru + PPP_HDRLEN);
 		if (ns == 0) {
-			printk(KERN_ERR "ppp_receive: no memory\n");
+			printk(KERN_ERR "ppp_decompress_frame: no memory\n");
 			goto err;
 		}
 		/* the decompressor still expects the A/C bytes in the hdr */
@@ -1101,70 +1413,288 @@
 
  err:
 	ppp->rstate |= SC_DC_ERROR;
-	if (ppp->vj != 0)
-		slhc_toss(ppp->vj);
-	++ppp->stats.rx_errors;
+	ppp_receive_error(ppp);
 	return skb;
 }
 
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Receive a multilink frame.
+ * We put it on the reconstruction queue and then pull off
+ * as many completed frames as we can.
+ */
+static void
+ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+	u32 mask, seq, minseq;
+	struct list_head *l;
+	int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? 2: 4;
+
+	if (skb->len < mphdrlen + 3)
+		goto err;		/* no good, throw it away */
+
+	/* Decode sequence number and begin/end bits */
+	if (ppp->flags & SC_MP_SHORTSEQ) {
+		seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];
+		mask = 0xfff;
+	} else {
+		seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
+		mask = 0xffffff;
+	}
+	skb->BEbits = skb->data[2];
+	skb_pull(skb, mphdrlen + 2);	/* pull off PPP and MP headers*/
+
+	/* Expand sequence number to 32 bits */
+	seq |= pch->lastseq & ~mask;
+	if (seq_before(seq, pch->lastseq)) {
+		if (seq_after(seq, pch->lastseq - 100)) {
+			printk(KERN_DEBUG "PPP: MP fragments out of order"
+			       " (%u, %u)\n", pch->lastseq, seq);
+			goto err;
+		}
+		seq += mask + 1;
+	}
+	skb->sequence = seq;
+	pch->lastseq = seq;
+
+	/*
+	 * Reevaluate minseq, the minimum over all channels of the
+	 * last sequence number received on each channel.  Because of
+	 * the increasing sequence number rule, we know that any fragment
+	 * before `minseq' which hasn't arrived is never going to arrive.
+	 * The list of channels can't change because we have the receive
+	 * side of the ppp unit locked.
+	 */
+	minseq = seq;
+	for (l = ppp->channels.next; l != &ppp->channels; l = l->next) {
+		struct channel *ch = list_entry(l, struct channel, clist);
+		if (seq_before(ch->lastseq, seq))
+			seq = ch->lastseq;
+	}
+	ppp->minseq = minseq;
+
+	/* Put the fragment on the reconstruction queue */
+	ppp_mp_insert(ppp, skb);
+
+	/* Pull completed packets off the queue and receive them. */
+	while ((skb = ppp_mp_reconstruct(ppp)) != 0)
+		ppp_receive_nonmp_frame(ppp, skb);
+
+	return;
+
+ err:
+	kfree_skb(skb);
+	ppp_receive_error(ppp);
+}
+
+/*
+ * Insert a fragment on the MP reconstruction queue.
+ * The queue is ordered by increasing sequence number.
+ */
+static void
+ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
+{
+	struct sk_buff *p;
+	struct sk_buff_head *list = &ppp->mrq;
+	u32 seq = skb->sequence;
+
+	/* N.B. we don't need to lock the list lock because we have the
+	   ppp unit receive-side lock. */
+	for (p = list->next; p != (struct sk_buff *)list; p = p->next)
+		if (seq_before(seq, p->sequence))
+			break;
+	__skb_insert(skb, p->prev, p, list);
+}
+
+/*
+ * Reconstruct a packet from the MP fragment queue.
+ * We go through increasing sequence numbers until we find a
+ * complete packet, or we get to the sequence number for a fragment
+ * which hasn't arrived but might still do so.
+ */
+struct sk_buff *
+ppp_mp_reconstruct(struct ppp *ppp)
+{
+	u32 seq = ppp->nextseq;
+	u32 minseq = ppp->minseq;
+	struct sk_buff_head *list = &ppp->mrq;
+	struct sk_buff *p, *next;
+	struct sk_buff *head, *tail;
+	struct sk_buff *skb = NULL;
+	int lost = 0, len = 0;
+
+	head = list->next;
+	tail = NULL;
+	for (p = head; p != (struct sk_buff *) list; p = next) {
+		next = p->next;
+		if (seq_before(p->sequence, seq)) {
+			/* this can't happen, anyway toss the skb */
+			printk(KERN_ERR "ppp_mp_reconstruct bad seq %x < %x\n",
+			       p->sequence, seq);
+			__skb_unlink(p, list);
+			kfree_skb(p);
+			continue;
+		}
+		if (p->sequence != seq) {
+			/* Fragment `seq' is missing.  If it is after
+			   minseq, it might arrive later, so stop here. */
+			if (seq_after(seq, minseq))
+				break;
+			/* Fragment `seq' is lost, keep going. */
+			lost = 1;
+			seq = seq_before(p->sequence, minseq)?
+				p->sequence: minseq;
+			next = p;
+			continue;
+		}
+
+		/*
+		 * At this point we know that all the fragments from
+		 * ppp->nextseq to seq are either present or lost.
+		 * Also, there are no complete packets in the queue
+		 * that have no missing fragments and end before this
+		 * fragment.
+		 */
+
+		/* B bit set indicates this fragment starts a packet */
+		if (p->BEbits & B) {
+			head = p;
+			lost = 0;
+			/* reset len, allow for protocol ID compression */
+			len = p->data[0] & 1;
+		}
+
+		len += p->len;
+
+		/* Got a complete packet yet? */
+		if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
+			if (len > ppp->mrru) {
+				++ppp->stats.rx_length_errors;
+			} else if ((skb = dev_alloc_skb(len)) == NULL) {
+				++ppp->stats.rx_missed_errors;
+			} else {
+				tail = p;
+				break;
+			}
+		}
+
+		/*
+		 * If this is the ending fragment of a packet,
+		 * and we haven't found a complete valid packet yet,
+		 * we can discard up to and including this fragment.
+		 */
+		if (p->BEbits & E)
+			head = next;
+
+		++seq;
+	}
+
+	/* If we have a complete packet, copy it all into one skb. */
+	if (tail != NULL) {
+		/* If we have discarded any fragments,
+		   signal a receive error. */
+		if (head->sequence != ppp->nextseq)
+			ppp_receive_error(ppp);
+
+		/* uncompress protocol ID */
+		if (head->data[0] & 1)
+			*skb_put(skb, 1) = 0;
+		p = head;
+		for (;;) {
+			memcpy(skb_put(skb, p->len), p->data, p->len);
+			if (p == tail)
+				break;
+			p = p->next;
+		}
+		ppp->nextseq = tail->sequence + 1;
+		head = tail->next;
+	}
+
+	/* Discard all the skbuffs that we have copied the data out of
+	   or that we can't use. */
+	while ((p = list->next) != head) {
+		__skb_unlink(p, list);
+		kfree_skb(p);
+	}
+
+	return skb;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
 /*
  * Channel interface.
  */
 
 /*
- * Connect a channel to a given PPP unit.
- * The channel MUST NOT be connected to a PPP unit already.
+ * Create a new, unattached ppp channel.
  */
 int
-ppp_register_channel(struct ppp_channel *chan, int unit)
+ppp_register_channel(struct ppp_channel *chan)
 {
-	struct ppp *ppp;
 	struct channel *pch;
-	int ret = -ENXIO;
 
-	spin_lock(&all_ppp_lock);
-	ppp = ppp_find_unit(unit);
-	if (ppp == 0)
-		goto out;
 	pch = kmalloc(sizeof(struct channel), GFP_ATOMIC);
-	ret = -ENOMEM;
 	if (pch == 0)
-		goto out;
+		return -ENOMEM;
 	memset(pch, 0, sizeof(struct channel));
-	pch->ppp = ppp;
+	pch->ppp = NULL;
 	pch->chan = chan;
-	list_add(&pch->list, &ppp->channels);
 	chan->ppp = pch;
-	++ppp->n_channels;
-	if (ppp->dev && chan->hdrlen + PPP_HDRLEN > ppp->dev->hard_header_len)
-		ppp->dev->hard_header_len = chan->hdrlen + PPP_HDRLEN;
-	ret = 0;
- out:
-	spin_unlock(&all_ppp_lock);
-	return ret;
+	init_ppp_file(&pch->file, CHANNEL);
+	pch->file.hdrlen = chan->hdrlen;
+	spin_lock_init(&pch->downl);
+	pch->upl = RW_LOCK_UNLOCKED;
+	spin_lock_bh(&all_channels_lock);
+	pch->file.index = ++last_channel_index;
+	list_add(&pch->file.list, &all_channels);
+	spin_unlock_bh(&all_channels_lock);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Return the index of a channel.
+ */
+int ppp_channel_index(struct ppp_channel *chan)
+{
+	struct channel *pch = chan->ppp;
+
+	return pch->file.index;
 }
 
 /*
- * Disconnect a channel from its PPP unit.
+ * Disconnect a channel from the generic layer.
+ * This can be called from mainline or BH/softirq level.
  */
 void
 ppp_unregister_channel(struct ppp_channel *chan)
 {
-	struct channel *pch;
+	struct channel *pch = chan->ppp;
 
-	spin_lock(&all_ppp_lock);
-	if ((pch = chan->ppp) != 0) {
-		chan->ppp = 0;
-		list_del(&pch->list);
-		--pch->ppp->n_channels;
-		kfree(pch);
-	}
-	spin_unlock(&all_ppp_lock);
+	if (pch == 0)
+		return;		/* should never happen */
+	chan->ppp = 0;
+
+	/*
+	 * This ensures that we have returned from any calls into the
+	 * the channel's start_xmit or ioctl routine before we proceed.
+	 */
+	spin_lock_bh(&pch->downl);
+	pch->chan = 0;
+	spin_unlock_bh(&pch->downl);
+	ppp_disconnect_channel(pch);
+	wake_up_interruptible(&pch->file.rwait);
+	spin_lock_bh(&all_channels_lock);
+	list_del(&pch->file.list);
+	spin_unlock_bh(&all_channels_lock);
+	if (atomic_dec_and_test(&pch->file.refcnt))
+		ppp_destroy_channel(pch);
+	MOD_DEC_USE_COUNT;
 }
 
 /*
  * Callback from a channel when it can accept more to transmit.
- * This should ideally be called at BH level, not interrupt level.
+ * This should be called at BH/softirq level, not interrupt level.
  */
 void
 ppp_output_wakeup(struct ppp_channel *chan)
@@ -1174,11 +1704,75 @@
 
 	if (pch == 0)
 		return;
-	ppp = pch->ppp;
-	pch->blocked = 0;
-	set_bit(XMIT_WAKEUP, &ppp->busy);
-	if (trylock_xmit_path(ppp))
-		ppp_xmit_unlock(ppp, 1);
+	ppp_channel_push(pch);
+	if (skb_queue_len(&pch->file.xq) == 0) {
+		read_lock_bh(&pch->upl);
+		ppp = pch->ppp;
+		if (ppp != 0)
+			ppp_xmit_process(ppp, 1);
+		read_unlock_bh(&pch->upl);
+	}
+}
+
+/*
+ * This is basically temporary compatibility stuff.
+ */
+ssize_t
+ppp_channel_read(struct ppp_channel *chan, struct file *file,
+		 char *buf, size_t count)
+{
+	struct channel *pch = chan->ppp;
+
+	if (pch == 0)
+		return -ENXIO;
+	return ppp_file_read(&pch->file, file, buf, count);
+}
+
+ssize_t
+ppp_channel_write(struct ppp_channel *chan, const char *buf, size_t count)
+{
+	struct channel *pch = chan->ppp;
+
+	if (pch == 0)
+		return -ENXIO;
+	return ppp_file_write(&pch->file, buf, count);
+}
+
+unsigned int
+ppp_channel_poll(struct ppp_channel *chan, struct file *file, poll_table *wait)
+{
+	unsigned int mask;
+	struct channel *pch = chan->ppp;
+
+	mask = POLLOUT | POLLWRNORM;
+	if (pch != 0) {
+		poll_wait(file, &pch->file.rwait, wait);
+		if (skb_peek(&pch->file.rq) != 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	return mask;
+}
+
+int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
+		      unsigned long arg)
+{
+	struct channel *pch = chan->ppp;
+	int err = -ENOTTY;
+	int unit;
+
+	if (pch == 0)
+		return -EINVAL;
+	switch (cmd) {
+	case PPPIOCATTACH:
+		if (get_user(unit, (int *) arg))
+			break;
+		err = ppp_connect_channel(pch, unit);
+		break;
+	case PPPIOCDETACH:
+		err = ppp_disconnect_channel(pch);
+		break;
+	}
+	return err;
 }
 
 /*
@@ -1192,6 +1786,7 @@
 	int err;
 	struct compressor *cp;
 	struct ppp_option_data data;
+	void *state;
 	unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
 #ifdef CONFIG_KMOD
 	char modname[32];
@@ -1220,34 +1815,41 @@
 
 	err = -ENOBUFS;
 	if (data.transmit) {
-		lock_xmit_path(ppp);
+		ppp_xmit_lock(ppp);
 		ppp->xstate &= ~SC_COMP_RUN;
 		if (ppp->xc_state != 0) {
 			ppp->xcomp->comp_free(ppp->xc_state);
 			ppp->xc_state = 0;
 		}
+		ppp_xmit_unlock(ppp);
 
-		ppp->xcomp = cp;
-		ppp->xc_state = cp->comp_alloc(ccp_option, data.length);
-		ppp_xmit_unlock(ppp, 1);
-		if (ppp->xc_state == 0)
-			goto out;
+		state = cp->comp_alloc(ccp_option, data.length);
+		if (state != 0) {
+			ppp_xmit_lock(ppp);
+			ppp->xcomp = cp;
+			ppp->xc_state = state;
+			ppp_xmit_unlock(ppp);
+			err = 0;
+		}
 
 	} else {
-		lock_recv_path(ppp);
+		ppp_recv_lock(ppp);
 		ppp->rstate &= ~SC_DECOMP_RUN;
 		if (ppp->rc_state != 0) {
 			ppp->rcomp->decomp_free(ppp->rc_state);
 			ppp->rc_state = 0;
 		}
-
-		ppp->rcomp = cp;
-		ppp->rc_state = cp->decomp_alloc(ccp_option, data.length);
 		ppp_recv_unlock(ppp);
-		if (ppp->rc_state == 0)
-			goto out;
+
+		state = cp->decomp_alloc(ccp_option, data.length);
+		if (state != 0) {
+			ppp_recv_lock(ppp);
+			ppp->rcomp = cp;
+			ppp->rc_state = state;
+			ppp_recv_unlock(ppp);
+			err = 0;
+		}
 	}
-	err = 0;
 
  out:
 	return err;
@@ -1292,7 +1894,7 @@
 			if (ppp->rc_state == 0)
 				break;
 			if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
-					ppp->index, 0, ppp->mru, ppp->debug)) {
+					ppp->file.index, 0, ppp->mru, ppp->debug)) {
 				ppp->rstate |= SC_DECOMP_RUN;
 				ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
 			}
@@ -1301,7 +1903,7 @@
 			if (ppp->xc_state == 0)
 				break;
 			if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
-					ppp->index, 0, ppp->debug))
+					ppp->file.index, 0, ppp->debug))
 				ppp->xstate |= SC_COMP_RUN;
 		}
 		break;
@@ -1329,21 +1931,17 @@
 {
 	ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
 
-	lock_xmit_path(ppp);
 	ppp->xstate &= ~SC_COMP_RUN;
 	if (ppp->xc_state) {
 		ppp->xcomp->comp_free(ppp->xc_state);
 		ppp->xc_state = 0;
 	}
-	ppp_xmit_unlock(ppp, 1);
 
-	lock_recv_path(ppp);
 	ppp->xstate &= ~SC_DECOMP_RUN;
 	if (ppp->rc_state) {
 		ppp->rcomp->decomp_free(ppp->rc_state);
 		ppp->rc_state = 0;
 	}
-	ppp_recv_unlock(ppp);
 }
 
 /* List of compressors. */
@@ -1451,16 +2049,17 @@
 }
 
 /*
- * Stuff for handling the list of ppp units and for initialization.
+ * Stuff for handling the lists of ppp units and channels
+ * and for initialization.
  */
 
 /*
- * Create a new ppp unit.  Fails if it can't allocate memory or
- * if there is already a unit with the requested number.
+ * Create a new ppp interface unit.  Fails if it can't allocate memory
+ * or if there is already a unit with the requested number.
  * unit == -1 means allocate a new number.
  */
 static struct ppp *
-ppp_create_unit(int unit, int *retp)
+ppp_create_interface(int unit, int *retp)
 {
 	struct ppp *ppp;
 	struct net_device *dev;
@@ -1472,13 +2071,13 @@
 	spin_lock(&all_ppp_lock);
 	list = &all_ppp_units;
 	while ((list = list->next) != &all_ppp_units) {
-		ppp = list_entry(list, struct ppp, list);
-		if ((unit < 0 && ppp->index > last_unit + 1)
-		    || (unit >= 0 && unit < ppp->index))
+		ppp = list_entry(list, struct ppp, file.list);
+		if ((unit < 0 && ppp->file.index > last_unit + 1)
+		    || (unit >= 0 && unit < ppp->file.index))
 			break;
-		if (unit == ppp->index)
+		if (unit == ppp->file.index)
 			goto out;	/* unit already exists */
-		last_unit = ppp->index;
+		last_unit = ppp->file.index;
 	}
 	if (unit < 0)
 		unit = last_unit + 1;
@@ -1496,17 +2095,15 @@
 	}
 	memset(dev, 0, sizeof(struct net_device));
 
-	ppp->index = unit;
+	ppp->file.index = unit;
 	sprintf(ppp->name, "ppp%d", unit);
 	ppp->mru = PPP_MRU;
-	skb_queue_head_init(&ppp->xq);
-	skb_queue_head_init(&ppp->rq);
-	init_waitqueue_head(&ppp->rwait);
-	ppp->refcnt = 1;
+	init_ppp_file(&ppp->file, INTERFACE);
 	for (i = 0; i < NUM_NP; ++i)
 		ppp->npmode[i] = NPMODE_PASS;
 	INIT_LIST_HEAD(&ppp->channels);
-	skb_queue_head_init(&ppp->recv_pending);
+	spin_lock_init(&ppp->rlock);
+	spin_lock_init(&ppp->wlock);
 
 	ppp->dev = dev;
 	dev->init = ppp_net_init;
@@ -1524,7 +2121,7 @@
 		goto out;
 	}
 
-	list_add(&ppp->list, list->prev);
+	list_add(&ppp->file.list, list->prev);
  out:
 	spin_unlock(&all_ppp_lock);
 	*retp = ret;
@@ -1534,56 +2131,59 @@
 }
 
 /*
- * Remove a reference to a ppp unit, and destroy it if
- * the reference count goes to 0.
+ * Initialize a ppp_file structure.
  */
-static void ppp_release_unit(struct ppp *ppp)
+static void
+init_ppp_file(struct ppp_file *pf, int kind)
+{
+	pf->kind = kind;
+	skb_queue_head_init(&pf->xq);
+	skb_queue_head_init(&pf->rq);
+	atomic_set(&pf->refcnt, 1);
+	init_waitqueue_head(&pf->rwait);
+}
+
+/*
+ * Free up all the resources used by a ppp interface unit.
+ */
+static void ppp_destroy_interface(struct ppp *ppp)
 {
-	struct list_head *list, *next;
-	int ref;
+	struct net_device *dev;
 
 	spin_lock(&all_ppp_lock);
-	ref = --ppp->refcnt;
-	if (ref == 0)
-		list_del(&ppp->list);
-	spin_unlock(&all_ppp_lock);
-	if (ref != 0)
-		return;
+	list_del(&ppp->file.list);
 
 	/* Last fd open to this ppp unit is being closed or detached:
 	   mark the interface down, free the ppp unit */
-	if (ppp->dev) {
-		rtnl_lock();
-		dev_close(ppp->dev);
-		rtnl_unlock();
-	}
-	for (list = ppp->channels.next; list != &ppp->channels; list = next) {
-		/* forcibly detach this channel */
-		struct channel *chan;
-		chan = list_entry(list, struct channel, list);
-		chan->chan->ppp = 0;
-		next = list->next;
-		kfree(chan);
-	}
-
-	/* Free up resources. */
+	ppp_lock(ppp);
 	ppp_ccp_closed(ppp);
-	lock_xmit_path(ppp);
-	lock_recv_path(ppp);
 	if (ppp->vj) {
 		slhc_free(ppp->vj);
 		ppp->vj = 0;
 	}
-	free_skbs(&ppp->xq);
-	free_skbs(&ppp->rq);
-	free_skbs(&ppp->recv_pending);
-	if (ppp->dev) {
+	skb_queue_purge(&ppp->file.xq);
+	skb_queue_purge(&ppp->file.rq);
+	dev = ppp->dev;
+	ppp->dev = 0;
+	ppp_unlock(ppp);
+
+	if (dev) {
 		rtnl_lock();
-		unregister_netdevice(ppp->dev);
-		ppp->dev = 0;
+		dev_close(dev);
+		unregister_netdevice(dev);
 		rtnl_unlock();
 	}
-	kfree(ppp);
+
+	/*
+	 * We can't acquire any new channels (since we have the
+	 * all_ppp_lock) so if n_channels is 0, we can free the
+	 * ppp structure.  Otherwise we leave it around until the
+	 * last channel disconnects from it.
+	 */
+	if (ppp->n_channels == 0)
+		kfree(ppp);
+
+	spin_unlock(&all_ppp_lock);
 }
 
 /*
@@ -1598,32 +2198,136 @@
 
 	list = &all_ppp_units;
 	while ((list = list->next) != &all_ppp_units) {
-		ppp = list_entry(list, struct ppp, list);
-		if (ppp->index == unit)
+		ppp = list_entry(list, struct ppp, file.list);
+		if (ppp->file.index == unit)
 			return ppp;
 	}
 	return 0;
 }
 
 /*
- * Module stuff.
+ * Locate an existing ppp channel.
+ * The caller should have locked the all_channels_lock.
  */
-#ifdef MODULE
-int
-init_module(void)
+static struct channel *
+ppp_find_channel(int unit)
 {
-	ppp_init();
+	struct channel *pch;
+	struct list_head *list;
+
+	list = &all_channels;
+	while ((list = list->next) != &all_channels) {
+		pch = list_entry(list, struct channel, file.list);
+		if (pch->file.index == unit)
+			return pch;
+	}
 	return 0;
 }
 
-void
-cleanup_module(void)
+/*
+ * Connect a PPP channel to a PPP interface unit.
+ */
+static int
+ppp_connect_channel(struct channel *pch, int unit)
+{
+	struct ppp *ppp;
+	int ret = -ENXIO;
+	int hdrlen;
+
+	spin_lock(&all_ppp_lock);
+	ppp = ppp_find_unit(unit);
+	if (ppp == 0)
+		goto out;
+	write_lock_bh(&pch->upl);
+	ret = -EINVAL;
+	if (pch->ppp != 0)
+		goto outw;
+	ppp_lock(ppp);
+	spin_lock_bh(&pch->downl);
+	if (pch->chan == 0)		/* need to check this?? */
+		goto outr;
+
+	hdrlen = pch->chan->hdrlen + PPP_HDRLEN;
+	if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
+		ppp->dev->hard_header_len = hdrlen;
+	list_add(&pch->clist, &ppp->channels);
+	++ppp->n_channels;
+	pch->ppp = ppp;
+	ret = 0;
+
+ outr:
+	spin_unlock_bh(&pch->downl);
+	ppp_unlock(ppp);
+ outw:
+	write_unlock_bh(&pch->upl);
+ out:
+	spin_unlock(&all_ppp_lock);
+	return ret;
+}
+
+/*
+ * Disconnect a channel from its ppp unit.
+ */
+static int
+ppp_disconnect_channel(struct channel *pch)
+{
+	struct ppp *ppp;
+	int err = -EINVAL;
+
+	write_lock_bh(&pch->upl);
+	ppp = pch->ppp;
+	if (ppp != 0) {
+		/* remove it from the ppp unit's list */
+		pch->ppp = NULL;
+		ppp_lock(ppp);
+		list_del(&pch->clist);
+		--ppp->n_channels;
+		if (ppp->dev == 0 && ppp->n_channels == 0)
+			/* Last disconnect from a ppp unit
+			   that is already dead: free it. */
+			kfree(ppp);
+		else
+			ppp_unlock(ppp);
+		err = 0;
+	}
+	write_unlock_bh(&pch->upl);
+	return err;
+}
+
+/*
+ * Free up the resources used by a ppp channel.
+ */
+static void ppp_destroy_channel(struct channel *pch)
+{
+	skb_queue_purge(&pch->file.xq);
+	skb_queue_purge(&pch->file.rq);
+	kfree(pch);
+}
+
+void __exit ppp_cleanup(void)
 {
 	/* should never happen */
-	if (!list_empty(&all_ppp_units))
+	if (!list_empty(&all_ppp_units) || !list_empty(&all_channels))
 		printk(KERN_ERR "PPP: removing module but units remain!\n");
 	if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0)
 		printk(KERN_ERR "PPP: failed to unregister PPP device\n");
-	devfs_unregister (devfs_handle);
+	devfs_unregister(devfs_handle);
 }
-#endif /* MODULE */
+
+module_init(ppp_init);
+module_exit(ppp_cleanup);
+
+EXPORT_SYMBOL(ppp_register_channel);
+EXPORT_SYMBOL(ppp_unregister_channel);
+EXPORT_SYMBOL(ppp_channel_index);
+EXPORT_SYMBOL(ppp_input);
+EXPORT_SYMBOL(ppp_input_error);
+EXPORT_SYMBOL(ppp_output_wakeup);
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
+EXPORT_SYMBOL(ppp_channel_read);
+EXPORT_SYMBOL(ppp_channel_write);
+EXPORT_SYMBOL(ppp_channel_poll);
+EXPORT_SYMBOL(ppp_channel_ioctl);
+EXPORT_SYMBOL(all_ppp_units); /* for debugging */
+EXPORT_SYMBOL(all_channels); /* for debugging */
diff -u --recursive --new-file v2.3.51/linux/drivers/net/ppp_synctty.c linux/drivers/net/ppp_synctty.c
--- v2.3.51/linux/drivers/net/ppp_synctty.c	Sun Nov  7 16:37:34 1999
+++ linux/drivers/net/ppp_synctty.c	Mon Mar 13 12:33:30 2000
@@ -439,7 +439,7 @@
 			break;
 		ap->chan.private = ap;
 		ap->chan.ops = &sync_ops;
-		err = ppp_register_channel(&ap->chan, val);
+		err = ppp_register_channel(&ap->chan);
 		if (err != 0)
 			break;
 		ap->connected = 1;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/rcpci45.c linux/drivers/net/rcpci45.c
--- v2.3.51/linux/drivers/net/rcpci45.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/rcpci45.c	Sun Mar 12 19:18:55 2000
@@ -1034,6 +1034,9 @@
     printk("RCioctl: cmd = 0x%x\n", cmd);
 #endif
  
+    if(!capable(CAP_NET_ADMIN))
+    	return -EPERM;
+    	
     switch (cmd)  {
  
     case RCU_PROTOCOL_REV:
@@ -1157,14 +1160,14 @@
             RCUD_DEFAULT -> rc = 0x11223344;
             break;
         }
-        copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser));
+        if(copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser)))
+        	return -EFAULT;
         break;
-    }   /* RCU_COMMAND */ 
+	}   /* RCU_COMMAND */ 
 
-    default:
-        printk("RC default\n");
-        rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
-        break;
+	default:
+        	rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+        	return -EINVAL;
     }
     return 0;
 }
diff -u --recursive --new-file v2.3.51/linux/drivers/net/rrunner.c linux/drivers/net/rrunner.c
--- v2.3.51/linux/drivers/net/rrunner.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/net/rrunner.c	Mon Mar 13 09:50:16 2000
@@ -76,8 +76,8 @@
 #else
 #define NET_BH              0
 #define rr_mark_net_bh(foo) {do{} while(0);}
-#define rr_if_busy(dev)     test_bit(LINK_STATE_XOFF, &dev->state)
-#define rr_if_running(dev)  test_bit(LINK_STATE_START, &dev->state)
+#define rr_if_busy(dev)     netif_queue_stopped(dev)
+#define rr_if_running(dev)  netif_running(dev)
 #define rr_if_down(dev)     {do{} while(0);}
 #endif
 
@@ -1550,7 +1550,7 @@
 
 	switch(cmd){
 	case SIOCRRGFW:
-		if (!suser()){
+		if (!capable(CAP_SYS_RAWIO)){
 			error = -EPERM;
 			goto out;
 		}
@@ -1582,7 +1582,7 @@
 		kfree(image);
 		break;
 	case SIOCRRPFW:
-		if (!suser()){
+		if (!capable(CAP_SYS_RAWIO)){
 			error = -EPERM;
 			goto out;
 		}
diff -u --recursive --new-file v2.3.51/linux/drivers/net/sb1000.c linux/drivers/net/sb1000.c
--- v2.3.51/linux/drivers/net/sb1000.c	Sat Feb 12 11:22:10 2000
+++ linux/drivers/net/sb1000.c	Mon Mar 13 09:50:16 2000
@@ -1052,7 +1052,7 @@
 		break;
 
 	case SIOCSCMFREQUENCY:		/* set frequency */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		if(get_user(frequency, (int*) ifr->ifr_data))
 			return -EFAULT;
@@ -1068,7 +1068,7 @@
 		break;
 
 	case SIOCSCMPIDS:			/* set PIDs */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		if(copy_from_user(PID, ifr->ifr_data, sizeof(PID)))
 			return -EFAULT;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/setup.c linux/drivers/net/setup.c
--- v2.3.51/linux/drivers/net/setup.c	Fri Mar 10 16:40:43 2000
+++ linux/drivers/net/setup.c	Mon Mar 13 09:43:37 2000
@@ -28,6 +28,7 @@
 extern int lapbeth_init(void);
 extern int sdla_setup(void); 
 extern int sdla_c_setup(void); 
+extern int comx_init(void);
 
 extern int abyss_probe(void);
 extern int madgemc_probe(void);
@@ -75,7 +76,9 @@
 #if defined(CONFIG_8xx)
         {cpm_enet_init, 0},
 #endif
-	/*
+#if defined(CONFIG_COMX)
+	{comx_init(), 0},
+#endif	/*
 	 *	SLHC if present needs attaching so other people see it
 	 *	even if not opened.
 	 */
diff -u --recursive --new-file v2.3.51/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.3.51/linux/drivers/net/shaper.c	Thu Feb 10 17:11:11 2000
+++ linux/drivers/net/shaper.c	Mon Mar 13 22:18:11 2000
@@ -480,6 +480,8 @@
 }
 #endif
 
+#ifdef CONFIG_INET
+
 static int shaper_neigh_setup(struct neighbour *n)
 {
 	if (n->nud_state == NUD_NONE) {
@@ -498,6 +500,15 @@
 	}
 	return 0;
 }
+
+#else /* !(CONFIG_INET) */
+
+static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
+{
+	return 0;
+}
+
+#endif
 
 static int shaper_attach(struct net_device *shdev, struct shaper *sh, struct net_device *dev)
 {
diff -u --recursive --new-file v2.3.51/linux/drivers/net/sis900.c linux/drivers/net/sis900.c
--- v2.3.51/linux/drivers/net/sis900.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/sis900.c	Mon Mar 13 09:50:16 2000
@@ -1127,7 +1127,7 @@
 		data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
 		return 0;
 	case SIOCDEVPRIVATE+2:          	/* Write the specified MII register */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
 		return 0;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/skfp/drvfbi.c linux/drivers/net/skfp/drvfbi.c
--- v2.3.51/linux/drivers/net/skfp/drvfbi.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/skfp/drvfbi.c	Sun Mar 12 19:11:17 2000
@@ -129,7 +129,7 @@
 /*
  * FDDI card reset
  */
-void card_start(smc)
+static void card_start(smc)
 struct s_smc *smc ;
 {
 	int i ;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/skfp/skfddi.c linux/drivers/net/skfp/skfddi.c
--- v2.3.51/linux/drivers/net/skfp/skfddi.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/skfp/skfddi.c	Mon Mar 13 09:50:16 2000
@@ -1249,7 +1249,7 @@
 		copy_to_user(ioc.data, skfp_ctl_get_stats(dev), ioc.len);
 		break;
 	case SKFP_CLR_STATS:	/* Zero out the driver statistics */
-		if (suser()) {
+		if (!capable(CAP_NET_ADMIN)) {
 			memset(&lp->MacStat, 0, sizeof(lp->MacStat));
 		} else {
 			status = -EPERM;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c
--- v2.3.51/linux/drivers/net/sunhme.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/sunhme.c	Sun Mar 12 19:11:17 2000
@@ -1,4 +1,4 @@
-/* $Id: sunhme.c,v 1.92 2000/02/18 13:49:22 davem Exp $
+/* $Id: sunhme.c,v 1.93 2000/03/12 04:02:14 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.
@@ -254,7 +254,7 @@
 #define hme_read_desc32(__hp, __p)	(*(__p))
 #define hme_dma_map(__hp, __ptr, __size, __dir) \
 	sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir))
-#define hme_dma_unmap(__hp, __addr, __size) \
+#define hme_dma_unmap(__hp, __addr, __size, __dir) \
 	sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir))
 #define hme_dma_sync(__hp, __addr, __size, __dir) \
 	sbus_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir))
diff -u --recursive --new-file v2.3.51/linux/drivers/net/tokenring/Config.in linux/drivers/net/tokenring/Config.in
--- v2.3.51/linux/drivers/net/tokenring/Config.in	Tue Mar  7 14:32:26 2000
+++ linux/drivers/net/tokenring/Config.in	Mon Mar 13 09:43:36 2000
@@ -9,6 +9,7 @@
 if [ "$CONFIG_TR" != "n" ]; then
    dep_tristate '  IBM Tropic chipset based adapter support' CONFIG_IBMTR $CONFIG_TR
    dep_tristate '  IBM Olympic chipset PCI adapter support' CONFIG_IBMOL $CONFIG_TR
+   dep_tristate '  IBM Lanstreamer chipset PCI adapter support' CONFIG_IBMLS $CONFIG_TR
    dep_tristate '  Generic TMS380 Token Ring ISA/PCI adapter support' CONFIG_TMS380TR $CONFIG_TR
    if [ "$CONFIG_TMS380TR" != "n" ]; then
       dep_tristate '    Generic TMS380 PCI support' CONFIG_TMSPCI $CONFIG_TMS380TR
diff -u --recursive --new-file v2.3.51/linux/drivers/net/tokenring/Makefile linux/drivers/net/tokenring/Makefile
--- v2.3.51/linux/drivers/net/tokenring/Makefile	Fri Mar 10 16:40:43 2000
+++ linux/drivers/net/tokenring/Makefile	Mon Mar 13 09:43:36 2000
@@ -18,6 +18,7 @@
 
 obj-$(CONFIG_IBMTR) 	+= ibmtr.o
 obj-$(CONFIG_IBMOL) 	+= olympic.o
+obj-$(CONFIG_IBMLS) 	+= lanstreamer.o
 obj-$(CONFIG_TMS380TR) 	+= tms380tr.o
 obj-$(CONFIG_ABYSS) 	+= abyss.o
 obj-$(CONFIG_MADGEMC) 	+= madgemc.o
diff -u --recursive --new-file v2.3.51/linux/drivers/net/tokenring/lanstreamer.c linux/drivers/net/tokenring/lanstreamer.c
--- v2.3.51/linux/drivers/net/tokenring/lanstreamer.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/tokenring/lanstreamer.c	Mon Mar 13 09:43:36 2000
@@ -0,0 +1,1776 @@
+/*
+ *   lanstreamer.c -- driver for the IBM Auto LANStreamer PCI Adapter
+ *
+ *  Written By: Mike Sullivan, IBM Corporation
+ *
+ *  Copyright (C) 1999 IBM Corporation
+ *
+ *  Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC
+ *  chipset. 
+ *
+ *  This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic
+ *  chipsets) written  by:
+ *      1999 Peter De Schrijver All Rights Reserved
+ *	1999 Mike Phillips (phillim@amtrak.com)
+ *
+ *  Base Driver Skeleton:
+ *      Written 1993-94 by Donald Becker.
+ *
+ *      Copyright 1993 United States Government as represented by the
+ *      Director, National Security Agency.
+ *
+ * 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.                              
+ *                                                                           
+ * NO WARRANTY                                                               
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR        
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT      
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,      
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is    
+ * solely responsible for determining the appropriateness of using and       
+ * distributing the Program and assumes all risks associated with its        
+ * exercise of rights under this Agreement, including but not limited to     
+ * the risks and costs of program errors, damage to or loss of data,         
+ * programs or equipment, and unavailability or interruption of operations.  
+ *                                                                           
+ * DISCLAIMER OF LIABILITY                                                   
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY   
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND   
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR     
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED  
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES             
+ *                                                                           
+ * 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 
+ *                                                                           
+ * 
+ *  12/10/99 - Alpha Release 0.1.0
+ *            First release to the public
+ *  03/03/00 - Merged to kernel, indented -kr -i8 -bri0, fixed some missing
+ *		malloc free checks, reviewed code. <alan@redhat.com>
+ *  
+ *  To Do:
+ *
+ *  1) Test Network Monitor Mode
+ *  2) Add auto reset logic on adapter errors
+ *  3) Test with varying options
+ *
+ *  If Problems do Occur
+ *  Most problems can be rectified by either closing and opening the interface
+ *  (ifconfig down and up) or rmmod and insmod'ing the driver (a bit difficult
+ *  if compiled into the kernel).
+ */
+
+/* Change STREAMER_DEBUG to 1 to get verbose, and I mean really verbose, messages */
+
+#define STREAMER_DEBUG 0
+#define STREAMER_DEBUG_PACKETS 0
+
+/* Change STREAMER_NETWORK_MONITOR to receive mac frames through the arb channel.
+ * Will also create a /proc/net/streamer_tr entry if proc_fs is compiled into the
+ * kernel.
+ * Intended to be used to create a ring-error reporting network module 
+ * i.e. it will give you the source address of beaconers on the ring 
+ */
+
+#define STREAMER_NETWORK_MONITOR 0
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/ptrace.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/trdevice.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <net/checksum.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include "lanstreamer.h"
+
+/* I've got to put some intelligence into the version number so that Peter and I know
+ * which version of the code somebody has got. 
+ * Version Number = a.b.c.d  where a.b.c is the level of code and d is the latest author.
+ * So 0.0.1.pds = Peter, 0.0.1.mlp = Mike
+ * 
+ * Official releases will only have an a.b.c version number format.
+ */
+
+static char *version = "LanStreamer.c v0.1.0 12/10/99 - Mike Sullivan";
+
+static char *open_maj_error[] = {
+	"No error", "Lobe Media Test", "Physical Insertion",
+	"Address Verification", "Neighbor Notification (Ring Poll)",
+	"Request Parameters", "FDX Registration Request",
+	"FDX Lobe Media Test", "FDX Duplicate Address Check",
+	"Unknown stage"
+};
+
+static char *open_min_error[] = {
+	"No error", "Function Failure", "Signal Lost", "Wire Fault",
+	"Ring Speed Mismatch", "Timeout", "Ring Failure", "Ring Beaconing",
+	"Duplicate Node Address", "Request Parameters", "Remove Received",
+	"Reserved", "Reserved", "No Monitor Detected for RPL",
+	"Monitor Contention failer for RPL", "FDX Protocol Error"
+};
+
+/* Module paramters */
+
+/* Ring Speed 0,4,16
+ * 0 = Autosense         
+ * 4,16 = Selected speed only, no autosense
+ * This allows the card to be the first on the ring
+ * and become the active monitor.
+ *
+ * WARNING: Some hubs will allow you to insert
+ * at the wrong speed
+ */
+
+static int ringspeed[STREAMER_MAX_ADAPTERS] = { 0, };
+
+MODULE_PARM(ringspeed, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+/* Packet buffer size */
+
+static int pkt_buf_sz[STREAMER_MAX_ADAPTERS] = { 0, };
+
+MODULE_PARM(pkt_buf_sz, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+/* Message Level */
+
+static int message_level[STREAMER_MAX_ADAPTERS] = { 1, };
+
+MODULE_PARM(message_level,
+	    "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+static int streamer_scan(struct net_device *dev);
+static int streamer_init(struct net_device *dev);
+static int streamer_open(struct net_device *dev);
+static int streamer_xmit(struct sk_buff *skb, struct net_device *dev);
+static int streamer_close(struct net_device *dev);
+static void streamer_set_rx_mode(struct net_device *dev);
+static void streamer_interrupt(int irq, void *dev_id,
+			       struct pt_regs *regs);
+static struct net_device_stats *streamer_get_stats(struct net_device *dev);
+static int streamer_set_mac_address(struct net_device *dev, void *addr);
+static void streamer_arb_cmd(struct net_device *dev);
+static int streamer_change_mtu(struct net_device *dev, int mtu);
+static void streamer_srb_bh(struct net_device *dev);
+static void streamer_asb_bh(struct net_device *dev);
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+static int sprintf_info(char *buffer, struct net_device *dev);
+#endif
+#endif
+
+int __init streamer_probe(struct net_device *dev)
+{
+	int cards_found;
+
+	cards_found = streamer_scan(dev);
+	return cards_found ? 0 : -ENODEV;
+}
+
+static int __init streamer_scan(struct net_device *dev)
+{
+	struct pci_dev *pci_device = NULL;
+	struct streamer_private *streamer_priv;
+	int card_no = 0;
+	if (pci_present()) 
+	{
+		while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM,	PCI_DEVICE_ID_IBM_TR, pci_device))) 
+		{
+			pci_set_master(pci_device);
+
+			/* Check to see if io has been allocated, if so, we've already done this card,
+			   so continue on the card discovery loop  */
+
+			if (check_region(pci_device->resource[0].start, STREAMER_IO_SPACE)) 
+			{
+				card_no++;
+				continue;
+			}
+
+			streamer_priv = kmalloc(sizeof(struct streamer_private), GFP_KERNEL);
+			if(streamer_priv==NULL)
+			{
+				printk(KERN_ERR "lanstreamer: out of memory.\n");
+				break;
+			}
+			memset(streamer_priv, 0, sizeof(struct streamer_private));
+#ifndef MODULE
+			dev = init_trdev(dev, 0);
+			if(dev==NULL)
+			{
+				kfree(streamer_priv);
+				printk(KERN_ERR "lanstreamer: out of memory.\n");
+				break;
+			}
+#endif
+			dev->priv = (void *) streamer_priv;
+#if STREAMER_DEBUG
+			printk("pci_device: %p, dev:%p, dev->priv: %p\n",
+			       pci_device, dev, dev->priv);
+#endif
+			dev->irq = pci_device->irq;
+			dev->base_addr = pci_device->resource[0].start;
+			dev->init = &streamer_init;
+			streamer_priv->streamer_mmio = ioremap(pci_device->resource[1].start, 256);
+			init_waitqueue_head(&streamer_priv->srb_wait);
+			init_waitqueue_head(&streamer_priv->trb_wait);
+			if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000))
+				streamer_priv->pkt_buf_sz = PKT_BUF_SZ;
+			else
+				streamer_priv->pkt_buf_sz = pkt_buf_sz[card_no];
+
+			streamer_priv->streamer_ring_speed = ringspeed[card_no];
+			streamer_priv->streamer_message_level = message_level[card_no];
+			streamer_priv->streamer_multicast_set = 0;
+
+			if (streamer_init(dev) == -1) {
+				unregister_netdevice(dev);
+				kfree(dev->priv);
+				return 0;
+			}
+
+			dev->open = &streamer_open;
+			dev->hard_start_xmit = &streamer_xmit;
+			dev->change_mtu = &streamer_change_mtu;
+
+			dev->stop = &streamer_close;
+			dev->do_ioctl = NULL;
+			dev->set_multicast_list = &streamer_set_rx_mode;
+			dev->get_stats = &streamer_get_stats;
+			dev->set_mac_address = &streamer_set_mac_address;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+
+static int __init streamer_init(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv;
+	__u8 *streamer_mmio;
+	unsigned long t;
+	unsigned int uaa_addr;
+	struct sk_buff *skb = 0;
+	__u16 misr;
+
+	streamer_priv = (struct streamer_private *) dev->priv;
+	streamer_mmio = streamer_priv->streamer_mmio;
+
+	printk("%s \n", version);
+	printk(KERN_INFO "%s: IBM PCI tokenring card. I/O at %hx, MMIO at %p, using irq %d\n",
+			dev->name, (unsigned int) dev->base_addr,
+			streamer_priv->streamer_mmio, dev->irq);
+
+	request_region(dev->base_addr, STREAMER_IO_SPACE, "streamer");
+	writew(readw(streamer_mmio + BCTL) | BCTL_SOFTRESET, streamer_mmio + BCTL);
+	t = jiffies;
+	/* Hold soft reset bit for a while */
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule_timeout(HZ);
+	
+	writew(readw(streamer_mmio + BCTL) & ~BCTL_SOFTRESET,
+	       streamer_mmio + BCTL);
+
+#if STREAMER_DEBUG
+	printk("BCTL: %x\n", readw(streamer_mmio + BCTL));
+	printk("GPR: %x\n", readw(streamer_mmio + GPR));
+	printk("SISRMASK: %x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+
+	if (streamer_priv->streamer_ring_speed == 0) {	/* Autosense */
+		writew(readw(streamer_mmio + GPR) | GPR_AUTOSENSE,
+		       streamer_mmio + GPR);
+		if (streamer_priv->streamer_message_level)
+			printk(KERN_INFO "%s: Ringspeed autosense mode on\n",
+			       dev->name);
+	} else if (streamer_priv->streamer_ring_speed == 16) {
+		if (streamer_priv->streamer_message_level)
+			printk(KERN_INFO "%s: Trying to open at 16 Mbps as requested\n",
+			       dev->name);
+		writew(GPR_16MBPS, streamer_mmio + GPR);
+	} else if (streamer_priv->streamer_ring_speed == 4) {
+		if (streamer_priv->streamer_message_level)
+			printk(KERN_INFO "%s: Trying to open at 4 Mbps as requested\n",
+			       dev->name);
+		writew(0, streamer_mmio + GPR);
+	}
+
+	skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+	if (!skb) {
+		printk(KERN_INFO "%s: skb allocation for diagnostics failed...proceeding\n",
+		       dev->name);
+	} else {
+		streamer_priv->streamer_rx_ring[0].forward = 0;
+		streamer_priv->streamer_rx_ring[0].status = 0;
+		streamer_priv->streamer_rx_ring[0].buffer = virt_to_bus(skb->data);
+		streamer_priv->streamer_rx_ring[0].framelen_buflen = 512;	/* streamer_priv->pkt_buf_sz; */
+		writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+	}
+
+#if STREAMER_DEBUG
+	printk("GPR = %x\n", readw(streamer_mmio + GPR));
+#endif
+	/* start solo init */
+	writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+
+	while (!((readw(streamer_mmio + SISR)) & SISR_SRB_REPLY)) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ/10);
+		if (jiffies - t > 40 * HZ) {
+			printk(KERN_ERR
+			       "IBM PCI tokenring card not responding\n");
+			release_region(dev->base_addr, STREAMER_IO_SPACE);
+			return -1;
+		}
+	}
+	writew(~SISR_SRB_REPLY, streamer_mmio + SISR_RUM);
+	misr = readw(streamer_mmio + MISR_RUM);
+	writew(~misr, streamer_mmio + MISR_RUM);
+
+	if (skb)
+		dev_kfree_skb(skb);	/* release skb used for diagnostics */
+
+#if STREAMER_DEBUG
+	printk("LAPWWO: %x, LAPA: %x LAPE:  %x\n",
+	       readw(streamer_mmio + LAPWWO), readw(streamer_mmio + LAPA),
+	       readw(streamer_mmio + LAPE));
+#endif
+
+#if STREAMER_DEBUG
+	{
+		int i;
+		writew(readw(streamer_mmio + LAPWWO),
+		       streamer_mmio + LAPA);
+		printk("initialization response srb dump: ");
+		for (i = 0; i < 10; i++)
+			printk("%x:",
+			       ntohs(readw(streamer_mmio + LAPDINC)));
+		printk("\n");
+	}
+#endif
+
+	writew(readw(streamer_mmio + LAPWWO) + 6, streamer_mmio + LAPA);
+	if (readw(streamer_mmio + LAPD)) {
+		printk(KERN_INFO "tokenring card intialization failed. errorcode : %x\n",
+		       readw(streamer_mmio + LAPD));
+		release_region(dev->base_addr, STREAMER_IO_SPACE);
+		return -1;
+	}
+
+	writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
+	uaa_addr = ntohs(readw(streamer_mmio + LAPDINC));
+	readw(streamer_mmio + LAPDINC);	/* skip over Level.Addr field */
+	streamer_priv->streamer_addr_table_addr = ntohs(readw(streamer_mmio + LAPDINC));
+	streamer_priv->streamer_parms_addr = ntohs(readw(streamer_mmio + LAPDINC));
+
+#if STREAMER_DEBUG
+	printk("UAA resides at %x\n", uaa_addr);
+#endif
+
+	/* setup uaa area for access with LAPD */
+	writew(uaa_addr, streamer_mmio + LAPA);
+
+	/* setup uaa area for access with LAPD */
+	{
+		int i;
+		__u16 addr;
+		writew(uaa_addr, streamer_mmio + LAPA);
+		for (i = 0; i < 6; i += 2) {
+			addr = readw(streamer_mmio + LAPDINC);
+			dev->dev_addr[i] = addr & 0xff;
+			dev->dev_addr[i + 1] = (addr >> 8) & 0xff;
+		}
+#if STREAMER_DEBUG
+		printk("Adapter address: ");
+		for (i = 0; i < 6; i++) {
+			printk("%02x:", dev->dev_addr[i]);
+		}
+		printk("\n");
+#endif
+	}
+	return 0;
+}
+
+static int streamer_open(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	unsigned long flags;
+	char open_error[255];
+	int i, open_finished = 1;
+	__u16 srb_word;
+	__u16 srb_open;
+
+
+	if (request_irq(dev->irq, &streamer_interrupt, SA_SHIRQ, "streamer", dev)) {
+		return -EAGAIN;
+	}
+#if STREAMER_DEBUG
+	printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM));
+	printk("pending ints: %x\n", readw(streamer_mmio + SISR));
+#endif
+
+	writew(SISR_MI | SISR_SRB_REPLY, streamer_mmio + SISR_MASK);	/* more ints later, doesn't stop arb cmd interrupt */
+	writew(LISR_LIE, streamer_mmio + LISR);	/* more ints later */
+
+	/* adapter is closed, so SRB is pointed to by LAPWWO */
+	writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+
+#if STREAMER_DEBUG
+	printk("LAPWWO: %x, LAPA: %x\n", readw(streamer_mmio + LAPWWO),
+	       readw(streamer_mmio + LAPA));
+	printk("LAPE: %x\n", readw(streamer_mmio + LAPE));
+	printk("SISR Mask = %04x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+	do {
+		int i;
+
+		save_flags(flags);
+		cli();
+		for (i = 0; i < SRB_COMMAND_SIZE; i += 2) {
+			writew(0, streamer_mmio + LAPDINC);
+		}
+
+		writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+		writew(SRB_OPEN_ADAPTER, streamer_mmio + LAPDINC);	/* open */
+		writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+
+		writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
+#if STREAMER_NETWORK_MONITOR
+		/* If Network Monitor, instruct card to copy MAC frames through the ARB */
+		writew(ntohs(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), streamer_mmio + LAPDINC);	/* offset 8 word contains open options */
+#else
+		writew(ntohs(OPEN_ADAPTER_ENABLE_FDX), streamer_mmio + LAPDINC);	/* Offset 8 word contains Open.Options */
+#endif
+
+		if (streamer_priv->streamer_laa[0]) {
+			writew(readw(streamer_mmio + LAPWWO) + 12, streamer_mmio + LAPA);
+			writew(((__u16 *) (streamer_priv->streamer_laa))[0], streamer_mmio + LAPDINC);	/* offset 12 word */
+			writew(((__u16 *) (streamer_priv->streamer_laa))[2], streamer_mmio + LAPDINC);	/* offset 14 word */
+			writew(((__u16 *) (streamer_priv->streamer_laa))[4], streamer_mmio + LAPDINC);	/* offset 16 word */
+			memcpy(dev->dev_addr, streamer_priv->streamer_laa, dev->addr_len);
+		}
+
+		/* save off srb open offset */
+		srb_open = readw(streamer_mmio + LAPWWO);
+#if STREAMER_DEBUG
+		writew(readw(streamer_mmio + LAPWWO),
+		       streamer_mmio + LAPA);
+		printk("srb open request: \n");
+		for (i = 0; i < 16; i++) {
+			printk("%x:", ntohs(readw(streamer_mmio + LAPDINC)));
+		}
+		printk("\n");
+#endif
+
+		streamer_priv->srb_queued = 1;
+
+		/* signal solo that SRB command has been issued */
+		writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+		while (streamer_priv->srb_queued) {
+			interruptible_sleep_on_timeout(&streamer_priv->srb_wait, 5 * HZ);
+			if (signal_pending(current)) {
+				printk(KERN_WARNING "%s: SRB timed out.\n", dev->name);
+				printk(KERN_WARNING "SISR=%x MISR=%x, LISR=%x\n",
+				       readw(streamer_mmio + SISR),
+				       readw(streamer_mmio + MISR_RUM),
+				       readw(streamer_mmio + LISR));
+				streamer_priv->srb_queued = 0;
+				break;
+			}
+		}
+		restore_flags(flags);
+
+#if STREAMER_DEBUG
+		printk("SISR_MASK: %x\n", readw(streamer_mmio + SISR_MASK));
+		printk("srb open response:\n");
+		writew(srb_open, streamer_mmio + LAPA);
+		for (i = 0; i < 10; i++) {
+			printk("%x:",
+			       ntohs(readw(streamer_mmio + LAPDINC)));
+		}
+#endif
+
+		/* If we get the same return response as we set, the interrupt wasn't raised and the open
+		 * timed out.
+		 */
+		writew(srb_open + 2, streamer_mmio + LAPA);
+		srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+		if (srb_word == STREAMER_CLEAR_RET_CODE) {
+			printk(KERN_WARNING "%s: Adapter Open time out or error.\n",
+			       dev->name);
+			return -EIO;
+		}
+
+		if (srb_word != 0) {
+			if (srb_word == 0x07) {
+				if (!streamer_priv->streamer_ring_speed && open_finished) {	/* Autosense , first time around */
+					printk(KERN_WARNING "%s: Retrying at different ring speed \n",
+					       dev->name);
+					open_finished = 0;
+				} else {
+					__u16 error_code;
+
+					writew(srb_open + 6, streamer_mmio + LAPA);
+					error_code = ntohs(readw(streamer_mmio + LAPD));
+					strcpy(open_error, open_maj_error[(error_code & 0xf0) >> 4]);
+					strcat(open_error, " - ");
+					strcat(open_error, open_min_error[(error_code & 0x0f)]);
+
+					if (!streamer_priv->streamer_ring_speed
+					    && ((error_code & 0x0f) == 0x0d)) 
+					{
+						printk(KERN_WARNING "%s: Tried to autosense ring speed with no monitors present\n", dev->name);
+						printk(KERN_WARNING "%s: Please try again with a specified ring speed \n", dev->name);
+						free_irq(dev->irq, dev);
+						return -EIO;
+					}
+
+					printk(KERN_WARNING "%s: %s\n",
+					       dev->name, open_error);
+					free_irq(dev->irq, dev);
+					return -EIO;
+
+				}	/* if autosense && open_finished */
+			} else {
+				printk(KERN_WARNING "%s: Bad OPEN response: %x\n",
+				       dev->name, srb_word);
+				free_irq(dev->irq, dev);
+				return -EIO;
+			}
+		} else
+			open_finished = 1;
+	} while (!(open_finished));	/* Will only loop if ring speed mismatch re-open attempted && autosense is on */
+
+	writew(srb_open + 18, streamer_mmio + LAPA);
+	srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+	if (srb_word & (1 << 3))
+		if (streamer_priv->streamer_message_level)
+			printk(KERN_INFO "%s: Opened in FDX Mode\n", dev->name);
+
+	if (srb_word & 1)
+		streamer_priv->streamer_ring_speed = 16;
+	else
+		streamer_priv->streamer_ring_speed = 4;
+
+	if (streamer_priv->streamer_message_level)
+		printk(KERN_INFO "%s: Opened in %d Mbps mode\n", 
+			dev->name,
+			streamer_priv->streamer_ring_speed);
+
+	writew(srb_open + 8, streamer_mmio + LAPA);
+	streamer_priv->asb = ntohs(readw(streamer_mmio + LAPDINC));
+	streamer_priv->srb = ntohs(readw(streamer_mmio + LAPDINC));
+	streamer_priv->arb = ntohs(readw(streamer_mmio + LAPDINC));
+	readw(streamer_mmio + LAPDINC);	/* offset 14 word is rsvd */
+	streamer_priv->trb = ntohs(readw(streamer_mmio + LAPDINC));
+
+	streamer_priv->streamer_receive_options = 0x00;
+	streamer_priv->streamer_copy_all_options = 0;
+
+	/* setup rx ring */
+	/* enable rx channel */
+	writew(~BMCTL_RX_DIS, streamer_mmio + BMCTL_RUM);
+
+	/* setup rx descriptors */
+	for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
+		struct sk_buff *skb;
+
+		skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+		if (skb == NULL)
+			break;
+
+		skb->dev = dev;
+
+		streamer_priv->streamer_rx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_rx_ring[i + 1]);
+		streamer_priv->streamer_rx_ring[i].status = 0;
+		streamer_priv->streamer_rx_ring[i].buffer = virt_to_bus(skb->data);
+		streamer_priv->streamer_rx_ring[i].framelen_buflen = streamer_priv->pkt_buf_sz;
+		streamer_priv->rx_ring_skb[i] = skb;
+	}
+	streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1].forward =
+				virt_to_bus(&streamer_priv->streamer_rx_ring[0]);
+
+	if (i == 0) {
+		printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers. Adapter disabled\n", dev->name);
+		free_irq(dev->irq, dev);
+		return -EIO;
+	}
+
+	streamer_priv->rx_ring_last_received = STREAMER_RX_RING_SIZE - 1;	/* last processed rx status */
+
+	writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+	writel(virt_to_bus(&streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1]), streamer_mmio + RXLBDA);
+
+	/* set bus master interrupt event mask */
+	writew(MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK);
+
+
+	/* setup tx ring */
+	writew(~BMCTL_TX2_DIS, streamer_mmio + BMCTL_RUM);	/* Enables TX channel 2 */
+	for (i = 0; i < STREAMER_TX_RING_SIZE; i++) {
+		streamer_priv->streamer_tx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_tx_ring[i + 1]);
+		streamer_priv->streamer_tx_ring[i].status = 0;
+		streamer_priv->streamer_tx_ring[i].bufcnt_framelen = 0;
+		streamer_priv->streamer_tx_ring[i].buffer = 0;
+		streamer_priv->streamer_tx_ring[i].buflen = 0;
+	}
+	streamer_priv->streamer_tx_ring[STREAMER_TX_RING_SIZE - 1].forward =
+					virt_to_bus(&streamer_priv->streamer_tx_ring[0]);;
+
+	streamer_priv->free_tx_ring_entries = STREAMER_TX_RING_SIZE;
+	streamer_priv->tx_ring_free = 0;	/* next entry in tx ring to use */
+	streamer_priv->tx_ring_last_status = STREAMER_TX_RING_SIZE - 1;
+
+	/* set Busmaster interrupt event mask (handle receives on interrupt only */
+	writew(MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK);
+	/* set system event interrupt mask */
+	writew(SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE, streamer_mmio + SISR_MASK_SUM);
+
+#if STREAMER_DEBUG
+	printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM));
+	printk("SISR MASK: %x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+
+#if STREAMER_NETWORK_MONITOR
+
+	writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+	printk("%s: Node Address: %04x:%04x:%04x\n", dev->name,
+		ntohs(readw(streamer_mmio + LAPDINC)),
+		ntohs(readw(streamer_mmio + LAPDINC)),
+		ntohs(readw(streamer_mmio + LAPDINC)));
+	readw(streamer_mmio + LAPDINC);
+	readw(streamer_mmio + LAPDINC);
+	printk("%s: Functional Address: %04x:%04x\n", dev->name,
+		ntohs(readw(streamer_mmio + LAPDINC)),
+		ntohs(readw(streamer_mmio + LAPDINC)));
+
+	writew(streamer_priv->streamer_parms_addr + 4,
+		streamer_mmio + LAPA);
+	printk("%s: NAUN Address: %04x:%04x:%04x\n", dev->name,
+		ntohs(readw(streamer_mmio + LAPDINC)),
+		ntohs(readw(streamer_mmio + LAPDINC)),
+		ntohs(readw(streamer_mmio + LAPDINC)));
+#endif
+
+	netif_start_queue(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	When we enter the rx routine we do not know how many frames have been 
+ *	queued on the rx channel.  Therefore we start at the next rx status
+ *	position and travel around the receive ring until we have completed
+ *	all the frames.
+ *
+ *	This means that we may process the frame before we receive the end
+ *	of frame interrupt. This is why we always test the status instead
+ *	of blindly processing the next frame.
+ *	
+ */
+static void streamer_rx(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	struct streamer_rx_desc *rx_desc;
+	int rx_ring_last_received, length, frame_length, buffer_cnt = 0;
+	struct sk_buff *skb, *skb2;
+
+	/* setup the next rx descriptor to be received */
+	rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)];
+	rx_ring_last_received = streamer_priv->rx_ring_last_received;
+
+	while (rx_desc->status & 0x01000000) {	/* While processed descriptors are available */
+		if (rx_ring_last_received != streamer_priv->rx_ring_last_received) 
+		{
+			printk(KERN_WARNING "RX Error 1 rx_ring_last_received not the same %x %x\n",
+				rx_ring_last_received, streamer_priv->rx_ring_last_received);
+		}
+		streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+		rx_ring_last_received = streamer_priv->rx_ring_last_received;
+
+		length = rx_desc->framelen_buflen & 0xffff;	/* buffer length */
+		frame_length = (rx_desc->framelen_buflen >> 16) & 0xffff;
+
+		if (rx_desc->status & 0x7E830000) {	/* errors */
+			if (streamer_priv->streamer_message_level) {
+				printk(KERN_WARNING "%s: Rx Error %x \n",
+				       dev->name, rx_desc->status);
+			}
+		} else {	/* received without errors */
+			if (rx_desc->status & 0x80000000) {	/* frame complete */
+				buffer_cnt = 1;
+				skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+			} else {
+				skb = dev_alloc_skb(frame_length);
+			}
+
+			if (skb == NULL) 
+			{
+				printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n",	dev->name);
+				streamer_priv->streamer_stats.rx_dropped++;
+			} else {	/* we allocated an skb OK */
+				skb->dev = dev;
+
+				if (buffer_cnt == 1) {
+					skb2 = streamer_priv->rx_ring_skb[rx_ring_last_received];
+#if STREAMER_DEBUG_PACKETS
+					{
+						int i;
+						printk("streamer_rx packet print: skb->data2 %p  skb->head %p\n", skb2->data, skb2->head);
+						for (i = 0; i < frame_length; i++) 
+						{
+							printk("%x:", skb2->data[i]);
+							if (((i + 1) % 16) == 0)
+								printk("\n");
+						}
+						printk("\n");
+					}
+#endif
+					skb_put(skb2, length);
+					skb2->protocol = tr_type_trans(skb2, dev);
+					/* recycle this descriptor */
+					streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
+					streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
+					streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+					streamer_priv-> rx_ring_skb[rx_ring_last_received] = skb;
+					/* place recycled descriptor back on the adapter */
+					writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]),streamer_mmio + RXLBDA);
+					/* pass the received skb up to the protocol */
+					netif_rx(skb2);
+				} else {
+					do {	/* Walk the buffers */
+						memcpy(skb_put(skb, length),bus_to_virt(rx_desc->buffer), length);	/* copy this fragment */
+						streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
+						streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
+						streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+						/* give descriptor back to the adapter */
+						writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]), streamer_mmio + RXLBDA);
+
+						if (rx_desc->status & 0x80000000)
+							break;	/* this descriptor completes the frame */
+
+						/* else get the next pending descriptor */
+						if (rx_ring_last_received!= streamer_priv->rx_ring_last_received)
+						{
+							printk("RX Error rx_ring_last_received not the same %x %x\n",
+								rx_ring_last_received,
+								streamer_priv->rx_ring_last_received);
+						}
+						rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE-1)];
+
+						length = rx_desc->framelen_buflen & 0xffff;	/* buffer length */
+						streamer_priv->rx_ring_last_received =	(streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE - 1);
+						rx_ring_last_received = streamer_priv->rx_ring_last_received;
+					} while (1);
+
+					skb->protocol = tr_type_trans(skb, dev);
+					/* send up to the protocol */
+					netif_rx(skb);
+				}
+				streamer_priv->streamer_stats.rx_packets++;
+				streamer_priv->streamer_stats.rx_bytes += length;
+			}	/* if skb == null */
+		}		/* end received without errors */
+
+		/* try the next one */
+		rx_desc = &streamer_priv->streamer_rx_ring[(rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)];
+	}			/* end for all completed rx descriptors */
+}
+
+static void streamer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	__u16 sisr;
+	__u16 misr;
+	__u16 sisrmask;
+
+	sisrmask = SISR_MI;
+	writew(~sisrmask, streamer_mmio + SISR_MASK_RUM);
+	sisr = readw(streamer_mmio + SISR);
+	writew(~sisr, streamer_mmio + SISR_RUM);
+	misr = readw(streamer_mmio + MISR_RUM);
+	writew(~misr, streamer_mmio + MISR_RUM);
+
+	if (!sisr) {		/* Interrupt isn't for us */
+		return;
+	}
+
+	if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY))
+	    || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) {
+		if (sisr & SISR_SRB_REPLY) {
+			if (streamer_priv->srb_queued == 1) {
+				wake_up_interruptible(&streamer_priv->srb_wait);
+			} else if (streamer_priv->srb_queued == 2) {
+				streamer_srb_bh(dev);
+			}
+			streamer_priv->srb_queued = 0;
+		}
+		/* SISR_SRB_REPLY */
+		if (misr & MISR_TX2_EOF) {
+			while (streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status) 
+			{
+				streamer_priv->tx_ring_last_status = (streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1);
+				streamer_priv->free_tx_ring_entries++;
+				streamer_priv->streamer_stats.tx_bytes += streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]->len;
+				streamer_priv->streamer_stats.tx_packets++;
+				dev_kfree_skb_irq(streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]);
+				streamer_priv-> streamer_tx_ring[streamer_priv->tx_ring_last_status].buffer = 0xdeadbeef;
+				streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].status = 0;
+				streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].bufcnt_framelen = 0;
+				streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].buflen = 0;
+			}
+			netif_wake_queue(dev);
+		}
+
+		if (misr & MISR_RX_EOF) {
+			streamer_rx(dev);
+		}
+		/* MISR_RX_EOF */
+		if (sisr & SISR_ADAPTER_CHECK) {
+			printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name);
+			writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+			printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n",
+			       dev->name, readw(streamer_mmio + LAPDINC),
+			       readw(streamer_mmio + LAPDINC),
+			       readw(streamer_mmio + LAPDINC),
+			       readw(streamer_mmio + LAPDINC));
+			free_irq(dev->irq, dev);
+		}
+
+		/* SISR_ADAPTER_CHECK */
+		if (sisr & SISR_ASB_FREE) {
+			/* Wake up anything that is waiting for the asb response */
+			if (streamer_priv->asb_queued) {
+				streamer_asb_bh(dev);
+			}
+		}
+		/* SISR_ASB_FREE */
+		if (sisr & SISR_ARB_CMD) {
+			streamer_arb_cmd(dev);
+		}
+		/* SISR_ARB_CMD */
+		if (sisr & SISR_TRB_REPLY) {
+			/* Wake up anything that is waiting for the trb response */
+			if (streamer_priv->trb_queued) {
+				wake_up_interruptible(&streamer_priv->
+						      trb_wait);
+			}
+			streamer_priv->trb_queued = 0;
+		}
+		/* SISR_TRB_REPLY */
+		if (misr & MISR_RX_NOBUF) {
+			/* According to the documentation, we don't have to do anything, but trapping it keeps it out of
+			   /var/log/messages.  */
+		}		/* SISR_RX_NOBUF */
+	} else {
+		printk(KERN_WARNING "%s: Unexpected interrupt: %x\n",
+		       dev->name, sisr);
+		printk(KERN_WARNING "%s: SISR_MASK: %x\n", dev->name,
+		       readw(streamer_mmio + SISR_MASK));
+	}			/* One if the interrupts we want */
+
+	writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+}
+
+
+static int streamer_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+	netif_stop_queue(dev);
+	
+	if (streamer_priv->free_tx_ring_entries) {
+		streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0;
+		streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len;
+		streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buffer = virt_to_bus(skb->data);
+		streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buflen = skb->len;
+		streamer_priv->tx_ring_skb[streamer_priv->tx_ring_free] = skb;
+		streamer_priv->free_tx_ring_entries--;
+#if STREAMER_DEBUG_PACKETS
+		{
+			int i;
+			printk("streamer_xmit packet print:\n");
+			for (i = 0; i < skb->len; i++) {
+				printk("%x:", skb->data[i]);
+				if (((i + 1) % 16) == 0)
+					printk("\n");
+			}
+			printk("\n");
+		}
+#endif
+
+		writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA);
+
+		streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1);
+		netif_start_queue(dev);
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+
+static int streamer_close(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	unsigned long flags;
+	int i;
+
+	writew(streamer_priv->srb, streamer_mmio + LAPA);
+	writew(SRB_CLOSE_ADAPTER, streamer_mmio + LAPDINC);
+	writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+
+	save_flags(flags);
+	cli();
+
+	streamer_priv->srb_queued = 1;
+	writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+	while (streamer_priv->srb_queued) 
+	{
+		interruptible_sleep_on_timeout(&streamer_priv->srb_wait,
+					       jiffies + 60 * HZ);
+		if (signal_pending(current)) 
+		{
+			printk(KERN_WARNING "%s: SRB timed out.\n", dev->name);
+			printk(KERN_WARNING "SISR=%x MISR=%x LISR=%x\n",
+			       readw(streamer_mmio + SISR),
+			       readw(streamer_mmio + MISR_RUM),
+			       readw(streamer_mmio + LISR));
+			streamer_priv->srb_queued = 0;
+			break;
+		}
+	}
+
+	restore_flags(flags);
+	streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+
+	for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
+		dev_kfree_skb(streamer_priv->rx_ring_skb[streamer_priv->rx_ring_last_received]);
+		streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+	}
+
+	/* reset tx/rx fifo's and busmaster logic */
+
+	/* TBD. Add graceful way to reset the LLC channel without doing a soft reset. 
+	   writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL);
+	   udelay(1);
+	   writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL);
+	 */
+
+#if STREAMER_DEBUG
+	writew(streamer_priv->srb, streamer_mmio + LAPA);
+	printk("srb): ");
+	for (i = 0; i < 2; i++) {
+		printk("%x ", htons(readw(streamer_mmio + LAPDINC)));
+	}
+	printk("\n");
+#endif
+	netif_stop_queue(dev);
+	free_irq(dev->irq, dev);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static void streamer_set_rx_mode(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	__u8 options = 0, set_mc_list = 0;
+	__u16 ata1, ata2;
+	struct dev_mc_list *dmi;
+
+	writel(streamer_priv->srb, streamer_mmio + LAPA);
+	options = streamer_priv->streamer_copy_all_options;
+
+	if (dev->flags & IFF_PROMISC)
+		options |= (3 << 5);	/* All LLC and MAC frames, all through the main rx channel */
+	else
+		options &= ~(3 << 5);
+
+	if (dev->mc_count) {
+		set_mc_list = 1;
+	}
+
+	/* Only issue the srb if there is a change in options */
+
+	if ((options ^ streamer_priv->streamer_copy_all_options)) 
+	{
+		/* Now to issue the srb command to alter the copy.all.options */
+
+		writew(SRB_MODIFY_RECEIVE_OPTIONS,
+		       streamer_mmio + LAPDINC);
+		writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+		writew(streamer_priv->streamer_receive_options | (options << 8), streamer_mmio + LAPDINC);
+		writew(0x414a, streamer_mmio + LAPDINC);
+		writew(0x454d, streamer_mmio + LAPDINC);
+		writew(0x2053, streamer_mmio + LAPDINC);
+		writew(0x2020, streamer_mmio + LAPDINC);
+
+		streamer_priv->srb_queued = 2;	/* Can't sleep, use srb_bh */
+
+		writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+		streamer_priv->streamer_copy_all_options = options;
+		return;
+	}
+
+	if (set_mc_list ^ streamer_priv->streamer_multicast_set)
+	{	/* Multicast options have changed */
+		dmi = dev->mc_list;
+
+		writel(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+		ata1 = readw(streamer_mmio + LAPDINC);
+		ata2 = readw(streamer_mmio + LAPD);
+
+		writel(streamer_priv->srb, streamer_mmio + LAPA);
+
+		if (set_mc_list) 
+		{
+			/* Turn multicast on */
+
+			/* RFC 1469 Says we must support using the functional address C0 00 00 04 00 00
+			 * We do this with a set functional address mask.
+			 */
+
+			if (!(ata1 & 0x0400)) {	/* need to set functional mask */
+				writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
+				writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+				writew(0, streamer_mmio + LAPDINC);
+				writew(ata1 | 0x0400, streamer_mmio + LAPDINC);
+				writew(ata2, streamer_mmio + LAPD);
+
+				streamer_priv->srb_queued = 2;
+				writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+				streamer_priv->streamer_multicast_set = 1;
+			}
+
+		} else {	/* Turn multicast off */
+
+			if ((ata1 & 0x0400)) {	/* Hmmm, need to reset the functional mask */
+				writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
+				writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+				writew(0, streamer_mmio + LAPDINC);
+				writew(ata1 & ~0x0400, streamer_mmio + LAPDINC);
+				writew(ata2, streamer_mmio + LAPD);
+
+				streamer_priv->srb_queued = 2;
+				writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+				streamer_priv->streamer_multicast_set = 0;
+			}
+		}
+
+	}
+}
+
+static void streamer_srb_bh(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	__u16 srb_word;
+
+	writew(streamer_priv->srb, streamer_mmio + LAPA);
+	srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+
+	switch (srb_word) {
+
+		/* SRB_MODIFY_RECEIVE_OPTIONS i.e. set_multicast_list options (promiscuous) 
+		 * At some point we should do something if we get an error, such as
+		 * resetting the IFF_PROMISC flag in dev
+		 */
+
+	case SRB_MODIFY_RECEIVE_OPTIONS:
+		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+		switch (srb_word) {
+		case 0x01:
+			printk(KERN_WARNING "%s: Unrecognized srb command\n", dev->name);
+			break;
+		case 0x04:
+			printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+			break;
+		default:
+			if (streamer_priv->streamer_message_level)
+				printk(KERN_WARNING "%s: Receive Options Modified to %x,%x\n",
+				       dev->name,
+				       streamer_priv->streamer_copy_all_options,
+				       streamer_priv->streamer_receive_options);
+			break;
+		}		/* switch srb[2] */
+		break;
+
+
+		/* SRB_SET_GROUP_ADDRESS - Multicast group setting 
+		 */
+	case SRB_SET_GROUP_ADDRESS:
+		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+		switch (srb_word) {
+		case 0x00:
+			streamer_priv->streamer_multicast_set = 1;
+			break;
+		case 0x01:
+			printk(KERN_WARNING "%s: Unrecognized srb command \n",dev->name);
+			break;
+		case 0x04:
+			printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+			break;
+		case 0x3c:
+			printk(KERN_WARNING "%s: Group/Functional address indicator bits not set correctly\n", dev->name);
+			break;
+		case 0x3e:	/* If we ever implement individual multicast addresses, will need to deal with this */
+			printk(KERN_WARNING "%s: Group address registers full\n", dev->name);
+			break;
+		case 0x55:
+			printk(KERN_INFO "%s: Group Address already set.\n", dev->name);
+			break;
+		default:
+			break;
+		}		/* switch srb[2] */
+		break;
+
+
+		/* SRB_RESET_GROUP_ADDRESS - Remove a multicast address from group list
+		 */
+	case SRB_RESET_GROUP_ADDRESS:
+		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+		switch (srb_word) {
+		case 0x00:
+			streamer_priv->streamer_multicast_set = 0;
+			break;
+		case 0x01:
+			printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+			break;
+		case 0x04:
+			printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+			break;
+		case 0x39:	/* Must deal with this if individual multicast addresses used */
+			printk(KERN_INFO "%s: Group address not found \n", dev->name);
+			break;
+		default:
+			break;
+		}		/* switch srb[2] */
+		break;
+
+
+		/* SRB_SET_FUNC_ADDRESS - Called by the set_rx_mode 
+		 */
+
+	case SRB_SET_FUNC_ADDRESS:
+		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+		switch (srb_word) {
+		case 0x00:
+			if (streamer_priv->streamer_message_level)
+				printk(KERN_INFO "%s: Functional Address Mask Set \n", dev->name);
+			break;
+		case 0x01:
+			printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+			break;
+		case 0x04:
+			printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+			break;
+		default:
+			break;
+		}		/* switch srb[2] */
+		break;
+
+		/* SRB_READ_LOG - Read and reset the adapter error counters
+		 */
+
+	case SRB_READ_LOG:
+		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+		switch (srb_word) {
+		case 0x00:
+			{
+				int i;
+				if (streamer_priv->streamer_message_level)
+					printk(KERN_INFO "%s: Read Log command complete\n", dev->name);
+				printk("Read Log statistics: ");
+				writew(streamer_priv->srb + 6,
+				       streamer_mmio + LAPA);
+				for (i = 0; i < 5; i++) {
+					printk("%x:", ntohs(readw(streamer_mmio + LAPDINC)));
+				}
+				printk("\n");
+			}
+			break;
+		case 0x01:
+			printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+			break;
+		case 0x04:
+			printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+			break;
+
+		}		/* switch srb[2] */
+		break;
+
+		/* SRB_READ_SR_COUNTERS - Read and reset the source routing bridge related counters */
+
+	case SRB_READ_SR_COUNTERS:
+		srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+		switch (srb_word) {
+		case 0x00:
+			if (streamer_priv->streamer_message_level)
+				printk(KERN_INFO "%s: Read Source Routing Counters issued\n", dev->name);
+			break;
+		case 0x01:
+			printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+			break;
+		case 0x04:
+			printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+			break;
+		default:
+			break;
+		}		/* switch srb[2] */
+		break;
+
+	default:
+		printk(KERN_WARNING "%s: Unrecognized srb bh return value.\n", dev->name);
+		break;
+	}			/* switch srb[0] */
+}
+
+static struct net_device_stats *streamer_get_stats(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv;
+	streamer_priv = (struct streamer_private *) dev->priv;
+	return (struct net_device_stats *) &streamer_priv->streamer_stats;
+}
+
+static int streamer_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *saddr = addr;
+	struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+
+	if (netif_running(dev)) {
+		printk(KERN_WARNING "%s: Cannot set mac/laa address while card is open\n", dev->name);
+		return -EBUSY;
+	}
+
+	memcpy(streamer_priv->streamer_laa, saddr->sa_data, dev->addr_len);
+
+	if (streamer_priv->streamer_message_level) {
+		printk(KERN_INFO "%s: MAC/LAA Set to  = %x.%x.%x.%x.%x.%x\n",
+		       dev->name, streamer_priv->streamer_laa[0],
+		       streamer_priv->streamer_laa[1],
+		       streamer_priv->streamer_laa[2],
+		       streamer_priv->streamer_laa[3],
+		       streamer_priv->streamer_laa[4],
+		       streamer_priv->streamer_laa[5]);
+	}
+	return 0;
+}
+
+static void streamer_arb_cmd(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	__u8 header_len;
+	__u16 frame_len, buffer_len;
+	struct sk_buff *mac_frame;
+	__u8 frame_data[256];
+	__u16 buff_off;
+	__u16 lan_status = 0, lan_status_diff;	/* Initialize to stop compiler warning */
+	__u8 fdx_prot_error;
+	__u16 next_ptr;
+	__u16 arb_word;
+
+#if STREAMER_NETWORK_MONITOR
+	struct trh_hdr *mac_hdr;
+#endif
+
+	writew(streamer_priv->arb, streamer_mmio + LAPA);
+	arb_word = readw(streamer_mmio + LAPD) & 0xFF;
+
+	if (arb_word == ARB_RECEIVE_DATA) {	/* Receive.data, MAC frames */
+		writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
+		streamer_priv->mac_rx_buffer = buff_off = ntohs(readw(streamer_mmio + LAPDINC));
+		header_len = readw(streamer_mmio + LAPDINC) & 0xff;	/* 802.5 Token-Ring Header Length */
+		frame_len = ntohs(readw(streamer_mmio + LAPDINC));
+
+#if STREAMER_DEBUG
+		{
+			int i;
+			__u16 next;
+			__u8 status;
+			__u16 len;
+
+			writew(ntohs(buff_off), streamer_mmio + LAPA);	/*setup window to frame data */
+			next = ntohs(readw(streamer_mmio + LAPDINC));
+			status =
+			    ntohs(readw(streamer_mmio + LAPDINC)) & 0xff;
+			len = ntohs(readw(streamer_mmio + LAPDINC));
+
+			/* print out 1st 14 bytes of frame data */
+			for (i = 0; i < 7; i++) {
+				printk("Loc %d = %04x\n", i,
+				       ntohs(readw
+					     (streamer_mmio + LAPDINC)));
+			}
+
+			printk("next %04x, fs %02x, len %04x \n", next,
+			       status, len);
+		}
+#endif
+		mac_frame = dev_alloc_skb(frame_len);
+
+		/* Walk the buffer chain, creating the frame */
+
+		do {
+			int i;
+			__u16 rx_word;
+
+			writew(ntohs(buff_off), streamer_mmio + LAPA);	/* setup window to frame data */
+			next_ptr = ntohs(readw(streamer_mmio + LAPDINC));
+			readw(streamer_mmio + LAPDINC);	/* read thru status word */
+			buffer_len = ntohs(readw(streamer_mmio + LAPDINC));
+
+			if (buffer_len > 256)
+				break;
+
+			i = 0;
+			while (i < buffer_len) {
+				rx_word = readw(streamer_mmio + LAPDINC);
+				frame_data[i] = rx_word & 0xff;
+				frame_data[i + 1] = (rx_word >> 8) & 0xff;
+				i += 2;
+			}
+
+			memcpy_fromio(skb_put(mac_frame, buffer_len),
+				      frame_data, buffer_len);
+		} while (next_ptr && (buff_off = next_ptr));
+
+#if STREAMER_NETWORK_MONITOR
+		printk(KERN_WARNING "%s: Received MAC Frame, details: \n",
+		       dev->name);
+		mac_hdr = (struct trh_hdr *) mac_frame->data;
+		printk(KERN_WARNING
+		       "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
+		       dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1],
+		       mac_hdr->daddr[2], mac_hdr->daddr[3],
+		       mac_hdr->daddr[4], mac_hdr->daddr[5]);
+		printk(KERN_WARNING
+		       "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
+		       dev->name, mac_hdr->saddr[0], mac_hdr->saddr[1],
+		       mac_hdr->saddr[2], mac_hdr->saddr[3],
+		       mac_hdr->saddr[4], mac_hdr->saddr[5]);
+#endif
+		mac_frame->dev = dev;
+		mac_frame->protocol = tr_type_trans(mac_frame, dev);
+		netif_rx(mac_frame);
+
+		/* Now tell the card we have dealt with the received frame */
+
+		/* Set LISR Bit 1 */
+		writel(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
+
+		/* Is the ASB free ? */
+
+		if (!(readl(streamer_priv->streamer_mmio + SISR) & SISR_ASB_FREE)) 
+		{
+			streamer_priv->asb_queued = 1;
+			writel(LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+			return;
+			/* Drop out and wait for the bottom half to be run */
+		}
+
+
+		writew(streamer_priv->asb, streamer_mmio + LAPA);
+		writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC);	/* Receive data */
+		writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);	/* Necessary ?? */
+		writew(0, streamer_mmio + LAPDINC);
+		writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+
+		writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+
+		streamer_priv->asb_queued = 2;
+		return;
+
+	} else if (arb_word == ARB_LAN_CHANGE_STATUS) {	/* Lan.change.status */
+		writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
+		lan_status = ntohs(readw(streamer_mmio + LAPDINC));
+		fdx_prot_error = readw(streamer_mmio + LAPD) & 0xFF;
+
+		/* Issue ARB Free */
+		writew(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
+
+		lan_status_diff = streamer_priv->streamer_lan_status ^ lan_status;
+
+		if (lan_status_diff & (LSC_LWF | LSC_ARW | LSC_FPE | LSC_RR)) 
+		{
+			if (lan_status_diff & LSC_LWF)
+				printk(KERN_WARNING "%s: Short circuit detected on the lobe\n", dev->name);
+			if (lan_status_diff & LSC_ARW)
+				printk(KERN_WARNING "%s: Auto removal error\n", dev->name);
+			if (lan_status_diff & LSC_FPE)
+				printk(KERN_WARNING "%s: FDX Protocol Error\n", dev->name);
+			if (lan_status_diff & LSC_RR)
+				printk(KERN_WARNING "%s: Force remove MAC frame received\n", dev->name);
+
+			/* Adapter has been closed by the hardware */
+
+			/* reset tx/rx fifo's and busmaster logic */
+
+			/* @TBD. no llc reset on autostreamer writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL);
+			   udelay(1);
+			   writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL); */
+			netif_stop_queue(dev);
+			free_irq(dev->irq, dev);
+
+			printk(KERN_WARNING "%s: Adapter has been closed \n", dev->name);
+
+		}
+		/* If serious error */
+		if (streamer_priv->streamer_message_level) {
+			if (lan_status_diff & LSC_SIG_LOSS)
+				printk(KERN_WARNING "%s: No receive signal detected \n", dev->name);
+			if (lan_status_diff & LSC_HARD_ERR) 
+				printk(KERN_INFO "%s: Beaconing \n", dev->name);
+			if (lan_status_diff & LSC_SOFT_ERR)
+				printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame \n", dev->name);
+			if (lan_status_diff & LSC_TRAN_BCN)
+				printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n", dev->name);
+			if (lan_status_diff & LSC_SS)
+				printk(KERN_INFO "%s: Single Station on the ring \n", dev->name);
+			if (lan_status_diff & LSC_RING_REC)
+				printk(KERN_INFO "%s: Ring recovery ongoing\n", dev->name);
+			if (lan_status_diff & LSC_FDX_MODE)
+				printk(KERN_INFO "%s: Operating in FDX mode\n", dev->name);
+		}
+
+		if (lan_status_diff & LSC_CO) {
+			if (streamer_priv->streamer_message_level)
+				printk(KERN_INFO "%s: Counter Overflow \n", dev->name);
+
+			/* Issue READ.LOG command */
+
+			writew(streamer_priv->srb, streamer_mmio + LAPA);
+			writew(SRB_READ_LOG, streamer_mmio + LAPDINC);
+			writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+			writew(0, streamer_mmio + LAPDINC);
+			streamer_priv->srb_queued = 2;	/* Can't sleep, use srb_bh */
+
+			writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+		}
+
+		if (lan_status_diff & LSC_SR_CO) {
+			if (streamer_priv->streamer_message_level)
+				printk(KERN_INFO "%s: Source routing counters overflow\n", dev->name);
+
+			/* Issue a READ.SR.COUNTERS */
+			writew(streamer_priv->srb, streamer_mmio + LAPA);
+			writew(SRB_READ_SR_COUNTERS,
+			       streamer_mmio + LAPDINC);
+			writew(STREAMER_CLEAR_RET_CODE,
+			       streamer_mmio + LAPDINC);
+			streamer_priv->srb_queued = 2;	/* Can't sleep, use srb_bh */
+			writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+		}
+		streamer_priv->streamer_lan_status = lan_status;
+	} /* Lan.change.status */
+	else
+		printk(KERN_WARNING "%s: Unknown arb command \n", dev->name);
+}
+
+static void streamer_asb_bh(struct net_device *dev)
+{
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+	if (streamer_priv->asb_queued == 1) 
+	{
+		/* Dropped through the first time */
+
+		writew(streamer_priv->asb, streamer_mmio + LAPA);
+		writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC);	/* Receive data */
+		writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);	/* Necessary ?? */
+		writew(0, streamer_mmio + LAPDINC);
+		writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+
+		writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+		streamer_priv->asb_queued = 2;
+
+		return;
+	}
+
+	if (streamer_priv->asb_queued == 2) {
+		__u8 rc;
+		writew(streamer_priv->asb + 2, streamer_mmio + LAPA);
+		rc = readw(streamer_mmio + LAPD) & 0xff;
+		switch (rc) {
+		case 0x01:
+			printk(KERN_WARNING "%s: Unrecognized command code \n", dev->name);
+			break;
+		case 0x26:
+			printk(KERN_WARNING "%s: Unrecognized buffer address \n", dev->name);
+			break;
+		case 0xFF:
+			/* Valid response, everything should be ok again */
+			break;
+		default:
+			printk(KERN_WARNING "%s: Invalid return code in asb\n", dev->name);
+			break;
+		}
+	}
+	streamer_priv->asb_queued = 0;
+}
+
+static int streamer_change_mtu(struct net_device *dev, int mtu)
+{
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u16 max_mtu;
+
+	if (streamer_priv->streamer_ring_speed == 4)
+		max_mtu = 4500;
+	else
+		max_mtu = 18000;
+
+	if (mtu > max_mtu)
+		return -EINVAL;
+	if (mtu < 100)
+		return -EINVAL;
+
+	dev->mtu = mtu;
+	streamer_priv->pkt_buf_sz = mtu + TR_HLEN;
+
+	return 0;
+}
+
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+static int streamer_proc_info(char *buffer, char **start, off_t offset,
+			      int length, int *eof, void *data)
+{
+	struct pci_dev *pci_device = NULL;
+	int len = 0;
+	off_t begin = 0;
+	off_t pos = 0;
+	int size;
+
+	struct net_device *dev;
+
+
+	size = sprintf(buffer, "IBM LanStreamer/MPC Chipset Token Ring Adapters\n");
+
+	pos += size;
+	len += size;
+
+	while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device))) 
+	{
+
+		for (dev = dev_base; dev != NULL; dev = dev->next) 
+		{
+			if (dev->base_addr == (pci_device->base_address[0] & (~3))) 
+			{	/* Yep, a Streamer device */
+				size = sprintf_info(buffer + len, dev);
+				len += size;
+				pos = begin + len;
+
+				if (pos < offset) {
+					len = 0;
+					begin = pos;
+				}
+				if (pos > offset + length)
+					break;
+			}	/* if */
+		}		/* for */
+	}			/* While */
+
+	*start = buffer + (offset - begin);	/* Start of wanted data */
+	len -= (offset - begin);	/* Start slop */
+	if (len > length)
+		len = length;	/* Ending slop */
+	return len;
+}
+
+static int sprintf_info(char *buffer, struct net_device *dev)
+{
+	struct streamer_private *streamer_priv =
+	    (struct streamer_private *) dev->priv;
+	__u8 *streamer_mmio = streamer_priv->streamer_mmio;
+	struct streamer_adapter_addr_table sat;
+	struct streamer_parameters_table spt;
+	int size = 0;
+	int i;
+
+	writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+	for (i = 0; i < 14; i += 2) {
+		__u16 io_word;
+		__u8 *datap = (__u8 *) & sat;
+		io_word = readw(streamer_mmio + LAPDINC);
+		datap[size] = io_word & 0xff;
+		datap[size + 1] = (io_word >> 8) & 0xff;
+	}
+	writew(streamer_priv->streamer_parms_addr, streamer_mmio + LAPA);
+	for (i = 0; i < 68; i += 2) {
+		__u16 io_word;
+		__u8 *datap = (__u8 *) & spt;
+		io_word = readw(streamer_mmio + LAPDINC);
+		datap[size] = io_word & 0xff;
+		datap[size + 1] = (io_word >> 8) & 0xff;
+	}
+
+
+	size = sprintf(buffer, "\n%6s: Adapter Address   : Node Address      : Functional Addr\n", dev->name);
+
+	size += sprintf(buffer + size,
+		    "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x\n",
+		    dev->name, dev->dev_addr[0], dev->dev_addr[1],
+		    dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4],
+		    dev->dev_addr[5], sat.node_addr[0], sat.node_addr[1],
+		    sat.node_addr[2], sat.node_addr[3], sat.node_addr[4],
+		    sat.node_addr[5], sat.func_addr[0], sat.func_addr[1],
+		    sat.func_addr[2], sat.func_addr[3]);
+
+	size += sprintf(buffer + size, "\n%6s: Token Ring Parameters Table:\n", dev->name);
+
+	size += sprintf(buffer + size, "%6s: Physical Addr : Up Node Address   : Poll Address      : AccPri : Auth Src : Att Code :\n", dev->name);
+
+	size += sprintf(buffer + size,
+		    "%6s: %02x:%02x:%02x:%02x   : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %04x   : %04x     :  %04x    :\n",
+		    dev->name, spt.phys_addr[0], spt.phys_addr[1],
+		    spt.phys_addr[2], spt.phys_addr[3],
+		    spt.up_node_addr[0], spt.up_node_addr[1],
+		    spt.up_node_addr[2], spt.up_node_addr[3],
+		    spt.up_node_addr[4], spt.up_node_addr[4],
+		    spt.poll_addr[0], spt.poll_addr[1], spt.poll_addr[2],
+		    spt.poll_addr[3], spt.poll_addr[4], spt.poll_addr[5],
+		    ntohs(spt.acc_priority), ntohs(spt.auth_source_class),
+		    ntohs(spt.att_code));
+
+	size += sprintf(buffer + size, "%6s: Source Address    : Bcn T : Maj. V : Lan St : Lcl Rg : Mon Err : Frame Correl : \n", dev->name);
+
+	size += sprintf(buffer + size,
+		    "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %04x  : %04x   : %04x   : %04x   : %04x    :     %04x     : \n",
+		    dev->name, spt.source_addr[0], spt.source_addr[1],
+		    spt.source_addr[2], spt.source_addr[3],
+		    spt.source_addr[4], spt.source_addr[5],
+		    ntohs(spt.beacon_type), ntohs(spt.major_vector),
+		    ntohs(spt.lan_status), ntohs(spt.local_ring),
+		    ntohs(spt.mon_error), ntohs(spt.frame_correl));
+
+	size += sprintf(buffer + size, "%6s: Beacon Details :  Tx  :  Rx  : NAUN Node Address : NAUN Node Phys : \n",
+		    dev->name);
+
+	size += sprintf(buffer + size,
+		    "%6s:                :  %02x  :  %02x  : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x    : \n",
+		    dev->name, ntohs(spt.beacon_transmit),
+		    ntohs(spt.beacon_receive), spt.beacon_naun[0],
+		    spt.beacon_naun[1], spt.beacon_naun[2],
+		    spt.beacon_naun[3], spt.beacon_naun[4],
+		    spt.beacon_naun[5], spt.beacon_phys[0],
+		    spt.beacon_phys[1], spt.beacon_phys[2],
+		    spt.beacon_phys[3]);
+	return size;
+}
+#endif
+#endif
+
+#ifdef MODULE
+
+static struct net_device *dev_streamer[STREAMER_MAX_ADAPTERS];
+
+int init_module(void)
+{
+	int i;
+
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+
+	ent = create_proc_entry("net/streamer_tr", 0, 0);
+	ent->read_proc = &streamer_proc_info;
+#endif
+#endif
+	for (i = 0; (i < STREAMER_MAX_ADAPTERS); i++) 
+	{
+		dev_streamer[i] = NULL;
+		dev_streamer[i] = init_trdev(dev_streamer[i], 0);
+		if (dev_streamer[i] == NULL)
+			return -ENOMEM;
+
+		dev_streamer[i]->init = &streamer_probe;
+
+		if (register_trdev(dev_streamer[i]) != 0) {
+			kfree_s(dev_streamer[i], sizeof(struct net_device));
+			dev_streamer[i] = NULL;
+			if (i == 0) 
+			{
+				printk(KERN_INFO "Streamer: No IBM LanStreamer PCI Token Ring cards found in system.\n");
+				return -EIO;
+			} else {
+				printk(KERN_INFO "Streamer: %d IBM LanStreamer PCI Token Ring card(s) found in system.\n", i);
+				return 0;
+			}
+		}
+	}
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+	for (i = 0; i < STREAMER_MAX_ADAPTERS; i++)
+		if (dev_streamer[i]) {
+			unregister_trdev(dev_streamer[i]);
+			release_region(dev_streamer[i]->base_addr, STREAMER_IO_SPACE);
+			kfree_s(dev_streamer[i]->priv, sizeof(struct streamer_private));
+			kfree_s(dev_streamer[i], sizeof(struct net_device));
+			dev_streamer[i] = NULL;
+		}
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("net/streamer_tr", NULL);
+#endif
+#endif
+}
+#endif				/* MODULE */
diff -u --recursive --new-file v2.3.51/linux/drivers/net/tokenring/lanstreamer.h linux/drivers/net/tokenring/lanstreamer.h
--- v2.3.51/linux/drivers/net/tokenring/lanstreamer.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/tokenring/lanstreamer.h	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,319 @@
+/*
+ *   lanstreamer.h -- driver for the IBM Auto LANStreamer PCI Adapter
+ *
+ *  Written By: Mike Sullivan, IBM Corporation
+ *
+ *  Copyright (C) 1999 IBM Corporation
+ *
+ *  Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC
+ *  chipset. 
+ *
+ *  This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic
+ *  chipsets) written  by:
+ *      1999 Peter De Schrijver All Rights Reserved
+ *	1999 Mike Phillips (phillim@amtrak.com)
+ *
+ *  Base Driver Skeleton:
+ *      Written 1993-94 by Donald Becker.
+ *
+ *      Copyright 1993 United States Government as represented by the
+ *      Director, National Security Agency.
+ *
+ * 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.                              
+ *                                                                           
+ * NO WARRANTY                                                               
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR        
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT      
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,      
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is    
+ * solely responsible for determining the appropriateness of using and       
+ * distributing the Program and assumes all risks associated with its        
+ * exercise of rights under this Agreement, including but not limited to     
+ * the risks and costs of program errors, damage to or loss of data,         
+ * programs or equipment, and unavailability or interruption of operations.  
+ *                                                                           
+ * DISCLAIMER OF LIABILITY                                                   
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY   
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND   
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR     
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED  
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES             
+ *                                                                           
+ * 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 
+ *                                                                           
+ * 
+ *  12/10/99 - Alpha Release 0.1.0
+ *            First release to the public
+ *
+ */
+
+#define BCTL 0x60
+#define BCTL_SOFTRESET (1<<15)
+
+#define GPR 0x4a
+#define GPR_AUTOSENSE (1<<2)
+#define GPR_16MBPS (1<<3)
+
+#define LISR 0x10
+#define LISR_SUM 0x12
+#define LISR_RUM 0x14
+
+#define LISR_LIE (1<<15)
+#define LISR_SLIM (1<<13)
+#define LISR_SLI (1<<12)
+#define LISR_BPEI (1<<9)
+#define LISR_BPE (1<<8)
+#define LISR_SRB_CMD (1<<5)
+#define LISR_ASB_REPLY (1<<4)
+#define LISR_ASB_FREE_REQ (1<<2)
+#define LISR_ARB_FREE (1<<1)
+#define LISR_TRB_FRAME (1<<0)
+
+#define SISR 0x16
+#define SISR_SUM 0x18
+#define SISR_RUM 0x1A
+#define SISR_MASK 0x54
+#define SISR_MASK_SUM 0x56
+#define SISR_MASK_RUM 0x58
+
+#define SISR_MI (1<<15)
+#define SISR_TIMER (1<<11)
+#define SISR_LAP_PAR_ERR (1<<10)
+#define SISR_LAP_ACC_ERR (1<<9)
+#define SISR_PAR_ERR (1<<8)
+#define SISR_ADAPTER_CHECK (1<<6)
+#define SISR_SRB_REPLY (1<<5)
+#define SISR_ASB_FREE (1<<4)
+#define SISR_ARB_CMD (1<<3)
+#define SISR_TRB_REPLY (1<<2)
+
+#define MISR_RUM 0x5A
+#define MISR_MASK 0x5C
+#define MISR_MASK_RUM 0x5E
+
+#define MISR_TX2_IDLE (1<<15)
+#define MISR_TX2_NO_STATUS (1<<14)
+#define MISR_TX2_HALT (1<<13)
+#define MISR_TX2_EOF (1<<12)
+#define MISR_TX1_IDLE (1<<11)
+#define MISR_TX1_NO_STATUS (1<<10)
+#define MISR_TX1_HALT (1<<9)
+#define MISR_TX1_EOF (1<<8)
+#define MISR_RX_NOBUF (1<<5)
+#define MISR_RX_EOB (1<<4)
+#define MISR_RX_NO_STATUS (1<<2)
+#define MISR_RX_HALT (1<<1)
+#define MISR_RX_EOF (1<<0)
+
+#define LAPA 0x62
+#define LAPE 0x64
+#define LAPD 0x66
+#define LAPDINC 0x68
+#define LAPWWO 0x6A
+#define LAPWWC 0x6C
+#define LAPCTL 0x6E
+
+#define TIMER 0x4E4
+
+#define BMCTL_SUM 0x50
+#define BMCTL_RUM 0x52
+#define BMCTL_TX1_DIS (1<<14)
+#define BMCTL_TX2_DIS (1<<10)
+#define BMCTL_RX_DIS (1<<6)
+
+#define RXLBDA  0x90
+#define RXBDA   0x94
+#define RXSTAT  0x98
+#define RXDBA   0x9C
+
+#define TX1LFDA 0xA0
+#define TX1FDA  0xA4
+#define TX1STAT 0xA8
+#define TX1DBA  0xAC
+#define TX2LFDA 0xB0
+#define TX2FDA  0xB4
+#define TX2STAT 0xB8
+#define TX2DBA  0xBC
+
+#define STREAMER_IO_SPACE 256
+
+#define SRB_COMMAND_SIZE 50
+
+#define STREAMER_MAX_ADAPTERS 8	/* 0x08 __MODULE_STRING can't hand 0xnn */
+
+/* Defines for LAN STATUS CHANGE reports */
+#define LSC_SIG_LOSS 0x8000
+#define LSC_HARD_ERR 0x4000
+#define LSC_SOFT_ERR 0x2000
+#define LSC_TRAN_BCN 0x1000
+#define LSC_LWF      0x0800
+#define LSC_ARW      0x0400
+#define LSC_FPE      0x0200
+#define LSC_RR       0x0100
+#define LSC_CO       0x0080
+#define LSC_SS       0x0040
+#define LSC_RING_REC 0x0020
+#define LSC_SR_CO    0x0010
+#define LSC_FDX_MODE 0x0004
+
+/* Defines for OPEN ADAPTER command */
+
+#define OPEN_ADAPTER_EXT_WRAP (1<<15)
+#define OPEN_ADAPTER_DIS_HARDEE (1<<14)
+#define OPEN_ADAPTER_DIS_SOFTERR (1<<13)
+#define OPEN_ADAPTER_PASS_ADC_MAC (1<<12)
+#define OPEN_ADAPTER_PASS_ATT_MAC (1<<11)
+#define OPEN_ADAPTER_ENABLE_EC (1<<10)
+#define OPEN_ADAPTER_CONTENDER (1<<8)
+#define OPEN_ADAPTER_PASS_BEACON (1<<7)
+#define OPEN_ADAPTER_ENABLE_FDX (1<<6)
+#define OPEN_ADAPTER_ENABLE_RPL (1<<5)
+#define OPEN_ADAPTER_INHIBIT_ETR (1<<4)
+#define OPEN_ADAPTER_INTERNAL_WRAP (1<<3)
+
+
+/* Defines for SRB Commands */
+#define SRB_CLOSE_ADAPTER 0x04
+#define SRB_CONFIGURE_BRIDGE 0x0c
+#define SRB_CONFIGURE_HP_CHANNEL 0x13
+#define SRB_MODIFY_BRIDGE_PARMS 0x15
+#define SRB_MODIFY_OPEN_OPTIONS 0x01
+#define SRB_MODIFY_RECEIVE_OPTIONS 0x17
+#define SRB_NO_OPERATION 0x00
+#define SRB_OPEN_ADAPTER 0x03
+#define SRB_READ_LOG 0x08
+#define SRB_READ_SR_COUNTERS 0x16
+#define SRB_RESET_GROUP_ADDRESS 0x02
+#define SRB_RESET_TARGET_SEGMETN 0x14
+#define SRB_SAVE_CONFIGURATION 0x1b
+#define SRB_SET_BRIDGE_PARMS 0x09
+#define SRB_SET_FUNC_ADDRESS 0x07
+#define SRB_SET_GROUP_ADDRESS 0x06
+#define SRB_SET_TARGET_SEGMENT 0x05
+
+/* Clear return code */
+#define STREAMER_CLEAR_RET_CODE 0xfe
+
+/* ARB Commands */
+#define ARB_RECEIVE_DATA 0x81
+#define ARB_LAN_CHANGE_STATUS 0x84
+
+/* ASB Response commands */
+#define ASB_RECEIVE_DATA 0x81
+
+
+/* Streamer defaults for buffers */
+
+#define STREAMER_RX_RING_SIZE 16	/* should be a power of 2 */
+#define STREAMER_TX_RING_SIZE 8	/* should be a power of 2 */
+
+#define PKT_BUF_SZ 4096		/* Default packet size */
+
+/* Streamer data structures */
+
+struct streamer_tx_desc {
+	__u32 forward;
+	__u32 status;
+	__u32 bufcnt_framelen;
+	__u32 buffer;
+	__u32 buflen;
+	__u32 rsvd1;
+	__u32 rsvd2;
+	__u32 rsvd3;
+};
+
+struct streamer_rx_desc {
+	__u32 forward;
+	__u32 status;
+	__u32 buffer;
+	__u32 framelen_buflen;
+};
+
+struct mac_receive_buffer {
+	__u16 next;
+	__u8 padding;
+	__u8 frame_status;
+	__u16 buffer_length;
+	__u8 frame_data;
+};
+
+struct streamer_private {
+
+	__u16 srb;
+	__u16 trb;
+	__u16 arb;
+	__u16 asb;
+
+	__u8 *streamer_mmio;
+
+	volatile int srb_queued;	/* True if an SRB is still posted */
+	wait_queue_head_t srb_wait;
+
+	volatile int asb_queued;	/* True if an ASB is posted */
+
+	volatile int trb_queued;	/* True if a TRB is posted */
+	wait_queue_head_t  trb_wait;
+
+	struct streamer_rx_desc streamer_rx_ring[STREAMER_RX_RING_SIZE];
+	struct streamer_tx_desc streamer_tx_ring[STREAMER_TX_RING_SIZE];
+	struct sk_buff *tx_ring_skb[STREAMER_TX_RING_SIZE],
+	    *rx_ring_skb[STREAMER_RX_RING_SIZE];
+	int tx_ring_free, tx_ring_last_status, rx_ring_last_received,
+	    free_tx_ring_entries;
+
+	struct net_device_stats streamer_stats;
+	__u16 streamer_lan_status;
+	__u8 streamer_ring_speed;
+	__u16 pkt_buf_sz;
+	__u8 streamer_receive_options, streamer_copy_all_options,
+	    streamer_message_level;
+	__u8 streamer_multicast_set;
+	__u16 streamer_addr_table_addr, streamer_parms_addr;
+	__u16 mac_rx_buffer;
+	__u8 streamer_laa[6];
+};
+
+struct streamer_adapter_addr_table {
+
+	__u8 node_addr[6];
+	__u8 reserved[4];
+	__u8 func_addr[4];
+};
+
+struct streamer_parameters_table {
+
+	__u8 phys_addr[4];
+	__u8 up_node_addr[6];
+	__u8 up_phys_addr[4];
+	__u8 poll_addr[6];
+	__u16 reserved;
+	__u16 acc_priority;
+	__u16 auth_source_class;
+	__u16 att_code;
+	__u8 source_addr[6];
+	__u16 beacon_type;
+	__u16 major_vector;
+	__u16 lan_status;
+	__u16 soft_error_time;
+	__u16 reserved1;
+	__u16 local_ring;
+	__u16 mon_error;
+	__u16 beacon_transmit;
+	__u16 beacon_receive;
+	__u16 frame_correl;
+	__u8 beacon_naun[6];
+	__u32 reserved2;
+	__u8 beacon_phys[4];
+};
diff -u --recursive --new-file v2.3.51/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- v2.3.51/linux/drivers/net/via-rhine.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/net/via-rhine.c	Mon Mar 13 09:50:16 2000
@@ -1105,7 +1105,7 @@
 		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
 		return 0;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
 		return 0;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/Config.in linux/drivers/net/wan/Config.in
--- v2.3.51/linux/drivers/net/wan/Config.in	Fri Mar 10 16:40:43 2000
+++ linux/drivers/net/wan/Config.in	Mon Mar 13 09:43:37 2000
@@ -16,6 +16,25 @@
 
 	dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m
 
+	#
+	# COMX drivers
+	#
+
+	tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX
+	if [ "$CONFIG_COMX" != "n" ]; then
+		dep_tristate '  Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX
+		dep_tristate '  Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX
+		dep_tristate '  Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX
+		dep_tristate '  Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX
+		if [ "$CONFIG_LAPB" = "y" ]; then
+			dep_tristate '  Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX
+		fi
+		if [ "$CONFIG_LAPB" = "m" ]; then
+			dep_tristate '  Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB
+		fi
+		dep_tristate '  Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX
+	fi
+
 	# There is no way to detect a Sealevel board. Force it modular
 
 	dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/Makefile linux/drivers/net/wan/Makefile
--- v2.3.51/linux/drivers/net/wan/Makefile	Fri Jan 28 15:09:07 2000
+++ linux/drivers/net/wan/Makefile	Mon Mar 13 09:43:37 2000
@@ -49,6 +49,58 @@
   endif
 endif
 
+ifeq ($(CONFIG_COMX_HW_COMX),y)
+L_OBJS += comx-hw-comx.o
+else 
+  ifeq ($(CONFIG_COMX_HW_COMX),m)
+  M_OBJS += comx-hw-comx.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_LOCOMX),y)
+L_OBJS += comx-hw-locomx.o
+CONFIG_85230_BUILTIN=y
+else 
+  ifeq ($(CONFIG_COMX_HW_LOCOMX),m)
+  M_OBJS += comx-hw-locomx.o
+  CONFIG_85230_MODULE=y
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_MIXCOM),y)
+L_OBJS += comx-hw-mixcom.o
+else 
+  ifeq ($(CONFIG_COMX_HW_MIXCOM),m)
+  M_OBJS += comx-hw-mixcom.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_PPP),y)
+L_OBJS += comx-proto-ppp.o
+CONFIG_SYNCPPP_BUILTIN = y
+else 
+  ifeq ($(CONFIG_COMX_PROTO_PPP),m)
+  M_OBJS += comx-proto-ppp.o
+  CONFIG_SYNCPPP_MODULE = y
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_LAPB),y)
+L_OBJS += comx-proto-lapb.o
+else 
+  ifeq ($(CONFIG_COMX_PROTO_LAPB),m)
+  M_OBJS += comx-proto-lapb.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_FR),y)
+L_OBJS += comx-proto-fr.o
+else 
+  ifeq ($(CONFIG_COMX_PROTO_FR),m)
+  M_OBJS += comx-proto-fr.o
+  endif
+endif
+
 ifeq ($(CONFIG_COSA),y)
 L_OBJS += cosa.o
 CONFIG_SYNCPPP_BUILTIN = y
@@ -180,8 +232,8 @@
 	rm -f core *.o *.a *.s
 
 wanpipe.o: $(WANPIPE_OBJS)
-	ld -r -o $@ $(WANPIPE_OBJS)
+	$(LD) -r -o $@ $(WANPIPE_OBJS)
 
 cyclomx.o: $(CYCLOMX_OBJS)
-	ld -r -o $@ $(CYCLOMX_OBJS)
+	$(LD) -r -o $@ $(CYCLOMX_OBJS)
 
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx-hw-comx.c linux/drivers/net/wan/comx-hw-comx.c
--- v2.3.51/linux/drivers/net/wan/comx-hw-comx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comx-hw-comx.c	Mon Mar 13 13:55:09 2000
@@ -0,0 +1,1426 @@
+/*
+ * Hardware-level driver for the COMX and HICOMX cards
+ * for Linux kernel 2.2.X
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Rewritten by: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.80 (99/06/11):
+ *		- port back to kernel, add support builtin driver 
+ *		- cleaned up the source code a bit
+ *
+ * Version 0.81 (99/06/22):
+ *		- cleaned up the board load functions, no more long reset
+ *		  timeouts
+ *		- lower modem lines on close
+ *		- some interrupt handling fixes
+ *
+ * Version 0.82 (99/08/24):
+ *		- fix multiple board support
+ *
+ * Version 0.83 (99/11/30):
+ *		- interrupt handling and locking fixes during initalization
+ *		- really fix multiple board support
+ * 
+ * Version 0.84 (99/12/02):
+ *		- some workarounds for problematic hardware/firmware
+ *
+ * Version 0.85 (00/01/14):
+ *		- some additional workarounds :/
+ *		- printk cleanups
+ */
+
+#define VERSION "0.85"
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
+MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
+
+#define	COMX_readw(dev, offset)	(readw(dev->mem_start + offset + \
+	(unsigned int)(((struct comx_privdata *)\
+	((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+	* COMX_CHANNEL_OFFSET))
+
+#define COMX_WRITE(dev, offset, value)	(writew(value, dev->mem_start + offset \
+	+ (unsigned int)(((struct comx_privdata *) \
+	((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+	* COMX_CHANNEL_OFFSET))
+
+#define COMX_CMD(dev, cmd)	(COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
+
+struct comx_firmware {
+	int	len;
+	unsigned char *data;
+};
+
+struct comx_privdata {
+	struct comx_firmware *firmware;
+	u16	clock;
+	char	channel;		// channel no.
+	int	memory_size;
+	short	io_extent;
+	u_long	histogram[5];
+};
+
+static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
+extern struct comx_hardware hicomx_hw;
+extern struct comx_hardware comx_hw;
+extern struct comx_hardware cmx_hw;
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void COMX_board_on(struct net_device *dev)
+{
+	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 
+	    COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_board_off(struct net_device *dev)
+{
+	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 
+	   COMX_ENABLE_BOARD_IT), dev->base_addr);
+}
+
+static void HICOMX_board_on(struct net_device *dev)
+{
+	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 
+	   HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void HICOMX_board_off(struct net_device *dev)
+{
+	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 
+	   HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_set_clock(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+
+	COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
+}
+
+static struct net_device *COMX_access_board(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct net_device *ret;
+	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+	unsigned long flags;
+
+
+	save_flags(flags); cli();
+	
+	ret = memory_used[mempos];
+
+	if(ret == dev) {
+		goto out;
+	}
+
+	memory_used[mempos] = dev;
+
+	if (!ch->twin || ret != ch->twin) {
+		if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret);
+		ch->HW_board_on(dev);
+	}
+out:
+	restore_flags(flags);
+	return ret;
+}
+
+static void COMX_release_board(struct net_device *dev, struct net_device *savep)
+{
+	unsigned long flags;
+	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+	struct comx_channel *ch = dev->priv;
+
+	save_flags(flags); cli();
+
+	if (memory_used[mempos] == savep) {
+		goto out;
+	}
+
+	memory_used[mempos] = savep;
+	if (!ch->twin || ch->twin != savep) {
+		ch->HW_board_off(dev);
+		if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep);
+	}
+out:
+	restore_flags(flags);
+}
+
+static int COMX_txe(struct net_device *dev) 
+{
+	struct net_device *savep;
+	struct comx_channel *ch = dev->priv;
+	int rc = 0;
+
+	savep = ch->HW_access_board(dev);
+	if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {
+		rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);
+	} 
+	ch->HW_release_board(dev,savep);
+	if(rc==0xffff) {
+		printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc);
+	}
+	return rc;
+}
+
+static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+	struct net_device *savep;
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	int ret = FRAME_DROPPED;
+	word tmp;
+
+	savep = ch->HW_access_board(dev);	
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
+	}
+
+	if (skb->len > COMX_MAX_TX_SIZE) {
+		ret=FRAME_DROPPED;
+		goto out;
+	}
+
+	tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+	if ((ch->line_status & LINE_UP) && tmp==1) {
+		int lensave = skb->len;
+		int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);
+		word *data = (word *)skb->data;
+
+		if(dest==0xffff) {
+			printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest);
+			ret=FRAME_DROPPED;
+			goto out;
+		}
+					
+		writew((unsigned short)skb->len, dev->mem_start + dest);
+		dest += 2;
+		while (skb->len > 1) {
+			writew(*data++, dev->mem_start + dest);
+			dest += 2; skb->len -= 2;
+		}
+		if (skb->len == 1) {
+			writew(*((byte *)data), dev->mem_start + dest);
+		}
+		writew(0, dev->mem_start + (int)hw->channel * 
+		   COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);
+		ch->stats.tx_packets++;	
+		ch->stats.tx_bytes += lensave; 
+		ret = FRAME_ACCEPTED;
+	} else {
+		ch->stats.tx_dropped++;
+		printk(KERN_INFO "%s: frame dropped\n",dev->name);
+		if(tmp) {
+			printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp);
+		}
+	}
+	
+out:
+	ch->HW_release_board(dev, savep);
+	dev_kfree_skb(skb);
+	return ret;
+}
+
+static inline int comx_read_buffer(struct net_device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+	word rbuf_offs;
+	struct sk_buff *skb;
+	word len;
+	int i=0;
+	word *writeptr;
+
+	i = 0;
+	rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);
+	if(rbuf_offs == 0xffff) {
+		printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs);
+		return 0;
+	}
+	len = readw(dev->mem_start + rbuf_offs);
+	if(len > COMX_MAX_RX_SIZE) {
+		printk(KERN_ERR "%s: packet length is %d\n",dev->name,len);
+		return 0;
+	}
+	if ((skb = dev_alloc_skb(len + 16)) == NULL) {
+		ch->stats.rx_dropped++;
+		COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+		return 0;
+	}
+	rbuf_offs += 2;
+	skb_reserve(skb, 16);
+	skb_put(skb, len);
+	skb->dev = dev;
+	writeptr = (word *)skb->data;
+	while (i < len) {
+		*writeptr++ = readw(dev->mem_start + rbuf_offs);
+		rbuf_offs += 2; 
+		i += 2;
+	}
+	COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+	ch->stats.rx_packets++;
+	ch->stats.rx_bytes += len;
+	if (ch->debug_flags & DEBUG_HW_RX) {
+		comx_debug_skb(dev, skb, "COMX_interrupt receiving");
+	}
+	ch->LINE_rx(dev, skb);
+	return 1;
+}
+
+static inline char comx_line_change(struct net_device *dev, char linestat)
+{
+	struct comx_channel *ch=dev->priv;
+	char idle=1;
+	
+	
+	if (linestat & LINE_UP) { /* Vonal fol */
+		if (ch->lineup_delay) {
+			if (!test_and_set_bit(0, &ch->lineup_pending)) {
+				ch->lineup_timer.function = comx_lineup_func;
+				ch->lineup_timer.data = (unsigned long)dev;
+				ch->lineup_timer.expires = jiffies +
+					HZ*ch->lineup_delay;
+				add_timer(&ch->lineup_timer);
+				idle=0;
+			}
+		} else {
+			idle=0;
+			ch->LINE_status(dev, ch->line_status |= LINE_UP);
+		}
+	} else { /* Vonal le */
+		idle=0;
+		if (test_and_clear_bit(0, &ch->lineup_pending)) {
+			del_timer(&ch->lineup_timer);
+		} else {
+			ch->line_status &= ~LINE_UP;
+			if (ch->LINE_status) {
+				ch->LINE_status(dev, ch->line_status);
+			}
+		}
+	}
+	return idle;
+}
+
+
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct net_device *interrupted;
+	unsigned long jiffs;
+	char idle = 0;
+	int count = 0;
+	word tmp;
+
+	if (dev == NULL) {
+		printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
+		return;
+	}
+
+	jiffs = jiffies;
+
+	interrupted = ch->HW_access_board(dev);
+
+	while (!idle && count < 5000) {
+		char channel = 0;
+		idle = 1;
+
+		while (channel < 2) {
+			char linestat = 0;
+			char buffers_emptied = 0;
+
+			if (channel == 1) {
+				if (ch->twin) {
+					dev = ch->twin;
+					ch = dev->priv;
+					hw = ch->HW_privdata;
+				} else {
+					break;
+				}
+			} else {
+				COMX_WRITE(dev, OFF_A_L1_REPENA, 
+				    COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
+			}
+			channel++;
+
+			if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != 
+			   (HW_OPEN | LINE_OPEN)) {
+				continue;
+			}
+	
+			/* Collect stats */
+			tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
+			COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
+			if(tmp==0xffff) {
+				printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp);
+				break;
+			} else {
+				ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
+				ch->stats.rx_over_errors += tmp & 0xff;
+			}
+			tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
+			COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
+			if(tmp==0xffff) {
+				printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp);
+				break;
+			} else {
+				ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
+				ch->stats.rx_missed_errors += tmp & 0xff;
+			}
+			
+			if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
+				tmp=COMX_readw(dev, OFF_A_L2_DAV); 
+				while (tmp==1) {
+					idle=0;
+					buffers_emptied+=comx_read_buffer(dev);
+					tmp=COMX_readw(dev, OFF_A_L2_DAV); 
+				}
+				if(tmp) {
+					printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp);
+					break;
+				}
+			}
+
+			tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+			if (tmp==1 && ch->LINE_tx) {
+				ch->LINE_tx(dev);
+			} 
+			if(tmp==0xffff) {
+				printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp);
+				break;
+			}
+
+			if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+				linestat &= ~LINE_UP;
+			} else {
+				linestat |= LINE_UP;
+			}
+
+			if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
+				ch->stats.tx_carrier_errors++;
+				idle &= comx_line_change(dev,linestat);
+			}
+				
+			hw->histogram[(int)buffers_emptied]++;
+		}
+		count++;
+	}
+
+	if(count==5000) {
+		printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
+	}
+
+	ch->HW_release_board(dev, interrupted);
+}
+
+static int COMX_open(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	unsigned long jiffs;
+	int twin_open=0;
+	int retval;
+	struct net_device *savep;
+
+	if (!dev->base_addr || !dev->irq || !dev->mem_start) {
+		return -ENODEV;
+	}
+
+	if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) {
+		twin_open=1;
+	}
+
+	if (!twin_open) {
+		if (check_region(dev->base_addr, hw->io_extent)) {
+			return -EAGAIN;
+		}
+		if (request_irq(dev->irq, COMX_interrupt, 0, dev->name, 
+		   (void *)dev)) {
+			printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
+			return -EAGAIN;
+		}
+		ch->init_status |= IRQ_ALLOCATED;
+		request_region(dev->base_addr, hw->io_extent, dev->name);
+		if (!ch->HW_load_board || ch->HW_load_board(dev)) {
+			ch->init_status &= ~IRQ_ALLOCATED;
+			retval=-ENODEV;
+			goto error;
+		}
+	}
+
+	savep = ch->HW_access_board(dev);
+	COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
+
+	if (ch->HW_set_clock) {
+		ch->HW_set_clock(dev);
+	}
+
+	COMX_CMD(dev, COMX_CMD_INIT); 
+	jiffs = jiffies;
+	while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) {
+		schedule_timeout(1);
+	}
+	
+	if (jiffies >= jiffs + HZ) {
+		printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name);
+		ch->HW_release_board(dev, savep);
+		retval=-EIO;
+		goto error;
+	}
+	udelay(1000);
+
+	COMX_CMD(dev, COMX_CMD_OPEN);
+
+	jiffs = jiffies;
+	while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && jiffies < jiffs + HZ) {
+		schedule_timeout(1);
+	}
+	
+	if (jiffies >= jiffs + HZ) {
+		printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name);
+		ch->HW_release_board(dev, savep);
+		retval=-EIO;
+		goto error;
+	}
+	
+	ch->init_status |= HW_OPEN;
+	
+	/* Ez eleg ciki, de ilyen a rendszer */
+	if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+		ch->line_status &= ~LINE_UP;
+	} else {
+		ch->line_status |= LINE_UP;
+	}
+	
+	if (ch->LINE_status) {
+		ch->LINE_status(dev, ch->line_status);
+	}
+
+	ch->HW_release_board(dev, savep);
+
+	for ( ; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IRQ) == 0 
+		    || strcmp(procfile->name, FILENAME_IO) == 0
+		    || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+		    || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+		    || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+		    || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+			procfile->mode = S_IFREG | 0444;
+		
+		}
+	}	
+	
+	return 0;	
+
+error:
+	if(!twin_open) {
+		release_region(dev->base_addr, hw->io_extent);
+		free_irq(dev->irq, (void *)dev);
+	}
+	return retval;
+
+}
+
+static int COMX_close(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct comx_channel *twin_ch;
+	struct net_device *savep;
+
+	savep = ch->HW_access_board(dev);
+
+	COMX_CMD(dev, COMX_CMD_CLOSE);
+	udelay(1000);
+	COMX_CMD(dev, COMX_CMD_EXIT);
+
+	ch->HW_release_board(dev, savep);
+
+	if (ch->init_status & IRQ_ALLOCATED) {
+		free_irq(dev->irq, (void *)dev);
+		ch->init_status &= ~IRQ_ALLOCATED;
+	}
+	release_region(dev->base_addr, hw->io_extent);
+
+	if (ch->twin && (twin_ch = ch->twin->priv) && 
+	    (twin_ch->init_status & HW_OPEN)) {
+		/* Pass the irq to the twin */
+		if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name, 
+		   (void *)ch->twin) == 0) {
+			twin_ch->init_status |= IRQ_ALLOCATED;
+		}
+	}
+
+	for ( ; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IRQ) == 0 
+		    || strcmp(procfile->name, FILENAME_IO) == 0
+		    || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+		    || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+		    || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+		    || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+			procfile->mode = S_IFREG | 0644;
+		}
+	}
+	
+	ch->init_status &= ~HW_OPEN;
+	return 0;
+}
+
+static int COMX_statistics(struct net_device *dev, char *page)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct net_device *savep;
+	int len = 0;
+
+	savep = ch->HW_access_board(dev);
+
+	len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, "
+		"MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, "
+		"TxEMPTY: %02x, TxBUFP: %02x\n",
+		(ch->init_status & HW_OPEN) ? "HW_OPEN" : "",
+		(ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "",
+		(ch->init_status & FW_LOADED) ? "FW_LOADED" : "",
+		(ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "",
+		COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff,
+		(COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff,
+		COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff,
+		COMX_readw(dev, OFF_A_L2_DAV) & 0xff,
+		COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff,
+		COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff,
+		COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff);
+
+	len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n"
+		"hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1],
+		hw->histogram[2],hw->histogram[3],hw->histogram[4]);
+
+	ch->HW_release_board(dev, savep);
+
+	return len;
+}
+
+static int COMX_load_board(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct comx_firmware *fw = hw->firmware;
+	word board_segment = dev->mem_start >> 16;
+	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+	unsigned long flags;
+	unsigned char id1, id2;
+	struct net_device *saved;
+	int retval;
+	int loopcount;
+	int len;
+	byte *COMX_address;
+
+	if (!fw || !fw->len) {
+		struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+		struct comx_privdata *twin_hw;
+
+		if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+			return -EAGAIN;
+		}
+
+		if (!(fw = twin_hw->firmware) || !fw->len) {
+			return -EAGAIN;
+		}
+	}
+
+	id1 = fw->data[OFF_FW_L1_ID]; 
+	id2 = fw->data[OFF_FW_L1_ID + 1];
+
+	if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
+		printk(KERN_ERR "%s: incorrect firmware, load aborted\n", 
+			dev->name);
+		return -EAGAIN;
+	}
+
+	printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name, 
+		(char *)(fw->data + OFF_FW_L1_ID + 2));
+
+	id1 = fw->data[OFF_FW_L2_ID]; 
+	id2 = fw->data[OFF_FW_L2_ID + 1];
+	if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+		printk(KERN_INFO "with Layer 2 code %s\n", 
+			(char *)(fw->data + OFF_FW_L2_ID + 2));
+	}
+
+	outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
+	/* 10 usec should be enough here */
+	udelay(100);
+
+	save_flags(flags); cli();
+	saved=memory_used[mempos];
+	if(saved) {
+		((struct comx_channel *)saved->priv)->HW_board_off(saved);
+	}
+	memory_used[mempos]=dev;
+
+	outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+	writeb(0, dev->mem_start + COMX_JAIL_OFFSET);	
+
+	loopcount=0;
+	while(loopcount++ < 10000 && 
+	    readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+		udelay(100);
+	}	
+	
+	if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+		printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n",
+			dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET));
+		retval=-ENODEV;
+		goto out;
+	}
+
+	writeb(0x55, dev->mem_start + 0x18ff);
+	
+	loopcount=0;
+	while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) {
+		udelay(100);
+	}
+
+	if(readb(dev->mem_start + 0x18ff) != 0) {
+		printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
+			dev->name);
+		retval=-ENODEV;
+		goto out;
+	}		
+
+	len = 0;
+	COMX_address = (byte *)dev->mem_start;
+	while (fw->len > len) {
+		writeb(fw->data[len++], COMX_address++);
+	}
+
+	len = 0;
+	COMX_address = (byte *)dev->mem_start;
+	while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+		len++;
+	}
+
+	if (len != fw->len) {
+		printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+			"instead of 0x%02x\n", dev->name, len, 
+			readb(COMX_address - 1), fw->data[len]);
+		retval=-EAGAIN;
+		goto out;
+	}
+
+	writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+	loopcount = 0;
+	while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+		udelay(100);
+	}
+
+	if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+		printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+			dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+		retval=-EAGAIN;
+		goto out;
+	}
+
+
+	ch->init_status |= FW_LOADED;
+	retval=0;
+
+out: 
+	outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+	if(saved) {
+		((struct comx_channel *)saved->priv)->HW_board_on(saved);
+	}
+	memory_used[mempos]=saved;
+	restore_flags(flags);
+	return retval;
+}
+
+static int CMX_load_board(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct comx_firmware *fw = hw->firmware;
+	word board_segment = dev->mem_start >> 16;
+	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+	#if 0
+	unsigned char id1, id2;
+	#endif
+	struct net_device *saved;
+	unsigned long flags;
+	int retval;
+	int loopcount;
+	int len;
+	byte *COMX_address;
+
+	if (!fw || !fw->len) {
+		struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+		struct comx_privdata *twin_hw;
+
+		if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+			return -EAGAIN;
+		}
+
+		if (!(fw = twin_hw->firmware) || !fw->len) {
+			return -EAGAIN;
+		}
+	}
+
+	/* Ide kell olyat tenni, hogy ellenorizze az ID-t */
+
+	if (inb_p(dev->base_addr) != CMX_ID_BYTE) {
+		printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name,
+			inb_p(dev->base_addr));
+		return -ENODEV;
+	}
+
+	printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name, 
+		(char *)(fw->data + OFF_FW_L1_ID + 2));
+
+	save_flags(flags); cli();
+	saved=memory_used[mempos];
+	if(saved) {
+		((struct comx_channel *)saved->priv)->HW_board_off(saved);
+	}
+	memory_used[mempos]=dev;
+	
+	outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET, 
+		dev->base_addr);
+
+	len = 0;
+	COMX_address = (byte *)dev->mem_start;
+	while (fw->len > len) {
+		writeb(fw->data[len++], COMX_address++);
+	}
+
+	len = 0;
+	COMX_address = (byte *)dev->mem_start;
+	while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+		len++;
+	}
+
+	outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+	if (len != fw->len) {
+		printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+			"instead of 0x%02x\n", dev->name, len, 
+			readb(COMX_address - 1), fw->data[len]);
+		retval=-EAGAIN;
+		goto out;
+	}
+
+	loopcount=0;
+	while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+		udelay(100);
+	}
+
+	if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+		printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+			dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+		retval=-EAGAIN;
+		goto out;
+	}
+
+	ch->init_status |= FW_LOADED;
+	retval=0;
+
+out: 
+	outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+	if(saved) {
+		((struct comx_channel *)saved->priv)->HW_board_on(saved);
+	}
+	memory_used[mempos]=saved;
+	restore_flags(flags);
+	return retval;
+}
+
+static int HICOMX_load_board(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	struct comx_firmware *fw = hw->firmware;
+	word board_segment = dev->mem_start >> 12;
+	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+	struct net_device *saved;
+	unsigned char id1, id2;
+	unsigned long flags;
+	int retval;
+	int loopcount;
+	int len;
+	word *HICOMX_address;
+	char id = 1;
+
+	if (!fw || !fw->len) {
+		struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+		struct comx_privdata *twin_hw;
+
+		if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+			return -EAGAIN;
+		}
+
+		if (!(fw = twin_hw->firmware) || !fw->len) {
+			return -EAGAIN;
+		}
+	}
+
+	while (id != 4) {
+		if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
+			break;
+		}
+	}
+
+	if (id != 4) {
+		printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n",
+			dev->name, (unsigned int)dev->base_addr, id - 1,
+			inb_p(dev->base_addr + id - 1));
+		return -1;	
+	}
+
+	id1 = fw->data[OFF_FW_L1_ID]; 
+	id2 = fw->data[OFF_FW_L1_ID + 1];
+	if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) {
+		printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name);
+		return -EAGAIN;
+	}
+
+	printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name, 
+		(char *)(fw->data + OFF_FW_L1_ID + 2));
+
+	id1 = fw->data[OFF_FW_L2_ID]; 
+	id2 = fw->data[OFF_FW_L2_ID + 1];
+	if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+		printk(KERN_INFO "with Layer 2 code %s\n", 
+			(char *)(fw->data + OFF_FW_L2_ID + 2));
+	}
+
+	outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+	udelay(10);	
+
+	save_flags(flags); cli();
+	saved=memory_used[mempos];
+	if(saved) {
+		((struct comx_channel *)saved->priv)->HW_board_off(saved);
+	}
+	memory_used[mempos]=dev;
+
+	outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+	outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
+
+	len = 0;
+	HICOMX_address = (word *)dev->mem_start;
+	while (fw->len > len) {
+		writeb(fw->data[len++], HICOMX_address++);
+	}
+
+	len = 0;
+	HICOMX_address = (word *)dev->mem_start;
+	while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
+		len++;
+	}
+
+	if (len != fw->len) {
+		printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+			"instead of 0x%02x\n", dev->name, len, 
+			readw(HICOMX_address - 1) & 0xff, fw->data[len]);
+		retval=-EAGAIN;
+		goto out;
+	}
+
+	outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+	outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+	outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+	loopcount=0;
+	while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+		udelay(100);
+	}
+
+	if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+		printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+			dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+		retval=-EAGAIN;
+		goto out;
+	}
+
+	ch->init_status |= FW_LOADED;
+	retval=0;
+
+out:
+	outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+	outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+	if(saved) {
+		((struct comx_channel *)saved->priv)->HW_board_on(saved);
+	}
+	memory_used[mempos]=saved;
+	restore_flags(flags);
+	return retval;
+}
+
+static struct net_device *comx_twin_check(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+	struct comx_privdata *hw = ch->HW_privdata;
+
+	struct net_device *twin;
+	struct comx_channel *ch_twin;
+	struct comx_privdata *hw_twin;
+
+
+	for ( ; procfile ; procfile = procfile->next) {
+	
+		if(!S_ISDIR(procfile->mode)) {
+			continue;
+		}
+	
+		twin=procfile->data;
+		ch_twin=twin->priv;
+		hw_twin=ch_twin->HW_privdata;
+
+
+		if (twin != dev && dev->irq && dev->base_addr && dev->mem_start &&
+		   dev->irq == twin->irq && dev->base_addr == twin->base_addr &&
+	  	   dev->mem_start == twin->mem_start &&
+		   hw->channel == (1 - hw_twin->channel) &&
+		   ch->hardware == ch_twin->hardware) {
+		   	return twin;
+		}
+	}
+	return NULL;
+}
+
+static int comxhw_write_proc(struct file *file, const char *buffer, 
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct net_device *dev = entry->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	char *page;
+
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if(ch->init_status & HW_OPEN) {
+		return -EAGAIN;	
+	}
+	
+	if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
+		if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+			return -ENOMEM;
+		}
+		copy_from_user(page, buffer, count = (min(count, PAGE_SIZE)));
+		if (*(page + count - 1) == '\n') {
+			*(page + count - 1) = 0;
+		}
+	} else {
+		byte *tmp;
+
+		if (!hw->firmware) {
+			if ((hw->firmware = kmalloc(sizeof(struct comx_firmware), 
+			    GFP_KERNEL)) == NULL) {
+			    	return -ENOMEM;
+			}
+			hw->firmware->len = 0;
+			hw->firmware->data = NULL;
+		}
+		
+		if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
+			return -ENOMEM;
+		}
+		
+		/* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */
+		if (hw->firmware && hw->firmware->len && file->f_pos 
+		    && hw->firmware->len < count + file->f_pos) {
+			memcpy(tmp, hw->firmware->data, hw->firmware->len);
+		}
+		if (hw->firmware->data) {
+			kfree(hw->firmware->data);
+		}
+		copy_from_user(tmp + file->f_pos, buffer, count);
+		hw->firmware->len = entry->size = file->f_pos + count;
+		hw->firmware->data = tmp;
+		file->f_pos += count;
+		return count;
+	}
+
+	if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+		hw->channel = simple_strtoul(page, NULL, 0);
+		if (hw->channel >= MAX_CHANNELNO) {
+			printk(KERN_ERR "Invalid channel number\n");
+			hw->channel = 0;
+		}
+		if ((ch->twin = comx_twin_check(dev)) != NULL) {
+			struct comx_channel *twin_ch = ch->twin->priv;
+			twin_ch->twin = dev;
+		}
+	} else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+		dev->irq = simple_strtoul(page, NULL, 0);
+		if (dev->irq == 2) {
+			dev->irq = 9;
+		}
+		if (dev->irq < 3 || dev->irq > 15) {
+			printk(KERN_ERR "comxhw: Invalid irq number\n");
+			dev->irq = 0;
+		}
+		if ((ch->twin = comx_twin_check(dev)) != NULL) {
+			struct comx_channel *twin_ch = ch->twin->priv;
+			twin_ch->twin = dev;
+		}
+	} else if (strcmp(entry->name, FILENAME_IO) == 0) {
+		dev->base_addr = simple_strtoul(page, NULL, 0);
+		if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300 
+		   || dev->base_addr > 0x3fc) {
+			printk(KERN_ERR "Invalid io value\n");
+			dev->base_addr = 0;
+		}
+		if ((ch->twin = comx_twin_check(dev)) != NULL) {
+			struct comx_channel *twin_ch = ch->twin->priv;
+
+			twin_ch->twin = dev;
+		}
+	} else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) {
+		dev->mem_start = simple_strtoul(page, NULL, 0);
+		if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) {
+			dev->mem_start *= 16;
+		}
+		if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN
+		    || dev->mem_start + hw->memory_size > COMX_MEM_MAX) {
+			printk(KERN_ERR "Invalid memory page\n");
+			dev->mem_start = 0;
+		}
+		dev->mem_end = dev->mem_start + hw->memory_size;
+		if ((ch->twin = comx_twin_check(dev)) != NULL) {
+			struct comx_channel *twin_ch = ch->twin->priv;
+
+			twin_ch->twin = dev;
+		}
+	} else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+		if (strncmp("ext", page, 3) == 0) {
+			hw->clock = 0;
+		} else {
+			int kbps;
+
+			kbps = simple_strtoul(page, NULL, 0);
+			hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
+		}
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static int comxhw_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct net_device *dev = file->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+	int len = 0;
+
+
+	if (strcmp(file->name, FILENAME_IO) == 0) {
+		len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr);
+	} else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+		len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq);
+	} else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+		len = sprintf(page, "%01d\n", hw->channel);
+	} else if (strcmp(file->name, FILENAME_MEMADDR) == 0) {
+		len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start);
+	} else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+		len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none");
+	} else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+		if (hw->clock) {
+			len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
+		} else {
+			len = sprintf(page, "external\n");
+		}
+	} else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
+		len = min(FILE_PAGESIZE, min(count, 
+			hw->firmware ? (hw->firmware->len - off) :  0));
+		if (len < 0) {
+			len = 0;
+		}
+		*start = hw->firmware ? (hw->firmware->data + off) : NULL;
+		if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
+			*eof = 1;
+		}
+		return len;
+	}	
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return(min(count, len - off));
+}
+
+/* Called on echo comx >boardtype */
+static int COMX_init(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw;
+	struct proc_dir_entry *new_file;
+
+	if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata), 
+	    GFP_KERNEL)) == NULL) {
+	    	return -ENOMEM;
+	}
+	memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
+
+	if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) {
+		hw->memory_size = COMX_MEMORY_SIZE;
+		hw->io_extent = COMX_IO_EXTENT;
+		dev->base_addr = COMX_DEFAULT_IO;
+		dev->irq = COMX_DEFAULT_IRQ;
+		dev->mem_start = COMX_DEFAULT_MEMADDR;
+		dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE;
+	} else if (ch->hardware == &hicomx_hw) {
+		hw->memory_size = HICOMX_MEMORY_SIZE;
+		hw->io_extent = HICOMX_IO_EXTENT;
+		dev->base_addr = HICOMX_DEFAULT_IO;
+		dev->irq = HICOMX_DEFAULT_IRQ;
+		dev->mem_start = HICOMX_DEFAULT_MEMADDR;
+		dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE;
+	} else {
+		printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+	}
+
+	if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
+	    == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->size = 6;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
+	    == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->size = 5;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->size = 2;		// Ezt tudjuk
+	new_file->nlink = 1;
+
+	if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+		if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+		   ch->procdir)) == NULL) {
+		   	return -EIO;
+		}
+		new_file->data = (void *)new_file;
+		new_file->read_proc = &comxhw_read_proc;
+		new_file->write_proc = &comxhw_write_proc;
+		new_file->proc_iops = &comx_normal_inode_ops;
+		new_file->size = 9;
+		new_file->nlink = 1;
+	}
+
+	if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->size = 8;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = NULL;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &comxhw_read_proc;
+	new_file->write_proc = &comxhw_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if (ch->hardware == &comx_hw) {
+		ch->HW_board_on = COMX_board_on;
+		ch->HW_board_off = COMX_board_off;
+		ch->HW_load_board = COMX_load_board;
+	} else if (ch->hardware == &cmx_hw) {
+		ch->HW_board_on = COMX_board_on;
+		ch->HW_board_off = COMX_board_off;
+		ch->HW_load_board = CMX_load_board;
+		ch->HW_set_clock = COMX_set_clock;
+	} else if (ch->hardware == &hicomx_hw) {
+		ch->HW_board_on = HICOMX_board_on;
+		ch->HW_board_off = HICOMX_board_off;
+		ch->HW_load_board = HICOMX_load_board;
+		ch->HW_set_clock = COMX_set_clock;
+	} else {
+		printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+	}
+
+	ch->HW_access_board = COMX_access_board;
+	ch->HW_release_board = COMX_release_board;
+	ch->HW_txe = COMX_txe;
+	ch->HW_open = COMX_open;
+	ch->HW_close = COMX_close;
+	ch->HW_send_packet = COMX_send_packet;
+	ch->HW_statistics = COMX_statistics;
+
+	if ((ch->twin = comx_twin_check(dev)) != NULL) {
+		struct comx_channel *twin_ch = ch->twin->priv;
+
+		twin_ch->twin = dev;
+	}
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* Called on echo valami >boardtype */
+static int COMX_exit(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_privdata *hw = ch->HW_privdata;
+
+	if (hw->firmware) {
+		if (hw->firmware->data) kfree(hw->firmware->data);
+		kfree(hw->firmware);
+	} if (ch->twin) {
+		struct comx_channel *twin_ch = ch->twin->priv;
+
+		twin_ch->twin = NULL;
+	}
+	
+	kfree(ch->HW_privdata);
+	remove_proc_entry(FILENAME_IO, ch->procdir);
+	remove_proc_entry(FILENAME_IRQ, ch->procdir);
+	remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+	remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
+	remove_proc_entry(FILENAME_FIRMWARE, ch->procdir);
+	remove_proc_entry(FILENAME_TWIN, ch->procdir);
+	if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+		remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+	}
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int COMX_dump(struct net_device *dev)
+{
+	printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
+	return 0;
+}
+
+static struct comx_hardware comx_hw = {
+	"comx",
+	VERSION,
+	COMX_init,
+	COMX_exit,
+	COMX_dump,
+	NULL
+};
+
+static struct comx_hardware cmx_hw = {
+	"cmx",
+	VERSION,
+	COMX_init,
+	COMX_exit,
+	COMX_dump,
+	NULL
+};
+
+static struct comx_hardware hicomx_hw = {
+	"hicomx",
+	VERSION,
+	COMX_init,
+	COMX_exit,
+	COMX_dump,
+	NULL
+};
+
+#ifdef MODULE
+#define comx_hw_comx_init init_module
+#endif
+
+int __init comx_hw_comx_init(void)
+{
+	comx_register_hardware(&comx_hw);
+	comx_register_hardware(&cmx_hw);
+	comx_register_hardware(&hicomx_hw);
+	memset(memory_used, 0, sizeof(memory_used));
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_hardware("comx");
+	comx_unregister_hardware("cmx");
+	comx_unregister_hardware("hicomx");
+}
+#endif
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx-hw-locomx.c linux/drivers/net/wan/comx-hw-locomx.c
--- v2.3.51/linux/drivers/net/wan/comx-hw-locomx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comx-hw-locomx.c	Mon Mar 13 13:55:09 2000
@@ -0,0 +1,496 @@
+/*
+ * Hardware driver for the LoCOMX card, using the generic z85230
+ * functions
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu> 
+ * and the hostess_sv11 driver
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.10 (99/06/17):
+ *		- rewritten for the z85230 layer
+ *
+ * Version 0.11 (99/06/21):
+ *		- some printk's fixed
+ *		- get rid of a memory leak (it was impossible though :))
+ * 
+ * Version 0.12 (99/07/07):
+ *		- check CTS for modem lines, not DCD (which is always high
+ *		  in case of this board)
+ * Version 0.13 (99/07/08):
+ *		- Fix the transmitter status check
+ *		- Handle the net device statistics better
+ */
+
+#define VERSION "0.13"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "z85230.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
+
+#define RX_DMA 3
+#define TX_DMA 1
+#define LOCOMX_ID 0x33
+#define LOCOMX_IO_EXTENT 8
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+u8 z8530_locomx[] = {
+	11,     TCRTxCP,
+	14,     DTRREQ,
+	255
+};
+
+struct locomx_data {
+	int	io_extent;
+	struct	z8530_dev board;
+	struct timer_list status_timer;
+};
+
+static int LOCOMX_txe(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct locomx_data *hw = ch->HW_privdata;
+
+	return (!hw->board.chanA.tx_next_skb);
+}
+
+
+static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb)
+{
+	struct net_device *dev=c->netdevice;
+	struct comx_channel *ch=dev->priv;
+	
+	if (ch->debug_flags & DEBUG_HW_RX) {
+		comx_debug_skb(dev, skb, "locomx_rx receiving");
+	}
+	ch->LINE_rx(dev,skb);
+}
+
+static int LOCOMX_send_packet(struct net_device *dev, struct sk_buff *skb) 
+{
+	struct comx_channel *ch = (struct comx_channel *)dev->priv;
+	struct locomx_data *hw = ch->HW_privdata;
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet");
+	}
+
+	if (!(ch->line_status & LINE_UP)) {
+		return FRAME_DROPPED;
+	}
+
+	if(z8530_queue_xmit(&hw->board.chanA,skb)) {
+		printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name);
+		return FRAME_DROPPED;
+	}
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name);
+	}
+
+	if(!hw->board.chanA.tx_next_skb) {
+		return FRAME_QUEUED;
+	} else {
+		return FRAME_ACCEPTED;
+	}
+}
+
+static void locomx_status_timerfun(unsigned long d)
+{
+	struct net_device *dev=(struct net_device *)d;
+	struct comx_channel *ch=dev->priv;
+	struct locomx_data *hw=ch->HW_privdata;
+
+	if(!(ch->line_status & LINE_UP) &&
+	    (hw->board.chanA.status & CTS)) {
+		ch->LINE_status(dev, ch->line_status | LINE_UP);
+	}
+	if((ch->line_status & LINE_UP) &&
+	    !(hw->board.chanA.status & CTS)) {
+		ch->LINE_status(dev, ch->line_status & ~LINE_UP);
+	}
+	mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ);
+}
+
+
+static int LOCOMX_open(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct locomx_data *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	unsigned long flags;
+	int ret;
+
+	if (!dev->base_addr || !dev->irq) {
+		return -ENODEV;
+	}
+
+	if (check_region(dev->base_addr, hw->io_extent)) {
+		return -EAGAIN;
+	}
+
+	request_region(dev->base_addr, hw->io_extent, dev->name);
+
+	hw->board.chanA.ctrlio=dev->base_addr + 5;
+	hw->board.chanA.dataio=dev->base_addr + 7;
+	
+	hw->board.irq=dev->irq;
+	hw->board.chanA.netdevice=dev;
+	hw->board.chanA.dev=&hw->board;
+	hw->board.name=dev->name;
+	hw->board.chanA.txdma=TX_DMA;
+	hw->board.chanA.rxdma=RX_DMA;
+	hw->board.chanA.irqs=&z8530_nop;
+	hw->board.chanB.irqs=&z8530_nop;
+
+	if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT, 
+	    dev->name, &hw->board)) {
+		printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name, 
+			dev->irq);
+		ret=-EAGAIN;
+		goto irq_fail;
+	}
+	if(request_dma(TX_DMA,"LoCOMX (TX)")) {
+		printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n", 
+			dev->name, TX_DMA);
+		ret=-EAGAIN;
+		goto dma1_fail;
+	}
+
+	if(request_dma(RX_DMA,"LoCOMX (RX)")) {
+		printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n", 
+			dev->name, RX_DMA);
+		ret=-EAGAIN;
+		goto dma2_fail;
+	}
+	
+	save_flags(flags); 
+	cli();
+
+	if(z8530_init(&hw->board)!=0)
+	{
+		printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name);
+		ret=-ENODEV;
+		goto z8530_fail;
+	}
+
+	hw->board.chanA.dcdcheck=CTS;
+
+	z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230);
+	z8530_channel_load(&hw->board.chanA, z8530_locomx);
+	z8530_channel_load(&hw->board.chanB, z8530_dead_port);
+
+	z8530_describe(&hw->board, "I/O", dev->base_addr);
+
+	if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) {
+		goto z8530_fail;
+	}
+
+	restore_flags(flags);
+
+
+	hw->board.active=1;
+	hw->board.chanA.rx_function=locomx_rx;
+
+	ch->init_status |= HW_OPEN;
+	if (hw->board.chanA.status & DCD) {
+		ch->line_status |= LINE_UP;
+	} else {
+		ch->line_status &= ~LINE_UP;
+	}
+
+	comx_status(dev, ch->line_status);
+
+	init_timer(&hw->status_timer);
+	hw->status_timer.function=locomx_status_timerfun;
+	hw->status_timer.data=(unsigned long)dev;
+	hw->status_timer.expires=jiffies + ch->lineup_delay * HZ;
+	add_timer(&hw->status_timer);
+
+	for (; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+		     strcmp(procfile->name, FILENAME_IRQ) == 0) {
+			procfile->mode = S_IFREG |  0444;
+		}
+	}
+	return 0;
+
+z8530_fail:
+	restore_flags(flags);
+	free_dma(RX_DMA);
+dma2_fail:
+	free_dma(TX_DMA);
+dma1_fail:
+	free_irq(dev->irq, &hw->board);
+irq_fail:
+	release_region(dev->base_addr, hw->io_extent);
+	return ret;
+}
+
+static int LOCOMX_close(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct locomx_data *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+
+	hw->board.chanA.rx_function=z8530_null_rx;
+	netif_stop_queue(dev);
+	z8530_sync_dma_close(dev, &hw->board.chanA);
+
+	z8530_shutdown(&hw->board);
+
+	del_timer(&hw->status_timer);
+	free_dma(RX_DMA);
+	free_dma(TX_DMA);
+	free_irq(dev->irq,&hw->board);
+	release_region(dev->base_addr,8);
+
+	for (; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+		    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+			procfile->mode = S_IFREG |  0644;
+		}
+	}
+
+	ch->init_status &= ~HW_OPEN;
+	return 0;
+}
+
+static int LOCOMX_statistics(struct net_device *dev,char *page)
+{
+	int len = 0;
+
+	len += sprintf(page + len, "Hello\n");
+
+	return len;
+}
+
+static int LOCOMX_dump(struct net_device *dev) {
+	printk(KERN_INFO "LOCOMX_dump called\n");
+	return(-1);
+}
+
+static int locomx_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct net_device *dev = file->parent->data;
+	int len = 0;
+
+	if (strcmp(file->name, FILENAME_IO) == 0) {
+		len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr);
+	} else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+		len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+	} else {
+		printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", 
+			file->name);
+		return -EBADF;
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return ( min(count, len - off) );
+}
+
+static int locomx_write_proc(struct file *file, const char *buffer,
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct net_device *dev = (struct net_device *)entry->parent->data;
+	int val;
+	char *page;
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "hw_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+	if (*(page + count - 1) == '\n') {
+		*(page + count - 1) = 0;
+	}
+
+	if (strcmp(entry->name, FILENAME_IO) == 0) {
+		val = simple_strtoul(page, NULL, 0);
+		if (val != 0x360 && val != 0x368 && val != 0x370 && 
+		   val != 0x378) {
+			printk(KERN_ERR "LoCOMX: incorrect io address!\n");	
+		} else {
+			dev->base_addr = val;
+		}
+	} else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+		val = simple_strtoul(page, NULL, 0);
+		if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) {
+			printk(KERN_ERR "LoCOMX: incorrect irq value!\n");
+		} else {
+			dev->irq = val;
+		}	
+	} else {
+		printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n", 
+			entry->name);
+		free_page((unsigned long)page);
+		return -EBADF;
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+
+
+static int LOCOMX_init(struct net_device *dev) 
+{
+	struct comx_channel *ch = (struct comx_channel *)dev->priv;
+	struct locomx_data *hw;
+	struct proc_dir_entry *new_file;
+
+	/* Alloc data for private structure */
+	if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data), 
+	   GFP_KERNEL)) == NULL) {
+	   	return -ENOMEM;
+	}
+
+	memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data));
+	hw->io_extent = LOCOMX_IO_EXTENT;
+
+	/* Register /proc files */
+	if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &locomx_read_proc;
+	new_file->write_proc = &locomx_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
+	    ch->procdir)) == NULL)  {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &locomx_read_proc;
+	new_file->write_proc = &locomx_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+/* 	No clock yet */
+/*
+	if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &locomx_read_proc;
+	new_file->write_proc = &locomx_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+*/
+
+	ch->HW_access_board = NULL;
+	ch->HW_release_board = NULL;
+	ch->HW_txe = LOCOMX_txe;
+	ch->HW_open = LOCOMX_open;
+	ch->HW_close = LOCOMX_close;
+	ch->HW_send_packet = LOCOMX_send_packet;
+	ch->HW_statistics = LOCOMX_statistics;
+	ch->HW_set_clock = NULL;
+
+	ch->current_stats = &hw->board.chanA.stats;
+	memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats));
+
+	dev->base_addr = LOCOMX_DEFAULT_IO;
+	dev->irq = LOCOMX_DEFAULT_IRQ;
+	
+	
+	/* O.K. Count one more user on this module */
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+static int LOCOMX_exit(struct net_device *dev)
+{
+	struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+	ch->HW_access_board = NULL;
+	ch->HW_release_board = NULL;
+	ch->HW_txe = NULL;
+	ch->HW_open = NULL;
+	ch->HW_close = NULL;
+	ch->HW_send_packet = NULL;
+	ch->HW_statistics = NULL;
+	ch->HW_set_clock = NULL;
+	memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats));
+	ch->current_stats = &ch->stats;
+
+	kfree(ch->HW_privdata);
+
+	remove_proc_entry(FILENAME_IO, ch->procdir);
+	remove_proc_entry(FILENAME_IRQ, ch->procdir);
+//	remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct comx_hardware locomx_hw = {
+	"locomx",
+	VERSION,
+	LOCOMX_init, 
+	LOCOMX_exit,
+	LOCOMX_dump,
+	NULL
+};
+	
+#ifdef MODULE
+#define comx_hw_locomx_init init_module
+#endif
+
+int __init comx_hw_locomx_init(void)
+{
+	comx_register_hardware(&locomx_hw);
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_hardware("locomx");
+	return;
+}
+#endif
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx-hw-mixcom.c linux/drivers/net/wan/comx-hw-mixcom.c
--- v2.3.51/linux/drivers/net/wan/comx-hw-mixcom.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comx-hw-mixcom.c	Mon Mar 13 13:55:09 2000
@@ -0,0 +1,948 @@
+/* 
+ * Hardware driver for the MixCom synchronous serial board 
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton driver code and a preliminary hscx driver by 
+ * Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.60 (99/06/11):
+ *		- ported to the kernel, now works as builtin code
+ *
+ * Version 0.61 (99/06/11):
+ *		- recognize the one-channel MixCOM card (id byte = 0x13)
+ *		- printk fixes
+ * 
+ * Version 0.62 (99/07/15):
+ *		- fixes according to the new hw docs 
+ *		- report line status when open
+ *
+ * Version 0.63 (99/09/21):
+ *		- line status report fixes
+ *
+ * Version 0.64 (99/12/01):
+ *		- some more cosmetical fixes
+ */
+
+#define VERSION "0.64"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "mixcom.h"
+#include "hscx.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
+
+#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \
+	HW_privdata))
+
+#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \
+	(1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
+
+#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \
+	(1 - channel) * MIXCOM_CHANNEL_OFFSET)
+
+/* Values used to set the IRQ line */
+static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
+
+static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
+
+struct mixcom_privdata {
+	u16	clock;
+	char	channel;
+	char	txbusy;
+	struct sk_buff *sending;
+	unsigned tx_ptr;
+	struct sk_buff *recving;
+	unsigned rx_ptr;
+	unsigned char status;
+	char	card_has_status;
+};
+
+static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val) 
+{
+	outb(val, dev->base_addr + reg);
+}
+
+static inline unsigned char rd_hscx(struct net_device *dev, int reg)
+{
+	return inb(dev->base_addr + reg);
+}
+
+static inline void hscx_cmd(struct net_device *dev, int cmd)
+{
+	unsigned long jiffs = jiffies;
+	unsigned char cec;
+	unsigned delay = 0;
+
+	while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && 
+	    (jiffs + HZ > jiffies)) {
+		udelay(1);
+		if (++delay > (100000 / HZ)) break;
+	}
+	if (cec) {
+		printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);
+	} else {
+		wr_hscx(dev, HSCX_CMDR, cmd);
+	}
+}
+
+static inline void hscx_fill_fifo(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	register word to_send = hw->sending->len - hw->tx_ptr;
+
+
+	outsb(dev->base_addr + HSCX_FIFO,
+        	&(hw->sending->data[hw->tx_ptr]), min(to_send, 32));
+	if (to_send <= 32) {
+        	hscx_cmd(dev, HSCX_XTF | HSCX_XME);
+	        kfree_skb(hw->sending);
+        	hw->sending = NULL; 
+        	hw->tx_ptr = 0;
+        } else {
+	        hscx_cmd(dev, HSCX_XTF);
+        	hw->tx_ptr += 32;
+        }
+}
+
+static inline void hscx_empty_fifo(struct net_device *dev, int cnt)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	if (hw->recving == NULL) {
+        	if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
+	                ch->stats.rx_dropped++;
+        	        hscx_cmd(dev, HSCX_RHR);
+                } else {
+	                skb_reserve(hw->recving, 16);
+        	        skb_put(hw->recving, HSCX_MTU);
+                }
+	        hw->rx_ptr = 0;
+        }
+	if (cnt > 32 || !cnt || hw->recving == NULL) {
+        	printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",
+		        cnt, (void *)hw->recving);
+	        return;
+        }
+        
+	insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
+	hw->rx_ptr += cnt;
+	hscx_cmd(dev, HSCX_RMC);
+}
+
+
+static int MIXCOM_txe(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	return !test_bit(0, &hw->txbusy);
+}
+
+static int mixcom_probe(struct net_device *dev)
+{
+	unsigned long flags;
+	int id, vstr, ret=0;
+
+	save_flags(flags); cli();
+
+	id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f;
+
+ 	if (id != MIXCOM_ID ) {
+		ret=-ENODEV;
+		printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);
+		goto out;
+	}
+
+	vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
+	if(vstr>=sizeof(hscx_versions)/sizeof(char*) || 
+	    hscx_versions[vstr]==NULL) {
+		printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);
+		ret = -ENODEV;
+	} else {
+		printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);
+		ret = 0;
+	}
+
+out:
+
+	restore_flags(flags);
+	return ret;
+}
+
+#if 0
+static void MIXCOM_set_clock(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	if (hw->clock) {
+		;
+	} else {
+		;
+	}
+}
+#endif
+
+static void mixcom_board_on(struct net_device *dev)
+{
+	outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+	udelay(1000);
+	outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, 
+		MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+	udelay(1000);
+}
+
+static void mixcom_board_off(struct net_device *dev)
+{
+	outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+	udelay(1000);
+}
+
+static void mixcom_off(struct net_device *dev)
+{
+	wr_hscx(dev, HSCX_CCR1, 0x0);
+}
+
+static void mixcom_on(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
+	wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
+	wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
+	wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
+	wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
+	hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
+
+	if (ch->HW_set_clock) ch->HW_set_clock(dev);
+
+}
+
+static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	unsigned long flags;
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
+	}
+
+	if (!(ch->line_status & LINE_UP)) {
+		return FRAME_DROPPED;
+	}
+
+	if (skb->len > HSCX_MTU) {
+		ch->stats.tx_errors++;	
+		return FRAME_ERROR;
+	}
+
+	save_flags(flags); cli();
+
+	if (test_and_set_bit(0, &hw->txbusy)) {
+		printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);
+		restore_flags(flags);
+		return FRAME_DROPPED;
+	}
+
+
+	hw->sending = skb;
+	hw->tx_ptr = 0;
+	hw->txbusy = 1;
+//	atomic_inc(&skb->users);	// save it
+	hscx_fill_fifo(dev);
+	restore_flags(flags);
+
+	ch->stats.tx_packets++;
+	ch->stats.tx_bytes += skb->len; 
+
+	if (ch->debug_flags & DEBUG_HW_TX) {
+		comx_debug(dev, "MIXCOM_send_packet was successful\n\n");
+	}
+
+	return FRAME_ACCEPTED;
+}
+
+static inline void mixcom_receive_frame(struct net_device *dev) 
+{
+	struct comx_channel *ch=dev->priv;
+	struct mixcom_privdata *hw=ch->HW_privdata;
+	register byte rsta;
+	register word length;
+
+	rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | 
+		HSCX_CRC | HSCX_RAB);
+	length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | 
+		rd_hscx(dev, HSCX_RBCL);
+
+	if ( length > hw->rx_ptr ) {
+		hscx_empty_fifo(dev, length - hw->rx_ptr);
+	}
+	
+	if (!(rsta & HSCX_VFR)) {
+		ch->stats.rx_length_errors++;
+	}
+	if (rsta & HSCX_RDO) {
+		ch->stats.rx_over_errors++;
+	}
+	if (!(rsta & HSCX_CRC)) {
+		ch->stats.rx_crc_errors++;
+	}
+	if (rsta & HSCX_RAB) {
+		ch->stats.rx_frame_errors++;
+	}
+	ch->stats.rx_packets++; 
+	ch->stats.rx_bytes += length;
+
+	if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
+		skb_trim(hw->recving, hw->rx_ptr - 1);
+		if (ch->debug_flags & DEBUG_HW_RX) {
+			comx_debug_skb(dev, hw->recving,
+				"MIXCOM_interrupt receiving");
+		}
+		hw->recving->dev = dev;
+		if (ch->LINE_rx) {
+			ch->LINE_rx(dev, hw->recving);
+		}
+	}
+	else if(hw->recving) {
+		kfree_skb(hw->recving);
+	}
+	hw->recving = NULL; 
+	hw->rx_ptr = 0;
+}
+
+
+static inline void mixcom_extended_interrupt(struct net_device *dev) 
+{
+	struct comx_channel *ch=dev->priv;
+	struct mixcom_privdata *hw=ch->HW_privdata;
+	register byte exir;
+
+	exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
+
+	if (exir & HSCX_RFO) {
+		ch->stats.rx_over_errors++;
+		if (hw->rx_ptr) {
+			kfree_skb(hw->recving);
+			hw->recving = NULL; hw->rx_ptr = 0;
+		}
+		printk(KERN_ERR "MIXCOM: rx overrun\n");
+		hscx_cmd(dev, HSCX_RHR);
+	}
+
+	if (exir & HSCX_XDU) { // xmit underrun
+		ch->stats.tx_errors++;
+		ch->stats.tx_aborted_errors++;
+		if (hw->tx_ptr) {
+			kfree_skb(hw->sending);
+			hw->sending = NULL; 
+			hw->tx_ptr = 0;
+		}
+		hscx_cmd(dev, HSCX_XRES);
+		clear_bit(0, &hw->txbusy);
+		if (ch->LINE_tx) {
+			ch->LINE_tx(dev);
+		}
+		printk(KERN_ERR "MIXCOM: tx underrun\n");
+	}
+
+	if (exir & HSCX_CSC) {        
+		ch->stats.tx_carrier_errors++;
+		if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
+			if (test_and_clear_bit(0, &ch->lineup_pending)) {
+               			del_timer(&ch->lineup_timer);
+			} else if (ch->line_status & LINE_UP) {
+        		       	ch->line_status &= ~LINE_UP;
+                		if (ch->LINE_status) {
+                      			ch->LINE_status(dev,ch->line_status);
+                      		}
+		      	}
+		}
+		if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & 
+		    HSCX_CTS)) { // Vonal fol
+			if (!test_and_set_bit(0,&ch->lineup_pending)) {
+				ch->lineup_timer.function = comx_lineup_func;
+	        	        ch->lineup_timer.data = (unsigned long)dev;
+        	        	ch->lineup_timer.expires = jiffies + HZ * 
+        	        		ch->lineup_delay;
+	                	add_timer(&ch->lineup_timer);
+		                hscx_cmd(dev, HSCX_XRES);
+        		        clear_bit(0, &hw->txbusy);
+                		if (hw->sending) {
+					kfree_skb(hw->sending);
+				}
+				hw->sending=NULL;
+				hw->tx_ptr = 0;
+			}
+		}
+	}
+}
+
+
+static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long flags;
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct comx_channel *ch, *twin_ch;
+	struct mixcom_privdata *hw, *twin_hw;
+	register unsigned char ista;
+
+	if (dev==NULL) {
+		printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);
+		return;
+	}
+
+	ch = dev->priv; 
+	hw = ch->HW_privdata;
+
+	save_flags(flags); cli(); 
+
+	while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | 
+	    HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
+		register byte ista2 = 0;
+
+		if (ista & HSCX_RME) {
+			mixcom_receive_frame(dev);
+		}
+		if (ista & HSCX_RPF) {
+			hscx_empty_fifo(dev, 32);
+		}
+		if (ista & HSCX_XPR) {
+			if (hw->tx_ptr) {
+				hscx_fill_fifo(dev);
+			} else {
+				clear_bit(0, &hw->txbusy);
+               			ch->LINE_tx(dev);
+			}
+		}
+		
+		if (ista & HSCX_EXB) {
+			mixcom_extended_interrupt(dev);
+		}
+		
+		if ((ista & HSCX_EXA) && ch->twin)  {
+			mixcom_extended_interrupt(ch->twin);
+		}
+	
+		if ((ista & HSCX_ICA) && ch->twin &&
+		    (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
+		    (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
+			if (ista2 & HSCX_RME) {
+				mixcom_receive_frame(ch->twin);
+			}
+			if (ista2 & HSCX_RPF) {
+				hscx_empty_fifo(ch->twin, 32);
+			}
+			if (ista2 & HSCX_XPR) {
+				twin_ch=ch->twin->priv;
+				twin_hw=twin_ch->HW_privdata;
+				if (twin_hw->tx_ptr) {
+					hscx_fill_fifo(ch->twin);
+				} else {
+					clear_bit(0, &twin_hw->txbusy);
+					ch->LINE_tx(ch->twin);
+				}
+			}
+		}
+	}
+
+	restore_flags(flags);
+	return;
+}
+
+static int MIXCOM_open(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	unsigned long flags; 
+
+	if (!dev->base_addr || !dev->irq) return -ENODEV;
+
+
+	if(hw->channel==1) {
+		if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status & 
+		    IRQ_ALLOCATED)) {
+			printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name);
+			return -EAGAIN;
+		}
+	}
+
+
+	/* Is our hw present at all ? Not checking for channel 0 if it is already 
+	   open */
+	if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
+		if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) {
+			return -EAGAIN;
+		}
+		if (mixcom_probe(dev)) {
+			return -ENODEV;
+		}
+	}
+
+	save_flags(flags); cli();
+
+	if(hw->channel==1) {
+		request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+	} 
+
+	if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
+		if (request_irq(dev->irq, MIXCOM_interrupt, 0, 
+		    dev->name, (void *)dev)) {
+			printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq);
+			return -EAGAIN;
+		}
+		ch->init_status|=IRQ_ALLOCATED;
+		request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+		mixcom_board_on(dev);
+	}
+
+	mixcom_on(dev);
+
+
+	hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
+	if(hw->status != 0xff) {
+		printk(KERN_DEBUG "%s: board has status register, good\n", dev->name);
+		hw->card_has_status=1;
+	}
+
+	hw->txbusy = 0;
+	ch->init_status |= HW_OPEN;
+	
+	if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
+		ch->line_status |= LINE_UP;
+	} else {
+		ch->line_status &= ~LINE_UP;
+	}
+
+	restore_flags(flags);
+
+	ch->LINE_status(dev, ch->line_status);
+
+	for (; procfile ; procfile = procfile->next) {
+		if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+		    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+		    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+		    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+			procfile->mode = S_IFREG |  0444;
+		}
+	}
+
+	return 0;
+}
+
+static int MIXCOM_close(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	struct proc_dir_entry *procfile = ch->procdir->subdir;
+	unsigned long flags;
+
+
+	save_flags(flags); cli();
+
+	mixcom_off(dev);
+
+	/* This is channel 0, twin is not open, we can safely turn off everything */
+	if(hw->channel==0 && (!(TWIN(dev)) || 
+	    !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
+		mixcom_board_off(dev);
+		free_irq(dev->irq, dev);
+		release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+		ch->init_status &= ~IRQ_ALLOCATED;
+	}
+
+	/* This is channel 1, channel 0 has already been shutdown, we can release
+	   this one too */
+	if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+		if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
+			mixcom_board_off(TWIN(dev));
+			free_irq(TWIN(dev)->irq, TWIN(dev));
+			release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
+			COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
+		}
+	}
+
+	/* the ioports for channel 1 can be safely released */
+	if(hw->channel==1) {
+		release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+	}
+
+	restore_flags(flags);
+
+	/* If we don't hold any hardware open */
+	if(!(ch->init_status & IRQ_ALLOCATED)) {
+		for (; procfile ; procfile = procfile->next) {
+			if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+			    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+			    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+			    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+				procfile->mode = S_IFREG |  0644;
+			}
+		}
+	}
+
+	/* channel 0 was only waiting for us to close channel 1 
+	   close it completely */
+   
+	if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+		for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; 
+		    procfile ; procfile = procfile->next) {
+			if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+			    strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+			    strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+			    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+				procfile->mode = S_IFREG |  0644;
+			}
+		}
+	}
+	
+	ch->init_status &= ~HW_OPEN;
+	return 0;
+}
+
+static int MIXCOM_statistics(struct net_device *dev,char *page)
+{
+	struct comx_channel *ch = dev->priv;
+	// struct mixcom_privdata *hw = ch->HW_privdata;
+	int len = 0;
+
+	if(ch->init_status && IRQ_ALLOCATED) {
+		len += sprintf(page + len, "Mixcom board: hardware open\n");
+	}
+
+	return len;
+}
+
+static int MIXCOM_dump(struct net_device *dev) {
+	return 0;
+}
+
+static int mixcom_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct net_device *dev = file->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	int len = 0;
+
+	if (strcmp(file->name, FILENAME_IO) == 0) {
+		len = sprintf(page, "0x%x\n", 
+			(unsigned int)MIXCOM_BOARD_BASE(dev));
+	} else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+		len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+	} else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+		if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
+			else len = sprintf(page, "external\n");
+	} else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+		len = sprintf(page, "%01d\n", hw->channel);
+	} else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+		if (ch->twin) {
+			len = sprintf(page, "%s\n",ch->twin->name);
+		} else {
+			len = sprintf(page, "none\n");
+		}
+	} else {
+		printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
+		return -EBADF;
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+	*start = page + off;
+	if (count >= len - off) *eof = 1;
+	return ( min(count, len - off) );
+}
+
+
+static struct net_device *mixcom_twin_check(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	struct net_device *twin;
+	struct comx_channel *ch_twin;
+	struct mixcom_privdata *hw_twin;
+
+
+	for ( ; procfile ; procfile = procfile->next) {
+		if(!S_ISDIR(procfile->mode)) continue;
+                
+        	twin = procfile->data;
+	        ch_twin = twin->priv;
+        	hw_twin = ch_twin->HW_privdata;
+
+
+	        if (twin != dev && dev->irq && dev->base_addr && 
+        	    dev->irq == twin->irq && 
+        	    ch->hardware == ch_twin->hardware &&
+		    dev->base_addr == twin->base_addr + 
+		    (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
+		    hw->channel == (1 - hw_twin->channel)) {
+	        	if  (!TWIN(twin) || TWIN(twin)==dev) {
+	        		return twin;
+	        	}
+		}
+        }
+	return NULL;
+}
+
+
+static void setup_twin(struct net_device* dev) 
+{
+
+	if(TWIN(dev) && TWIN(TWIN(dev))) {
+		TWIN(TWIN(dev))=NULL;
+	}
+	if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
+		if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
+			TWIN(dev)=NULL;
+		} else {
+			TWIN(TWIN(dev))=dev;
+		}
+	}	
+}
+
+static int mixcom_write_proc(struct file *file, const char *buffer,
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct net_device *dev = (struct net_device *)entry->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+	char *page;
+	int value;
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+	if (*(page + count - 1) == '\n') {
+		*(page + count - 1) = 0;
+	}
+
+	if (strcmp(entry->name, FILENAME_IO) == 0) {
+		value = simple_strtoul(page, NULL, 0);
+		if (value != 0x180 && value != 0x280 && value != 0x380) {
+			printk(KERN_ERR "MIXCOM: incorrect io address!\n");
+		} else {
+			dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
+		}
+	} else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+		value = simple_strtoul(page, NULL, 0); 
+		if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
+			printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
+		} else {
+			dev->irq = value;	
+		}
+	} else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+		if (strncmp("ext", page, 3) == 0) {
+			hw->clock = 0;
+		} else {
+			int kbps;
+
+			kbps = simple_strtoul(page, NULL, 0);
+			if (!kbps) {
+				hw->clock = 0;
+			} else {
+				hw->clock = kbps;
+			}
+			if (hw->clock < 32 || hw->clock > 2000) {
+				hw->clock = 0;
+				printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
+			}
+		}
+		if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
+			ch->HW_set_clock(dev);
+		}
+	} else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+		value = simple_strtoul(page, NULL, 0);
+        	if (value > 2) {
+                	printk(KERN_ERR "Invalid channel number\n");
+	        } else {
+        		dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
+	        	hw->channel = value;
+		}	        
+	} else {
+		printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", 
+			entry->name);
+		return -EBADF;
+	}
+
+	setup_twin(dev);
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static int MIXCOM_init(struct net_device *dev) {
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw;
+	struct proc_dir_entry *new_file;
+
+	if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), 
+	    GFP_KERNEL)) == NULL) {
+	    	return -ENOMEM;
+	}
+
+	memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
+
+	if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+#if 0
+	if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+#endif
+
+	if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
+	    ch->procdir)) == NULL) {
+	    	return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &mixcom_read_proc;
+	new_file->write_proc = &mixcom_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->nlink = 1;
+
+	setup_twin(dev);
+
+	/* Fill in ch_struct hw specific pointers */
+	ch->HW_access_board = NULL;
+	ch->HW_release_board = NULL;
+	ch->HW_txe = MIXCOM_txe;
+	ch->HW_open = MIXCOM_open;
+	ch->HW_close = MIXCOM_close;
+	ch->HW_send_packet = MIXCOM_send_packet;
+	ch->HW_statistics = MIXCOM_statistics;
+	ch->HW_set_clock = NULL;
+
+	dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
+	dev->irq = MIXCOM_DEFAULT_IRQ;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int MIXCOM_exit(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct mixcom_privdata *hw = ch->HW_privdata;
+
+	if(hw->channel==0 && TWIN(dev)) {
+		return -EBUSY;
+	}
+
+	if(hw->channel==1 && TWIN(dev)) {
+		TWIN(TWIN(dev))=NULL;
+	}
+
+	kfree(ch->HW_privdata);
+	remove_proc_entry(FILENAME_IO, ch->procdir);
+	remove_proc_entry(FILENAME_IRQ, ch->procdir);
+#if 0
+	remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+#endif
+	remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+	remove_proc_entry(FILENAME_TWIN, ch->procdir);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct comx_hardware mixcomhw = {
+	"mixcom",
+	VERSION,
+	MIXCOM_init, 
+	MIXCOM_exit,
+	MIXCOM_dump,
+	NULL
+};
+	
+/* Module management */
+
+#ifdef MODULE
+#define comx_hw_mixcom_init init_module
+#endif
+
+int __init comx_hw_mixcom_init(void)
+{
+	return(comx_register_hardware(&mixcomhw));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+	comx_unregister_hardware("mixcom");
+}
+#endif
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx-proto-fr.c linux/drivers/net/wan/comx-proto-fr.c
--- v2.3.51/linux/drivers/net/wan/comx-proto-fr.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comx-proto-fr.c	Mon Mar 13 13:55:09 2000
@@ -0,0 +1,1006 @@
+/*
+ * Frame-relay protocol module for the COMX driver 
+ * for Linux 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.70 (99/06/14):
+ *		- cleaned up the source code a bit
+ *		- ported back to kernel, now works as builtin code 
+ *
+ * Version 0.71 (99/06/25):
+ *		- use skb priorities and queues for sending keepalive
+ * 		- use device queues for slave->master data transmit
+ *		- set IFF_RUNNING only line protocol up
+ *		- fixes on slave device flags
+ * 
+ * Version 0.72 (99/07/09):
+ *		- handle slave tbusy with master tbusy (should be fixed)
+ *		- fix the keepalive timer addition/deletion
+ */
+
+#define VERSION "0.72"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <linux/pkt_sched.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");
+MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers"
+	"for Linux kernel 2.2.X");
+
+#define	FRAD_UI		0x03
+#define	NLPID_IP	0xcc
+#define	NLPID_Q933_LMI	0x08
+#define	NLPID_CISCO_LMI	0x09	
+#define Q933_ENQ	0x75
+#define	Q933_LINESTAT	0x51
+#define	Q933_COUNTERS	0x53
+
+#define	MAXALIVECNT	3		/* No. of failures */
+
+struct fr_data {
+	u16	dlci;
+	struct	net_device *master;
+	char	keepa_pend;
+	char	keepa_freq;
+	char	keepalivecnt, keeploopcnt;
+	struct	timer_list keepa_timer;
+	u8	local_cnt, remote_cnt;
+};
+
+static struct comx_protocol fr_master_protocol;
+static struct comx_protocol fr_slave_protocol;
+static struct comx_hardware fr_dlci;
+
+static void fr_keepalive_send(struct net_device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct sk_buff *skb;
+	u8 *fr_packet;
+	
+	skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC);
+	
+	if(skb==NULL)
+		return;
+               
+        skb_reserve(skb, dev->hard_header_len);
+        
+        fr_packet=(u8*)skb_put(skb, 13);
+                 
+	fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2;
+	fr_packet[1] = (fr->dlci & 15) << 4 | 1;	// EA bit 1
+	fr_packet[2] = FRAD_UI;
+	fr_packet[3] = NLPID_Q933_LMI;
+	fr_packet[4] = 0;
+	fr_packet[5] = Q933_ENQ;
+	fr_packet[6] = Q933_LINESTAT;
+	fr_packet[7] = 0x01;
+	fr_packet[8] = 0x01;
+	fr_packet[9] = Q933_COUNTERS;
+	fr_packet[10] = 0x02;
+	fr_packet[11] = ++fr->local_cnt;
+	fr_packet[12] = fr->remote_cnt;
+
+	skb->dev = dev;
+	skb->priority = TC_PRIO_CONTROL;
+	dev_queue_xmit(skb);
+}
+
+static void fr_keepalive_timerfun(unsigned long d) 
+{
+	struct net_device *dev = (struct net_device *)d;
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	struct net_device *sdev;
+
+	if (ch->init_status & LINE_OPEN) {
+		if (fr->keepalivecnt == MAXALIVECNT) {
+			comx_status(dev, ch->line_status & ~PROTO_UP);
+			dev->flags &= ~IFF_RUNNING;
+			for (; dir ; dir = dir->next) {
+				if(!S_ISDIR(dir->mode)) {
+				    continue;
+				}
+	
+				if ((sdev = dir->data) && (sch = sdev->priv) && 
+				    (sdev->type == ARPHRD_DLCI) && 
+				    (sfr = sch->LINE_privdata) 
+				    && (sfr->master == dev) && 
+				    (sdev->flags & IFF_UP)) {
+					sdev->flags &= ~IFF_RUNNING;
+					comx_status(sdev, 
+						sch->line_status & ~PROTO_UP);
+				}
+			}
+		}
+		if (fr->keepalivecnt <= MAXALIVECNT) {
+			++fr->keepalivecnt;
+		}
+		fr_keepalive_send(dev);
+	}
+	mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);
+}
+
+static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb, 
+	u16 dlci, u8 nlpid) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	struct net_device *sdev;
+
+	if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) {
+		return;
+	}
+
+	fr->remote_cnt = skb->data[7];
+	if (skb->data[8] == fr->local_cnt) { // keepalive UP!
+		fr->keepalivecnt = 0;
+		if ((ch->line_status & LINE_UP) && 
+		    !(ch->line_status & PROTO_UP)) {
+			comx_status(dev, ch->line_status |= PROTO_UP);
+			dev->flags |= IFF_RUNNING;
+			for (; dir ; dir = dir->next) {
+				if(!S_ISDIR(dir->mode)) {
+				    continue;
+				}
+	
+				if ((sdev = dir->data) && (sch = sdev->priv) && 
+				    (sdev->type == ARPHRD_DLCI) && 
+				    (sfr = sch->LINE_privdata) 
+				    && (sfr->master == dev) && 
+				    (sdev->flags & IFF_UP)) {
+					sdev->flags |= IFF_RUNNING;
+					comx_status(sdev, 
+						sch->line_status | PROTO_UP);
+				}
+			}
+		}
+	}
+}
+
+static void fr_set_keepalive(struct net_device *dev, int keepa) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+
+	if (!keepa && fr->keepa_freq) { // switch off
+		fr->keepa_freq = 0;
+		if (ch->line_status & LINE_UP) {
+			comx_status(dev, ch->line_status | PROTO_UP);
+			dev->flags |= IFF_RUNNING;
+			del_timer(&fr->keepa_timer);
+		}
+		return;
+	}
+
+	if (keepa) { // bekapcs
+		if(fr->keepa_freq && (ch->line_status & LINE_UP)) {
+			del_timer(&fr->keepa_timer);
+		}
+		fr->keepa_freq = keepa;
+		fr->local_cnt = fr->remote_cnt = 0;
+		fr->keepa_timer.expires = jiffies + HZ;
+		fr->keepa_timer.function = fr_keepalive_timerfun;
+		fr->keepa_timer.data = (unsigned long)dev;
+		ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+		dev->flags &= ~IFF_RUNNING;
+		comx_status(dev, ch->line_status);
+		if(ch->line_status & LINE_UP) {
+			add_timer(&fr->keepa_timer);
+		}
+	}
+}
+
+static void fr_rx(struct net_device *dev, struct sk_buff *skb) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct net_device *sdev = dev;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	u16 dlci;
+	u8 nlpid;
+
+	if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
+		kfree_skb(skb);
+		return;
+	}
+
+	/* Itt majd ki kell talalni, melyik slave kapja a csomagot */
+	dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
+	if ((nlpid = skb->data[3]) == 0) { // Optional padding 
+		nlpid = skb->data[4];
+		skb_pull(skb, 1);
+	}
+	skb_pull(skb, 4);	/* DLCI and header throw away */
+
+	if (ch->debug_flags & DEBUG_COMX_DLCI) {
+		comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n", 
+			dlci, nlpid);
+		comx_debug_skb(dev, skb, "Contents");
+	}
+
+	/* Megkeressuk, kihez tartozik */
+	for (; dir ; dir = dir->next) {
+		if(!S_ISDIR(dir->mode)) {
+			continue;
+		}
+		if ((sdev = dir->data) && (sch = sdev->priv) && 
+		    (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+		    (sfr->master == dev) && (sfr->dlci == dlci)) {
+			skb->dev = sdev;	
+			if (ch->debug_flags & DEBUG_COMX_DLCI) {
+				comx_debug(dev, "Passing it to %s\n",sdev->name);
+			}
+			if (dev != sdev) {
+				sch->stats.rx_packets++;
+				sch->stats.rx_bytes += skb->len;
+			}
+			break;
+		}
+	}
+	switch(nlpid) {
+		case NLPID_IP:
+			skb->protocol = htons(ETH_P_IP);
+			skb->mac.raw = skb->data;
+			comx_rx(sdev, skb);
+			break;
+		case NLPID_Q933_LMI:
+			fr_rx_lmi(dev, skb, dlci, nlpid);
+		default:
+			kfree_skb(skb);
+			break;
+	}
+}
+
+static int fr_tx(struct net_device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct net_device *sdev;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	int cnt = 1;
+
+	/* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel, 
+	   es annal a slave-nel aki eppen kuldott.
+	   Egy helyen akkor all, ha a master kuldott.
+	   Ez megint jo lesz majd, ha utemezni akarunk */
+	   
+	/* This should be fixed, the slave tbusy should be set when 
+	   the masters queue is full and reset when not */
+
+	for (; dir ; dir = dir->next) {
+		if(!S_ISDIR(dir->mode)) {
+		    continue;
+		}
+		if ((sdev = dir->data) && (sch = sdev->priv) && 
+		    (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+		    (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+		    	netif_wake_queue(sdev);
+			cnt++;
+		}
+	}
+
+	netif_wake_queue(dev);
+	return 0;
+}
+
+static void fr_status(struct net_device *dev, unsigned short status)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+	struct net_device *sdev;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+
+	if (status & LINE_UP) {
+		if (!fr->keepa_freq) {
+			status |= PROTO_UP;
+		}
+	} else {
+		status &= ~(PROTO_UP | PROTO_LOOP);
+	}
+
+	if (dev == fr->master && fr->keepa_freq) {
+		if (status & LINE_UP) {
+			fr->keepa_timer.expires = jiffies + HZ;
+			add_timer(&fr->keepa_timer);
+			fr->keepalivecnt = MAXALIVECNT + 1;
+			fr->keeploopcnt = 0;
+		} else {
+			del_timer(&fr->keepa_timer);
+		}
+	}
+		
+	/* Itt a status valtozast vegig kell vinni az osszes slave-n */
+	for (; dir ; dir = dir->next) {
+		if(!S_ISDIR(dir->mode)) {
+		    continue;
+		}
+	
+		if ((sdev = dir->data) && (sch = sdev->priv) && 
+		    (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) && 
+		    (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+			if(status & LINE_UP) {
+				netif_wake_queue(sdev);
+			}
+			comx_status(sdev, status);
+			if(status & (PROTO_UP | PROTO_LOOP)) {
+				dev->flags |= IFF_RUNNING;
+			} else {
+				dev->flags &= ~IFF_RUNNING;
+			}
+		}
+	}
+}
+
+static int fr_open(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *comxdir = ch->procdir;
+	struct comx_channel *mch;
+
+	if (!(ch->init_status & HW_OPEN)) {
+		return -ENODEV;
+	}
+
+	if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
+	    (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
+		printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
+		return -EINVAL;
+	}
+
+	if (!fr->master) {
+		return -ENODEV;
+	}
+	mch = fr->master->priv;
+	if (fr->master != dev && (!(mch->init_status & LINE_OPEN) 
+	   || (mch->protocol != &fr_master_protocol))) {
+		printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
+			"unable to open %s\n", fr->master->name, dev->name);
+		return -ENODEV;
+	}
+
+	ch->init_status |= LINE_OPEN;
+	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+	dev->flags &= ~IFF_RUNNING;
+
+	if (fr->master == dev) {
+		if (fr->keepa_freq) {
+			fr->keepa_timer.function = fr_keepalive_timerfun;
+			fr->keepa_timer.data = (unsigned long)dev;
+			add_timer(&fr->keepa_timer);
+		} else {
+			if (ch->line_status & LINE_UP) {
+				ch->line_status |= PROTO_UP;
+				dev->flags |= IFF_RUNNING;
+			}
+		}
+	} else {
+		ch->line_status = mch->line_status;
+		if(fr->master->flags & IFF_RUNNING) {
+			dev->flags |= IFF_RUNNING;
+		}
+	}
+
+	for (; comxdir ; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+		   strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+		   strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+			comxdir->mode = S_IFREG | 0444;
+		}
+	}
+//	comx_status(dev, ch->line_status);
+	return 0;
+}
+
+static int fr_close(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct proc_dir_entry *comxdir = ch->procdir;
+
+	if (fr->master == dev) { // Ha master 
+		struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+		struct net_device *sdev = dev;
+		struct comx_channel *sch;
+		struct fr_data *sfr;
+
+		if (!(ch->init_status & HW_OPEN)) {
+			return -ENODEV;
+		}
+
+		if (fr->keepa_freq) {
+			del_timer(&fr->keepa_timer);
+		}
+		
+		for (; dir ; dir = dir->next) {
+			if(!S_ISDIR(dir->mode)) {
+				continue;
+			}
+			if ((sdev = dir->data) && (sch = sdev->priv) && 
+			    (sdev->type == ARPHRD_DLCI) && 
+			    (sfr = sch->LINE_privdata) &&
+			    (sfr->master == dev) && 
+			    (sch->init_status & LINE_OPEN)) {
+				dev_close(sdev);
+			}
+		}
+	}
+
+	ch->init_status &= ~LINE_OPEN;
+	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+	dev->flags &= ~IFF_RUNNING;
+
+	for (; comxdir ; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+		    strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+		    strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+			comxdir->mode = S_IFREG | 0444;
+		}
+	}
+
+	return 0;
+}
+
+static int fr_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct comx_channel *sch, *mch;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct fr_data *sfr;
+	struct net_device *sdev;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+	if (!fr->master) {
+		printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
+		return 0;
+	}
+
+	mch = fr->master->priv;
+
+	/* Ennek majd a slave utemezeskor lesz igazan jelentosege */
+	if (ch->debug_flags & DEBUG_COMX_DLCI) {
+		comx_debug_skb(dev, skb, "Sending frame");
+	}
+
+	if (dev != fr->master) {
+		struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+		newskb->dev=fr->master;
+		dev_queue_xmit(newskb);
+		dev_kfree_skb(skb);
+		ch->stats.tx_packets++;
+		ch->stats.tx_bytes += skb->len;
+	} else {
+		netif_stop_queue(dev);
+		for (; dir ; dir = dir->next) {
+			if(!S_ISDIR(dir->mode)) {
+			    continue;
+			}
+			if ((sdev = dir->data) && (sch = sdev->priv) && 
+			    (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+			    (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+				netif_stop_queue(sdev);
+			}
+		}
+		 	
+		switch(mch->HW_send_packet(dev, skb)) {
+			case FRAME_QUEUED:
+				netif_wake_queue(dev);
+				break;
+			case FRAME_ACCEPTED:
+			case FRAME_DROPPED:
+				break;
+			case FRAME_ERROR:
+				printk(KERN_ERR "%s: Transmit frame error (len %d)\n", 
+					dev->name, skb->len);
+				break;
+		}
+	}
+	return 0;
+}
+
+static int fr_header(struct sk_buff *skb, struct net_device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+
+	skb_push(skb, dev->hard_header_len);	  
+	/* Put in DLCI */
+	skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
+	skb->data[1] = (fr->dlci & 15) << 4 | 1;	// EA bit 1
+	skb->data[2] = FRAD_UI;
+	skb->data[3] = NLPID_IP;
+
+	return dev->hard_header_len;  
+}
+
+static int fr_statistics(struct net_device *dev, char *page) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	int len = 0;
+
+	if (fr->master == dev) {
+		struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+		struct net_device *sdev;
+		struct comx_channel *sch;
+		struct fr_data *sfr;
+		int slaves = 0;
+
+		len += sprintf(page + len, 
+			"This is a Frame Relay master device\nSlaves: ");
+		for (; dir ; dir = dir->next) {
+			if(!S_ISDIR(dir->mode)) {
+				continue;
+			}
+			if ((sdev = dir->data) && (sch = sdev->priv) && 
+			    (sdev->type == ARPHRD_DLCI) &&
+			    (sfr = sch->LINE_privdata) && 
+			    (sfr->master == dev) && (sdev != dev)) {
+				slaves++;
+				len += sprintf(page + len, "%s ", sdev->name);
+			}
+		}
+		len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
+		if (fr->keepa_freq) {
+			len += sprintf(page + len, "Line keepalive (value %d) "
+				"status %s [%d]\n", fr->keepa_freq, 
+				ch->line_status & PROTO_LOOP ? "LOOP" :
+				ch->line_status & PROTO_UP ? "UP" : "DOWN", 
+				fr->keepalivecnt);
+		} else {
+			len += sprintf(page + len, "Line keepalive protocol "
+				"is not set\n");
+		}
+	} else {		// if slave
+		len += sprintf(page + len, 
+			"This is a Frame Relay slave device, master: %s\n",
+			fr->master ? fr->master->name : "(not set)");
+	}
+	return len;
+}
+
+static int fr_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct net_device *dev = file->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = NULL;
+	int len = 0;
+
+	if (ch) {
+		fr = ch->LINE_privdata;
+	}
+
+	if (strcmp(file->name, FILENAME_DLCI) == 0) {
+		len = sprintf(page, "%04d\n", fr->dlci);
+	} else if (strcmp(file->name, FILENAME_MASTER) == 0) {
+		len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
+			"(none)");
+	} else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
+		len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq) 
+			: sprintf(page, "off\n");
+	} else {
+		printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
+		return -EBADF;
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) *eof = 1;
+	return ( min(count, len - off) );
+}
+
+static int fr_write_proc(struct file *file, const char *buffer, 
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct net_device *dev = entry->parent->data;
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = NULL; 
+	char *page;
+
+	if (ch) {
+		fr = ch->LINE_privdata;
+	}
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	copy_from_user(page, buffer, count);
+	if (*(page + count - 1) == '\n') {
+		*(page + count - 1) = 0;
+	}
+
+	if (strcmp(entry->name, FILENAME_DLCI) == 0) {
+		u16 dlci_new = simple_strtoul(page, NULL, 10);
+
+		if (dlci_new > 1023) {
+			printk(KERN_ERR "Invalid DLCI value\n");
+		}
+		else fr->dlci = dlci_new;
+	} else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
+		struct net_device *new_master = dev_get_by_name(page);
+
+		if (new_master && new_master->type == ARPHRD_FRAD) {
+			struct comx_channel *sch = new_master->priv;
+			struct fr_data *sfr = sch->LINE_privdata;
+
+			if (sfr && sfr->master == new_master) {
+				if(fr->master)
+					dev_put(fr->master);
+				fr->master = new_master;
+				/* Megorokli a master statuszat */
+				ch->line_status = sch->line_status;
+			}
+		}
+	} else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
+		int keepa_new = -1;
+
+		if (strcmp(page, KEEPALIVE_OFF) == 0) {
+			keepa_new = 0;
+		} else {
+			keepa_new = simple_strtoul(page, NULL, 10);
+		}
+
+		if (keepa_new < 0 || keepa_new > 100) {
+			printk(KERN_ERR "invalid keepalive\n");
+		} else {
+			if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
+				fr_set_keepalive(dev, 0);
+			}
+			if (keepa_new) {
+				fr_set_keepalive(dev, keepa_new);
+			}
+		}
+	} else {
+		printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n", 
+			entry->name);
+		return -EBADF;
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static int fr_exit(struct net_device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+	struct net_device *sdev = dev;
+	struct comx_channel *sch;
+	struct fr_data *sfr;
+	struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+	/* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
+	if (fr->master && fr->master == dev) {
+		for (; dir ; dir = dir->next) {
+			if(!S_ISDIR(dir->mode)) {
+				continue;
+			}
+			if ((sdev = dir->data) && (sch = sdev->priv) && 
+			    (sdev->type == ARPHRD_DLCI) && 
+			    (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+				dev_close(sdev);
+				sfr->master = NULL;
+			}
+		}
+	}
+	dev->flags		= 0;
+	dev->type		= 0;
+	dev->mtu		= 0;
+	dev->hard_header_len    = 0;
+
+	ch->LINE_rx	= NULL;
+	ch->LINE_tx	= NULL;
+	ch->LINE_status = NULL;
+	ch->LINE_open	= NULL;
+	ch->LINE_close	= NULL;
+	ch->LINE_xmit	= NULL;
+	ch->LINE_header	= NULL;
+	ch->LINE_rebuild_header	= NULL;
+	ch->LINE_statistics = NULL;
+
+	ch->LINE_status = 0;
+
+	if (fr->master != dev) { // if not master, remove dlci
+		if(fr->master)
+			dev_put(fr->master);
+		remove_proc_entry(FILENAME_DLCI, ch->procdir);
+		remove_proc_entry(FILENAME_MASTER, ch->procdir);
+	} else {
+		if (fr->keepa_freq) {
+			fr_set_keepalive(dev, 0);
+		}
+		remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
+		remove_proc_entry(FILENAME_DLCI, ch->procdir);
+	}
+
+	kfree(fr);
+	ch->LINE_privdata = NULL;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int fr_master_init(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr;
+	struct proc_dir_entry *new_file;
+
+	if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), 
+	    GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(fr, 0, sizeof(struct fr_data));
+	fr->master = dev;	// this means master
+	fr->dlci = 0;		// let's say default
+
+	dev->flags	= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+	dev->type	= ARPHRD_FRAD;
+	dev->mtu	= 1500;
+	dev->hard_header_len    = 4;		
+	dev->addr_len	= 0;
+
+	ch->LINE_rx	= fr_rx;
+	ch->LINE_tx	= fr_tx;
+	ch->LINE_status = fr_status;
+	ch->LINE_open	= fr_open;
+	ch->LINE_close	= fr_close;
+	ch->LINE_xmit	= fr_xmit;
+	ch->LINE_header	= fr_header;
+	ch->LINE_rebuild_header	= NULL;
+	ch->LINE_statistics = fr_statistics;
+
+	if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -ENOMEM;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &fr_read_proc;
+	new_file->write_proc = &fr_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->size = 5;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -ENOMEM;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &fr_read_proc;
+	new_file->write_proc = &fr_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->size = 4;
+	new_file->nlink = 1;
+
+	fr_set_keepalive(dev, 0);
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int fr_slave_init(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr;
+	struct proc_dir_entry *new_file;
+
+	if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), 
+	    GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(fr, 0, sizeof(struct fr_data));
+
+	dev->flags	= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+	dev->type	= ARPHRD_DLCI;
+	dev->mtu	= 1500;
+	dev->hard_header_len    = 4;		
+	dev->addr_len	= 0;
+
+	ch->LINE_rx	= fr_rx;
+	ch->LINE_tx	= fr_tx;
+	ch->LINE_status = fr_status;
+	ch->LINE_open	= fr_open;
+	ch->LINE_close	= fr_close;
+	ch->LINE_xmit	= fr_xmit;
+	ch->LINE_header	= fr_header;
+	ch->LINE_rebuild_header	= NULL;
+	ch->LINE_statistics = fr_statistics;
+
+	if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -ENOMEM;
+	}
+	
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &fr_read_proc;
+	new_file->write_proc = &fr_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->size = 5;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644, 
+	    ch->procdir)) == NULL) {
+		return -EIO;
+	}
+	new_file->data = (void *)new_file;
+	new_file->read_proc = &fr_read_proc;
+	new_file->write_proc = &fr_write_proc;
+	new_file->proc_iops = &comx_normal_inode_ops;
+	new_file->size = 10;
+	new_file->nlink = 1;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_open(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	ch->init_status |= HW_OPEN;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_close(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	ch->init_status &= ~HW_OPEN;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_txe(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct fr_data *fr = ch->LINE_privdata;
+
+	if (!fr->master) {
+		return 0;
+	}
+
+	ch = fr->master->priv;
+	fr = ch->LINE_privdata;
+	return ch->HW_txe(fr->master);
+}
+
+static int dlci_statistics(struct net_device *dev, char *page) 
+{
+	return 0;
+}
+
+static int dlci_init(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	ch->HW_open = dlci_open;
+	ch->HW_close = dlci_close;
+	ch->HW_txe = dlci_txe;
+	ch->HW_statistics = dlci_statistics;
+
+	/* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_exit(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	ch->HW_open = NULL;
+	ch->HW_close = NULL;
+	ch->HW_txe = NULL;
+	ch->HW_statistics = NULL;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int dlci_dump(struct net_device *dev)
+{
+	printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
+	return -1;
+}
+
+static struct comx_protocol fr_master_protocol = {
+	"frad", 
+	VERSION,
+	ARPHRD_FRAD, 
+	fr_master_init, 
+	fr_exit, 
+	NULL 
+};
+
+static struct comx_protocol fr_slave_protocol = {
+	"ietf-ip", 
+	VERSION,
+	ARPHRD_DLCI, 
+	fr_slave_init, 
+	fr_exit, 
+	NULL 
+};
+
+static struct comx_hardware fr_dlci = { 
+	"dlci", 
+	VERSION,
+	dlci_init, 
+	dlci_exit, 
+	dlci_dump, 
+	NULL 
+};
+
+#ifdef MODULE
+#define comx_proto_fr_init init_module
+#endif
+
+int __init comx_proto_fr_init(void)
+{
+	int ret; 
+
+	if ((ret = comx_register_hardware(&fr_dlci))) {
+		return ret;
+	}
+	if ((ret = comx_register_protocol(&fr_master_protocol))) {
+		return ret;
+	}
+	return comx_register_protocol(&fr_slave_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_hardware(fr_dlci.name);
+	comx_unregister_protocol(fr_master_protocol.name);
+	comx_unregister_protocol(fr_slave_protocol.name);
+}
+#endif /* MODULE */
+
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx-proto-lapb.c linux/drivers/net/wan/comx-proto-lapb.c
--- v2.3.51/linux/drivers/net/wan/comx-proto-lapb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comx-proto-lapb.c	Mon Mar 13 13:55:09 2000
@@ -0,0 +1,548 @@
+/*
+ * LAPB protocol module for the COMX driver 
+ * for Linux kernel 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.80 (99/06/14):
+ *		- cleaned up the source code a bit
+ *		- ported back to kernel, now works as non-module
+ * 
+ */
+
+#define VERSION "0.80"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/lapb.h>
+#include <linux/init.h>
+
+#include	"comx.h"
+#include	"comxhw.h"
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+	int size, struct proc_dir_entry *dir);
+
+static void comxlapb_rx(struct net_device *dev, struct sk_buff *skb) 
+{
+	if (!dev || !dev->priv) {
+		dev_kfree_skb(skb);
+	} else {
+		lapb_data_received(dev->priv, skb);
+	}
+}
+
+static int comxlapb_tx(struct net_device *dev) 
+{
+	netif_wake_queue(dev);
+	return 0;
+}
+
+static int comxlapb_header(struct sk_buff *skb, struct net_device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+	return dev->hard_header_len;  
+}
+
+static void comxlapb_status(struct net_device *dev, unsigned short status)
+{
+	struct comx_channel *ch;
+
+	if (!dev || !(ch = dev->priv)) {
+		return;
+	}
+	if (status & LINE_UP) {
+		netif_wake_queue(dev);
+	}
+	comx_status(dev, status);
+}
+
+static int comxlapb_open(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	int err = 0;
+
+	if (!(ch->init_status & HW_OPEN)) {
+		return -ENODEV;
+	}
+
+	err = lapb_connect_request(ch);
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(dev, "%s: lapb opened, error code: %d\n", 
+			dev->name, err);
+	}
+
+	if (!err) {
+		ch->init_status |= LINE_OPEN;
+		MOD_INC_USE_COUNT;
+	}
+	return err;
+}
+
+static int comxlapb_close(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (!(ch->init_status & HW_OPEN)) {
+		return -ENODEV;
+	}
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(dev, "%s: lapb closed\n", dev->name);
+	}
+
+	lapb_disconnect_request(ch);
+
+	ch->init_status &= ~LINE_OPEN;
+	ch->line_status &= ~PROTO_UP;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int comxlapb_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct sk_buff *skb2;
+
+	if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) {
+		return -ENODEV;
+	}
+
+	if (dev->type == ARPHRD_X25) { // first byte tells what to do 
+		switch(skb->data[0]) {
+			case 0x00:	
+				break;	// transmit
+			case 0x01:	
+				lapb_connect_request(ch);
+				kfree_skb(skb);
+				return 0;
+			case 0x02:	
+				lapb_disconnect_request(ch);
+			default:
+				kfree_skb(skb);
+				return 0;
+		}
+		skb_pull(skb,1);
+	}
+
+	netif_stop_queue(dev);
+	
+	if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+		lapb_data_request(ch, skb2);
+	}
+
+	return FRAME_ACCEPTED;
+}
+
+static int comxlapb_statistics(struct net_device *dev, char *page) 
+{
+	struct lapb_parms_struct parms;
+	int len = 0;
+
+	len += sprintf(page + len, "Line status: ");
+	if (lapb_getparms(dev->priv, &parms) != LAPB_OK) {
+		len += sprintf(page + len, "not initialized\n");
+		return len;
+	}
+	len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, "
+		"window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE", 
+		parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD",
+		parms.t1timer, parms.t1, parms.t2timer, parms.t2, 
+		parms.n2count, parms.n2, parms.window);
+
+	return len;
+}
+
+static int comxlapb_read_proc(char *page, char **start, off_t off, int count,
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct net_device *dev = file->parent->data;
+	struct lapb_parms_struct parms;
+	int len = 0;
+
+	if (lapb_getparms(dev->priv, &parms)) {
+		return -ENODEV;
+	}
+
+	if (strcmp(file->name, FILENAME_T1) == 0) {
+		len += sprintf(page + len, "%02u / %02u\n", 
+			parms.t1timer, parms.t1);
+	} else if (strcmp(file->name, FILENAME_T2) == 0) {
+		len += sprintf(page + len, "%02u / %02u\n", 
+			parms.t2timer, parms.t2);
+	} else if (strcmp(file->name, FILENAME_N2) == 0) {
+		len += sprintf(page + len, "%02u / %02u\n", 
+			parms.n2count, parms.n2);
+	} else if (strcmp(file->name, FILENAME_WINDOW) == 0) {
+		len += sprintf(page + len, "%u\n", parms.window);
+	} else if (strcmp(file->name, FILENAME_MODE) == 0) {
+		len += sprintf(page + len, "%s, %s\n", 
+			parms.mode & LAPB_DCE ? "DCE" : "DTE",
+			parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD");
+	} else {
+		printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name);
+		return -EBADF;
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return ( min(count, len - off) );
+}
+
+static int comxlapb_write_proc(struct file *file, const char *buffer, 
+	u_long count, void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct net_device *dev = entry->parent->data;
+	struct lapb_parms_struct parms;
+	unsigned long parm;
+	char *page;
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (lapb_getparms(dev->priv, &parms)) {
+		return -ENODEV;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	copy_from_user(page, buffer, count);
+	if (*(page + count - 1) == '\n') {
+		*(page + count - 1) = 0;
+	}
+
+	if (strcmp(entry->name, FILENAME_T1) == 0) {
+		parm=simple_strtoul(page,NULL,10);
+		if (parm > 0 && parm < 100) {
+			parms.t1=parm;
+			lapb_setparms(dev->priv, &parms);
+		}
+	} else if (strcmp(entry->name, FILENAME_T2) == 0) {
+		parm=simple_strtoul(page, NULL, 10);
+		if (parm > 0 && parm < 100) {
+			parms.t2=parm;
+			lapb_setparms(dev->priv, &parms);
+		}
+	} else if (strcmp(entry->name, FILENAME_N2) == 0) {
+		parm=simple_strtoul(page, NULL, 10);
+		if (parm > 0 && parm < 100) {
+			parms.n2=parm;
+			lapb_setparms(dev->priv, &parms);
+		}
+	} else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
+		parms.window = simple_strtoul(page, NULL, 10);
+		lapb_setparms(dev->priv, &parms);
+	} else if (strcmp(entry->name, FILENAME_MODE) == 0) {
+		if (comx_strcasecmp(page, "dte") == 0) {
+			parms.mode &= ~(LAPB_DCE | LAPB_DTE); 
+			parms.mode |= LAPB_DTE;
+		} else if (comx_strcasecmp(page, "dce") == 0) {
+			parms.mode &= ~(LAPB_DTE | LAPB_DCE); 
+			parms.mode |= LAPB_DCE;
+		} else if (comx_strcasecmp(page, "std") == 0 || 
+		    comx_strcasecmp(page, "standard") == 0) {
+			parms.mode &= ~LAPB_EXTENDED; 
+			parms.mode |= LAPB_STANDARD;
+		} else if (comx_strcasecmp(page, "ext") == 0 || 
+		    comx_strcasecmp(page, "extended") == 0) {
+			parms.mode &= ~LAPB_STANDARD; 
+			parms.mode |= LAPB_EXTENDED;
+		}
+		lapb_setparms(dev->priv, &parms);
+	} else {
+		printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n", 
+			entry->name);
+		return -EBADF;
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static void comxlapb_connected(void *token, int reason)
+{
+	struct comx_channel *ch = token; 
+	struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(ch->dev, "%s: lapb connected, reason: %d\n", 
+			ch->dev->name, reason);
+	}
+
+	if (ch->dev->type == ARPHRD_X25) {
+		unsigned char *p;
+		struct sk_buff *skb;
+
+		if ((skb = dev_alloc_skb(1)) == NULL) {
+			printk(KERN_ERR "comxlapb: out of memory!\n");
+			return;
+		}
+		p = skb_put(skb,1);
+		*p = 0x01;		// link established
+		skb->dev = ch->dev;
+		skb->protocol = htons(ETH_P_X25);
+		skb->mac.raw = skb->data;
+		skb->pkt_type = PACKET_HOST;
+
+		netif_rx(skb);
+	}
+
+	for (; comxdir; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+			comxdir->mode = S_IFREG | 0444;
+		}
+	}
+
+
+	ch->line_status |= PROTO_UP;
+	comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_disconnected(void *token, int reason)
+{
+	struct comx_channel *ch = token; 
+	struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n", 
+			ch->dev->name, reason);
+	}
+
+	if (ch->dev->type == ARPHRD_X25) {
+		unsigned char *p;
+		struct sk_buff *skb;
+
+		if ((skb = dev_alloc_skb(1)) == NULL) {
+			printk(KERN_ERR "comxlapb: out of memory!\n");
+			return;
+		}
+		p = skb_put(skb,1);
+		*p = 0x02;		// link disconnected
+		skb->dev = ch->dev;
+		skb->protocol = htons(ETH_P_X25);
+		skb->mac.raw = skb->data;
+		skb->pkt_type = PACKET_HOST;
+
+		netif_rx(skb);
+	}
+
+	for (; comxdir; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+			comxdir->mode = S_IFREG | 0644;
+		}
+	}
+	
+	ch->line_status &= ~PROTO_UP;
+	comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_data_indication(void *token, struct sk_buff *skb)
+{
+	struct comx_channel *ch = token; 
+
+	if (ch->dev->type == ARPHRD_X25) {
+		skb_push(skb, 1);
+		skb->data[0] = 0;	// indicate data for X25
+		skb->protocol = htons(ETH_P_X25);
+	} else {
+		skb->protocol = htons(ETH_P_IP);
+	}
+
+	skb->dev = ch->dev;
+	skb->mac.raw = skb->data;
+	comx_rx(ch->dev, skb);
+}
+
+static void comxlapb_data_transmit(void *token, struct sk_buff *skb)
+{
+	struct comx_channel *ch = token; 
+
+	if (ch->HW_send_packet) {
+		ch->HW_send_packet(ch->dev, skb);
+	}
+}
+
+static int comxlapb_exit(struct net_device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+
+	dev->flags		= 0;
+	dev->type		= 0;
+	dev->mtu		= 0;
+	dev->hard_header_len    = 0;
+
+	ch->LINE_rx	= NULL;
+	ch->LINE_tx	= NULL;
+	ch->LINE_status = NULL;
+	ch->LINE_open	= NULL;
+	ch->LINE_close	= NULL;
+	ch->LINE_xmit	= NULL;
+	ch->LINE_header	= NULL;
+	ch->LINE_statistics = NULL;
+
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(dev, "%s: unregistering lapb\n", dev->name);
+	}
+	lapb_unregister(dev->priv);
+
+	remove_proc_entry(FILENAME_T1, ch->procdir);
+	remove_proc_entry(FILENAME_T2, ch->procdir);
+	remove_proc_entry(FILENAME_N2, ch->procdir);
+	remove_proc_entry(FILENAME_MODE, ch->procdir);
+	remove_proc_entry(FILENAME_WINDOW, ch->procdir);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int comxlapb_init(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct lapb_register_struct lapbreg;
+
+	dev->mtu		= 1500;
+	dev->hard_header_len    = 4;
+	dev->addr_len		= 0;
+
+	ch->LINE_rx	= comxlapb_rx;
+	ch->LINE_tx	= comxlapb_tx;
+	ch->LINE_status = comxlapb_status;
+	ch->LINE_open	= comxlapb_open;
+	ch->LINE_close	= comxlapb_close;
+	ch->LINE_xmit	= comxlapb_xmit;
+	ch->LINE_header	= comxlapb_header;
+	ch->LINE_statistics = comxlapb_statistics;
+
+	lapbreg.connect_confirmation = comxlapb_connected;
+	lapbreg.connect_indication = comxlapb_connected;
+	lapbreg.disconnect_confirmation = comxlapb_disconnected;
+	lapbreg.disconnect_indication = comxlapb_disconnected;
+	lapbreg.data_indication = comxlapb_data_indication;
+	lapbreg.data_transmit = comxlapb_data_transmit;
+	if (lapb_register(dev->priv, &lapbreg)) {
+		return -ENOMEM;
+	}
+	if (ch->debug_flags & DEBUG_COMX_LAPB) {
+		comx_debug(dev, "%s: lapb registered\n", dev->name);
+	}
+
+	if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) {
+		return -ENOMEM;
+	}
+	if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) {
+		return -ENOMEM;
+	}
+	if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) {
+		return -ENOMEM;
+	}
+	if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) {
+		return -ENOMEM;
+	}
+	if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) {
+		return -ENOMEM;
+	}
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int comxlapb_init_lapb(struct net_device *dev) 
+{
+	dev->flags	= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+	dev->type	= ARPHRD_LAPB;
+
+	return(comxlapb_init(dev));
+}
+
+static int comxlapb_init_x25(struct net_device *dev)
+{
+	dev->flags		= IFF_NOARP;
+	dev->type		= ARPHRD_X25;
+
+	return(comxlapb_init(dev));
+}
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+	int size, struct proc_dir_entry *dir)
+{
+	struct proc_dir_entry *new_file;
+
+	if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+		new_file->data = (void *)new_file;
+		new_file->read_proc = &comxlapb_read_proc;
+		new_file->write_proc = &comxlapb_write_proc;
+		new_file->proc_iops = &comx_normal_inode_ops;
+		new_file->size = size;
+		new_file->nlink = 1;
+	}
+	return(new_file);
+}
+
+static struct comx_protocol comxlapb_protocol = {
+	"lapb", 
+	VERSION,
+	ARPHRD_LAPB, 
+	comxlapb_init_lapb, 
+	comxlapb_exit, 
+	NULL 
+};
+
+static struct comx_protocol comx25_protocol = {
+	"x25", 
+	VERSION,
+	ARPHRD_X25, 
+	comxlapb_init_x25, 
+	comxlapb_exit, 
+	NULL 
+};
+
+#ifdef MODULE
+#define comx_proto_lapb_init init_module
+#endif
+
+__initfunc(int comx_proto_lapb_init(void))
+{
+	int ret;
+
+	if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
+		return ret;
+	}
+	return comx_register_protocol(&comx25_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_protocol(comxlapb_protocol.name);
+	comx_unregister_protocol(comx25_protocol.name);
+}
+#endif /* MODULE */
+
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx-proto-ppp.c linux/drivers/net/wan/comx-proto-ppp.c
--- v2.3.51/linux/drivers/net/wan/comx-proto-ppp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comx-proto-ppp.c	Mon Mar 13 13:55:09 2000
@@ -0,0 +1,269 @@
+/*
+ * Synchronous PPP / Cisco-HDLC driver for the COMX boards
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton code by Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ *
+ * Version 0.10 (99/06/10):
+ *		- written the first code :)
+ *
+ * Version 0.20 (99/06/16):
+ *		- added hdlc protocol 
+ *		- protocol up is IFF_RUNNING
+ *
+ * Version 0.21 (99/07/15):
+ *		- some small fixes with the line status
+ *
+ * Version 0.22 (99/08/05):
+ *		- don't test IFF_RUNNING but the pp_link_state of the sppp
+ * 
+ * Version 0.23 (99/12/02):
+ *		- tbusy fixes
+ *
+ */
+
+#define VERSION "0.23"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#include	"syncppp.h"
+#include	"comx.h"
+
+MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
+
+static struct comx_protocol syncppp_protocol;
+static struct comx_protocol hdlc_protocol;
+
+struct syncppp_data {
+	struct timer_list status_timer;
+};
+
+static void syncppp_status_timerfun(unsigned long d) {
+	struct net_device *dev=(struct net_device *)d;
+	struct comx_channel *ch=dev->priv;
+	struct syncppp_data *spch=ch->LINE_privdata;
+	struct sppp *sp = (struct sppp *)sppp_of(dev);
+        
+	if(!(ch->line_status & PROTO_UP) && 
+	    (sp->pp_link_state==SPPP_LINK_UP)) {
+    		comx_status(dev, ch->line_status | PROTO_UP);
+	}
+	if((ch->line_status & PROTO_UP) &&
+	    (sp->pp_link_state==SPPP_LINK_DOWN)) {
+	    	comx_status(dev, ch->line_status & ~PROTO_UP);
+	}
+	mod_timer(&spch->status_timer,jiffies + HZ*3);
+}
+
+static int syncppp_tx(struct net_device *dev) 
+{
+	struct comx_channel *ch=dev->priv;
+	
+	if(ch->line_status & LINE_UP) {
+		netif_wake_queue(dev);
+	}
+	return 0;
+}
+
+static void syncppp_status(struct net_device *dev, unsigned short status)
+{
+	status &= ~(PROTO_UP | PROTO_LOOP);
+	if(status & LINE_UP) {
+		netif_wake_queue(dev);
+		sppp_open(dev);
+	} else 	{
+		/* Line went down */
+		netif_stop_queue(dev);
+		sppp_close(dev);
+	}
+	comx_status(dev, status);
+}
+
+static int syncppp_open(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct syncppp_data *spch = ch->LINE_privdata;
+
+	if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+
+	ch->init_status |= LINE_OPEN;
+	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+	if(ch->line_status & LINE_UP) {
+		sppp_open(dev);
+	}
+
+	init_timer(&spch->status_timer);
+	spch->status_timer.function=syncppp_status_timerfun;
+	spch->status_timer.data=(unsigned long)dev;
+	spch->status_timer.expires=jiffies + HZ*3;
+	add_timer(&spch->status_timer);
+	
+	return 0;
+}
+
+static int syncppp_close(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct syncppp_data *spch = ch->LINE_privdata;
+
+	if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+	del_timer(&spch->status_timer);
+	
+	sppp_close(dev);
+
+	ch->init_status &= ~LINE_OPEN;
+	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+	return 0;
+}
+
+static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+
+	netif_stop_queue(dev);
+	switch(ch->HW_send_packet(dev, skb)) {
+		case FRAME_QUEUED:
+			netif_wake_queue(dev);
+			break;
+		case FRAME_ACCEPTED:
+		case FRAME_DROPPED:
+			break;
+		case FRAME_ERROR:
+			printk(KERN_ERR "%s: Transmit frame error (len %d)\n", 
+				dev->name, skb->len);
+		break;
+	}
+	return 0;
+}
+
+
+static int syncppp_statistics(struct net_device *dev, char *page) 
+{
+	int len = 0;
+
+	len += sprintf(page + len, " ");
+	return len;
+}
+
+
+static int syncppp_exit(struct net_device *dev) 
+{
+	struct comx_channel *ch = dev->priv;
+
+	sppp_detach(dev);
+
+	dev->flags = 0;
+	dev->type = 0;
+	dev->mtu = 0;
+
+	ch->LINE_rx = NULL;
+	ch->LINE_tx = NULL;
+	ch->LINE_status = NULL;
+	ch->LINE_open = NULL;
+	ch->LINE_close = NULL;
+	ch->LINE_xmit = NULL;
+	ch->LINE_header	= NULL;
+	ch->LINE_rebuild_header	= NULL;
+	ch->LINE_statistics = NULL;
+
+	kfree(ch->LINE_privdata);
+	ch->LINE_privdata = NULL;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int syncppp_init(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr;
+
+	ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
+
+	pppdev->dev = dev;
+	sppp_attach(pppdev);
+
+	if(ch->protocol == &hdlc_protocol) {
+		pppdev->sppp.pp_flags |= PP_CISCO;
+		dev->type = ARPHRD_HDLC;
+	} else {
+		pppdev->sppp.pp_flags &= ~PP_CISCO;
+		dev->type = ARPHRD_PPP;
+	}
+
+	ch->LINE_rx = sppp_input;
+	ch->LINE_tx = syncppp_tx;
+	ch->LINE_status = syncppp_status;
+	ch->LINE_open = syncppp_open;
+	ch->LINE_close = syncppp_close;
+	ch->LINE_xmit = syncppp_xmit;
+	ch->LINE_header	= NULL;
+	ch->LINE_statistics = syncppp_statistics;
+
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static struct comx_protocol syncppp_protocol = {
+	"ppp", 
+	VERSION,
+	ARPHRD_PPP, 
+	syncppp_init, 
+	syncppp_exit, 
+	NULL 
+};
+
+static struct comx_protocol hdlc_protocol = {
+	"hdlc", 
+	VERSION,
+	ARPHRD_PPP, 
+	syncppp_init, 
+	syncppp_exit, 
+	NULL 
+};
+
+
+#ifdef MODULE
+#define comx_proto_ppp_init init_module
+#endif
+
+int __init comx_proto_ppp_init(void)
+{
+	int ret;
+
+	if(0!=(ret=comx_register_protocol(&hdlc_protocol))) {
+		return ret;
+	}
+	return comx_register_protocol(&syncppp_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	comx_unregister_protocol(syncppp_protocol.name);
+	comx_unregister_protocol(hdlc_protocol.name);
+}
+#endif /* MODULE */
+
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx.c linux/drivers/net/wan/comx.c
--- v2.3.51/linux/drivers/net/wan/comx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comx.c	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,1238 @@
+/*
+ * Device driver framework for the COMX line of synchronous serial boards
+ * 
+ * for Linux kernel 2.2.X
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co.
+ *
+ * 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.
+ *
+ * Version 0.80 (99/06/11):
+ *		- clean up source code (playing a bit of indent)
+ *		- port back to kernel, add support for non-module versions
+ *		- add support for board resets when channel protocol is down
+ *		- reset the device structure after protocol exit
+ *		  the syncppp driver needs it
+ *		- add support for /proc/comx/protocols and 
+ *		  /proc/comx/boardtypes
+ *
+ * Version 0.81 (99/06/21):
+ *		- comment out the board reset support code, the locomx
+ *		  driver seems not buggy now
+ *		- printk() levels fixed
+ *
+ * Version 0.82 (99/07/08):
+ *		- Handle stats correctly if the lowlevel driver is
+ *		  is not a comx one (locomx - z85230)
+ *
+ * Version 0.83 (99/07/15):
+ *		- reset line_status when interface is down
+ *
+ * Version 0.84 (99/12/01):
+ *		- comx_status should not check for IFF_UP (to report
+ *		  line status from dev->open())
+ */
+
+#define VERSION "0.84"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#ifndef CONFIG_PROC_FS
+#error For now, COMX really needs the /proc filesystem
+#endif
+
+#include "comx.h"
+#include "syncppp.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
+
+extern int comx_hw_comx_init(void);
+extern int comx_hw_locomx_init(void);
+extern int comx_hw_mixcom_init(void);
+extern int comx_proto_hdlc_init(void);
+extern int comx_proto_ppp_init(void);
+extern int comx_proto_syncppp_init(void);
+extern int comx_proto_lapb_init(void);
+extern int comx_proto_fr_init(void);
+
+static struct comx_hardware *comx_channels = NULL;
+static struct comx_protocol *comx_lines = NULL;
+
+struct inode_operations comx_normal_inode_ops; 
+static struct inode_operations comx_root_inode_ops; // for mkdir
+static struct inode_operations comx_debug_inode_ops; // mas a file_ops
+static struct file_operations comx_normal_file_ops;  // with open/relase
+static struct file_operations comx_debug_file_ops;  // with lseek+read
+
+static void comx_delete_dentry(struct dentry *dentry);
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+	int size, struct proc_dir_entry *dir);
+
+static void comx_fill_inode(struct inode *inode, int fill);
+
+static struct dentry_operations comx_dentry_operations = {
+	NULL,			/* revalidate */
+	NULL,			/* d_hash */
+	NULL, 			/* d_compare */
+	&comx_delete_dentry	/* d_delete */
+};
+
+
+struct proc_dir_entry comx_root_dir = {
+	0, 4, "comx",
+	S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0,
+	0, &comx_root_inode_ops,
+	NULL, comx_fill_inode,
+	NULL, &proc_root, NULL 
+};
+
+struct comx_debugflags_struct	comx_debugflags[] = {
+	{ "comx_rx",		DEBUG_COMX_RX		},
+	{ "comx_tx", 		DEBUG_COMX_TX		},
+	{ "hw_tx",		DEBUG_HW_TX		},
+	{ "hw_rx", 		DEBUG_HW_RX		},
+	{ "hdlc_keepalive",	DEBUG_HDLC_KEEPALIVE	},
+	{ "comxppp",		DEBUG_COMX_PPP		},
+	{ "comxlapb",		DEBUG_COMX_LAPB		},
+	{ "dlci",		DEBUG_COMX_DLCI		},
+	{ NULL,			0			} 
+};
+
+static void comx_fill_inode(struct inode *inode, int fill)
+{
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+}
+
+
+int comx_debug(struct net_device *dev, char *fmt, ...)
+{
+	struct comx_channel *ch = dev->priv;
+	char *page,*str;
+	va_list args;
+	int len;
+
+	if (!ch->debug_area) return 0;
+
+	if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
+
+	va_start(args, fmt);
+	len = vsprintf(str = page, fmt, args);
+	va_end(args);
+
+	if (len >= PAGE_SIZE) {
+		printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
+		free_page((unsigned long)page);
+		return -EINVAL;
+	}
+
+	while (len) {
+		int to_copy;
+		int free = (ch->debug_start - ch->debug_end + ch->debug_size) 
+			% ch->debug_size;
+
+		to_copy = min( free ? free : ch->debug_size, 
+			min (ch->debug_size - ch->debug_end, len) );
+		memcpy(ch->debug_area + ch->debug_end, str, to_copy);
+		str += to_copy;
+		len -= to_copy;
+		ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
+		if (ch->debug_start == ch->debug_end) // Full ? push start away
+			ch->debug_start = (ch->debug_start + len + 1) % 
+					ch->debug_size;
+		ch->debug_file->size = (ch->debug_end - ch->debug_start +
+					ch->debug_size) % ch->debug_size;
+	} 
+
+	free_page((unsigned long)page);
+	return 0;
+}
+
+int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (!ch->debug_area) return 0;
+	if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
+	if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
+
+	return comx_debug_bytes(dev, skb->data, skb->len, msg);
+}
+
+int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len, 
+		char *msg)
+{
+	int pos = 0;
+	struct comx_channel *ch = dev->priv;
+
+	if (!ch->debug_area) return 0;
+
+	comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
+
+	while (pos != len) {
+		char line[80];
+		int i = 0;
+
+		memset(line, 0, 80);
+		sprintf(line,"%04d ", pos);
+		do {
+			sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
+			sprintf(line + 60 + (pos % 16), "%c", 
+				isprint(bytes[pos]) ? bytes[pos] : '.');
+			pos++;
+		} while (pos != len && pos % 16);
+
+		while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
+		line[77] = '\n';
+		line[78] = 0;
+	
+		comx_debug(dev, "%s", line);
+	}
+	comx_debug(dev, "\n");
+	return 0;
+}
+
+static void comx_loadavg_timerfun(unsigned long d)
+{
+	struct net_device *dev = (struct net_device *)d;
+	struct comx_channel *ch = dev->priv;
+
+	ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
+	ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] = 
+		ch->current_stats->tx_bytes;
+
+	ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
+
+	mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
+}
+
+#if 0
+static void comx_reset_timerfun(unsigned long d)
+{ 
+	struct net_device *dev = (struct net_device *)d;
+	struct comx_channel *ch = dev->priv;
+
+	if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
+		if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
+			ch->HW_reset(dev);
+		}
+	}
+
+	mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
+}
+#endif                                            
+
+static int comx_open(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *comxdir = ch->procdir->subdir;
+	int ret=0;
+
+	if (!ch->protocol || !ch->hardware) return -ENODEV;
+
+	if ((ret = ch->HW_open(dev))) return ret;
+	if ((ret = ch->LINE_open(dev))) { 
+		ch->HW_close(dev); 
+		return ret; 
+	};
+
+	for (; comxdir ; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+		   strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+			comxdir->mode = S_IFREG | 0444;
+	}
+
+#if 0
+	ch->reset_pending = 1;
+	ch->reset_timeout = 30;
+	ch->reset_timer.function = comx_reset_timerfun;
+	ch->reset_timer.data = (unsigned long)dev;
+	ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
+	add_timer(&ch->reset_timer);
+#endif
+
+	return 0;
+}
+
+static int comx_close(struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	struct proc_dir_entry *comxdir = ch->procdir->subdir;
+	int ret = -ENODEV;
+
+	if (test_and_clear_bit(0, &ch->lineup_pending)) {
+		del_timer(&ch->lineup_timer);
+	}
+
+#if 0	
+	del_timer(&ch->reset_timer);
+#endif
+
+	if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
+		ret = ch->LINE_close(dev);
+	}
+
+	if (ret) return ret;
+
+	if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
+		ret = ch->HW_close(dev);
+	}
+	
+	ch->line_status=0;
+
+	for (; comxdir ; comxdir = comxdir->next) {
+		if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+		    strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+			comxdir->mode = S_IFREG | 0644;
+	}
+
+	return ret;
+}
+
+void comx_status(struct net_device *dev, int status)
+{
+	struct comx_channel *ch = dev->priv;
+
+#if 0
+	if(status & (PROTO_UP | PROTO_LOOP)) {
+		clear_bit(0,&ch->reset_pending);
+	}
+#endif
+
+	printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
+		    dev->name, status & LINE_UP ? "UP" : "DOWN", 
+		    status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ? 
+		    "UP" : "DOWN");
+	
+	ch->line_status = status;
+}
+
+static int comx_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct comx_channel *ch = dev->priv;
+	int rc;
+
+	if (skb->len > dev->mtu + dev->hard_header_len) {
+		printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
+		(int)skb->len, dev->mtu);
+	}
+	
+	if (ch->debug_flags & DEBUG_COMX_TX) {
+		comx_debug_skb(dev, skb, "comx_xmit skb");
+	}
+	
+	rc=ch->LINE_xmit(skb, dev);
+//	if (!rc) dev_kfree_skb(skb);
+
+	return rc;
+}
+
+static int comx_header(struct sk_buff *skb, struct net_device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (ch->LINE_header) {
+		return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
+	} else {
+		return 0;
+	}
+}
+
+static int comx_rebuild_header(struct sk_buff *skb) 
+{
+	struct net_device *dev = skb->dev;
+	struct comx_channel *ch = dev->priv;
+
+	if (ch->LINE_rebuild_header) {
+		return(ch->LINE_rebuild_header(skb));
+	} else {
+		return 0;
+	}
+}
+
+int comx_rx(struct net_device *dev, struct sk_buff *skb)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (ch->debug_flags & DEBUG_COMX_RX) {
+		comx_debug_skb(dev, skb, "comx_rx skb");
+	}
+	if (skb) {
+		netif_rx(skb);
+	}
+	return 0;
+}
+
+static struct net_device_stats *comx_stats(struct net_device *dev)
+{
+	struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+	return ch->current_stats;
+}
+
+void comx_lineup_func(unsigned long d)
+{
+	struct net_device *dev = (struct net_device *)d;
+	struct comx_channel *ch = dev->priv;
+
+	del_timer(&ch->lineup_timer);
+	clear_bit(0, &ch->lineup_pending);
+
+	if (ch->LINE_status) {
+		ch->LINE_status(dev, ch->line_status |= LINE_UP);
+	}
+}
+
+#define LOADAVG(avg, off) (int) \
+	((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
+	% ch->loadavg_size + off] -  ch->avg_bytes[(ch->loadavg_counter - 1 \
+		- ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
+		% ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
+
+static int comx_statistics(struct net_device *dev, char *page)
+{
+	struct comx_channel *ch = dev->priv;
+	int len = 0;
+	int tmp;
+	int i = 0;
+	char tmpstr[20];
+	int tmpstrlen = 0;
+
+	len += sprintf(page + len, "Interface administrative status is %s, "
+		"modem status is %s, protocol is %s\n", 
+		dev->flags & IFF_UP ? "UP" : "DOWN",
+		ch->line_status & LINE_UP ? "UP" : "DOWN",
+		ch->line_status & PROTO_LOOP ? "LOOP" : 
+		ch->line_status & PROTO_UP ? "UP" : "DOWN");
+	len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
+		"is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ? 
+		ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+	len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
+		LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
+	tmpstr[0] = 0;
+	for (i=0; i != 3; i++) {
+		char tf;
+
+		tf = ch->loadavg[i] % 60 == 0 && 
+			ch->loadavg[i] / 60 > 0 ? 'm' : 's';
+		tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s", 
+			ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf, 
+			i == 2 ? ")\n" : "/");
+	}
+	len += sprintf(page + len, 
+		"%s              (output): %d / %d / %d bits/s (%s", tmpstr, 
+		LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size), 
+		LOADAVG(2, ch->loadavg_size), tmpstr);
+
+	len += sprintf(page + len, "Debug flags: ");
+	tmp = len; i = 0;
+	while (comx_debugflags[i].name) {
+		if (ch->debug_flags & comx_debugflags[i].value) 
+			len += sprintf(page + len, "%s ", 
+				comx_debugflags[i].name);
+		i++;
+	}
+	len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
+
+	len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
+		"aborts: %lu\n           buffer overrun: %lu, pbuffer overrun: %lu\n"
+		"TX errors: underrun: %lu\n",
+		ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors, 
+		ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors, 
+		ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
+		ch->current_stats->tx_fifo_errors);
+
+	if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
+		len += ch->LINE_statistics(dev, page + len);
+	} else {
+		len += sprintf(page+len, "Line status: driver not initialized\n");
+	}
+	if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
+		len += ch->HW_statistics(dev, page + len);
+	} else {
+		len += sprintf(page+len, "Board status: driver not initialized\n");
+	}
+
+	return len;
+}
+
+static int comx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct comx_channel *ch = dev->priv;
+
+	if (ch->LINE_ioctl) {
+		return(ch->LINE_ioctl(dev, ifr, cmd));
+	}
+	return -EINVAL;
+}
+
+static void comx_reset_dev(struct net_device *dev)
+{
+	dev->open = comx_open;
+	dev->stop = comx_close;
+	dev->hard_start_xmit = comx_xmit;
+	dev->hard_header = comx_header;
+	dev->rebuild_header = comx_rebuild_header;
+	dev->get_stats = comx_stats;
+	dev->do_ioctl = comx_ioctl;
+	dev->change_mtu = NULL;
+	dev->tx_queue_len = 20;
+	dev->flags = IFF_NOARP;
+}
+
+static int comx_init_dev(struct net_device *dev)
+{
+	struct comx_channel *ch;
+
+	if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(ch, 0, sizeof(struct comx_channel));
+
+	ch->loadavg[0] = 5;
+	ch->loadavg[1] = 300;
+	ch->loadavg[2] = 900;
+	ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1; 
+	if ((ch->avg_bytes = kmalloc(ch->loadavg_size * 
+		sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+
+	memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
+	ch->loadavg_counter = 0;
+	ch->loadavg_timer.function = comx_loadavg_timerfun;
+	ch->loadavg_timer.data = (unsigned long)dev;
+	ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
+	add_timer(&ch->loadavg_timer);
+
+	dev->priv = (void *)ch;
+	ch->dev = dev;
+	ch->line_status &= ~LINE_UP;
+
+	ch->current_stats = &ch->stats;
+
+	comx_reset_dev(dev);
+	return 0;
+}
+
+static int comx_read_proc(char *page, char **start, off_t off, int count, 
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct net_device *dev = file->parent->data;
+	struct comx_channel *ch=(struct comx_channel *)dev->priv;
+	int len = 0;
+
+	if (strcmp(file->name, FILENAME_STATUS) == 0) {
+		len = comx_statistics(dev, page);
+	} else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
+		len = sprintf(page, "%s\n", ch->hardware ? 
+			ch->hardware->name : HWNAME_NONE);
+	} else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
+		len = sprintf(page, "%s\n", ch->protocol ? 
+			ch->protocol->name : PROTONAME_NONE);
+	} else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
+		len = sprintf(page, "%01d\n", ch->lineup_delay);
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return( min(count, len - off) );
+}
+
+
+static int comx_root_read_proc(char *page, char **start, off_t off, int count, 
+	int *eof, void *data)
+{
+	struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+	struct comx_hardware *hw;
+	struct comx_protocol *line;
+
+	int len = 0;
+
+	if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
+		for(hw=comx_channels;hw;hw=hw->next) 
+			len+=sprintf(page+len, "%s\n", hw->name);
+	} else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
+		for(line=comx_lines;line;line=line->next)
+			len+=sprintf(page+len, "%s\n", line->name);
+	}
+
+	if (off >= len) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = page + off;
+	if (count >= len - off) {
+		*eof = 1;
+	}
+	return( min(count, len - off) );
+}
+
+
+
+static int comx_write_proc(struct file *file, const char *buffer, u_long count,
+	void *data)
+{
+	struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+	struct net_device *dev = (struct net_device *)entry->parent->data;
+	struct comx_channel *ch=(struct comx_channel *)dev->priv;
+	char *page;
+	struct comx_hardware *hw = comx_channels;
+	struct comx_protocol *line = comx_lines;
+	char str[30];
+	int ret=0;
+
+	if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+		printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+		return -EIO;
+	}
+
+	if (count > PAGE_SIZE) {
+		printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+		return -ENOSPC;
+	}
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+	copy_from_user(page, buffer, count);
+
+	if (*(page + count - 1) == '\n') *(page + count - 1) = 0;
+
+	if (strcmp(entry->name, FILENAME_DEBUG) == 0) {
+		int i;
+		int ret = 0;
+
+		if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+			unsigned long flags;
+
+			save_flags(flags); cli();
+			if (ch->debug_area) kfree(ch->debug_area);
+			if ((ch->debug_area = kmalloc(ch->debug_size = i, 
+				GFP_KERNEL)) == NULL) {
+				ret = -ENOMEM;
+			}
+			ch->debug_start = ch->debug_end = 0;
+			restore_flags(flags);
+			free_page((unsigned long)page);
+			return count;
+		}
+		
+		if (*page != '+' && *page != '-') {
+			free_page((unsigned long)page);
+			return -EINVAL;
+		}
+		while (comx_debugflags[i].value && 
+			strncmp(comx_debugflags[i].name, page + 1, 
+			strlen(comx_debugflags[i].name))) {
+			i++;
+		}
+	
+		if (comx_debugflags[i].value == 0) {
+			printk(KERN_ERR "Invalid debug option\n");
+			free_page((unsigned long)page);
+			return -EINVAL;
+		}
+		if (*page == '+') {
+			ch->debug_flags |= comx_debugflags[i].value;
+		} else {
+			ch->debug_flags &= ~comx_debugflags[i].value;
+		}
+	} else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
+		if(strlen(page)>10) {
+			free_page((unsigned long)page);
+			return -EINVAL;
+		}
+		while (hw) { 
+			if (strcmp(hw->name, page) == 0) {
+				break;
+			} else {
+				hw = hw->next;
+			}
+		}
+#ifdef CONFIG_KMOD
+		if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
+			sprintf(str,"comx-hw-%s",page);
+			request_module(str);
+		}		
+		hw=comx_channels;
+		while (hw) {
+			if (comx_strcasecmp(hw->name, page) == 0) {
+				break;
+			} else {
+				hw = hw->next;
+			}
+		}
+#endif
+
+		if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw)  {
+			free_page((unsigned long)page);
+			return -ENODEV;
+		}
+		if (ch->init_status & HW_OPEN) {
+			free_page((unsigned long)page);
+			return -EBUSY;
+		}
+		if (ch->hardware && ch->hardware->hw_exit && 
+		   (ret=ch->hardware->hw_exit(dev))) {
+			free_page((unsigned long)page);
+			return ret;
+		}
+		ch->hardware = hw;
+		entry->size = strlen(page) + 1;
+		if (hw && hw->hw_init) hw->hw_init(dev);
+	} else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
+		if(strlen(page)>10) {
+			free_page((unsigned long)page);
+			return -EINVAL;
+		}
+		while (line) {
+			if (comx_strcasecmp(line->name, page) == 0) {
+				break;
+			} else {
+				line = line->next;
+			}
+		}
+#ifdef CONFIG_KMOD
+		if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
+			sprintf(str,"comx-proto-%s",page);
+			request_module(str);
+		}		
+		line=comx_lines;
+		while (line) {
+			if (comx_strcasecmp(line->name, page) == 0) {
+				break;
+			} else {
+				line = line->next;
+			}
+		}
+#endif
+		
+		if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
+			free_page((unsigned long)page);
+			return -ENODEV;
+		}
+		
+		if (ch->init_status & LINE_OPEN) {
+			free_page((unsigned long)page);
+			return -EBUSY;
+		}
+		
+		if (ch->protocol && ch->protocol->line_exit && 
+		    (ret=ch->protocol->line_exit(dev))) {
+			free_page((unsigned long)page);
+			return ret;
+		}
+		ch->protocol = line;
+		entry->size = strlen(page) + 1;
+		comx_reset_dev(dev);
+		if (line && line->line_init) line->line_init(dev);
+	} else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
+		int i;
+
+		if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+			if (i >=0 && i < 10) { 
+				ch->lineup_delay = i; 
+			} else {
+				printk(KERN_ERR "comx: invalid lineup_delay value\n");
+			}
+		}
+	}
+
+	free_page((unsigned long)page);
+	return count;
+}
+
+static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig)
+{
+	switch(orig) {
+		case 0:	
+			file->f_pos = max(0, min(offset, 
+				file->f_dentry->d_inode->i_size));
+			return(file->f_pos);
+		case 1: 
+			file->f_pos = max(0, min(offset + file->f_pos, 
+				file->f_dentry->d_inode->i_size));
+			return(file->f_pos);
+		case 2: 
+			file->f_pos = max(0, 
+				min(offset + file->f_dentry->d_inode->i_size, 
+				file->f_dentry->d_inode->i_size));
+			return(file->f_pos);
+	}
+	return(file->f_pos);
+}
+
+static int comx_file_open(struct inode *inode, struct file *file)
+{
+
+	if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) {
+		return -EACCES;
+	}
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int comx_file_release(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count,
+	loff_t *ppos)
+{
+	struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip;
+	struct net_device *dev = de->parent->data;
+	struct comx_channel *ch = dev->priv;
+	loff_t copied = 0;
+	unsigned long flags;
+
+	save_flags(flags); cli(); // We may run into trouble when debug_area is filled
+				  // from irq inside read. no problem if the buffer is
+				  // large enough
+
+	while (count > 0 && ch->debug_start != ch->debug_end) {
+		int len;
+
+		len = min( (ch->debug_end - ch->debug_start + ch->debug_size)
+			%ch->debug_size, min (ch->debug_size - 
+			ch->debug_start, count));
+
+		if (len) copy_to_user(buffer + copied, 
+			ch->debug_area + ch->debug_start, len);
+		ch->debug_start = (ch->debug_start + len) % ch->debug_size;
+
+		de->size -= len;
+		count -= len;
+		copied += len;
+	}
+
+	restore_flags(flags);
+	return copied;
+}
+
+static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct proc_dir_entry *new_dir, *debug_file;
+	struct net_device *dev;
+	struct comx_channel *ch;
+
+	if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+	if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, 
+		&comx_root_dir)) == NULL) {
+		return -EIO;
+	}
+
+	new_dir->ops = &proc_dir_inode_operations;  // ez egy normalis /proc konyvtar
+	new_dir->nlink = 2;
+	new_dir->data = NULL; // ide jon majd a struct dev
+
+	/* Ezek kellenek */
+	if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644, 
+	    strlen(HWNAME_NONE) + 1, new_dir)) {
+		return -ENOMEM;
+	}
+	if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644, 
+	    strlen(PROTONAME_NONE) + 1, new_dir)) {
+		return -ENOMEM;
+	}
+	if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
+		return -ENOMEM;
+	}
+	if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
+		return -ENOMEM;
+	}
+
+	if ((debug_file = create_proc_entry(FILENAME_DEBUG, 
+	    S_IFREG | 0644, new_dir)) == NULL) {
+		return -ENOMEM;
+	}
+	debug_file->ops = &comx_debug_inode_ops;
+	debug_file->data = (void *)debug_file; 
+	debug_file->read_proc = NULL; // see below
+	debug_file->write_proc = &comx_write_proc;
+	debug_file->nlink = 1;
+
+	if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(dev, 0, sizeof(struct net_device));
+	dev->name = (char *)new_dir->name;
+	dev->init = comx_init_dev;
+
+	if (register_netdevice(dev)) {
+		return -EIO;
+	}
+	ch=dev->priv;
+	if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device), 
+				 GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+	memset(ch->if_ptr, 0, sizeof(struct ppp_device));
+	ch->debug_file = debug_file; 
+	ch->procdir = new_dir;
+	new_dir->data = dev;
+
+	ch->debug_start = ch->debug_end = 0;
+	if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE, 
+	    GFP_KERNEL)) == NULL) {
+		return -ENOMEM;
+	}
+
+	ch->lineup_delay = DEFAULT_LINEUP_DELAY;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int comx_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip;
+	struct net_device *dev = entry->data;
+	struct comx_channel *ch = dev->priv;
+	int ret;
+
+	/* Egyelore miert ne ? */
+	if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+	if (dev->flags & IFF_UP) {
+		printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
+		return -EBUSY;
+	}
+
+	if (ch->protocol && ch->protocol->line_exit && 
+	    (ret=ch->protocol->line_exit(dev))) {
+		return ret;
+	}
+	if (ch->hardware && ch->hardware->hw_exit && 
+	   (ret=ch->hardware->hw_exit(dev))) { 
+		if(ch->protocol && ch->protocol->line_init) {
+			ch->protocol->line_init(dev);
+		}
+		return ret;
+	}
+	ch->protocol = NULL;
+	ch->hardware = NULL;
+
+	del_timer(&ch->loadavg_timer);
+	kfree(ch->avg_bytes);
+
+	unregister_netdev(dev);
+	if (ch->debug_area) {
+		kfree(ch->debug_area);
+	}
+	if (dev->priv) {
+		kfree(dev->priv);
+	}
+	kfree(dev);
+
+	remove_proc_entry(FILENAME_DEBUG, entry);
+	remove_proc_entry(FILENAME_LINEUPDELAY, entry);
+	remove_proc_entry(FILENAME_STATUS, entry);
+	remove_proc_entry(FILENAME_HARDWARE, entry);
+	remove_proc_entry(FILENAME_PROTOCOL, entry);
+	remove_proc_entry(dentry->d_name.name, &comx_root_dir);
+//	proc_unregister(&comx_root_dir, dentry->d_inode->i_ino);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry)
+{
+	struct proc_dir_entry *de;
+	struct inode *inode = NULL;
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		return ERR_PTR(-ENOTDIR);
+	}
+
+	if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) {
+		for (de = de->subdir ; de ; de = de->next) {
+			if ((de && de->low_ino) && 
+			    (de->namelen == dentry->d_name.len) &&
+			    (memcmp(dentry->d_name.name, de->name, 
+			    de->namelen) == 0))	{
+			 	if ((inode = proc_get_inode(dir->i_sb, 
+			 	    de->low_ino, de)) == NULL) { 
+			 		printk(KERN_ERR "COMX: lookup error\n"); 
+			 		return ERR_PTR(-EINVAL); 
+			 	}
+				break;
+			}
+		}
+	}
+	dentry->d_op = &comx_dentry_operations;
+	d_add(dentry, inode);
+	return NULL;
+}
+
+int comx_strcasecmp(const char *cs, const char *ct)
+{
+	register signed char __res;
+
+	while (1) {
+		if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
+			break;
+		}
+	}
+	return __res;
+}
+
+static void comx_delete_dentry(struct dentry *dentry)
+{
+	d_drop(dentry);
+}
+
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+	int size, struct proc_dir_entry *dir)
+{
+	struct proc_dir_entry *new_file;
+
+	if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+		new_file->ops = &comx_normal_inode_ops;
+		new_file->data = (void *)new_file;
+		new_file->read_proc = &comx_read_proc;
+		new_file->write_proc = &comx_write_proc;
+		new_file->size = size;
+		new_file->nlink = 1;
+	}
+	return(new_file);
+}
+
+int comx_register_hardware(struct comx_hardware *comx_hw)
+{
+	struct comx_hardware *hw = comx_channels;
+
+	if (!hw) {
+		comx_channels = comx_hw;
+	} else {
+		while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
+			hw = hw->next;
+		}
+		if (strcmp(comx_hw->name, hw->name) == 0) {
+			return -1;
+		}
+		hw->next = comx_hw;
+	}
+
+	printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
+	return 0;
+}
+
+int comx_unregister_hardware(char *name)
+{
+	struct comx_hardware *hw = comx_channels;
+
+	if (!hw) {
+		return -1;
+	}
+
+	if (strcmp(hw->name, name) == 0) {
+		comx_channels = comx_channels->next;
+		return 0;
+	}
+
+	while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
+		hw = hw->next;
+	}
+
+	if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
+		hw->next = hw->next->next;
+		return 0;
+	}
+	return -1;
+}
+
+int comx_register_protocol(struct comx_protocol *comx_line)
+{
+	struct comx_protocol *pr = comx_lines;
+
+	if (!pr) {
+		comx_lines = comx_line;
+	} else {
+		while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
+			pr = pr->next;
+		}
+		if (strcmp(comx_line->name, pr->name) == 0) {
+			return -1;
+		}
+		pr->next = comx_line;
+	}
+
+	printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
+	return 0;
+}
+
+int comx_unregister_protocol(char *name)
+{
+	struct comx_protocol *pr = comx_lines;
+
+	if (!pr) {
+		return -1;
+	}
+
+	if (strcmp(pr->name, name) == 0) {
+		comx_lines = comx_lines->next;
+		return 0;
+	}
+
+	while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
+		pr = pr->next;
+	}
+
+	if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
+		pr->next = pr->next->next;
+		return 0;
+	}
+	return -1;
+}
+
+#ifdef MODULE
+#define comx_init init_module
+#endif
+
+__initfunc(int comx_init(void))
+{
+	struct proc_dir_entry *new_file;
+
+	memcpy(&comx_root_inode_ops, &proc_dir_inode_operations, 
+		sizeof(struct inode_operations));
+	comx_root_inode_ops.lookup = &comx_lookup;
+	comx_root_inode_ops.mkdir = &comx_mkdir;
+	comx_root_inode_ops.rmdir = &comx_rmdir;
+
+	memcpy(&comx_normal_inode_ops, &proc_net_inode_operations, 
+		sizeof(struct inode_operations));
+	comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops;
+	comx_normal_inode_ops.lookup = &comx_lookup;
+
+	memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops, 
+		sizeof(struct inode_operations));
+	comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops;
+
+	memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops,
+		sizeof(struct file_operations));
+	comx_normal_file_ops.open = &comx_file_open;
+	comx_normal_file_ops.release = &comx_file_release;
+
+	memcpy(&comx_debug_file_ops, &comx_normal_file_ops, 
+		sizeof(struct file_operations));
+	comx_debug_file_ops.llseek = &comx_debug_lseek;
+	comx_debug_file_ops.read = &comx_debug_read;
+
+	if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM;
+
+
+	if ((new_file = create_proc_entry(FILENAME_HARDWARELIST, 
+	   S_IFREG | 0444, &comx_root_dir)) == NULL) {
+		return -ENOMEM;
+	}
+	
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->data = new_file;
+	new_file->read_proc = &comx_root_read_proc;
+	new_file->write_proc = NULL;
+	new_file->nlink = 1;
+
+	if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST, 
+	   S_IFREG | 0444, &comx_root_dir)) == NULL) {
+		return -ENOMEM;
+	}
+	
+	new_file->ops = &comx_normal_inode_ops;
+	new_file->data = new_file;
+	new_file->read_proc = &comx_root_read_proc;
+	new_file->write_proc = NULL;
+	new_file->nlink = 1;
+
+
+	printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n", 
+		VERSION);
+
+#ifndef MODULE
+#ifdef CONFIG_COMX_HW_COMX
+	comx_hw_comx_init();
+#endif
+#ifdef CONFIG_COMX_HW_LOCOMX
+	comx_hw_locomx_init();
+#endif
+#ifdef CONFIG_COMX_HW_MIXCOM
+	comx_hw_mixcom_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_HDLC
+	comx_proto_hdlc_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_PPP
+	comx_proto_ppp_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_LAPB
+	comx_proto_lapb_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_FR
+	comx_proto_fr_init();
+#endif
+#endif
+
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir);
+	remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir);
+	proc_unregister(&proc_root, comx_root_dir.low_ino);
+}
+#endif
+
+EXPORT_SYMBOL(comx_register_hardware);
+EXPORT_SYMBOL(comx_unregister_hardware);
+EXPORT_SYMBOL(comx_register_protocol);
+EXPORT_SYMBOL(comx_unregister_protocol);
+EXPORT_SYMBOL(comx_debug_skb);
+EXPORT_SYMBOL(comx_debug_bytes);
+EXPORT_SYMBOL(comx_debug);
+EXPORT_SYMBOL(comx_lineup_func);
+EXPORT_SYMBOL(comx_status);
+EXPORT_SYMBOL(comx_rx);
+EXPORT_SYMBOL(comx_strcasecmp);
+EXPORT_SYMBOL(comx_normal_inode_ops);
+EXPORT_SYMBOL(comx_root_dir);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comx.h linux/drivers/net/wan/comx.h
--- v2.3.51/linux/drivers/net/wan/comx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comx.h	Mon Mar 13 13:55:09 2000
@@ -0,0 +1,240 @@
+/*
+ * General definitions for the COMX driver 
+ * 
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ *
+ * net_device_stats:
+ *	rx_length_errors	rec_len < 4 || rec_len > 2000
+ *	rx_over_errors		receive overrun (OVR)
+ *	rx_crc_errors		rx crc error
+ *	rx_frame_errors		aborts rec'd (ABO)
+ *	rx_fifo_errors		status fifo overrun (PBUFOVR)
+ *	rx_missed_errors	receive buffer overrun (BUFOVR)
+ *	tx_aborted_errors	?
+ *	tx_carrier_errors	modem line status changes
+ *	tx_fifo_errors		tx underrun (locomx)
+ */
+#include <linux/config.h>
+
+struct comx_protocol {
+	char	*name;
+	char	*version;
+	unsigned short encap_type;
+	int	(*line_init)(struct net_device *dev);
+	int	(*line_exit)(struct net_device *dev);
+	struct comx_protocol *next;
+	};
+
+struct comx_hardware {
+	char *name; 
+	char *version;
+	int	(*hw_init)(struct net_device *dev);
+	int	(*hw_exit)(struct net_device *dev);
+	int	(*hw_dump)(struct net_device *dev);
+	struct comx_hardware *next;
+	};
+
+struct comx_channel {
+	void		*if_ptr;	// General purpose pointer
+	struct net_device 	*dev;		// Where we belong to
+	struct net_device	*twin;		// On dual-port cards
+	struct proc_dir_entry *procdir;	// the directory
+
+	unsigned char	init_status;
+	unsigned char	line_status;
+
+	struct timer_list lineup_timer;	// against line jitter
+	int		lineup_pending;
+	unsigned char	lineup_delay;
+
+#if 0
+	struct timer_list reset_timer; // for board resetting
+	int		reset_pending;
+	int		reset_timeout;
+#endif
+
+	struct net_device_stats	stats;	
+	struct net_device_stats *current_stats;
+#if 0
+	unsigned long	board_resets;
+#endif
+	unsigned long 	*avg_bytes;
+	int		loadavg_counter, loadavg_size;
+	int		loadavg[3];
+	struct timer_list loadavg_timer;
+	int		debug_flags;
+	char 		*debug_area;
+	int		debug_start, debug_end, debug_size;
+	struct proc_dir_entry *debug_file;
+#ifdef	CONFIG_COMX_DEBUG_RAW
+	char		*raw;
+	int		raw_len;
+#endif
+	// LINE specific	
+	struct comx_protocol *protocol;
+	void		(*LINE_rx)(struct net_device *dev, struct sk_buff *skb);
+	int		(*LINE_tx)(struct net_device *dev);
+	void		(*LINE_status)(struct net_device *dev, u_short status);
+	int		(*LINE_open)(struct net_device *dev);
+	int		(*LINE_close)(struct net_device *dev);
+	int		(*LINE_xmit)(struct sk_buff *skb, struct net_device *dev);
+	int		(*LINE_header)(struct sk_buff *skb, struct net_device *dev,
+				u_short type,void *daddr, void *saddr, 
+				unsigned len);
+	int		(*LINE_rebuild_header)(struct sk_buff *skb);
+	int		(*LINE_statistics)(struct net_device *dev, char *page);
+	int		(*LINE_parameter_check)(struct net_device *dev);
+	int		(*LINE_ioctl)(struct net_device *dev, struct ifreq *ifr,
+				int cmd);
+	void		(*LINE_mod_use)(int);
+	void *		LINE_privdata;
+
+	// HW specific
+
+	struct comx_hardware *hardware;
+	void	(*HW_board_on)(struct net_device *dev);
+	void	(*HW_board_off)(struct net_device *dev);
+	struct net_device *(*HW_access_board)(struct net_device *dev);
+	void	(*HW_release_board)(struct net_device *dev, struct net_device *savep);
+	int	(*HW_txe)(struct net_device *dev);
+	int	(*HW_open)(struct net_device *dev);
+	int	(*HW_close)(struct net_device *dev);
+	int	(*HW_send_packet)(struct net_device *dev,struct sk_buff *skb);
+	int	(*HW_statistics)(struct net_device *dev, char *page);
+#if 0
+	int	(*HW_reset)(struct net_device *dev, char *page);
+#endif
+	int	(*HW_load_board)(struct net_device *dev);
+	void	(*HW_set_clock)(struct net_device *dev);
+	void	*HW_privdata;
+	};
+
+struct comx_debugflags_struct {
+	char *name;
+	int  value;
+	};
+
+#define	COMX_ROOT_DIR_NAME	"comx"
+
+#define	FILENAME_HARDWARE	"boardtype"
+#define FILENAME_HARDWARELIST	"boardtypes"
+#define FILENAME_PROTOCOL	"protocol"
+#define FILENAME_PROTOCOLLIST	"protocols"
+#define FILENAME_DEBUG		"debug"
+#define FILENAME_CLOCK		"clock"
+#define	FILENAME_STATUS		"status"
+#define	FILENAME_IO		"io"
+#define FILENAME_IRQ		"irq"
+#define	FILENAME_KEEPALIVE	"keepalive"
+#define FILENAME_LINEUPDELAY	"lineup_delay"
+#define FILENAME_CHANNEL	"channel"
+#define FILENAME_FIRMWARE	"firmware"
+#define FILENAME_MEMADDR	"memaddr"
+#define	FILENAME_TWIN		"twin"
+#define FILENAME_T1		"t1"
+#define FILENAME_T2		"t2"
+#define FILENAME_N2		"n2"
+#define FILENAME_WINDOW		"window"
+#define FILENAME_MODE		"mode"
+#define	FILENAME_DLCI		"dlci"
+#define	FILENAME_MASTER		"master"
+#ifdef	CONFIG_COMX_DEBUG_RAW
+#define	FILENAME_RAW		"raw"
+#endif
+
+#define PROTONAME_NONE		"none"
+#define HWNAME_NONE		"none"
+#define KEEPALIVE_OFF		"off"
+
+#define FRAME_ACCEPTED		0		/* sending and xmitter busy */
+#define FRAME_DROPPED		1
+#define FRAME_ERROR		2		/* xmitter error */
+#define	FRAME_QUEUED		3		/* sending but more can come */
+
+#define	LINE_UP			1		/* Modem UP */
+#define PROTO_UP		2
+#define PROTO_LOOP		4
+
+#define	HW_OPEN			1
+#define	LINE_OPEN		2
+#define FW_LOADED		4
+#define IRQ_ALLOCATED		8
+
+#define DEBUG_COMX_RX		2
+#define	DEBUG_COMX_TX		4
+#define	DEBUG_HW_TX		16
+#define	DEBUG_HW_RX		32
+#define	DEBUG_HDLC_KEEPALIVE	64
+#define	DEBUG_COMX_PPP		128
+#define DEBUG_COMX_LAPB		256
+#define	DEBUG_COMX_DLCI		512
+
+#define	DEBUG_PAGESIZE		3072
+#define DEFAULT_DEBUG_SIZE	4096
+#define	DEFAULT_LINEUP_DELAY	1
+#define	FILE_PAGESIZE		3072
+
+#ifndef	COMX_PPP_MAJOR
+#define	COMX_PPP_MAJOR		88
+#endif
+
+
+#ifndef min
+#define min(a,b)		((a) > (b) ? (b) : (a))
+#endif
+#ifndef max
+#define max(a,b)		((a) > (b) ? (a) : (b))
+#endif
+
+
+#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv)
+
+#define TWIN(dev) (COMX_CHANNEL(dev)->twin)
+
+
+#ifndef byte
+typedef u8	byte;
+#endif
+#ifndef word
+typedef u16	word;
+#endif
+
+#ifndef	SEEK_SET
+#define	SEEK_SET	0
+#endif
+#ifndef	SEEK_CUR
+#define	SEEK_CUR	1
+#endif
+#ifndef	SEEK_END
+#define	SEEK_END	2
+#endif
+
+extern struct proc_dir_entry comx_root_dir;
+
+extern int	comx_register_hardware(struct comx_hardware *comx_hw);
+extern int	comx_unregister_hardware(char *name);
+extern int	comx_register_protocol(struct comx_protocol *comx_line);
+extern int	comx_unregister_protocol(char *name);
+
+extern int	comx_rx(struct net_device *dev, struct sk_buff *skb);
+extern void	comx_status(struct net_device *dev, int status);
+extern void	comx_lineup_func(unsigned long d);
+
+extern int	comx_debug(struct net_device *dev, char *fmt, ...);
+extern int	comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg);
+extern int	comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+		char *msg);
+extern int	comx_strcasecmp(const char *cs, const char *ct);
+
+extern struct inode_operations comx_normal_inode_ops;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/comxhw.h linux/drivers/net/wan/comxhw.h
--- v2.3.51/linux/drivers/net/wan/comxhw.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/comxhw.h	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,113 @@
+/*
+ * Defines for comxhw.c
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ */
+
+#define	LOCOMX_IO_EXTENT	8
+#define COMX_IO_EXTENT		4
+#define	HICOMX_IO_EXTENT	16
+
+#define COMX_MAX_TX_SIZE	1600
+#define COMX_MAX_RX_SIZE	2048
+
+#define COMX_JAIL_OFFSET	0xffff
+#define COMX_JAIL_VALUE		0xfe
+#define	COMX_MEMORY_SIZE	65536
+#define HICOMX_MEMORY_SIZE	16384
+#define COMX_MEM_MIN		0xa0000
+#define COMX_MEM_MAX		0xf0000
+
+#define	COMX_DEFAULT_IO		0x360
+#define	COMX_DEFAULT_IRQ	10
+#define	COMX_DEFAULT_MEMADDR	0xd0000
+#define	HICOMX_DEFAULT_IO	0x320
+#define	HICOMX_DEFAULT_IRQ	10
+#define	HICOMX_DEFAULT_MEMADDR	0xd0000
+#define	LOCOMX_DEFAULT_IO	0x368
+#define	LOCOMX_DEFAULT_IRQ	7
+
+#define MAX_CHANNELNO		2
+
+#define	COMX_CHANNEL_OFFSET	0x2000
+
+#define COMX_ENABLE_BOARD_IT    0x40
+#define COMX_BOARD_RESET       	0x20
+#define COMX_ENABLE_BOARD_MEM   0x10
+#define COMX_DISABLE_BOARD_MEM  0
+#define COMX_DISABLE_ALL	0x00
+
+#define HICOMX_DISABLE_ALL	0x00
+#define HICOMX_ENABLE_BOARD_MEM	0x02
+#define HICOMX_DISABLE_BOARD_MEM 0x0
+#define HICOMX_BOARD_RESET	0x01
+#define HICOMX_PRG_MEM		4
+#define HICOMX_DATA_MEM		0
+#define HICOMX_ID_BYTE		0x55
+
+#define CMX_ID_BYTE		0x31
+#define COMX_CLOCK_CONST	8000
+
+#define	LINKUP_READY		3
+
+#define	OFF_FW_L1_ID	0x01e	 /* ID bytes */
+#define OFF_FW_L2_ID	0x1006
+#define	FW_L1_ID_1	0xab
+#define FW_L1_ID_2_COMX		0xc0
+#define FW_L1_ID_2_HICOMX	0xc1
+#define	FW_L2_ID_1	0xab
+
+#define OFF_A_L2_CMD     0x130   /* command register for L2 */
+#define OFF_A_L2_CMDPAR  0x131   /* command parameter byte */
+#define OFF_A_L1_STATB   0x122   /* stat. block for L1 */
+#define OFF_A_L1_ABOREC  0x122   /* receive ABORT counter */
+#define OFF_A_L1_OVERRUN 0x123   /* receive overrun counter */
+#define OFF_A_L1_CRCREC  0x124   /* CRC error counter */
+#define OFF_A_L1_BUFFOVR 0x125   /* buffer overrun counter */
+#define OFF_A_L1_PBUFOVR 0x126   /* priority buffer overrun counter */
+#define OFF_A_L1_MODSTAT 0x127   /* current state of modem ctrl lines */
+#define OFF_A_L1_STATE   0x127   /* end of stat. block for L1 */
+#define OFF_A_L1_TXPC    0x128   /* Tx counter for the PC */
+#define OFF_A_L1_TXZ80   0x129   /* Tx counter for the Z80 */
+#define OFF_A_L1_RXPC    0x12a   /* Rx counter for the PC */
+#define OFF_A_L1_RXZ80   0x12b   /* Rx counter for the Z80 */
+#define OFF_A_L1_REPENA  0x12c   /* IT rep disable */
+#define OFF_A_L1_CHNR    0x12d   /* L1 channel logical number */
+#define OFF_A_L1_CLKINI  0x12e   /* Timer Const */
+#define OFF_A_L2_LINKUP	 0x132	 /* Linkup byte */
+#define OFF_A_L2_DAV	 0x134   /* Rx DAV */
+#define OFF_A_L2_RxBUFP  0x136	 /* Rx buff relative to membase */
+#define OFF_A_L2_TxEMPTY 0x138   /* Tx Empty */
+#define OFF_A_L2_TxBUFP  0x13a   /* Tx Buf */
+#define OFF_A_L2_NBUFFS	 0x144	 /* Number of buffers to fetch */
+
+#define OFF_A_L2_SABMREC 0x164	 /* LAPB no. of SABMs received */
+#define OFF_A_L2_SABMSENT 0x165	 /* LAPB no. of SABMs sent */
+#define OFF_A_L2_REJREC  0x166	 /* LAPB no. of REJs received */
+#define OFF_A_L2_REJSENT 0x167	 /* LAPB no. of REJs sent */
+#define OFF_A_L2_FRMRREC 0x168	 /* LAPB no. of FRMRs received */
+#define OFF_A_L2_FRMRSENT 0x169	 /* LAPB no. of FRMRs sent */
+#define OFF_A_L2_PROTERR 0x16A	 /* LAPB no. of protocol errors rec'd */
+#define OFF_A_L2_LONGREC 0x16B	 /* LAPB no. of long frames */
+#define OFF_A_L2_INVNR   0x16C	 /* LAPB no. of invalid N(R)s rec'd */
+#define OFF_A_L2_UNDEFFR 0x16D	 /* LAPB no. of invalid frames */
+
+#define	OFF_A_L2_T1	0x174	 /* T1 timer */
+#define	OFF_A_L2_ADDR	0x176	 /* DCE = 1, DTE = 3 */
+
+#define	COMX_CMD_INIT	1
+#define COMX_CMD_EXIT	2
+#define COMX_CMD_OPEN	16
+#define COMX_CMD_CLOSE	17
+
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/cosa.c linux/drivers/net/wan/cosa.c
--- v2.3.51/linux/drivers/net/wan/cosa.c	Fri Mar 10 16:40:43 2000
+++ linux/drivers/net/wan/cosa.c	Mon Mar 13 09:50:16 2000
@@ -1150,19 +1150,19 @@
 {
 	switch(cmd) {
 	case COSAIORSET:	/* Reset the device */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
 		return cosa_reset(cosa);
 	case COSAIOSTRT:	/* Start the firmware */
-		if (!suser())
+		if (!capable(CAP_SYS_RAWIO))
 			return -EACCES;
 		return cosa_start(cosa, arg);
 	case COSAIODOWNLD:	/* Download the firmware */
-		if (!suser())
+		if (!capable(CAP_SYS_RAWIO))
 			return -EACCES;
 		return cosa_download(cosa, (struct cosa_download *)arg);
 	case COSAIORMEM:
-		if (!suser())
+		if (!capable(CAP_SYS_RAWIO))
 			return -EACCES;
 		return cosa_readmem(cosa, (struct cosa_download *)arg);
 	case COSAIORTYPE:
@@ -1189,7 +1189,7 @@
 	case COSAIONRCHANS:
 		return cosa->nchannels;
 	case COSAIOBMSET:
-		if (!suser())
+		if (!capable(CAP_SYS_RAWIO))
 			return -EACCES;
 		if (is_8bit(cosa))
 			return -EINVAL;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/hscx.h linux/drivers/net/wan/hscx.h
--- v2.3.51/linux/drivers/net/wan/hscx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/hscx.h	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,103 @@
+#define	HSCX_MTU	1600
+
+#define	HSCX_ISTA	0x00
+#define HSCX_MASK	0x00
+#define HSCX_STAR	0x01
+#define HSCX_CMDR	0x01
+#define HSCX_MODE	0x02
+#define HSCX_TIMR	0x03
+#define HSCX_EXIR	0x04
+#define HSCX_XAD1	0x04
+#define HSCX_RBCL	0x05
+#define HSCX_SAD2	0x05
+#define HSCX_RAH1	0x06
+#define HSCX_RSTA	0x07
+#define HSCX_RAH2	0x07
+#define HSCX_RAL1	0x08
+#define HSCX_RCHR	0x09
+#define HSCX_RAL2	0x09
+#define HSCX_XBCL	0x0a
+#define HSCX_BGR	0x0b
+#define HSCX_CCR2	0x0c
+#define HSCX_RBCH	0x0d
+#define HSCX_XBCH	0x0d
+#define HSCX_VSTR	0x0e
+#define HSCX_RLCR	0x0e
+#define HSCX_CCR1	0x0f
+#define HSCX_FIFO	0x1e
+
+#define HSCX_HSCX_CHOFFS	0x400
+#define HSCX_SEROFFS	0x1000
+
+#define HSCX_RME	0x80
+#define HSCX_RPF	0x40
+#define HSCX_RSC	0x20
+#define HSCX_XPR	0x10
+#define HSCX_TIN	0x08
+#define HSCX_ICA	0x04
+#define HSCX_EXA	0x02
+#define HSCX_EXB	0x01
+
+#define HSCX_XMR	0x80
+#define HSCX_XDU	0x40
+#define HSCX_EXE	0x40
+#define HSCX_PCE	0x20
+#define HSCX_RFO	0x10
+#define HSCX_CSC	0x08
+#define HSCX_RFS	0x04
+
+#define HSCX_XDOV	0x80
+#define HSCX_XFW	0x40
+#define HSCX_XRNR	0x20
+#define HSCX_RRNR	0x10
+#define HSCX_RLI	0x08
+#define HSCX_CEC	0x04
+#define HSCX_CTS	0x02
+#define HSCX_WFA	0x01
+
+#define HSCX_RMC	0x80
+#define HSCX_RHR	0x40
+#define HSCX_RNR	0x20
+#define HSCX_XREP	0x20
+#define HSCX_STI	0x10
+#define HSCX_XTF	0x08
+#define HSCX_XIF	0x04
+#define HSCX_XME	0x02
+#define HSCX_XRES	0x01
+
+#define HSCX_AUTO	0x00
+#define HSCX_NONAUTO	0x40
+#define HSCX_TRANS	0x80
+#define HSCX_XTRANS	0xc0
+#define HSCX_ADM16	0x20
+#define HSCX_ADM8	0x00
+#define HSCX_TMD_EXT	0x00
+#define HSCX_TMD_INT	0x10
+#define HSCX_RAC	0x08
+#define HSCX_RTS	0x04
+#define HSCX_TLP	0x01
+
+#define HSCX_VFR	0x80
+#define HSCX_RDO	0x40
+#define HSCX_CRC	0x20
+#define HSCX_RAB	0x10
+
+#define HSCX_CIE	0x04
+#define HSCX_RIE	0x02
+
+#define HSCX_DMA	0x80
+#define HSCX_NRM	0x40
+#define HSCX_CAS	0x20
+#define HSCX_XC	0x10
+
+#define HSCX_OV	0x10
+
+#define HSCX_CD	0x80
+
+#define HSCX_RC	0x80
+
+#define HSCX_PU	0x80
+#define HSCX_NRZ	0x00
+#define HSCX_NRZI	0x40
+#define HSCX_ODS	0x10
+#define HSCX_ITF	0x08
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/mixcom.h linux/drivers/net/wan/mixcom.h
--- v2.3.51/linux/drivers/net/wan/mixcom.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/mixcom.h	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,35 @@
+/*
+ * Defines for the mixcom board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ */
+
+#define	MIXCOM_IO_EXTENT	0x20
+
+#define	MIXCOM_DEFAULT_IO	0x180
+#define	MIXCOM_DEFAULT_IRQ	5
+
+#define MIXCOM_ID		0x11
+#define MIXCOM_SERIAL_OFFSET	0x1000
+#define MIXCOM_CHANNEL_OFFSET	0x400
+#define MIXCOM_IT_OFFSET	0xc14
+#define MIXCOM_STATUS_OFFSET	0xc14
+#define MIXCOM_ID_OFFSET	0xc10
+#define MIXCOM_ON		0x1
+#define MIXCOM_OFF		0x0
+
+/* Status register bits */
+
+#define MIXCOM_CTSB		0x1
+#define MIXCOM_CTSA		0x2
+#define MIXCOM_CHANNELNO	0x20
+#define MIXCOM_POWERFAIL	0x40
+#define MIXCOM_BOOT		0x80
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/sbni.c linux/drivers/net/wan/sbni.c
--- v2.3.51/linux/drivers/net/wan/sbni.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/net/wan/sbni.c	Sun Mar 12 19:18:55 2000
@@ -101,7 +101,7 @@
 static int  sbni_close(struct net_device *dev);
 static void sbni_drop_tx_queue(struct net_device *dev);
 static struct enet_statistics *sbni_get_stats(struct net_device *dev);
-void card_start(struct net_device *dev);
+static void card_start(struct net_device *dev);
 static inline unsigned short sbni_recv(struct net_device *dev);
 void change_level(struct net_device *dev);
 static inline void sbni_xmit(struct net_device *dev);
@@ -647,7 +647,7 @@
 	return 0;
 }
 
-void card_start(struct net_device *dev)
+static void card_start(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local*)dev->priv;
    
@@ -1200,6 +1200,8 @@
 		}
 		case SIOCDEVRESINSTATS:
 		{
+			if(!capable(CAP_NET_ADMIN))
+				return -EPERM;
 			DP( printk("%s: SIOCDEVRESINSTATS\n",dev->name); )
 			lp->in_stats.all_rx_number = 0;
 			lp->in_stats.bad_rx_number = 0;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/sdla.c linux/drivers/net/wan/sdla.c
--- v2.3.51/linux/drivers/net/wan/sdla.c	Thu Mar  2 14:36:22 2000
+++ linux/drivers/net/wan/sdla.c	Mon Mar 13 09:50:16 2000
@@ -1247,7 +1247,7 @@
 {
 	struct frad_local *flp;
 
-	if(!suser())
+	if(!capable(CAP_NET_ADMIN))
 		return -EPERM;
 		
 	flp = dev->priv;
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/syncppp.c linux/drivers/net/wan/syncppp.c
--- v2.3.51/linux/drivers/net/wan/syncppp.c	Sat Feb 26 22:31:47 2000
+++ linux/drivers/net/wan/syncppp.c	Mon Mar 13 09:43:37 2000
@@ -147,13 +147,17 @@
 
 static int debug = 0;
 
+MODULE_PARM(debug,"1i");
+
 /*
  *	Interface down stub
  */	
 
 static void if_down(struct net_device *dev)
 {
-	;
+	struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+
+	sp->pp_link_state=SPPP_LINK_DOWN;
 }
 
 /*
@@ -182,8 +186,19 @@
 	}
 }
 
-/*
- * Process the received packet.
+/**
+ *	sppp_input -	receive and process a WAN PPP frame
+ *	@skb:	The buffer to process
+ *	@dev:	The device it arrived on
+ *
+ *	This can be called directly by cards that do not have
+ *	timing constraints but is normally called from the network layer
+ *	after interrupt servicing to process frames queued via netif_rx.
+ *
+ *	We process the options in the card. If the frame is destined for
+ *	the protocol stacks then it requeues the frame for the upper level
+ *	protocol. If it is a control from it is processed and discarded
+ *	here.
  */
  
 void sppp_input (struct net_device *dev, struct sk_buff *skb)
@@ -194,7 +209,7 @@
 	skb->dev=dev;
 	skb->mac.raw=skb->data;
 	
-	if (dev->flags & IFF_UP)
+	if (dev->flags & IFF_RUNNING)
 	{
 		/* Count received bytes, add FCS and one flag */
 		sp->ibytes+= skb->len + 3;
@@ -324,7 +339,7 @@
 	h=(struct ppp_header *)skb->data;
 	if(sp->pp_flags&PP_CISCO)
 	{
-		h->address = CISCO_MULTICAST;
+		h->address = CISCO_UNICAST;
 		h->control = 0;
 	}
 	else
@@ -370,7 +385,7 @@
 
 		/* Keepalive mode disabled or channel down? */
 		if (! (sp->pp_flags & PP_KEEPALIVE) ||
-		    ! (dev->flags & IFF_RUNNING))
+		    ! (dev->flags & IFF_UP))
 			continue;
 
 		/* No keepalive in PPP mode if LCP not opened yet. */
@@ -530,10 +545,10 @@
 		if (h->ident != sp->lcp.confid)
 			break;
 		sppp_clear_timeout (sp);
-		if (! (dev->flags & IFF_UP) &&
-		    (dev->flags & IFF_RUNNING)) {
+		if ((sp->pp_link_state != SPPP_LINK_UP) &&
+		    (dev->flags & IFF_UP)) {
 			/* Coming out of loopback mode. */
-			dev->flags |= IFF_UP;
+			sp->pp_link_state=SPPP_LINK_UP;
 			printk (KERN_INFO "%s: up\n", dev->name);
 		}
 		switch (sp->lcp.state) {
@@ -698,9 +713,9 @@
 			break;
 		}
 		sp->pp_loopcnt = 0;
-		if (! (dev->flags & IFF_UP) &&
-		    (dev->flags & IFF_RUNNING)) {
-			dev->flags |= IFF_UP;
+		if (sp->pp_link_state==SPPP_LINK_DOWN &&
+		    (dev->flags & IFF_UP)) {
+			sp->pp_link_state=SPPP_LINK_UP;
 			printk (KERN_INFO "%s: up\n", dev->name);
 		}
 		break;
@@ -825,11 +840,19 @@
 	dev_queue_xmit(skb);
 }
 
+/**
+ *	sppp_close - close down a synchronous PPP or Cisco HDLC link
+ *	@dev: The network device to drop the link of
+ *
+ *	This drops the logical interface to the channel. It is not
+ *	done politely as we assume we will also be dropping DTR. Any
+ *	timeouts are killed.
+ */
 
 int sppp_close (struct net_device *dev)
 {
 	struct sppp *sp = (struct sppp *)sppp_of(dev);
-	dev->flags &= ~IFF_RUNNING;
+	sp->pp_link_state = SPPP_LINK_DOWN;
 	sp->lcp.state = LCP_STATE_CLOSED;
 	sp->ipcp.state = IPCP_STATE_CLOSED;
 	sppp_clear_timeout (sp);
@@ -838,24 +861,49 @@
 
 EXPORT_SYMBOL(sppp_close);
 
+/**
+ *	sppp_open - open a synchronous PPP or Cisco HDLC link
+ *	@dev:	Network device to activate
+ *	
+ *	Close down any existing synchronous session and commence
+ *	from scratch. In the PPP case this means negotiating LCP/IPCP
+ *	and friends, while for Cisco HDLC we simply need to staet sending
+ *	keepalives
+ */
 
 int sppp_open (struct net_device *dev)
 {
 	struct sppp *sp = (struct sppp *)sppp_of(dev);
 	sppp_close(dev);
-	dev->flags |= IFF_RUNNING;
-	if (!(sp->pp_flags & PP_CISCO))
+	if (!(sp->pp_flags & PP_CISCO)) {
 		sppp_lcp_open (sp);
+	}
+	sp->pp_link_state = SPPP_LINK_DOWN;
 	return 0;
 }
 
 EXPORT_SYMBOL(sppp_open);
 
+/**
+ *	sppp_reopen - notify of physical link loss
+ *	@dev: Device that lost the link
+ *
+ *	This function informs the synchronous protocol code that
+ *	the underlying link died (for example a carrier drop on X.21)
+ *
+ *	We increment the magic numbers to ensure that if the other end
+ *	failed to notice we will correctly start a new session. It happens
+ *	do to the nature of telco circuits is that you can lose carrier on
+ *	one endonly.
+ *
+ *	Having done this we go back to negotiating. This function may
+ *	be called from an interrupt context.
+ */
+ 
 int sppp_reopen (struct net_device *dev)
 {
 	struct sppp *sp = (struct sppp *)sppp_of(dev);
 	sppp_close(dev);
-	dev->flags |= IFF_RUNNING;
 	if (!(sp->pp_flags & PP_CISCO))
 	{
 		sp->lcp.magic = jiffies;
@@ -864,12 +912,23 @@
 		sp->ipcp.state = IPCP_STATE_CLOSED;
 		/* Give it a moment for the line to settle then go */
 		sppp_set_timeout (sp, 1);
-	}
+	} 
+	sp->pp_link_state=SPPP_LINK_DOWN;
 	return 0;
 }
 
 EXPORT_SYMBOL(sppp_reopen);
 
+/**
+ *	sppp_change_mtu - Change the link MTU
+ *	@dev:	Device to change MTU on
+ *	@new_mtu: New MTU
+ *
+ *	Change the MTU on the link. This can only be called with
+ *	the link down. It returns an error if the link is up or
+ *	the mtu is out of range.
+ */
+ 
 int sppp_change_mtu(struct net_device *dev, int new_mtu)
 {
 	if(new_mtu<128||new_mtu>PPP_MTU||(dev->flags&IFF_UP))
@@ -880,6 +939,18 @@
 
 EXPORT_SYMBOL(sppp_change_mtu);
 
+/**
+ *	sppp_do_ioctl - Ioctl handler for ppp/hdlc
+ *	@dev: Device subject to ioctl
+ *	@ifr: Interface request block from the user
+ *	@cmd: Command that is being issued
+ *	
+ *	This function handles the ioctls that may be issued by the user
+ *	to control the settings of a PPP/HDLC link. It does both busy
+ *	and security checks. This function is intended to be wrapped by
+ *	callers who wish to add additional ioctl calls of their own.
+ */
+ 
 int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
 	struct sppp *sp = (struct sppp *)sppp_of(dev);
@@ -913,6 +984,16 @@
 
 EXPORT_SYMBOL(sppp_do_ioctl);
 
+/**
+ *	sppp_attach - attach synchronous PPP/HDLC to a device
+ *	@pd:	PPP device to initialise
+ *
+ *	This initialises the PPP/HDLC support on an interface. At the
+ *	time of calling the dev element must point to the network device
+ *	that this interface is attached to. The interface should not yet
+ *	be registered. 
+ */
+ 
 void sppp_attach(struct ppp_device *pd)
 {
 	struct net_device *dev = pd->dev;
@@ -973,6 +1054,15 @@
 
 EXPORT_SYMBOL(sppp_attach);
 
+/**
+ *	sppp_detach - release PPP resources from a device
+ *	@dev:	Network device to release
+ *
+ *	Stop and free up any PPP/HDLC resources used by this
+ *	interface. This must be called before the device is
+ *	freed.
+ */
+ 
 void sppp_detach (struct net_device *dev)
 {
 	struct sppp **q, *p, *sp = (struct sppp *)sppp_of(dev);
@@ -1187,7 +1277,7 @@
 	cli();
 
 	sp->pp_flags &= ~PP_TIMO;
-	if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) {
+	if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) {
 		restore_flags(flags);
 		return;
 	}
@@ -1273,18 +1363,24 @@
 		printk ("-%x", *p++);
 }
 
-/*
+/**
+ *	sppp_rcv -	receive and process a WAN PPP frame
+ *	@skb:	The buffer to process
+ *	@dev:	The device it arrived on
+ *	@p: Unused
+ *
  *	Protocol glue. This drives the deferred processing mode the poorer
- *	cards use.
+ *	cards use. This can be called directly by cards that do not have
+ *	timing constraints but is normally called from the network layer
+ *	after interrupt servicing to process frames queued via netif_rx.
  */
 
-int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
+static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
 {
 	sppp_input(dev,skb);
 	return 0;
 }
 
-EXPORT_SYMBOL(sppp_rcv);
 
 struct packet_type sppp_packet_type=
 {
@@ -1304,8 +1400,6 @@
 	sppp_packet_type.type=htons(ETH_P_WAN_PPP);	
 	dev_add_pack(&sppp_packet_type);
 }
-
-EXPORT_SYMBOL(sync_ppp_init);
 
 #ifdef MODULE
 
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/syncppp.h linux/drivers/net/wan/syncppp.h
--- v2.3.51/linux/drivers/net/wan/syncppp.h	Mon Dec 20 18:48:21 1999
+++ linux/drivers/net/wan/syncppp.h	Mon Mar 13 09:43:37 2000
@@ -47,6 +47,7 @@
 	u32		ipkts,opkts;	/* Packets in/out */
 	struct timer_list	pp_timer;
 	struct net_device	*pp_if;
+	char		pp_link_state;	/* Link status */
 };
 
 struct ppp_device
@@ -74,6 +75,9 @@
 #define IPCP_STATE_ACK_RCVD     1       /* IPCP state: conf-ack received */
 #define IPCP_STATE_ACK_SENT     2       /* IPCP state: conf-ack sent */
 #define IPCP_STATE_OPENED       3       /* IPCP state: opened */
+
+#define SPPP_LINK_DOWN		0	/* link down - no keepalive */
+#define SPPP_LINK_UP		1	/* link is up - keepalive ok */
 
 void sppp_attach (struct ppp_device *pd);
 void sppp_detach (struct net_device *dev);
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wan/z85230.c linux/drivers/net/wan/z85230.c
--- v2.3.51/linux/drivers/net/wan/z85230.c	Sun Feb 13 19:29:04 2000
+++ linux/drivers/net/wan/z85230.c	Sun Mar 12 19:39:47 2000
@@ -55,7 +55,7 @@
 static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED;
 
 /**
- *	z8530_read_port:
+ *	z8530_read_port - Architecture specific interface function
  *	@p: port to read
  *
  *	Provided port access methods. The Comtrol SV11 requires no delays
@@ -79,7 +79,7 @@
 }
 
 /**
- *	z8530_write_port:
+ *	z8530_write_port - Architecture specific interface function
  *	@p: port to write
  *	@d: value to write
  *
@@ -108,7 +108,7 @@
 
 
 /**
- *	read_zsreg:
+ *	read_zsreg - Read a register from a Z85230 
  *	@c: Z8530 channel to read from (2 per chip)
  *	@reg: Register to read
  *	FIXME: Use a spinlock.
@@ -133,7 +133,7 @@
 }
 
 /**
- *	read_zsdata:
+ *	read_zsdata - Read the data port of a Z8530 channel
  *	@c: The Z8530 channel to read the data port from
  *
  *	The data port provides fast access to some things. We still
@@ -148,7 +148,7 @@
 }
 
 /**
- *	write_zsreg:
+ *	write_zsreg - Write to a Z8530 channel register
  *	@c: The Z8530 channel
  *	@reg: Register number
  *	@val: Value to write
@@ -169,11 +169,28 @@
 	restore_flags(flags);
 }
 
+/**
+ *	write_zsctrl - Write to a Z8530 control register
+ *	@c: The Z8530 channel
+ *	@val: Value to write
+ *
+ *	Write directly to the control register on the Z8530
+ */
+
 extern inline void write_zsctrl(struct z8530_channel *c, u8 val)
 {
 	z8530_write_port(c->ctrlio, val);
 }
 
+/**
+ *	write_zsdata - Write to a Z8530 control register
+ *	@c: The Z8530 channel
+ *	@val: Value to write
+ *
+ *	Write directly to the data register on the Z8530
+ */
+
+
 extern inline void write_zsdata(struct z8530_channel *c, u8 val)
 {
 	z8530_write_port(c->dataio, val);
@@ -249,7 +266,7 @@
 EXPORT_SYMBOL(z8530_hdlc_kilostream_85230);
 
 /**
- *	z8530_flush_fifo:
+ *	z8530_flush_fifo - Flush on chip RX FIFO
  *	@c: Channel to flush
  *
  *	Flush the receive FIFO. There is no specific option for this, we 
@@ -276,8 +293,8 @@
 }	
 
 /**
- *	z8530_rtsdtr:
- *	@c: The Z8530 channel to contro;
+ *	z8530_rtsdtr - Control the outgoing DTS/RTS line
+ *	@c: The Z8530 channel to control;
  *	@set: 1 to set, 0 to clear
  *
  *	Sets or clears DTR/RTS on the requested line. All locking is handled
@@ -296,7 +313,7 @@
 }
 
 /**
- *	z8530_rx:
+ *	z8530_rx - Handle a PIO receive event
  *	@c: Z8530 channel to process
  *
  *	Receive handler for receiving in PIO mode. This is much like the 
@@ -378,7 +395,7 @@
 
 
 /**
- *	z8530_tx:
+ *	z8530_tx - Handle a PIO transmit event
  *	@c: Z8530 channel to process
  *
  *	Z8530 transmit interrupt handler for the PIO mode. The basic
@@ -421,7 +438,7 @@
 }
 
 /**
- *	z8530_status:
+ *	z8530_status - Handle a PIO status exception
  *	@chan: Z8530 channel to process
  *
  *	A status event occured in PIO synchronous mode. There are several
@@ -479,7 +496,7 @@
 EXPORT_SYMBOL(z8530_sync);
 
 /**
- *	z8530_dma_rx:
+ *	z8530_dma_rx - Handle a DMA RX event
  *	@chan: Channel to handle
  *
  *	Non bus mastering DMA interfaces for the Z8x30 devices. This
@@ -514,7 +531,7 @@
 }
 
 /**
- *	z8530_dma_tx:
+ *	z8530_dma_tx - Handle a DMA TX event
  *	@chan:	The Z8530 channel to handle
  *
  *	We have received an interrupt while doing DMA transmissions. It
@@ -525,17 +542,17 @@
 {
 	if(!chan->dma_tx)
 	{
-		printk("Hey who turned the DMA off?\n");
+		printk(KERN_WARNING "Hey who turned the DMA off?\n");
 		z8530_tx(chan);
 		return;
 	}
 	/* This shouldnt occur in DMA mode */
-	printk(KERN_ERR "DMA tx ??\n");
+	printk(KERN_ERR "DMA tx - bogus event!\n");
 	z8530_tx(chan);
 }
 
 /**
- *	z8530_dma_status:
+ *	z8530_dma_status - Handle a DMA status exception
  *	@chan: Z8530 channel to process
  *	
  *	A status event occured on the Z8530. We receive these for two reasons
@@ -606,7 +623,7 @@
 EXPORT_SYMBOL(z8530_txdma_sync);
 
 /**
- *	z8530_rx_clear:
+ *	z8530_rx_clear - Handle RX events from a stopped chip
  *	@c: Z8530 channel to shut up
  *
  *	Receive interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -635,7 +652,7 @@
 }
 
 /**
- *	z8530_tx_clear:
+ *	z8530_tx_clear - Handle TX events from a stopped chip
  *	@c: Z8530 channel to shut up
  *
  *	Transmit interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -650,7 +667,7 @@
 }
 
 /**
- *	z8530_status_clear:
+ *	z8530_status_clear - Handle status events from a stopped chip
  *	@chan: Z8530 channel to shut up
  *
  *	Status interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -678,7 +695,7 @@
 EXPORT_SYMBOL(z8530_nop);
 
 /**
- *	z8530_interrupt:
+ *	z8530_interrupt - Handle an interrupt from a Z8530
  *	@irq: 	Interrupt number
  *	@dev_id: The Z8530 device that is interrupting.
  *	@regs: unused
@@ -758,7 +775,7 @@
 
 
 /**
- *	z8530_sync_open:
+ *	z8530_sync_open - Open a Z8530 channel for PIO
  *	@dev:	The network interface we are using
  *	@c:	The Z8530 channel to open in synchronous PIO mode
  *
@@ -789,7 +806,7 @@
 EXPORT_SYMBOL(z8530_sync_open);
 
 /**
- *	z8530_sync_close:
+ *	z8530_sync_close - Close a PIO Z8530 channel
  *	@dev: Network device to close
  *	@c: Z8530 channel to disassociate and move to idle
  *
@@ -814,7 +831,7 @@
 EXPORT_SYMBOL(z8530_sync_close);
 
 /**
- *	z8530_sync_dma_open:
+ *	z8530_sync_dma_open - Open a Z8530 for DMA I/O
  *	@dev: The network device to attach
  *	@c: The Z8530 channel to configure in sync DMA mode.
  *
@@ -934,7 +951,7 @@
 EXPORT_SYMBOL(z8530_sync_dma_open);
 
 /**
- *	z8530_sync_dma_close:
+ *	z8530_sync_dma_close - Close down DMA I/O
  *	@dev: Network device to detach
  *	@c: Z8530 channel to move into discard mode
  *
@@ -999,7 +1016,7 @@
 EXPORT_SYMBOL(z8530_sync_dma_close);
 
 /**
- *	z8530_sync_txdma_open:
+ *	z8530_sync_txdma_open - Open a Z8530 for TX driven DMA
  *	@dev: The network device to attach
  *	@c: The Z8530 channel to configure in sync DMA mode.
  *
@@ -1099,7 +1116,7 @@
 EXPORT_SYMBOL(z8530_sync_txdma_open);
 
 /**
- *	z8530_sync_txdma_close:
+ *	z8530_sync_txdma_close - Close down a TX driven DMA channel
  *	@dev: Network device to detach
  *	@c: Z8530 channel to move into discard mode
  *
@@ -1168,7 +1185,7 @@
 };
 
 /**
- *	z8530_describe:
+ *	z8530_describe - Uniformly describe a Z8530 port
  *	@dev: Z8530 device to describe
  *	@mapping: string holding mapping type (eg "I/O" or "Mem")
  *	@io: the port value in question
@@ -1191,7 +1208,7 @@
 EXPORT_SYMBOL(z8530_describe);
 
 /**
- *	z8530_init:
+ *	z8530_init - Initialise a Z8530 device
  *	@dev: Z8530 device to initialise.
  *
  *	Configure up a Z8530/Z85C30 or Z85230 chip. We check the device
@@ -1273,7 +1290,7 @@
 EXPORT_SYMBOL(z8530_init);
 
 /**
- *	z8530_shutdown:
+ *	z8530_shutdown - Shutdown a Z8530 device
  *	@dev: The Z8530 chip to shutdown
  *
  *	We set the interrupt handlers to silence any interrupts. We then 
@@ -1294,7 +1311,7 @@
 EXPORT_SYMBOL(z8530_shutdown);
 
 /**
- *	z8530_channel_load:
+ *	z8530_channel_load - Load channel data
  *	@c: Z8530 channel to configure
  *	@rtable: Table of register, value pairs
  *	FIXME: ioctl to allow user uploaded tables
@@ -1333,7 +1350,7 @@
 
 
 /**
- *	z8530_tx_begin:
+ *	z8530_tx_begin - Begin packet transmission
  *	@c: The Z8530 channel to kick
  *
  *	This is the speed sensitive side of transmission. If we are called
@@ -1430,7 +1447,7 @@
 }
 
 /**
- *	z8530_tx_done:
+ *	z8530_tx_done - TX complete callback
  *	@c: The channel that completed a transmit.
  *
  *	This is called when we complete a packet send. We wake the queue,
@@ -1461,7 +1478,7 @@
 }
 
 /**
- *	z8530_null_rx:
+ *	z8530_null_rx - Discard a packet
  *	@c: The channel the packet arrived on
  *	@skb: The buffer
  *
@@ -1477,7 +1494,7 @@
 EXPORT_SYMBOL(z8530_null_rx);
 
 /**
- *	z8530_rx_done:
+ *	z8530_rx_done - Receive completion callback
  *	@c: The channel that completed a receive
  *
  *	A new packet is complete. Our goal here is to get back into receive
@@ -1630,7 +1647,7 @@
 }
 
 /**
- *	spans_boundary:
+ *	spans_boundary - Check a packet can be ISA DMA'd
  *	@skb: The buffer to check
  *
  *	Returns true if the buffer cross a DMA boundary on a PC. The poor
@@ -1642,15 +1659,12 @@
 	unsigned long a=(unsigned long)skb->data;
 	a^=(a+skb->len);
 	if(a&0x00010000)	/* If the 64K bit is different.. */
-	{
-		printk("spanner\n");
 		return 1;
-	}
 	return 0;
 }
 
 /**
- *	z8530_queue_xmit:
+ *	z8530_queue_xmit - Queue a packet
  *	@c: The channel to use
  *	@skb: The packet to kick down the channel
  *
@@ -1707,7 +1721,7 @@
 EXPORT_SYMBOL(z8530_queue_xmit);
 
 /**
- *	z8530_get_stats:
+ *	z8530_get_stats - Get network statistics
  *	@c: The channel to use
  *
  *	Get the statistics block. We keep the statistics in software as
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.3.51/linux/drivers/net/wavelan.c	Fri Mar 10 16:40:43 2000
+++ linux/drivers/net/wavelan.c	Mon Mar 13 09:50:16 2000
@@ -1990,7 +1990,7 @@
 		}
 
 		/* only super-user can see encryption key */
-		if (!suser()) {
+		if (!capable(CAP_NET_ADMIN)) {
 			ret = -EPERM;
 			break;
 		}
@@ -2224,7 +2224,7 @@
 		/* ------------------ PRIVATE IOCTL ------------------ */
 
 	case SIOCSIPQTHR:
-		if (!suser()) {
+		if (!capable(CAP_NET_ADMIN)) {
 			ret = -EPERM;
 			break;
 		}
@@ -2248,7 +2248,7 @@
 #ifdef HISTOGRAM
 	case SIOCSIPHISTO:
 		/* Verify that the user is root. */
-		if (!suser()) {
+		if (!capable(CAP_NET_ADMIN)) {
 			ret = -EPERM;
 			break;
 		}
diff -u --recursive --new-file v2.3.51/linux/drivers/net/wavelan.h linux/drivers/net/wavelan.h
--- v2.3.51/linux/drivers/net/wavelan.h	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/wavelan.h	Sun Mar 12 19:11:17 2000
@@ -26,7 +26,7 @@
  * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson),
  * you might need to modify this part to accommodate your hardware.
  */
-const char	MAC_ADDRESSES[][3] =
+static const char	MAC_ADDRESSES[][3] =
 {
   { 0x08, 0x00, 0x0E },		/* AT&T WaveLAN (standard) & DEC RoamAbout */
   { 0x08, 0x00, 0x6A },		/* AT&T WaveLAN (alternate) */
@@ -49,14 +49,14 @@
  * (as read in the offset register of the dac area).
  * Used to map channel numbers used by `wfreqsel' to frequencies
  */
-const short	channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+static const short	channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
 				    0xD0, 0xF0, 0xF8, 0x150 };
 
 /* Frequencies of the 1.0 modem (fixed frequencies).
  * Use to map the PSA `subband' to a frequency
  * Note : all frequencies apart from the first one need to be multiplied by 10
  */
-const int	fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+static const int	fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
 
 
 
diff -u --recursive --new-file v2.3.51/linux/drivers/net/yellowfin.c linux/drivers/net/yellowfin.c
--- v2.3.51/linux/drivers/net/yellowfin.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/net/yellowfin.c	Sun Mar 12 19:11:17 2000
@@ -77,7 +77,6 @@
 
 #include <linux/version.h>
 #include <linux/module.h>
-#include <linux/modversions.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/timer.h>
diff -u --recursive --new-file v2.3.51/linux/drivers/parport/ChangeLog linux/drivers/parport/ChangeLog
--- v2.3.51/linux/drivers/parport/ChangeLog	Fri Mar 10 16:40:43 2000
+++ linux/drivers/parport/ChangeLog	Tue Mar 14 18:04:50 2000
@@ -1,3 +1,24 @@
+2000-03-13    <twaugh@redhat.com>
+
+	* parport_pc.c (parport_pc_init): Moved from asm/parport.h.
+
+	* Config.in: CONFIG_PARPORT_PC_SUPERIO: new option.
+
+	* parport_pc.c (show_parconfig_smsc37c669): Make __devinit.
+	(show_parconfig_winbond): Likewise.
+	(decode_winbond): Likewise.
+	(decode_smsc): Likewise.
+	(winbond_check): Likewise.
+	(winbond_check2): Likewise.
+	(smsc_check): Likewise.
+	(detect_and_report_winbond): Likewise.
+	(detect_and_report_smsc): Likewise.
+	(get_superio_dma): Likewise.
+	(get_superio_irq): Likewise.
+	(parport_pc_find_isa_ports): New function.
+	(parport_pc_find_ports): New function.
+	(init_module): Make superio a config option, not a parameter.
+
 2000-03-10    <twaugh@redhat.com>
 
 	* parport_pc.c (decode_winbond): Use correct 83877ATF chip ID.
diff -u --recursive --new-file v2.3.51/linux/drivers/parport/Config.in linux/drivers/parport/Config.in
--- v2.3.51/linux/drivers/parport/Config.in	Tue Mar  7 14:32:26 2000
+++ linux/drivers/parport/Config.in	Tue Mar 14 18:04:50 2000
@@ -13,6 +13,9 @@
    dep_tristate '  PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
    if [ "$CONFIG_PARPORT_PC" != "n" ]; then
       bool '    Use FIFO/DMA if available' CONFIG_PARPORT_PC_FIFO
+      if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+         bool '    SuperIO chipset support (EXPERIMENTAL)' CONFIG_PARPORT_PC_SUPERIO
+      fi
    fi
    if [ "$CONFIG_PARPORT_PC" = "y" ]; then
       # Don't bother with this if parport_pc is a module; it only affects
diff -u --recursive --new-file v2.3.51/linux/drivers/parport/parport_pc.c linux/drivers/parport/parport_pc.c
--- v2.3.51/linux/drivers/parport/parport_pc.c	Fri Mar 10 16:40:43 2000
+++ linux/drivers/parport/parport_pc.c	Tue Mar 14 18:04:50 2000
@@ -60,6 +60,8 @@
 #include <linux/parport_pc.h>
 #include <asm/parport.h>
 
+#define PARPORT_PC_MAX_PORTS PARPORT_MAX
+
 /* ECR modes */
 #define ECR_SPP 00
 #define ECR_PS2 01
@@ -84,8 +86,10 @@
 	int io;
 	int irq;
 	int dma;
-} superios[NR_SUPERIOS]= { {0,},};
-	
+} superios[NR_SUPERIOS] __devinitdata = { {0,},};
+
+static int user_specified __devinitdata = 0;
+
 /* frob_control, but for ECR */
 static void frob_econtrol (struct parport *pb, unsigned char m,
 			   unsigned char v)
@@ -1015,9 +1019,9 @@
 	parport_ieee1284_read_byte,
 };
 
+#ifdef CONFIG_PARPORT_PC_SUPERIO
 /* Super-IO chipset detection, Winbond, SMSC */
-
-static void show_parconfig_smsc37c669(int io, int key)
+static void __devinit show_parconfig_smsc37c669(int io, int key)
 {
 	int cr1,cr4,cra,cr23,cr26,cr27,i=0;
 	char *modes[]={ "SPP and Bidirectional (PS/2)",	
@@ -1092,7 +1096,7 @@
 }
 
 
-static void show_parconfig_winbond(int io, int key)
+static void __devinit show_parconfig_winbond(int io, int key)
 {
 	int cr30,cr60,cr61,cr70,cr74,crf0,i=0;
 	char *modes[]={ "Standard (SPP) and Bidirectional(PS/2)", /* 0 */
@@ -1152,7 +1156,7 @@
 	}
 }
 
-static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
+static void __devinit decode_winbond(int efer, int key, int devid, int devrev, int oldid)
 {
 	char *type=NULL;
 	int id,progif=2;
@@ -1188,7 +1192,7 @@
 	return;
 }
 
-static void decode_smsc(int efer, int key, int devid, int devrev)
+static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
 {
         char *type=NULL;
 	void (*func)(int io, int key);
@@ -1219,7 +1223,7 @@
 }
 
 
-static void winbond_check(int io, int key)
+static void __devinit winbond_check(int io, int key)
 {
 	int devid,devrev,oldid;
 
@@ -1237,7 +1241,7 @@
 	decode_winbond(io,key,devid,devrev,oldid);
 }
 
-static void winbond_check2(int io,int key)
+static void __devinit winbond_check2(int io,int key)
 {
         int devid,devrev,oldid;
 
@@ -1254,7 +1258,7 @@
         decode_winbond(io,key,devid,devrev,oldid);
 }
 
-static void smsc_check(int io, int key)
+static void __devinit smsc_check(int io, int key)
 {
         int devid,devrev;
 
@@ -1271,7 +1275,7 @@
 }
 
 
-static void detect_and_report_winbond (void)
+static void __devinit detect_and_report_winbond (void)
 { 
 	printk("Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
 
@@ -1284,7 +1288,7 @@
 	winbond_check2(0x250,0x89);
 }
 
-static void detect_and_report_smsc (void)
+static void __devinit detect_and_report_smsc (void)
 {
 	printk("SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
 	smsc_check(0x3f0,0x55);
@@ -1292,8 +1296,9 @@
 	smsc_check(0x3f0,0x44);
 	smsc_check(0x370,0x44);
 }
+#endif /* CONFIG_PARPORT_PC_SUPERIO */
 
-static int get_superio_dma (struct parport *p)
+static int __devinit get_superio_dma (struct parport *p)
 {
 	int i=0;
 	while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
@@ -1303,7 +1308,7 @@
 	return PARPORT_DMA_NONE;
 }
 
-static int get_superio_irq (struct parport *p)
+static int __devinit get_superio_irq (struct parport *p)
 {
 	int i=0;
         while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
@@ -2330,7 +2335,7 @@
 	probe:		parport_pc_pci_probe,
 };
 
-static int __devinit parport_pc_init_superio (void)
+static int __init parport_pc_init_superio (void)
 {
 #ifdef CONFIG_PCI
 	const struct pci_device_id *id;
@@ -2348,6 +2353,74 @@
 	return 0; /* zero devices found */
 }
 
+/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
+static int __init __attribute__((unused))
+parport_pc_find_isa_ports (int autoirq, int autodma)
+{
+	int count = 0;
+
+	if (parport_pc_probe_port(0x3bc, 0x7bc, autoirq, autodma, NULL))
+		count++;
+	if (parport_pc_probe_port(0x378, 0x778, autoirq, autodma, NULL))
+		count++;
+	if (parport_pc_probe_port(0x278, 0x678, autoirq, autodma, NULL))
+		count++;
+
+	return count;
+}
+
+/* This function is called by parport_pc_init if the user didn't
+ * specify any ports to probe.  Its job is to find some ports.  Order
+ * is important here -- we want ISA ports to be registered first,
+ * followed by PCI cards (for least surprise), but before that we want
+ * to do chipset-specific tests for some onboard ports that we know
+ * about.
+ *
+ * autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY
+ * autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO
+ */
+static int __init parport_pc_find_ports (int autoirq, int autodma)
+{
+	int count = 0, r;
+
+#ifdef CONFIG_PARPORT_PC_SUPERIO
+	detect_and_report_winbond ();
+	detect_and_report_smsc ();
+#endif
+
+	/* Onboard SuperIO chipsets that show themselves on the PCI bus. */
+	count += parport_pc_init_superio ();
+
+	/* ISA ports and whatever (see asm/parport.h). */
+	count += parport_pc_find_nonpci_ports (autoirq, autodma);
+
+	r = pci_register_driver (&parport_pc_pci_driver);
+	if (r > 0)
+		count += r;
+
+	return count;
+}
+
+int __init parport_pc_init (int *io, int *io_hi, int *irq, int *dma)
+{
+	int count = 0, i = 0;
+
+	if (io && *io) {
+		/* Only probe the ports we were given. */
+		user_specified = 1;
+		do {
+			if (!*io_hi) *io_hi = 0x400 + *io;
+			if (parport_pc_probe_port(*(io++), *(io_hi++),
+						  *(irq++), *(dma++), NULL))
+				count++;
+		} while (*io && (++i < PARPORT_PC_MAX_PORTS));
+	} else {
+		count += parport_pc_find_ports (irq[0], dma[0]);
+	}
+
+	return count;
+}
+
 /* Exported symbols. */
 #ifdef CONFIG_PARPORT_PC_PCMCIA
 
@@ -2367,7 +2440,6 @@
 static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
 static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
 static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
-static int superio = 0;
 
 MODULE_AUTHOR("Phil Blundell, Tim Waugh, others");
 MODULE_DESCRIPTION("PC-style parallel port driver");
@@ -2379,18 +2451,12 @@
 MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
 MODULE_PARM_DESC(dma, "DMA channel");
 MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-MODULE_PARM_DESC(superio, "Enable Super-IO chipset probe");
-MODULE_PARM(superio, "i");
 
 int init_module(void)
 {	
 	/* Work out how many ports we have, then get parport_share to parse
 	   the irq values. */
-	unsigned int i, n;
-	if (superio) {
-		detect_and_report_winbond ();
-		detect_and_report_smsc ();
-	}
+	unsigned int i;
 	for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
 	if (i) {
 		if (parport_parse_irqs(i, irq, irqval)) return 1;
@@ -2415,12 +2481,7 @@
 			}
 	}
 
-	n = parport_pc_init_superio ();
-	n += parport_pc_init (io, io_hi, irqval, dmaval);
-	i = pci_register_driver (&parport_pc_pci_driver);
-
-	if (i > 0) n += i;
-	return !n;
+	return !parport_pc_init (io, io_hi, irqval, dmaval);
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.3.51/linux/drivers/pcmcia/yenta.c linux/drivers/pcmcia/yenta.c
--- v2.3.51/linux/drivers/pcmcia/yenta.c	Fri Mar 10 16:40:43 2000
+++ linux/drivers/pcmcia/yenta.c	Mon Mar 13 10:16:16 2000
@@ -498,7 +498,7 @@
 	return 0;
 }
 
-static unsigned int yenta_probe_irq(pci_socket_t *socket)
+static unsigned int yenta_probe_irq(pci_socket_t *socket, u32 isa_irq_mask)
 {
 	int i;
 	unsigned long val;
@@ -518,7 +518,7 @@
 	 */
 	cb_writel(socket, CB_SOCKET_EVENT, -1);
 	cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
-	val = probe_irq_on();
+	val = probe_irq_on() & isa_irq_mask;
 	for (i = 1; i < 16; i++) {
 		if (!((val >> i) & 1))
 			continue;
@@ -647,12 +647,12 @@
 /*
  * Set static data that doesn't need re-initializing..
  */
-static void yenta_get_socket_capabilities(pci_socket_t *socket)
+static void yenta_get_socket_capabilities(pci_socket_t *socket, u32 isa_irq_mask)
 {
 	socket->cap.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS;
 	socket->cap.map_size = 0x1000;
 	socket->cap.pci_irq = socket->cb_irq;
-	socket->cap.irq_mask = yenta_probe_irq(socket);
+	socket->cap.irq_mask = yenta_probe_irq(socket, isa_irq_mask);
 	socket->cap.cb_dev = socket->dev;
 	socket->cap.bus = NULL;
 
@@ -752,6 +752,17 @@
 #define NR_OVERRIDES (sizeof(cardbus_override)/sizeof(struct cardbus_override_struct))
 
 /*
+ * Only probe "regular" interrupts, don't
+ * touch dangerous spots like the mouse irq,
+ * because there are mice that apparently
+ * get really confused if they get fondled
+ * too intimately.
+ *
+ * Default to 11, 10, 9, 7, 6, 5, 4, 3.
+ */
+static u32 isa_interrupts = 0x0ef8;
+
+/*
  * Initialize a cardbus controller. Make sure we have a usable
  * interrupt, and that we can map the cardbus area. Fill in the
  * socket information structure..
@@ -790,9 +801,6 @@
 	if (dev->irq && !request_irq(dev->irq, yenta_interrupt, SA_SHIRQ, dev->name, socket))
 		socket->cb_irq = dev->irq;
 
-	/* And figure out what the dang thing can do for the PCMCIA layer... */
-	yenta_get_socket_capabilities(socket);
-
 	/* Do we have special options for the device? */
 	for (i = 0; i < NR_OVERRIDES; i++) {
 		struct cardbus_override_struct *d = cardbus_override+i;
@@ -805,6 +813,9 @@
 			}
 		}
 	}
+
+	/* Figure out what the dang thing can do for the PCMCIA layer... */
+	yenta_get_socket_capabilities(socket, isa_interrupts);
 
 	kernel_thread(yenta_socket_thread, socket, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 	printk("Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
diff -u --recursive --new-file v2.3.51/linux/drivers/sbus/audio/audio.c linux/drivers/sbus/audio/audio.c
--- v2.3.51/linux/drivers/sbus/audio/audio.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/sbus/audio/audio.c	Mon Mar 13 09:35:06 2000
@@ -1,4 +1,4 @@
-/* $Id: audio.c,v 1.49 2000/02/17 05:52:41 davem Exp $
+/* $Id: audio.c,v 1.50 2000/03/13 03:54:07 davem Exp $
  * drivers/sbus/audio/audio.c
  *
  * Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
@@ -70,7 +70,8 @@
 static void kill_procs( struct strevent *elist, int sig, short e);
 
 static struct sparcaudio_driver *drivers[SPARCAUDIO_MAX_DEVICES] = {NULL};
-
+static devfs_handle_t devfs_handle = NULL;
+ 
 /* This crap to be pulled off into a local include file */
 #if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
 
@@ -92,185 +93,6 @@
 
 #endif
 
-int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
-{
-	int i, dev;
-
-	/* If we've used up SPARCAUDIO_MAX_DEVICES, fail */
-	for (dev = 0; dev < SPARCAUDIO_MAX_DEVICES; dev++) {
-                if (drivers[dev] == NULL)
-                        break;
-	}
-
-	if (drivers[dev])
-                return -EIO;
-
-	/* Ensure that the driver has a proper operations structure. */
-	if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output ||
-	    !drv->ops->start_input || !drv->ops->stop_input)
-		return -EINVAL;
-
-        /* Setup the circular queues of output and input buffers
-         *
-         * Each buffer is a single page, but output buffers might
-         * be partially filled (by a write with count < output_buffer_size),
-         * so each output buffer also has a paired output size.
-         *
-         * Input buffers, on the other hand, always fill completely,
-         * so we don't need input counts - each contains input_buffer_size
-         * bytes of audio data.
-         *
-         * TODO: Make number of input/output buffers tunable parameters
-         */
-
-#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x202ff
-        init_waitqueue_head(&drv->open_wait);
-        init_waitqueue_head(&drv->output_write_wait);
-        init_waitqueue_head(&drv->output_drain_wait);
-        init_waitqueue_head(&drv->input_read_wait);
-#endif
-
-        drv->num_output_buffers = 8;
-	drv->output_buffer_size = (4096 * 2);
-	drv->playing_count = 0;
-	drv->output_offset = 0;
-	drv->output_eof = 0;
-        drv->output_front = 0;
-        drv->output_rear = 0;
-        drv->output_count = 0;
-        drv->output_active = 0;
-        drv->output_buffers = kmalloc(drv->num_output_buffers * 
-				      sizeof(__u8 *), GFP_KERNEL);
-        drv->output_sizes = kmalloc(drv->num_output_buffers * 
-				    sizeof(size_t), GFP_KERNEL);
-        drv->output_notify = kmalloc(drv->num_output_buffers * 
-				    sizeof(char), GFP_KERNEL);
-        if (!drv->output_buffers || !drv->output_sizes || !drv->output_notify)
-                goto kmalloc_failed1;
-
-	drv->output_buffer = kmalloc((drv->output_buffer_size * 
-                                      drv->num_output_buffers),
-                                     GFP_KERNEL);
-	if (!drv->output_buffer)
-                goto kmalloc_failed2;
-
-        /* Allocate the pages for each output buffer. */
-        for (i = 0; i < drv->num_output_buffers; i++) {
-	        drv->output_buffers[i] = (void *)(drv->output_buffer + 
-                                                  (i * drv->output_buffer_size));
-		drv->output_sizes[i] = 0;
-		drv->output_notify[i] = 0;
-        }
-
-        /* Setup the circular queue of input buffers. */
-        drv->num_input_buffers = 8;
-	drv->input_buffer_size = (4096 * 2);
-	drv->recording_count = 0;
-        drv->input_front = 0;
-        drv->input_rear = 0;
-        drv->input_count = 0;
-	drv->input_offset = 0;
-        drv->input_size = 0;
-        drv->input_active = 0;
-        drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *),
-				     GFP_KERNEL);
-        drv->input_sizes = kmalloc(drv->num_input_buffers * 
-                                   sizeof(size_t), GFP_KERNEL);
-        if (!drv->input_buffers || !drv->input_sizes)
-                goto kmalloc_failed3;
-
-        /* Allocate the pages for each input buffer. */
-	if (duplex == 1) {
-                drv->input_buffer = kmalloc((drv->input_buffer_size * 
-                                             drv->num_input_buffers), 
-                                            GFP_DMA);
-                if (!drv->input_buffer)
-                        goto kmalloc_failed4;
-
-                for (i = 0; i < drv->num_input_buffers; i++)
-                        drv->input_buffers[i] = (void *)(drv->input_buffer + 
-                                                         (i * drv->input_buffer_size));
-	} else {
-                if (duplex == 2) {
-                        drv->input_buffer = drv->output_buffer;
-                        drv->input_buffer_size = drv->output_buffer_size;
-                        drv->num_input_buffers = drv->num_output_buffers;
-                        for (i = 0; i < drv->num_input_buffers; i++) 
-                                drv->input_buffers[i] = drv->output_buffers[i];
-                } else {
-                        for (i = 0; i < drv->num_input_buffers; i++) 
-                                drv->input_buffers[i] = NULL;
-                }
-	}
-
-	/* Take note of our duplexity */
-	drv->duplex = duplex;
-
-	/* Ensure that the driver is marked as not being open. */
-	drv->flags = 0;
-
-	MOD_INC_USE_COUNT;
-
-	/* Take driver slot, note which we took */
-	drv->index = dev;
-	drivers[dev] = drv;
-
-	return 0;
-
-kmalloc_failed4:
-	kfree(drv->input_buffer);
-
-kmalloc_failed3:
-        if (drv->input_sizes)
-                kfree(drv->input_sizes);
-        if (drv->input_buffers)
-                kfree(drv->input_buffers);
-        i = drv->num_output_buffers;
-
-kmalloc_failed2:
-	kfree(drv->output_buffer);
-
-kmalloc_failed1:
-        if (drv->output_buffers)
-                kfree(drv->output_buffers);
-        if (drv->output_sizes)
-                kfree(drv->output_sizes);
-        if (drv->output_notify)
-                kfree(drv->output_notify);
-
-        return -ENOMEM;
-}
-
-int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
-{
-	/* Figure out which driver is unregistering */
-	if (drivers[drv->index] != drv)
-		return -EIO;
-
-	/* Deallocate the queue of output buffers. */
-	kfree(drv->output_buffer);
-	kfree(drv->output_buffers);
-	kfree(drv->output_sizes);
-	kfree(drv->output_notify);
-
-	/* Deallocate the queue of input buffers. */
-	if (duplex == 1) {
-                kfree(drv->input_buffer);
-                kfree(drv->input_sizes);
-	}
-	kfree(drv->input_buffers);
-
-	if (&(drv->sd_siglist) != NULL)
-                lis_free_elist( &(drv->sd_siglist) );
-
-	MOD_DEC_USE_COUNT;
-
-	/* Null the appropriate driver */
-	drivers[drv->index] = NULL;
-
-	return 0;
-}
-
 void sparcaudio_output_done(struct sparcaudio_driver * drv, int status)
 {
         /* If !status, just restart current output.
@@ -2171,6 +1993,229 @@
 	release:	sparcaudio_release,
 };
 
+static struct {
+	unsigned short minor;
+	char *name;
+	umode_t mode;
+} dev_list[] = {
+	{ SPARCAUDIO_MIXER_MINOR, "mixer", S_IWUSR | S_IRUGO },
+	{ SPARCAUDIO_DSP_MINOR, "dsp", S_IWUGO | S_IRUSR | S_IRGRP },
+	{ SPARCAUDIO_AUDIO_MINOR, "audio", S_IWUGO | S_IRUSR | S_IRGRP },
+	{ SPARCAUDIO_DSP16_MINOR, "dspW", S_IWUGO | S_IRUSR | S_IRGRP },
+	{ SPARCAUDIO_STATUS_MINOR, "status", S_IRUGO },
+	{ SPARCAUDIO_AUDIOCTL_MINOR, "audioctl", S_IRUGO }
+};
+
+static void sparcaudio_mkname (char *buf, char *name, int dev)
+{
+        if (dev)
+                sprintf (buf, "%s%d", name, dev);
+        else
+                sprintf (buf, "%s", name);
+}
+
+int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
+{
+	int i, dev;
+	unsigned short minor;
+	char name_buf[32];
+
+	/* If we've used up SPARCAUDIO_MAX_DEVICES, fail */
+	for (dev = 0; dev < SPARCAUDIO_MAX_DEVICES; dev++) {
+                if (drivers[dev] == NULL)
+                        break;
+	}
+
+	if (drivers[dev])
+                return -EIO;
+
+	/* Ensure that the driver has a proper operations structure. */
+	if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output ||
+	    !drv->ops->start_input || !drv->ops->stop_input)
+		return -EINVAL;
+
+        /* Register ourselves with devfs */
+	for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) {
+		sparcaudio_mkname (name_buf, dev_list[i].name, dev);
+		minor = (dev << SPARCAUDIO_DEVICE_SHIFT) | dev_list[i].minor;
+		devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE,
+				SOUND_MAJOR, minor, S_IFCHR | dev_list[i].mode,
+				0, 0, &sparcaudio_fops, NULL);
+	}
+
+        /* Setup the circular queues of output and input buffers
+         *
+         * Each buffer is a single page, but output buffers might
+         * be partially filled (by a write with count < output_buffer_size),
+         * so each output buffer also has a paired output size.
+         *
+         * Input buffers, on the other hand, always fill completely,
+         * so we don't need input counts - each contains input_buffer_size
+         * bytes of audio data.
+         *
+         * TODO: Make number of input/output buffers tunable parameters
+         */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x202ff
+        init_waitqueue_head(&drv->open_wait);
+        init_waitqueue_head(&drv->output_write_wait);
+        init_waitqueue_head(&drv->output_drain_wait);
+        init_waitqueue_head(&drv->input_read_wait);
+#endif
+
+        drv->num_output_buffers = 8;
+	drv->output_buffer_size = (4096 * 2);
+	drv->playing_count = 0;
+	drv->output_offset = 0;
+	drv->output_eof = 0;
+        drv->output_front = 0;
+        drv->output_rear = 0;
+        drv->output_count = 0;
+        drv->output_active = 0;
+        drv->output_buffers = kmalloc(drv->num_output_buffers * 
+				      sizeof(__u8 *), GFP_KERNEL);
+        drv->output_sizes = kmalloc(drv->num_output_buffers * 
+				    sizeof(size_t), GFP_KERNEL);
+        drv->output_notify = kmalloc(drv->num_output_buffers * 
+				    sizeof(char), GFP_KERNEL);
+        if (!drv->output_buffers || !drv->output_sizes || !drv->output_notify)
+                goto kmalloc_failed1;
+
+	drv->output_buffer = kmalloc((drv->output_buffer_size * 
+                                      drv->num_output_buffers),
+                                     GFP_KERNEL);
+	if (!drv->output_buffer)
+                goto kmalloc_failed2;
+
+        /* Allocate the pages for each output buffer. */
+        for (i = 0; i < drv->num_output_buffers; i++) {
+	        drv->output_buffers[i] = (void *)(drv->output_buffer + 
+                                                  (i * drv->output_buffer_size));
+		drv->output_sizes[i] = 0;
+		drv->output_notify[i] = 0;
+        }
+
+        /* Setup the circular queue of input buffers. */
+        drv->num_input_buffers = 8;
+	drv->input_buffer_size = (4096 * 2);
+	drv->recording_count = 0;
+        drv->input_front = 0;
+        drv->input_rear = 0;
+        drv->input_count = 0;
+	drv->input_offset = 0;
+        drv->input_size = 0;
+        drv->input_active = 0;
+        drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *),
+				     GFP_KERNEL);
+        drv->input_sizes = kmalloc(drv->num_input_buffers * 
+                                   sizeof(size_t), GFP_KERNEL);
+        if (!drv->input_buffers || !drv->input_sizes)
+                goto kmalloc_failed3;
+
+        /* Allocate the pages for each input buffer. */
+	if (duplex == 1) {
+                drv->input_buffer = kmalloc((drv->input_buffer_size * 
+                                             drv->num_input_buffers), 
+                                            GFP_DMA);
+                if (!drv->input_buffer)
+                        goto kmalloc_failed4;
+
+                for (i = 0; i < drv->num_input_buffers; i++)
+                        drv->input_buffers[i] = (void *)(drv->input_buffer + 
+                                                         (i * drv->input_buffer_size));
+	} else {
+                if (duplex == 2) {
+                        drv->input_buffer = drv->output_buffer;
+                        drv->input_buffer_size = drv->output_buffer_size;
+                        drv->num_input_buffers = drv->num_output_buffers;
+                        for (i = 0; i < drv->num_input_buffers; i++) 
+                                drv->input_buffers[i] = drv->output_buffers[i];
+                } else {
+                        for (i = 0; i < drv->num_input_buffers; i++) 
+                                drv->input_buffers[i] = NULL;
+                }
+	}
+
+	/* Take note of our duplexity */
+	drv->duplex = duplex;
+
+	/* Ensure that the driver is marked as not being open. */
+	drv->flags = 0;
+
+	MOD_INC_USE_COUNT;
+
+	/* Take driver slot, note which we took */
+	drv->index = dev;
+	drivers[dev] = drv;
+
+	return 0;
+
+kmalloc_failed4:
+	kfree(drv->input_buffer);
+
+kmalloc_failed3:
+        if (drv->input_sizes)
+                kfree(drv->input_sizes);
+        if (drv->input_buffers)
+                kfree(drv->input_buffers);
+        i = drv->num_output_buffers;
+
+kmalloc_failed2:
+	kfree(drv->output_buffer);
+
+kmalloc_failed1:
+        if (drv->output_buffers)
+                kfree(drv->output_buffers);
+        if (drv->output_sizes)
+                kfree(drv->output_sizes);
+        if (drv->output_notify)
+                kfree(drv->output_notify);
+
+        return -ENOMEM;
+}
+
+int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
+{
+	devfs_handle_t de;
+	int i;
+	char name_buf[32];
+
+	/* Figure out which driver is unregistering */
+	if (drivers[drv->index] != drv)
+		return -EIO;
+
+	/* Deallocate the queue of output buffers. */
+	kfree(drv->output_buffer);
+	kfree(drv->output_buffers);
+	kfree(drv->output_sizes);
+	kfree(drv->output_notify);
+
+	/* Deallocate the queue of input buffers. */
+	if (duplex == 1) {
+                kfree(drv->input_buffer);
+                kfree(drv->input_sizes);
+	}
+	kfree(drv->input_buffers);
+
+	if (&(drv->sd_siglist) != NULL)
+                lis_free_elist( &(drv->sd_siglist) );
+
+	/* Unregister ourselves with devfs */
+	for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) {
+		sparcaudio_mkname (name_buf, dev_list[i].name, drv->index);
+		de = devfs_find_handle (devfs_handle, name_buf, 0, 0, 0,
+					DEVFS_SPECIAL_CHR, 0);
+		devfs_unregister (de);
+	}
+
+	MOD_DEC_USE_COUNT;
+
+	/* Null the appropriate driver */
+	drivers[drv->index] = NULL;
+
+	return 0;
+}
+
 #if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
 static struct symbol_table sparcaudio_syms = {
 #include <linux/symtab_begin.h>
@@ -2201,6 +2246,8 @@
 	/* Register our character device driver with the VFS. */
 	if (devfs_register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops))
 		return -EIO;
+
+	devfs_handle = devfs_mk_dir (NULL, "sound", 0, NULL);
 	
 #ifdef CONFIG_SPARCAUDIO_AMD7930
 	amd7930_init();
@@ -2222,6 +2269,7 @@
 void cleanup_module(void)
 {
 	devfs_unregister_chrdev(SOUND_MAJOR, "sparcaudio");
+	devfs_unregister (devfs_handle);
 }
 #endif
 
diff -u --recursive --new-file v2.3.51/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c
--- v2.3.51/linux/drivers/sbus/char/sab82532.c	Wed Dec 29 13:13:17 1999
+++ linux/drivers/sbus/char/sab82532.c	Mon Mar 13 09:35:06 2000
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.40 1999/12/19 23:28:08 davem Exp $
+/* $Id: sab82532.c,v 1.41 2000/03/13 03:54:17 davem Exp $
  * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -2163,7 +2163,7 @@
 
 static inline void __init show_serial_version(void)
 {
-	char *revision = "$Revision: 1.40 $";
+	char *revision = "$Revision: 1.41 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2196,7 +2196,7 @@
 	memset(&serial_driver, 0, sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
 	serial_driver.driver_name = "serial";
-	serial_driver.name = "ttyS";
+	serial_driver.name = "tts/%d";
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64 + su_num_ports;
 	serial_driver.num = NR_PORTS;
@@ -2236,7 +2236,7 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
-	callout_driver.name = "cua";
+	callout_driver.name = "cua/%d";
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 	callout_driver.read_proc = 0;
diff -u --recursive --new-file v2.3.51/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c
--- v2.3.51/linux/drivers/sbus/char/su.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/sbus/char/su.c	Mon Mar 13 09:35:06 2000
@@ -1,4 +1,4 @@
-/* $Id: su.c,v 1.36 2000/02/09 21:11:22 davem Exp $
+/* $Id: su.c,v 1.37 2000/03/13 03:54:15 davem Exp $
  * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -2223,7 +2223,7 @@
  */
 static __inline__ void __init show_su_version(void)
 {
-	char *revision = "$Revision: 1.36 $";
+	char *revision = "$Revision: 1.37 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2442,7 +2442,7 @@
 	memset(&serial_driver, 0, sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
 	serial_driver.driver_name = "su";
-	serial_driver.name = "ttyS";
+	serial_driver.name = "ttys/%d";
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64;
 	serial_driver.num = NR_PORTS;
@@ -2482,7 +2482,7 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
-	callout_driver.name = "cua";
+	callout_driver.name = "cua/%d";
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 	callout_driver.read_proc = 0;
diff -u --recursive --new-file v2.3.51/linux/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c
--- v2.3.51/linux/drivers/sbus/char/zs.c	Thu Feb 10 17:11:12 2000
+++ linux/drivers/sbus/char/zs.c	Sun Mar 12 19:11:16 2000
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.55 2000/02/09 21:11:24 davem Exp $
+/* $Id: zs.c,v 1.56 2000/03/12 04:02:11 davem Exp $
  * zs.c: Zilog serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1928,7 +1928,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.55 $";
+	char *revision = "$Revision: 1.56 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2415,7 +2415,7 @@
 	memset(&serial_driver, 0, sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
 	serial_driver.driver_name = "serial";
-	serial_driver.name = "ttyS";
+	serial_driver.name = "tts/%d";
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64;
 	serial_driver.num = NUM_CHANNELS;
@@ -2454,7 +2454,7 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
-	callout_driver.name = "cua";
+	callout_driver.name = "cua/%d";
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.3.51/linux/drivers/scsi/ChangeLog.ncr53c8xx	Tue Jan 11 22:31:41 2000
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Sun Mar 12 20:10:19 2000
@@ -1,3 +1,20 @@
+Mon March 6  23:15 2000 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.2g
+	- Add the file sym53c8xx_comm.h that collects code that should 
+	  be shared by sym53c8xx and ncr53c8xx drivers. For now, it is  
+	  a header file that is only included by the ncr53c8xx driver, 
+	  but things will be cleaned up later. This code addresses 
+	  notably:
+	  * Chip detection and PCI related initialisations
+	  * NVRAM detection and reading
+	  * DMA mapping
+	  * Boot setup command
+	  * And some other ...
+	- Add support for the new dynamic dma mapping kernel interface.
+	  Requires Linux-2.3.47 (tested with pre-2.3.47-6).
+	- Get data transfer direction from the scsi command structure 
+	  (Scsi_Cmnd) when this information is available.
+
 Sat Jan 8  22:00 2000 Gerard Roudier (groudier@club-internet.fr)
 	* revision 3.2e
 	- Add year 2000 copyright.
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/ChangeLog.sym53c8xx linux/drivers/scsi/ChangeLog.sym53c8xx
--- v2.3.51/linux/drivers/scsi/ChangeLog.sym53c8xx	Sat Feb 26 22:31:48 2000
+++ linux/drivers/scsi/ChangeLog.sym53c8xx	Sun Mar 12 20:09:55 2000
@@ -1,3 +1,12 @@
+Mon Mar  6 23:30 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5k
+	- Test against expected data transfer direction from SCRIPTS.
+	- Revert the change in 'ncr_flush_done_cmds()' but unmap the 
+	  scsi dma buffer prior to queueing the command to our done 
+	  list.
+	- Miscellaneous (minor) fixes in the code added in driver 
+	  version 1.5j.
+
 Sun Feb 20 11:00 2000 Gerard Roudier (groudier@club-internet.fr)
 	* version sym53c8xx-1.5j
 	- Add support for the new dynamic dma mapping kernel interface.
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.3.51/linux/drivers/scsi/Config.in	Fri Mar 10 16:40:43 2000
+++ linux/drivers/scsi/Config.in	Mon Mar 13 12:32:22 2000
@@ -8,10 +8,6 @@
 
 dep_tristate '  SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
 
-if [ "$CONFIG_CHR_DEV_ST" != "n" ]; then
-   int  'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2
-fi
-
 dep_tristate '  SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
 
 if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v2.3.51/linux/drivers/scsi/README.st	Mon Aug  9 10:23:42 1999
+++ linux/drivers/scsi/README.st	Mon Mar 13 12:32:22 2000
@@ -2,7 +2,7 @@
 The driver is currently maintained by Kai M{kisara (email
 Kai.Makisara@metla.fi)
 
-Last modified: Sat Aug  7 13:52:16 1999 by makisara@kai.makisara.local
+Last modified: Sat Mar 11 10:34:44 2000 by makisara@kai.makisara.local
 
 
 BASICS
@@ -134,11 +134,7 @@
 maximum number of these buffers is defined by ST_MAX_BUFFERS. The
 maximum can be changed with kernel or module startup options. One
 buffer is allocated for each drive detected when the driver is
-initialized up to the maximum. The minimum number of allocated buffers
-is ST_EXTRA_DEVS (in hosts.h) (unless this number exceeds the defined
-maximum). This ensures some functionality also for the drives found
-after tape driver initialization (a SCSI adapter driver is loaded as a
-module). The default for ST_EXTRA_DEVS is two.
+initialized up to the maximum.
 
 The driver tries to allocate new buffers at run-time if
 necessary. These buffers are freed after use. If the maximum number of
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/atp870u.c linux/drivers/scsi/atp870u.c
--- v2.3.51/linux/drivers/scsi/atp870u.c	Tue Dec 14 01:27:24 1999
+++ linux/drivers/scsi/atp870u.c	Sun Mar 12 19:39:48 2000
@@ -105,6 +105,11 @@
 	{
 		tmport += 0x1f;
 		j = inb(tmport);
+		if((j&0x80)==0)
+		{
+			dev->in_int=0;
+			return;
+		}
 
 		tmpcip = dev->pciport;
 		if ((inb(tmpcip) & 0x08) != 0) 
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/constants.c linux/drivers/scsi/constants.c
--- v2.3.51/linux/drivers/scsi/constants.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/constants.c	Mon Mar 13 22:15:03 2000
@@ -158,16 +158,20 @@
 }
 
 #if (CONSTANTS & CONST_XSENSE)
-#define D 0x001  /* DIRECT ACCESS DEVICE (disk) */
-#define T 0x002  /* SEQUENTIAL ACCESS DEVICE (tape) */
-#define L 0x004  /* PRINTER DEVICE */
-#define P 0x008  /* PROCESSOR DEVICE */
-#define W 0x010  /* WRITE ONCE READ MULTIPLE DEVICE */
-#define R 0x020  /* READ ONLY (CD-ROM) DEVICE */
-#define S 0x040  /* SCANNER DEVICE */
-#define O 0x080  /* OPTICAL MEMORY DEVICE */
-#define M 0x100  /* MEDIA CHANGER DEVICE */
-#define C 0x200  /* COMMUNICATION DEVICE */
+#define D 0x0001  /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x0002  /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x0004  /* PRINTER DEVICE */
+#define P 0x0008  /* PROCESSOR DEVICE */
+#define W 0x0010  /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x0020  /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x0040  /* SCANNER DEVICE */
+#define O 0x0080  /* OPTICAL MEMORY DEVICE */
+#define M 0x0100  /* MEDIA CHANGER DEVICE */
+#define C 0x0200  /* COMMUNICATION DEVICE */
+#define A 0x0400  /* ARRAY STORAGE */
+#define E 0x0800  /* ENCLOSURE SERVICES DEVICE */
+#define B 0x1000  /* SIMPLIFIED DIRECT ACCESS DEVICE */
+#define K 0x2000  /* OPTICAL CARD READER/WRITER DEVICE */
 
 struct error_info{
     unsigned char code1, code2;
@@ -192,131 +196,213 @@
 
 static struct error_info additional[] =
 {
+  {0x00,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"No additional sense information"},
   {0x00,0x01,T,"Filemark detected"},
   {0x00,0x02,T|S,"End-of-partition/medium detected"},
   {0x00,0x03,T,"Setmark detected"},
   {0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
-  {0x00,0x05,T|S,"End-of-data detected"},
-  {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+  {0x00,0x05,T|L|S,"End-of-data detected"},
+  {0x00,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"I/O process terminated"},
   {0x00,0x11,R,"Audio play operation in progress"},
   {0x00,0x12,R,"Audio play operation paused"},
   {0x00,0x13,R,"Audio play operation successfully completed"},
   {0x00,0x14,R,"Audio play operation stopped due to error"},
   {0x00,0x15,R,"No current audio status to return"},
-  {0x01,0x00,D|W|O,"No index/sector signal"},
-  {0x02,0x00,D|W|R|O|M,"No seek complete"},
-  {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+  {0x00,0x16,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Operation in progress"},
+  {0x00,0x17,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning requested"},
+  {0x01,0x00,D|W|O|B|K,"No index/sector signal"},
+  {0x02,0x00,D|W|R|O|M|B|K,"No seek complete"},
+  {0x03,0x00,D|T|L|W|S|O|B|K,"Peripheral device write fault"},
   {0x03,0x01,T,"No write current"},
   {0x03,0x02,T,"Excessive write errors"},
-  {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
-     "Logical unit not ready, cause not reportable"},
-  {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
-     "Logical unit is in process of becoming ready"},
-  {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
-     "Logical unit not ready, initializing command required"},
-  {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
-     "Logical unit not ready, manual intervention required"},
-  {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
-  {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
-  {0x06,0x00,D|W|R|O|M,"No reference position found"},
-  {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
-  {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
-  {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
-  {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
-  {0x09,0x00,D|T|W|R|O,"Track following error"},
-  {0x09,0x01,W|R|O,"Tracking servo failure"},
-  {0x09,0x02,W|R|O,"Focus servo failure"},
+  {0x04,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,cause not reportable"},
+  {0x04,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit is in process of becoming ready"},
+  {0x04,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,initializing cmd. required"},
+  {0x04,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,manual intervention required"},
+  {0x04,0x04,D|T|L|R|O|B,"Logical unit not ready,format in progress"},
+  {0x04,0x05,D|T|W|O|M|C|A|B|K,"Logical unit not ready,rebuild in progress"},
+  {0x04,0x06,D|T|W|O|M|C|A|B|K,"Logical unit not ready,recalculation in progress"},
+  {0x04,0x07,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,operation in progress"},
+  {0x04,0x08,R,"Logical unit not ready,long write in progress"},
+  {0x04,0x09,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,self-test in progress"},
+  {0x05,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit does not respond to selection"},
+  {0x06,0x00,D|W|R|O|M|B|K,"No reference position found"},
+  {0x07,0x00,D|T|L|W|R|S|O|M|B|K,"Multiple peripheral devices selected"},
+  {0x08,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication failure"},
+  {0x08,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication time-out"},
+  {0x08,0x02,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication parity error"},
+  {0x08,0x03,D|T|R|O|M|B|K,"Logical unit communication CRC error (Ultra-DMA/32)"},
+  {0x08,0x04,D|T|L|P|W|R|S|O|C|K,"Unreachable copy target"},
+  {0x09,0x00,D|T|W|R|O|B,"Track following error"},
+  {0x09,0x01,W|R|O|K,"Tracking servo failure"},
+  {0x09,0x02,W|R|O|K,"Focus servo failure"},
   {0x09,0x03,W|R|O,"Spindle servo failure"},
-  {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
-  {0x0C,0x00,T|S,"Write error"},
-  {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
-  {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
-  {0x10,0x00,D|W|O,"Id crc or ecc error"},
-  {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
-  {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
-  {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
-  {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
-  {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
-  {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
-  {0x11,0x06,W|R|O,"Circ unrecovered error"},
-  {0x11,0x07,W|O,"Data resynchronization error"},
+  {0x09,0x04,D|T|W|R|O|B,"Head select fault"},
+  {0x0A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Error log overflow"},
+  {0x0B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning"},
+  {0x0B,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - specified temperature exceeded"},
+  {0x0B,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - enclosure degraded"},
+  {0x0C,0x00,T|R|S,"Write error"},
+  {0x0C,0x01,K,"Write error - recovered with auto reallocation"},
+  {0x0C,0x02,D|W|O|B|K,"Write error - auto reallocation failed"},
+  {0x0C,0x03,D|W|O|B|K,"Write error - recommend reassignment"},
+  {0x0C,0x04,D|T|W|O|B,"Compression check miscompare error"},
+  {0x0C,0x05,D|T|W|O|B,"Data expansion occurred during compression"},
+  {0x0C,0x06,D|T|W|O|B,"Block not compressible"},
+  {0x0C,0x07,R,"Write error - recovery needed"},
+  {0x0C,0x08,R,"Write error - recovery failed"},
+  {0x0C,0x09,R,"Write error - loss of streaming"},
+  {0x0C,0x0A,R,"Write error - padding blocks added"},
+  {0x10,0x00,D|W|O|B|K,"Id CRC or ECC error"},
+  {0x11,0x00,D|T|W|R|S|O|B|K,"Unrecovered read error"},
+  {0x11,0x01,D|T|W|R|S|O|B|K,"Read retries exhausted"},
+  {0x11,0x02,D|T|W|R|S|O|B|K,"Error too long to correct"},
+  {0x11,0x03,D|T|W|S|O|B|K,"Multiple read errors"},
+  {0x11,0x04,D|W|O|B|K,"Unrecovered read error - auto reallocate failed"},
+  {0x11,0x05,W|R|O|B,"L-EC uncorrectable error"},
+  {0x11,0x06,W|R|O|B,"CIRC unrecovered error"},
+  {0x11,0x07,W|O|B,"Data re-synchronization error"},
   {0x11,0x08,T,"Incomplete block read"},
   {0x11,0x09,T,"No gap found"},
-  {0x11,0x0A,D|T|O,"Miscorrected error"},
-  {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
-  {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
-  {0x12,0x00,D|W|O,"Address mark not found for id field"},
-  {0x13,0x00,D|W|O,"Address mark not found for data field"},
-  {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
-  {0x14,0x01,D|T|W|R|O,"Record not found"},
+  {0x11,0x0A,D|T|O|B|K,"Miscorrected error"},
+  {0x11,0x0B,D|W|O|B|K,"Unrecovered read error - recommend reassignment"},
+  {0x11,0x0C,D|W|O|B|K,"Unrecovered read error - recommend rewrite the data"},
+  {0x11,0x0D,D|T|W|R|O|B,"De-compression CRC error"},
+  {0x11,0x0E,D|T|W|R|O|B,"Cannot decompress using declared algorithm"},
+  {0x11,0x0F,R,"Error reading UPC/EAN number"},
+  {0x11,0x10,R,"Error reading ISRC number"},
+  {0x11,0x11,R,"Read error - loss of streaming"},
+  {0x12,0x00,D|W|O|B|K,"Address mark not found for id field"},
+  {0x13,0x00,D|W|O|B|K,"Address mark not found for data field"},
+  {0x14,0x00,D|T|L|W|R|S|O|B|K,"Recorded entity not found"},
+  {0x14,0x01,D|T|W|R|O|B|K,"Record not found"},
   {0x14,0x02,T,"Filemark or setmark not found"},
   {0x14,0x03,T,"End-of-data not found"},
   {0x14,0x04,T,"Block sequence error"},
-  {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
-  {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
-  {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
-  {0x16,0x00,D|W|O,"Data synchronization mark error"},
-  {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
-  {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
-  {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
-  {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
-  {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
-  {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
-  {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
-  {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
-  {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
-  {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
-  {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
-  {0x18,0x03,R,"Recovered data with circ"},
-  {0x18,0x04,R,"Recovered data with lec"},
-  {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
-  {0x19,0x00,D|O,"Defect list error"},
-  {0x19,0x01,D|O,"Defect list not available"},
-  {0x19,0x02,D|O,"Defect list error in primary list"},
-  {0x19,0x03,D|O,"Defect list error in grown list"},
-  {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
-  {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
-  {0x1C,0x00,D|O,"Defect list not found"},
-  {0x1C,0x01,D|O,"Primary defect list not found"},
-  {0x1C,0x02,D|O,"Grown defect list not found"},
-  {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
-  {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
-  {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
-  {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
-  {0x21,0x01,M,"Invalid element address"},
-  {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
-  {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
-  {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
-  {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
-  {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
-  {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
-  {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
-  {0x27,0x00,D|T|W|O,"Write protected"},
-  {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
-  {0x28,0x01,M,"Import or export element accessed"},
-  {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
-  {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
-  {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
-  {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
-  {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
-  {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+  {0x14,0x05,D|T|W|O|B|K,"Record not found - recommend reassignment"},
+  {0x14,0x06,D|T|W|O|B|K,"Record not found - data auto-reallocated"},
+  {0x15,0x00,D|T|L|W|R|S|O|M|B|K,"Random positioning error"},
+  {0x15,0x01,D|T|L|W|R|S|O|M|B|K,"Mechanical positioning error"},
+  {0x15,0x02,D|T|W|R|O|B|K,"Positioning error detected by read of medium"},
+  {0x16,0x00,D|W|O|B|K,"Data synchronization mark error"},
+  {0x16,0x01,D|W|O|B|K,"Data sync error - data rewritten"},
+  {0x16,0x02,D|W|O|B|K,"Data sync error - recommend rewrite"},
+  {0x16,0x03,D|W|O|B|K,"Data sync error - data auto-reallocated"},
+  {0x16,0x04,D|W|O|B|K,"Data sync error - recommend reassignment"},
+  {0x17,0x00,D|T|W|R|S|O|B|K,"Recovered data with no error correction applied"},
+  {0x17,0x01,D|T|W|R|S|O|B|K,"Recovered data with retries"},
+  {0x17,0x02,D|T|W|R|O|B|K,"Recovered data with positive head offset"},
+  {0x17,0x03,D|T|W|R|O|B|K,"Recovered data with negative head offset"},
+  {0x17,0x04,W|R|O|B,"Recovered data with retries and/or circ applied"},
+  {0x17,0x05,D|W|R|O|B|K,"Recovered data using previous sector id"},
+  {0x17,0x06,D|W|O|B|K,"Recovered data without ecc - data auto-reallocated"},
+  {0x17,0x07,D|W|R|O|B|K,"Recovered data without ecc - recommend reassignment"},
+  {0x17,0x08,D|W|R|O|B|K,"Recovered data without ecc - recommend rewrite"},
+  {0x17,0x09,D|W|R|O|B|K,"Recovered data without ecc - data rewritten"},
+  {0x18,0x00,D|T|W|R|O|B|K,"Recovered data with error correction applied"},
+  {0x18,0x01,D|W|R|O|B|K,"Recovered data with error corr. & retries applied"},
+  {0x18,0x02,D|W|R|O|B|K,"Recovered data - data auto-reallocated"},
+  {0x18,0x03,R,"Recovered data with CIRC"},
+  {0x18,0x04,R,"Recovered data with L-EC"},
+  {0x18,0x05,D|W|R|O|B|K,"Recovered data - recommend reassignment"},
+  {0x18,0x06,D|W|R|O|B|K,"Recovered data - recommend rewrite"},
+  {0x18,0x07,D|W|O|B|K,"Recovered data with ecc - data rewritten"},
+  {0x19,0x00,D|O|K,"Defect list error"},
+  {0x19,0x01,D|O|K,"Defect list not available"},
+  {0x19,0x02,D|O|K,"Defect list error in primary list"},
+  {0x19,0x03,D|O|K,"Defect list error in grown list"},
+  {0x1A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter list length error"},
+  {0x1B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Synchronous data transfer error"},
+  {0x1C,0x00,D|O|B|K,"Defect list not found"},
+  {0x1C,0x01,D|O|B|K,"Primary defect list not found"},
+  {0x1C,0x02,D|O|B|K,"Grown defect list not found"},
+  {0x1D,0x00,D|T|W|R|O|B|K,"Miscompare during verify operation"},
+  {0x1E,0x00,D|W|O|B|K,"Recovered id with ecc correction"},
+  {0x1F,0x00,D|O|K,"Partial defect list transfer"},
+  {0x20,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid command operation code"},
+  {0x21,0x00,D|T|W|R|O|M|B|K,"Logical block address out of range"},
+  {0x21,0x01,D|T|W|R|O|M|B|K,"Invalid element address"},
+  {0x22,0x00,D,"Illegal function (use 20 00,24 00,or 26 00)"},
+  {0x24,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in cdb"},
+  {0x24,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"CDB decryption error"},
+  {0x25,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not supported"},
+  {0x26,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in parameter list"},
+  {0x26,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter not supported"},
+  {0x26,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter value invalid"},
+  {0x26,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Threshold parameters not supported"},
+  {0x26,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid release of persistent reservation"},
+  {0x26,0x05,D|T|L|P|W|R|S|O|M|C|A|B|K,"Data decryption error"},
+  {0x26,0x06,D|T|L|P|W|R|S|O|C|K,"Too many target descriptors"},
+  {0x26,0x07,D|T|L|P|W|R|S|O|C|K,"Unsupported target descriptor type code"},
+  {0x26,0x08,D|T|L|P|W|R|S|O|C|K,"Too many segment descriptors"},
+  {0x26,0x09,D|T|L|P|W|R|S|O|C|K,"Unsupported segment descriptor type code"},
+  {0x26,0x0A,D|T|L|P|W|R|S|O|C|K,"Unexpected inexact segment"},
+  {0x26,0x0B,D|T|L|P|W|R|S|O|C|K,"Inline data length exceeded"},
+  {0x26,0x0C,D|T|L|P|W|R|S|O|C|K,"Invalid operation for copy source or destination"},
+  {0x26,0x0D,D|T|L|P|W|R|S|O|C|K,"Copy segment granularity violation"},
+  {0x27,0x00,D|T|W|R|O|B|K,"Write protected"},
+  {0x27,0x01,D|T|W|R|O|B|K,"Hardware write protected"},
+  {0x27,0x02,D|T|W|R|O|B|K,"Logical unit software write protected"},
+  {0x27,0x03,T|R,"Associated write protect"},
+  {0x27,0x04,T|R,"Persistent write protect"},
+  {0x27,0x05,T|R,"Permanent write protect"},
+  {0x28,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Not ready to ready change,medium may have changed"},
+  {0x28,0x01,D|T|W|R|O|M|B,"Import or export element accessed"},
+  {0x29,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on,reset,or bus device reset occurred"},
+  {0x29,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on occurred"},
+  {0x29,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi bus reset occurred"},
+  {0x29,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Bus device reset function occurred"},
+  {0x29,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Device internal reset"},
+  {0x29,0x05,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to single-ended"},
+  {0x29,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to lvd"},
+  {0x2A,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Parameters changed"},
+  {0x2A,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Mode parameters changed"},
+  {0x2A,0x02,D|T|L|W|R|S|O|M|C|A|E|K,"Log parameters changed"},
+  {0x2A,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Reservations preempted"},
+  {0x2A,0x04,D|T|L|P|W|R|S|O|M|C|A|E,"Reservations released"},
+  {0x2A,0x05,D|T|L|P|W|R|S|O|M|C|A|E,"Registrations preempted"},
+  {0x2B,0x00,D|T|L|P|W|R|S|O|C|K,"Copy cannot execute since host cannot disconnect"},
+  {0x2C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command sequence error"},
   {0x2C,0x01,S,"Too many windows specified"},
   {0x2C,0x02,S,"Invalid combination of windows specified"},
+  {0x2C,0x03,R,"Current program area is not empty"},
+  {0x2C,0x04,R,"Current program area is empty"},
+  {0x2C,0x05,B,"Illegal power condition request"},
   {0x2D,0x00,T,"Overwrite error on update in place"},
-  {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
-  {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
-  {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
-  {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
-  {0x30,0x03,D|T,"Cleaning cartridge installed"},
-  {0x31,0x00,D|T|W|O,"Medium format corrupted"},
-  {0x31,0x01,D|L|O,"Format command failed"},
-  {0x32,0x00,D|W|O,"No defect spare location available"},
-  {0x32,0x01,D|W|O,"Defect list update failure"},
+  {0x2F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Commands cleared by another initiator"},
+  {0x30,0x00,D|T|W|R|O|M|B|K,"Incompatible medium installed"},
+  {0x30,0x01,D|T|W|R|O|B|K,"Cannot read medium - unknown format"},
+  {0x30,0x02,D|T|W|R|O|B|K,"Cannot read medium - incompatible format"},
+  {0x30,0x03,D|T|R|K,"Cleaning cartridge installed"},
+  {0x30,0x04,D|T|W|R|O|B|K,"Cannot write medium - unknown format"},
+  {0x30,0x05,D|T|W|R|O|B|K,"Cannot write medium - incompatible format"},
+  {0x30,0x06,D|T|W|R|O|B,"Cannot format medium - incompatible medium"},
+  {0x30,0x07,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning failure"},
+  {0x30,0x08,R,"Cannot write - application code mismatch"},
+  {0x30,0x09,R,"Current session not fixated for append"},
+  {0x31,0x00,D|T|W|R|O|B|K,"Medium format corrupted"},
+  {0x31,0x01,D|L|R|O|B,"Format command failed"},
+  {0x32,0x00,D|W|O|B|K,"No defect spare location available"},
+  {0x32,0x01,D|W|O|B|K,"Defect list update failure"},
   {0x33,0x00,T,"Tape length error"},
-  {0x36,0x00,L,"Ribbon, ink, or toner failure"},
-  {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
-  {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
-  {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+  {0x34,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure failure"},
+  {0x35,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services failure"},
+  {0x35,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Unsupported enclosure function"},
+  {0x35,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services unavailable"},
+  {0x35,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer failure"},
+  {0x35,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer refused"},
+  {0x36,0x00,L,"Ribbon,ink,or toner failure"},
+  {0x37,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Rounded parameter"},
+  {0x38,0x00,B,"Event status notification"},
+  {0x38,0x02,B,"Esn - power management class event"},
+  {0x38,0x04,B,"Esn - media class event"},
+  {0x38,0x06,B,"Esn - device busy class event"},
+  {0x39,0x00,D|T|L|W|R|S|O|M|C|A|E|K,"Saving parameters not supported"},
+  {0x3A,0x00,D|T|L|W|R|S|O|M|B|K,"Medium not present"},
+  {0x3A,0x01,D|T|W|R|O|M|B|K,"Medium not present - tray closed"},
+  {0x3A,0x02,D|T|W|R|O|M|B|K,"Medium not present - tray open"},
+  {0x3A,0x03,D|T|W|R|O|M|B,"Medium not present - loadable"},
+  {0x3A,0x04,D|T|W|R|O|M|B,"Medium not present - medium auxiliary memory accessible"},
   {0x3B,0x00,T|L,"Sequential positioning error"},
   {0x3B,0x01,T,"Tape position error at beginning-of-medium"},
   {0x3B,0x02,T,"Tape position error at end-of-medium"},
@@ -329,57 +415,244 @@
   {0x3B,0x09,S,"Read past end of medium"},
   {0x3B,0x0A,S,"Read past beginning of medium"},
   {0x3B,0x0B,S,"Position past end of medium"},
-  {0x3B,0x0C,S,"Position past beginning of medium"},
-  {0x3B,0x0D,M,"Medium destination element full"},
-  {0x3B,0x0E,M,"Medium source element empty"},
-  {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
-  {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
-  {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
-  {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
-  {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
-  {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
-  {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
-  {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
-  {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
-  {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
-  {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
-  {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
-  {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
-  {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
-  {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
-  {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
-  {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+  {0x3B,0x0C,T|S,"Position past beginning of medium"},
+  {0x3B,0x0D,D|T|W|R|O|M|B|K,"Medium destination element full"},
+  {0x3B,0x0E,D|T|W|R|O|M|B|K,"Medium source element empty"},
+  {0x3B,0x0F,R,"End of medium reached"},
+  {0x3B,0x11,D|T|W|R|O|M|B|K,"Medium magazine not accessible"},
+  {0x3B,0x12,D|T|W|R|O|M|B|K,"Medium magazine removed"},
+  {0x3B,0x13,D|T|W|R|O|M|B|K,"Medium magazine inserted"},
+  {0x3B,0x14,D|T|W|R|O|M|B|K,"Medium magazine locked"},
+  {0x3B,0x15,D|T|W|R|O|M|B|K,"Medium magazine unlocked"},
+  {0x3B,0x16,R,"Mechanical positioning or changer error"},
+  {0x3D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|K,"Invalid bits in identify message"},
+  {0x3E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit has not self-configured yet"},
+  {0x3E,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failure"},
+  {0x3E,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Timeout on logical unit"},
+  {0x3E,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-test"},
+  {0x3E,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit unable to update self-test log"},
+  {0x3F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Target operating conditions have changed"},
+  {0x3F,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Microcode has been changed"},
+  {0x3F,0x02,D|T|L|P|W|R|S|O|M|C|B|K,"Changed operating definition"},
+  {0x3F,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Inquiry data has changed"},
+  {0x3F,0x04,D|T|W|R|O|M|C|A|E|B|K,"Component device attached"},
+  {0x3F,0x05,D|T|W|R|O|M|C|A|E|B|K,"Device identifier changed"},
+  {0x3F,0x06,D|T|W|R|O|M|C|A|E|B,"Redundancy group created or modified"},
+  {0x3F,0x07,D|T|W|R|O|M|C|A|E|B,"Redundancy group deleted"},
+  {0x3F,0x08,D|T|W|R|O|M|C|A|E|B,"Spare created or modified"},
+  {0x3F,0x09,D|T|W|R|O|M|C|A|E|B,"Spare deleted"},
+  {0x3F,0x0A,D|T|W|R|O|M|C|A|E|B|K,"Volume set created or modified"},
+  {0x3F,0x0B,D|T|W|R|O|M|C|A|E|B|K,"Volume set deleted"},
+  {0x3F,0x0C,D|T|W|R|O|M|C|A|E|B|K,"Volume set deassigned"},
+  {0x3F,0x0D,D|T|W|R|O|M|C|A|E|B|K,"Volume set reassigned"},
+  {0x3F,0x0E,D|T|L|P|W|R|S|O|M|C|A|E,"Reported luns data has changed"},
+  {0x3F,0x10,D|T|W|R|O|M|B,"Medium loadable"},
+  {0x3F,0x11,D|T|W|R|O|M|B,"Medium auxiliary memory accessible"},
+  {0x40,0x00,D,"Ram failure (should use 40 nn)"},
+  /*
+   * FIXME(eric) - need a way to represent wildcards here.
+   */
+  {0x40,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Diagnostic failure on component nn (80h-ffh)"},
+  {0x41,0x00,D,"Data path failure (should use 40 nn)"},
+  {0x42,0x00,D,"Power-on or self-test failure (should use 40 nn)"},
+  {0x43,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Message error"},
+  {0x44,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Internal target failure"},
+  {0x45,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Select or reselect failure"},
+  {0x46,0x00,D|T|L|P|W|R|S|O|M|C|B|K,"Unsuccessful soft reset"},
+  {0x47,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error"},
+  {0x47,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase CRC error detected"},
+  {0x47,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error detected during st data phase"},
+  {0x47,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Information unit CRC error detected"},
+  {0x47,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Asynchronous information protection error detected"},
+  {0x48,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Initiator detected error message received"},
+  {0x49,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid message error"},
+  {0x4A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command phase error"},
+  {0x4B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase error"},
+  {0x4C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-configuration"},
+  /*
+   * FIXME(eric) - need a way to represent wildcards here.
+   */
+  {0x4D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Tagged overlapped commands (nn = queue tag)"},
+  {0x4E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Overlapped commands attempted"},
   {0x50,0x00,T,"Write append error"},
   {0x50,0x01,T,"Write append position error"},
   {0x50,0x02,T,"Position error related to timing"},
-  {0x51,0x00,T|O,"Erase failure"},
+  {0x51,0x00,T|R|O,"Erase failure"},
   {0x52,0x00,T,"Cartridge fault"},
-  {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+  {0x53,0x00,D|T|L|W|R|S|O|M|B|K,"Media load or eject failed"},
   {0x53,0x01,T,"Unload tape failure"},
-  {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+  {0x53,0x02,D|T|W|R|O|M|B|K,"Medium removal prevented"},
   {0x54,0x00,P,"Scsi to host system interface failure"},
   {0x55,0x00,P,"System resource failure"},
+  {0x55,0x01,D|O|B|K,"System buffer full"},
+  {0x55,0x02,D|T|L|P|W|R|S|O|M|A|E|K,"Insufficient reservation resources"},
+  {0x55,0x03,D|T|L|P|W|R|S|O|M|C|A|E,"Insufficient resources"},
+  {0x55,0x04,D|T|L|P|W|R|S|O|M|A|E,"Insufficient registration resources"},
   {0x57,0x00,R,"Unable to recover table-of-contents"},
   {0x58,0x00,O,"Generation does not exist"},
   {0x59,0x00,O,"Updated block read"},
-  {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
-  {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
-  {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
-  {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
-  {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
-  {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
-  {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
-  {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+  {0x5A,0x00,D|T|L|P|W|R|S|O|M|B|K,"Operator request or state change input"},
+  {0x5A,0x01,D|T|W|R|O|M|B|K,"Operator medium removal request"},
+  {0x5A,0x02,D|T|W|R|O|A|B|K,"Operator selected write protect"},
+  {0x5A,0x03,D|T|W|R|O|A|B|K,"Operator selected write permit"},
+  {0x5B,0x00,D|T|L|P|W|R|S|O|M|K,"Log exception"},
+  {0x5B,0x01,D|T|L|P|W|R|S|O|M|K,"Threshold condition met"},
+  {0x5B,0x02,D|T|L|P|W|R|S|O|M|K,"Log counter at maximum"},
+  {0x5B,0x03,D|T|L|P|W|R|S|O|M|K,"Log list codes exhausted"},
   {0x5C,0x00,D|O,"Rpl status change"},
   {0x5C,0x01,D|O,"Spindles synchronized"},
   {0x5C,0x02,D|O,"Spindles not synchronized"},
+  {0x5D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded"},
+  {0x5D,0x01,R|B,"Media failure prediction threshold exceeded"},
+  {0x5D,0x02,R,"Logical unit failure prediction threshold exceeded"},
+  {0x5D,0x10,D|B,"Hardware impending failure general hard drive failure"},
+  {0x5D,0x11,D|B,"Hardware impending failure drive error rate too high"},
+  {0x5D,0x12,D|B,"Hardware impending failure data error rate too high"},
+  {0x5D,0x13,D|B,"Hardware impending failure seek error rate too high"},
+  {0x5D,0x14,D|B,"Hardware impending failure too many block reassigns"},
+  {0x5D,0x15,D|B,"Hardware impending failure access times too high"},
+  {0x5D,0x16,D|B,"Hardware impending failure start unit times too high"},
+  {0x5D,0x17,D|B,"Hardware impending failure channel parametrics"},
+  {0x5D,0x18,D|B,"Hardware impending failure controller detected"},
+  {0x5D,0x19,D|B,"Hardware impending failure throughput performance"},
+  {0x5D,0x1A,D|B,"Hardware impending failure seek time performance"},
+  {0x5D,0x1B,D|B,"Hardware impending failure spin-up retry count"},
+  {0x5D,0x1C,D|B,"Hardware impending failure drive calibration retry count"},
+  {0x5D,0x20,D|B,"Controller impending failure general hard drive failure"},
+  {0x5D,0x21,D|B,"Controller impending failure drive error rate too high"},
+  {0x5D,0x22,D|B,"Controller impending failure data error rate too high"},
+  {0x5D,0x23,D|B,"Controller impending failure seek error rate too high"},
+  {0x5D,0x24,D|B,"Controller impending failure too many block reassigns"},
+  {0x5D,0x25,D|B,"Controller impending failure access times too high"},
+  {0x5D,0x26,D|B,"Controller impending failure start unit times too high"},
+  {0x5D,0x27,D|B,"Controller impending failure channel parametrics"},
+  {0x5D,0x28,D|B,"Controller impending failure controller detected"},
+  {0x5D,0x29,D|B,"Controller impending failure throughput performance"},
+  {0x5D,0x2A,D|B,"Controller impending failure seek time performance"},
+  {0x5D,0x2B,D|B,"Controller impending failure spin-up retry count"},
+  {0x5D,0x2C,D|B,"Controller impending failure drive calibration retry count"},
+  {0x5D,0x30,D|B,"Data channel impending failure general hard drive failure"},
+  {0x5D,0x31,D|B,"Data channel impending failure drive error rate too high"},
+  {0x5D,0x32,D|B,"Data channel impending failure data error rate too high"},
+  {0x5D,0x33,D|B,"Data channel impending failure seek error rate too high"},
+  {0x5D,0x34,D|B,"Data channel impending failure too many block reassigns"},
+  {0x5D,0x35,D|B,"Data channel impending failure access times too high"},
+  {0x5D,0x36,D|B,"Data channel impending failure start unit times too high"},
+  {0x5D,0x37,D|B,"Data channel impending failure channel parametrics"},
+  {0x5D,0x38,D|B,"Data channel impending failure controller detected"},
+  {0x5D,0x39,D|B,"Data channel impending failure throughput performance"},
+  {0x5D,0x3A,D|B,"Data channel impending failure seek time performance"},
+  {0x5D,0x3B,D|B,"Data channel impending failure spin-up retry count"},
+  {0x5D,0x3C,D|B,"Data channel impending failure drive calibration retry count"},
+  {0x5D,0x40,D|B,"Servo impending failure general hard drive failure"},
+  {0x5D,0x41,D|B,"Servo impending failure drive error rate too high"},
+  {0x5D,0x42,D|B,"Servo impending failure data error rate too high"},
+  {0x5D,0x43,D|B,"Servo impending failure seek error rate too high"},
+  {0x5D,0x44,D|B,"Servo impending failure too many block reassigns"},
+  {0x5D,0x45,D|B,"Servo impending failure access times too high"},
+  {0x5D,0x46,D|B,"Servo impending failure start unit times too high"},
+  {0x5D,0x47,D|B,"Servo impending failure channel parametrics"},
+  {0x5D,0x48,D|B,"Servo impending failure controller detected"},
+  {0x5D,0x49,D|B,"Servo impending failure throughput performance"},
+  {0x5D,0x4A,D|B,"Servo impending failure seek time performance"},
+  {0x5D,0x4B,D|B,"Servo impending failure spin-up retry count"},
+  {0x5D,0x4C,D|B,"Servo impending failure drive calibration retry count"},
+  {0x5D,0x50,D|B,"Spindle impending failure general hard drive failure"},
+  {0x5D,0x51,D|B,"Spindle impending failure drive error rate too high"},
+  {0x5D,0x52,D|B,"Spindle impending failure data error rate too high"},
+  {0x5D,0x53,D|B,"Spindle impending failure seek error rate too high"},
+  {0x5D,0x54,D|B,"Spindle impending failure too many block reassigns"},
+  {0x5D,0x55,D|B,"Spindle impending failure access times too high"},
+  {0x5D,0x56,D|B,"Spindle impending failure start unit times too high"},
+  {0x5D,0x57,D|B,"Spindle impending failure channel parametrics"},
+  {0x5D,0x58,D|B,"Spindle impending failure controller detected"},
+  {0x5D,0x59,D|B,"Spindle impending failure throughput performance"},
+  {0x5D,0x5A,D|B,"Spindle impending failure seek time performance"},
+  {0x5D,0x5B,D|B,"Spindle impending failure spin-up retry count"},
+  {0x5D,0x5C,D|B,"Spindle impending failure drive calibration retry count"},
+  {0x5D,0x60,D|B,"Firmware impending failure general hard drive failure"},
+  {0x5D,0x61,D|B,"Firmware impending failure drive error rate too high"},
+  {0x5D,0x62,D|B,"Firmware impending failure data error rate too high"},
+  {0x5D,0x63,D|B,"Firmware impending failure seek error rate too high"},
+  {0x5D,0x64,D|B,"Firmware impending failure too many block reassigns"},
+  {0x5D,0x65,D|B,"Firmware impending failure access times too high"},
+  {0x5D,0x66,D|B,"Firmware impending failure start unit times too high"},
+  {0x5D,0x67,D|B,"Firmware impending failure channel parametrics"},
+  {0x5D,0x68,D|B,"Firmware impending failure controller detected"},
+  {0x5D,0x69,D|B,"Firmware impending failure throughput performance"},
+  {0x5D,0x6A,D|B,"Firmware impending failure seek time performance"},
+  {0x5D,0x6B,D|B,"Firmware impending failure spin-up retry count"},
+  {0x5D,0x6C,D|B,"Firmware impending failure drive calibration retry count"},
+  {0x5D,0xFF,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded (false)"},
+  {0x5E,0x00,D|T|L|P|W|R|S|O|C|A|K,"Low power condition on"},
+  {0x5E,0x01,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by timer"},
+  {0x5E,0x02,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by timer"},
+  {0x5E,0x03,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by command"},
+  {0x5E,0x04,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by command"},
+  {0x5E,0x41,B,"Power state change to active"},
+  {0x5E,0x42,B,"Power state change to idle"},
+  {0x5E,0x43,B,"Power state change to standby"},
+  {0x5E,0x45,B,"Power state change to sleep"},
+  {0x5E,0x47,B|K,"Power state change to device control"},
   {0x60,0x00,S,"Lamp failure"},
   {0x61,0x00,S,"Video acquisition error"},
   {0x61,0x01,S,"Unable to acquire video"},
   {0x61,0x02,S,"Out of focus"},
   {0x62,0x00,S,"Scan head positioning error"},
   {0x63,0x00,R,"End of user area encountered on this track"},
+  {0x63,0x01,R,"Packet does not fit in available space"},
   {0x64,0x00,R,"Illegal mode for this track"},
+  {0x64,0x01,R,"Invalid packet size"},
+  {0x65,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Voltage fault"},
+  {0x66,0x00,S,"Automatic document feeder cover up"},
+  {0x66,0x01,S,"Automatic document feeder lift up"},
+  {0x66,0x02,S,"Document jam in automatic document feeder"},
+  {0x66,0x03,S,"Document miss feed automatic in document feeder"},
+  {0x67,0x00,A,"Configuration failure"},
+  {0x67,0x01,A,"Configuration of incapable logical units failed"},
+  {0x67,0x02,A,"Add logical unit failed"},
+  {0x67,0x03,A,"Modification of logical unit failed"},
+  {0x67,0x04,A,"Exchange of logical unit failed"},
+  {0x67,0x05,A,"Remove of logical unit failed"},
+  {0x67,0x06,A,"Attachment of logical unit failed"},
+  {0x67,0x07,A,"Creation of logical unit failed"},
+  {0x67,0x08,A,"Assign failure occurred"},
+  {0x67,0x09,A,"Multiply assigned logical unit"},
+  {0x68,0x00,A,"Logical unit not configured"},
+  {0x69,0x00,A,"Data loss on logical unit"},
+  {0x69,0x01,A,"Multiple logical unit failures"},
+  {0x69,0x02,A,"Parity/data mismatch"},
+  {0x6A,0x00,A,"Informational,refer to log"},
+  {0x6B,0x00,A,"State change has occurred"},
+  {0x6B,0x01,A,"Redundancy level got better"},
+  {0x6B,0x02,A,"Redundancy level got worse"},
+  {0x6C,0x00,A,"Rebuild failure occurred"},
+  {0x6D,0x00,A,"Recalculate failure occurred"},
+  {0x6E,0x00,A,"Command to logical unit failed"},
+  {0x6F,0x00,R,"Copy protection key exchange failure - authentication failure"},
+  {0x6F,0x01,R,"Copy protection key exchange failure - key not present"},
+  {0x6F,0x02,R,"Copy protection key exchange failure - key not established"},
+  {0x6F,0x03,R,"Read of scrambled sector without authentication"},
+  {0x6F,0x04,R,"Media region code is mismatched to logical unit region"},
+  {0x6F,0x05,R,"Drive region must be permanent/region reset count error"},
+  /*
+   * FIXME(eric) - need a way to represent wildcards here.
+   */
+  {0x70,0x00,T,"Decompression exception short algorithm id of nn"},
+  {0x71,0x00,T,"Decompression exception long algorithm id"},
+  {0x72,0x00,R,"Session fixation error"},
+  {0x72,0x01,R,"Session fixation error writing lead-in"},
+  {0x72,0x02,R,"Session fixation error writing lead-out"},
+  {0x72,0x03,R,"Session fixation error - incomplete track in session"},
+  {0x72,0x04,R,"Empty or partially written reserved track"},
+  {0x72,0x05,R,"No more track reservations allowed"},
+  {0x73,0x00,R,"Cd control error"},
+  {0x73,0x01,R,"Power calibration area almost full"},
+  {0x73,0x02,R,"Power calibration area is full"},
+  {0x73,0x03,R,"Power calibration area error"},
+  {0x73,0x04,R,"Program memory area update failure"},
+  {0x73,0x05,R,"Program memory area is full"},
+  {0x73,0x06,R,"RMA/PMA is full"},
   {0, 0, 0, NULL}
 };
 #endif
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/eata_dma_proc.c linux/drivers/scsi/eata_dma_proc.c
--- v2.3.51/linux/drivers/scsi/eata_dma_proc.c	Sat Feb 26 22:31:48 2000
+++ linux/drivers/scsi/eata_dma_proc.c	Mon Mar 13 22:15:03 2000
@@ -69,7 +69,7 @@
 
     Scsi_Device *scd, *SDev;
     struct Scsi_Host *HBA_ptr;
-    Scsi_Cmnd  * scmd;
+    Scsi_Request  * scmd;
     char cmnd[MAX_COMMAND_SIZE];
     static u8 buff[512];
     static u8 buff2[512];
@@ -153,7 +153,7 @@
 
     } else {
         SDev = scsi_get_host_dev(HBA_ptr);
-	scmd  = scsi_allocate_device(SDev, 1, FALSE);
+	scmd  = scsi_allocate_request(SDev);
 
 	cmnd[0] = LOG_SENSE;
 	cmnd[1] = 0;
@@ -166,13 +166,13 @@
 	cmnd[8] = 0x66;
 	cmnd[9] = 0;
 
-	scmd->cmd_len = 10;
-	scmd->sc_data_direction = SCSI_DATA_READ;
+	scmd->sr_cmd_len = 10;
+	scmd->sr_data_direction = SCSI_DATA_READ;
 	
 	/*
 	 * Do the command and wait for it to finish.
 	 */	
-	scsi_wait_cmd (scmd, cmnd, buff + 0x144, 0x66,  
+	scsi_wait_req (scmd, cmnd, buff + 0x144, 0x66,  
 		       1 * HZ, 1);
 
 	size = sprintf(buffer + len, "IRQ: %2d, %s triggered\n", cc->interrupt,
@@ -291,13 +291,13 @@
 	    cmnd[8] = 0x44;
 	    cmnd[9] = 0;
 	    
-	    scmd->cmd_len = 10;
-	    scmd->sc_data_direction = SCSI_DATA_READ;
+	    scmd->sr_cmd_len = 10;
+	    scmd->sr_data_direction = SCSI_DATA_READ;
 
 	    /*
 	     * Do the command and wait for it to finish.
 	     */	
-	    scsi_wait_cmd (scmd, cmnd, buff2, 0x144,
+	    scsi_wait_req (scmd, cmnd, buff2, 0x144,
 			   1 * HZ, 1);
 
 	    swap_statistics(buff2);
@@ -333,7 +333,7 @@
 	    pos = begin + len;
 	}
 
-	scsi_release_command(scmd);
+	scsi_release_request(scmd);
 	scsi_free_host_dev(SDev);
     }
     
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v2.3.51/linux/drivers/scsi/hosts.h	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/hosts.h	Tue Mar 14 18:38:26 2000
@@ -469,10 +469,10 @@
  * Prototypes for functions/data in scsi_scan.c
  */
 extern void scan_scsis(struct Scsi_Host *shpnt,
-		       unchar hardcoded,
-		       unchar hchannel,
-		       unchar hid,
-                       unchar hlun);
+		       uint hardcoded,
+		       uint hchannel,
+		       uint hid,
+                       uint hlun);
 
 extern void scsi_mark_host_reset(struct Scsi_Host *Host);
 
@@ -485,12 +485,12 @@
     const char * tag;
     struct module * module;	  /* Used for loadable modules */
     unsigned char scsi_type;
-    unsigned char major;
-    unsigned char min_major;      /* Minimum major in range. */ 
-    unsigned char max_major;      /* Maximum major in range. */
-    unsigned char nr_dev;	  /* Number currently attached */
-    unsigned char dev_noticed;	  /* Number of devices detected. */
-    unsigned char dev_max;	  /* Current size of arrays */
+    unsigned int major;
+    unsigned int min_major;      /* Minimum major in range. */ 
+    unsigned int max_major;      /* Maximum major in range. */
+    unsigned int nr_dev;	  /* Number currently attached */
+    unsigned int dev_noticed;	  /* Number of devices detected. */
+    unsigned int dev_max;	  /* Current size of arrays */
     unsigned blk:1;		  /* 0 if character device */
     int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
     int (*init)(void);		  /* Sizes arrays based upon number of devices
@@ -534,20 +534,18 @@
  * Note: These things are all evil and all need to go away.  My plan is to
  * tackle the character devices first, as there aren't any locking implications
  * in the block device layer.   The block devices will require more work.
+ *
+ * The generics driver has been updated to resize as required.  So as the tape
+ * driver. Two down, two more to go.
  */
 #ifndef CONFIG_SD_EXTRA_DEVS
 #define CONFIG_SD_EXTRA_DEVS 2
 #endif
-#ifndef CONFIG_ST_EXTRA_DEVS
-#define CONFIG_ST_EXTRA_DEVS 2
-#endif
 #ifndef CONFIG_SR_EXTRA_DEVS
 #define CONFIG_SR_EXTRA_DEVS 2
 #endif
 #define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS
-#define ST_EXTRA_DEVS CONFIG_ST_EXTRA_DEVS
 #define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS
-#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
 
 #endif
 /*
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.3.51/linux/drivers/scsi/ncr53c8xx.c	Tue Jan 11 22:31:41 2000
+++ linux/drivers/scsi/ncr53c8xx.c	Sun Mar 12 20:10:20 2000
@@ -73,7 +73,7 @@
 */
 
 /*
-**	January 8 2000, version 3.2e
+**	March 6 2000, version 3.2g
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -104,7 +104,7 @@
 /*
 **	Name and version of the driver
 */
-#define SCSI_NCR_DRIVER_NAME	"ncr53c8xx - version 3.2e"
+#define SCSI_NCR_DRIVER_NAME	"ncr53c8xx - version 3.2g"
 
 #define SCSI_NCR_DEBUG_FLAGS	(0)
 
@@ -184,98 +184,14 @@
 */
 typedef u32 u_int32;
 typedef u64 u_int64;
-
+typedef	u_long		vm_offset_t;
 #include "ncr53c8xx.h"
 
-/*==========================================================
-**
-**	A la VMS/CAM-3 queue management.
-**	Implemented from linux list management.
-**
-**==========================================================
-*/
-
-typedef struct xpt_quehead {
-	struct xpt_quehead *flink;	/* Forward  pointer */
-	struct xpt_quehead *blink;	/* Backward pointer */
-} XPT_QUEHEAD;
-
-#define xpt_que_init(ptr) do { \
-	(ptr)->flink = (ptr); (ptr)->blink = (ptr); \
-} while (0)
-
-static inline void __xpt_que_add(struct xpt_quehead * new,
-	struct xpt_quehead * blink,
-	struct xpt_quehead * flink)
-{
-	flink->blink	= new;
-	new->flink	= flink;
-	new->blink	= blink;
-	blink->flink	= new;
-}
-
-static inline void __xpt_que_del(struct xpt_quehead * blink,
-	struct xpt_quehead * flink)
-{
-	flink->blink = blink;
-	blink->flink = flink;
-}
-
-static inline int xpt_que_empty(struct xpt_quehead *head)
-{
-	return head->flink == head;
-}
-
-static inline void xpt_que_splice(struct xpt_quehead *list,
-	struct xpt_quehead *head)
-{
-	struct xpt_quehead *first = list->flink;
-
-	if (first != list) {
-		struct xpt_quehead *last = list->blink;
-		struct xpt_quehead *at   = head->flink;
-
-		first->blink = head;
-		head->flink  = first;
-
-		last->flink = at;
-		at->blink   = last;
-	}
-}
-
-#define xpt_que_entry(ptr, type, member) \
-	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
-
-#define xpt_insque(new, pos)		__xpt_que_add(new, pos, (pos)->flink)
-
-#define xpt_remque(el)			__xpt_que_del((el)->blink, (el)->flink)
-
-#define xpt_insque_head(new, head)	__xpt_que_add(new, head, (head)->flink)
-
-static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
-{
-	struct xpt_quehead *elem = head->flink;
-
-	if (elem != head)
-		__xpt_que_del(head, elem->flink);
-	else
-		elem = 0;
-	return elem;
-}
-
-#define xpt_insque_tail(new, head)	__xpt_que_add(new, (head)->blink, head)
+#define NAME53C			"ncr53c"
+#define NAME53C8XX		"ncr53c8xx"
+#define DRIVER_SMP_LOCK		ncr53c8xx_lock
 
-static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
-{
-	struct xpt_quehead *elem = head->blink;
-
-	if (elem != head)
-		__xpt_que_del(elem->blink, head);
-	else
-		elem = 0;
-	return elem;
-}
+#include "sym53c8xx_comm.h"
 
 /*==========================================================
 **
@@ -315,33 +231,6 @@
 
 /*==========================================================
 **
-**	On x86 architecture, write buffers management does 
-**	not reorder writes to memory. So, using compiler 
-**	optimization barriers is enough to guarantee some 
-**	ordering when the CPU is writing data accessed by 
-**	the NCR.
-**	On Alpha architecture, explicit memory barriers have 
-**	to be used.
-**	Other architectures are defaulted to mb() macro if  
-**	defined, otherwise use compiler barrier.
-**
-**==========================================================
-*/
-
-#if defined(__i386__)
-#define MEMORY_BARRIER()	barrier()
-#elif defined(__alpha__)
-#define MEMORY_BARRIER()	mb()
-#else
-#  ifdef mb
-#  define MEMORY_BARRIER()	mb()
-#  else
-#  define MEMORY_BARRIER()	barrier()
-#  endif
-#endif
-
-/*==========================================================
-**
 **	Configuration and Debugging
 **
 **==========================================================
@@ -464,379 +353,11 @@
 #endif
 
 /*
-**    Io mapped or memory mapped.
-*/
-
-#if defined(SCSI_NCR_IOMAPPED)
-#define NCR_IOMAPPED
-#endif
-
-/*
 **	other
 */
 
 #define NCR_SNOOP_TIMEOUT (1000000)
 
-/*==========================================================
-**
-**	Defines for Linux.
-**
-**	Linux and Bsd kernel functions are quite different.
-**	These defines allow a minimum change of the original
-**	code.
-**
-**==========================================================
-*/
-
- /*
- **	Obvious definitions
- */
-
-#define u_char		unsigned char
-#define u_short		unsigned short
-#define u_int		unsigned int
-#define u_long		unsigned long
-
-typedef	u_long		vm_offset_t;
-typedef	int		vm_size_t;
-
-#ifndef bcopy
-#define bcopy(s, d, n)	memcpy((d), (s), (n))
-#endif
-#ifndef bzero
-#define bzero(d, n)	memset((d), 0, (n))
-#endif
- 
-#ifndef offsetof
-#define offsetof(t, m)	((size_t) (&((t *)0)->m))
-#endif
-
-/*
-**	 Simple Wrapper to kernel PCI bus interface.
-**
-**	 This wrapper allows to get rid of old kernel PCI interface
-**	 and still allows to preserve linux-2.0 compatibilty.
-**	 In fact, it is mostly an incomplete emulation of the new
-**	 PCI code for pre-2.2 kernels. When kernel-2.0 support
-**	 will be dropped, we will just have to remove most of this
-**	 code.
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
-
-typedef struct pci_dev *pcidev_t;
-#define PCIDEV_NULL		(0)
-#define PciBusNumber(d)		(d)->bus->number
-#define PciDeviceFn(d)		(d)->devfn
-#define PciVendorId(d)		(d)->vendor
-#define PciDeviceId(d)		(d)->device
-#define PciIrqLine(d)		(d)->irq
-
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
-
-static int __init
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-{
-	*base = pdev->resource[index].start;
-	if ((pdev->resource[index].flags & 0x7) == 0x4)
-		++index;
-	return ++index;
-}
-#else
-static int __init
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-{
-	*base = pdev->base_address[index++];
-	if ((*base & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
-		*base |= (((u_long)pdev->base_address[index]) << 32);
-#endif
-		++index;
-	}
-	return index;
-}
-#endif
-
-#else  /* Incomplete emulation of current PCI code for pre-2.2 kernels */
-
-typedef unsigned int pcidev_t;
-#define PCIDEV_NULL            (~0u)
-#define PciBusNumber(d)                ((d)>>8)
-#define PciDeviceFn(n)         ((d)&0xff)
-#define __PciDev(busn, devfn)  (((busn)<<8)+(devfn))
-
-#define pci_present pcibios_present
-
-#define pci_read_config_byte(d, w, v) \
-	pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_read_config_word(d, w, v) \
-	pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_read_config_dword(d, w, v) \
-	pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
-
-
-#define pci_write_config_byte(d, w, v) \
-	pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_write_config_word(d, w, v) \
-	pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_write_config_dword(d, w, v) \
-	pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
-
-static pcidev_t __init
-pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
-{
-	static unsigned short pci_index;
-	int retv;
-	unsigned char bus_number, device_fn;
-
-	if (prev == PCIDEV_NULL)
-		pci_index = 0;
-	else
-		++pci_index;
-	retv = pcibios_find_device (vendor, device, pci_index,
-					&bus_number, &device_fn);
-	return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
-}
-
-static u_short __init PciVendorId(pcidev_t dev)
-{
-	u_short vendor_id;
-	pcibios_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
-	return vendor_id;
-}
-
-static u_short __init PciDeviceId(pcidev_t dev)
-{
-	u_short device_id;
-	pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
-	return device_id;
-}
-
-static u_int __init PciIrqLine(pcidev_t dev)
-{
-	u_short irq;
-	pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
-	return irq;
-}
-
-static int __init
-pci_get_base_address(pcidev_t dev, int offset, u_long *base)
-{
-	u_int32 tmp;
-
-	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
-	*base = tmp;
-	offset += sizeof(u_int32);
-	if ((tmp & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
-		pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
-		*base |= (((u_long)tmp) << 32);
-#endif
-		offset += sizeof(u_int32);
-	}
-	return offset;
-}
-
-#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
-
-/*
-**	SMP threading.
-**
-**	Assuming that SMP systems are generally high end systems and may 
-**	use several SCSI adapters, we are using one lock per controller 
-**	instead of some global one. For the moment (linux-2.1.95), driver's 
-**	entry points are called with the 'io_request_lock' lock held, so:
-**	- We are uselessly loosing a couple of micro-seconds to lock the 
-**	  controller data structure.
-**	- But the driver is not broken by design for SMP and so can be 
-**	  more resistant to bugs or bad changes in the IO sub-system code.
-**	- A small advantage could be that the interrupt code is grained as 
-**	  wished (e.g.: threaded by controller).
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
-
-#if 0	/* not yet needed */
-static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
-#define	NCR_LOCK_DRIVER(flags)     spin_lock_irqsave(&driver_lock, flags)
-#define	NCR_UNLOCK_DRIVER(flags)   spin_unlock_irqrestore(&driver_lock, flags)
-#endif
-
-#define NCR_INIT_LOCK_NCB(np)      spin_lock_init(&np->smp_lock);
-#define	NCR_LOCK_NCB(np, flags)    spin_lock_irqsave(&np->smp_lock, flags)
-#define	NCR_UNLOCK_NCB(np, flags)  spin_unlock_irqrestore(&np->smp_lock, flags)
-
-#define	NCR_LOCK_SCSI_DONE(np, flags) \
-		spin_lock_irqsave(&io_request_lock, flags)
-#define	NCR_UNLOCK_SCSI_DONE(np, flags) \
-		spin_unlock_irqrestore(&io_request_lock, flags)
-
-#else
-
-#if 0	/* not yet needed */
-#define	NCR_LOCK_DRIVER(flags)     do {;} while (0)
-#define	NCR_UNLOCK_DRIVER(flags)   do {;} while (0)
-#endif
-
-#define	NCR_INIT_LOCK_NCB(np)      do { } while (0)
-#define	NCR_LOCK_NCB(np, flags)    do { save_flags(flags); cli(); } while (0)
-#define	NCR_UNLOCK_NCB(np, flags)  do { restore_flags(flags); } while (0)
-
-#define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
-#define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
-
-#endif
-
-/*
-**	Address translation
-**
-**	The driver has to provide physical memory addresses to 
-**	the script processor. Because some architectures use 
-**	different physical addresses from the PCI BUS, we must 
-**	use virt_to_bus instead of virt_to_phys.
-*/
-
-#define vtophys(p)	virt_to_bus(p)
-
-/*
-**	Memory mapped IO
-**
-**	Since linux-2.1, we must use ioremap() to map the io memory space.
-**	iounmap() to unmap it. That allows portability.
-**	Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater 
-**	than the highest physical memory address to kernel virtual pages with 
-**	vremap() / vfree(). That was not portable but worked with i386 
-**	architecture.
-*/
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
-#define ioremap vremap
-#define iounmap vfree
-#endif
-
-#if defined (__sparc__)
-#include <asm/irq.h>
-#elif defined (__alpha__)
-#define bus_dvma_to_mem(p)		((p) & 0xfffffffful)
-#else
-#define bus_dvma_to_mem(p)		(p)
-#endif
-
-#if defined(__i386__) || !defined(NCR_IOMAPPED)
-static vm_offset_t __init remap_pci_mem(u_long base, u_long size)
-{
-	u_long page_base	= ((u_long) base) & PAGE_MASK;
-	u_long page_offs	= ((u_long) base) - page_base;
-	u_long page_remapped	= (u_long) ioremap(page_base, page_offs+size);
-
-	return (vm_offset_t) (page_remapped? (page_remapped + page_offs) : 0UL);
-}
-
-static void __init unmap_pci_mem(vm_offset_t vaddr, u_long size)
-{
-	if (vaddr)
-		iounmap((void *) (vaddr & PAGE_MASK));
-}
-#endif	/* __i386__ || !NCR_IOMAPPED */
-
-/*
-**	Insert a delay in micro-seconds and milli-seconds.
-**	-------------------------------------------------
-**	Under Linux, udelay() is restricted to delay < 1 milli-second.
-**	In fact, it generally works for up to 1 second delay.
-**	Since 2.1.105, the mdelay() function is provided for delays 
-**	in milli-seconds.
-**	Under 2.0 kernels, udelay() is an inline function that is very 
-**	inaccurate on Pentium processors.
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
-#define UDELAY udelay
-#define MDELAY mdelay
-#else
-static void UDELAY(long us) { udelay(us); }
-static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
-#endif
-
-/*
-**	Internal data structure allocation.
-**
-**	Linux scsi memory poor pool is adjusted for the need of
-**	middle-level scsi driver.
-**	We allocate our control blocks in the kernel memory pool
-**	to avoid scsi pool shortage.
-**
-**	kmalloc() only ensures 8 bytes boundary alignment.
-**	The NCR need better alignment for cache line bursting.
-**	The global header is moved between the NCB and CCBs and needs 
-**	origin and destination addresses to have same lower four bits.
-**
-**	We use 32 boundary alignment for NCB and CCBs and offset multiple 
-**	of 32 for global header fields. That's too much but at least enough.
-*/
-
-#define ALIGN_SIZE(shift)	(1UL << shift)
-#define ALIGN_MASK(shift)	(~(ALIGN_SIZE(shift)-1))
-
-#define CACHE_LINE_SHIFT	5
-#define CACHE_LINE_SIZE		ALIGN_SIZE(CACHE_LINE_SHIFT)
-#define CACHE_LINE_MASK		ALIGN_MASK(CACHE_LINE_SHIFT)
-
-static void *m_alloc(int size, int a_shift)
-{
-	u_long addr;
-	void *ptr;
-	u_long a_size, a_mask;
-
-	if (a_shift < 3)
-		a_shift = 3;
-
-	a_size	= ALIGN_SIZE(a_shift);
-	a_mask	= ALIGN_MASK(a_shift);
-
-	ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC);
-	if (ptr) {
-		addr	= (((u_long) ptr) + a_size) & a_mask;
-		*((void **) (addr - sizeof(void *))) = ptr;
-		ptr	= (void *) addr;
-	}
-
-	return ptr;
-}
-
-#ifdef MODULE
-static void m_free(void *ptr, int size)
-{
-	u_long addr;
-
-	if (ptr) {
-		addr	= (u_long) ptr;
-		ptr	= *((void **) (addr - sizeof(void *)));
-
-		kfree(ptr);
-	}
-}
-#endif
-
-/*
-**	Transfer direction
-**
-**	Low-level scsi drivers under Linux do not receive the expected 
-**	data transfer direction from upper scsi drivers.
-**	The driver will only check actual data direction for common 
-**	scsi opcodes. Other ones may cause problem, since they may 
-**	depend on device type or be vendor specific.
-**	I would prefer to never trust the device for data direction, 
-**	but that is not possible.
-**
-**	The original driver requires the expected direction to be known.
-**	The Linux version of the driver has been enhanced in order to 
-**	be able to transfer data in the direction choosen by the target. 
-*/
-
-#define XFER_IN		(1)
-#define XFER_OUT	(2)
-
 /*
 **	Head of list of NCR boards
 **
@@ -848,44 +369,8 @@
 static struct Scsi_Host		*first_host	= NULL;
 static Scsi_Host_Template	*the_template	= NULL;	
 
-
-/*
-**	/proc directory entry and proc_info function
-*/
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
-static struct proc_dir_entry proc_scsi_ncr53c8xx = {
-    PROC_SCSI_NCR53C8XX, 9, "ncr53c8xx",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
-#endif
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-static int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset,
-			int length, int hostno, int func);
-#endif
-
-/*
-**	Driver setup.
-**
-**	This structure is initialized from linux config options.
-**	It can be overridden at boot-up by the boot command line.
-*/
-static struct ncr_driver_setup
-	driver_setup			= SCSI_NCR_DRIVER_SETUP;
-
-#ifdef	SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
-static struct ncr_driver_setup
-	driver_safe_setup __initdata	= SCSI_NCR_DRIVER_SAFE_SETUP;
-# ifdef	MODULE
-char *ncr53c8xx = 0;	/* command line passed by insmod */
-#  if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
-MODULE_PARM(ncr53c8xx, "s");
-#  endif
-# endif
-#endif
-
 /*
-**	Other Linux definitions
+**	Other definitions
 */
 
 #define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))
@@ -903,307 +388,47 @@
 	{25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10};
 #endif /* SCSI_NCR_NVRAM_SUPPORT */
 
-/*
-**	Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to 
-**	transmit device configuration to the ncr_attach() function.
-*/
-typedef struct {
-	int	bus;
-	u_char	device_fn;
-	u_long	base;
-	u_long	base_2;
-	u_long	io_port;
-	int	irq;
-/* port and reg fields to use INB, OUTB macros */
-	u_long	port;
-	volatile struct ncr_reg	*reg;
-} ncr_slot;
-
-typedef struct {
-	int type;
-#define	SCSI_NCR_SYMBIOS_NVRAM	(1)
-#define	SCSI_NCR_TEKRAM_NVRAM	(2)
-#ifdef	SCSI_NCR_NVRAM_SUPPORT
-	union {
-		Symbios_nvram Symbios;
-		Tekram_nvram Tekram;
-	} data;
-#endif
-} ncr_nvram;
-
-/*
-**	Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
-**	to save data on each detected board for ncr_attach().
-*/
-typedef struct {
-	ncr_slot  slot;
-	ncr_chip  chip;
-	ncr_nvram *nvram;
-	u_char	  host_id;
-	int attach_done;
-} ncr_device;
-
 /*==========================================================
 **
-**	Debugging tags
+**	Command control block states.
 **
 **==========================================================
 */
 
-#define DEBUG_ALLOC    (0x0001)
-#define DEBUG_PHASE    (0x0002)
-#define DEBUG_QUEUE    (0x0008)
-#define DEBUG_RESULT   (0x0010)
-#define DEBUG_SCATTER  (0x0020)
-#define DEBUG_SCRIPT   (0x0040)
-#define DEBUG_TINY     (0x0080)
-#define DEBUG_TIMING   (0x0100)
-#define DEBUG_NEGO     (0x0200)
-#define DEBUG_TAGS     (0x0400)
+#define HS_IDLE		(0)
+#define HS_BUSY		(1)
+#define HS_NEGOTIATE	(2)	/* sync/wide data transfer*/
+#define HS_DISCONNECT	(3)	/* Disconnected by target */
+
+#define HS_DONEMASK	(0x80)
+#define HS_COMPLETE	(4|HS_DONEMASK)
+#define HS_SEL_TIMEOUT	(5|HS_DONEMASK)	/* Selection timeout      */
+#define HS_RESET	(6|HS_DONEMASK)	/* SCSI reset	          */
+#define HS_ABORTED	(7|HS_DONEMASK)	/* Transfer aborted       */
+#define HS_TIMEOUT	(8|HS_DONEMASK)	/* Software timeout       */
+#define HS_FAIL		(9|HS_DONEMASK)	/* SCSI or PCI bus errors */
+#define HS_UNEXPECTED	(10|HS_DONEMASK)/* Unexpected disconnect  */
 
 /*
-**    Enable/Disable debug messages.
-**    Can be changed at runtime too.
+**	Invalid host status values used by the SCRIPTS processor 
+**	when the nexus is not fully identified.
+**	Shall never appear in a CCB.
 */
 
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
-	#define DEBUG_FLAGS ncr_debug
-#else
-	#define DEBUG_FLAGS	SCSI_NCR_DEBUG_FLAGS
-#endif
-
-
+#define HS_INVALMASK	(0x40)
+#define	HS_SELECTING	(0|HS_INVALMASK)
+#define	HS_IN_RESELECT	(1|HS_INVALMASK)
+#define	HS_STARTING	(2|HS_INVALMASK)
 
-/*==========================================================
-**
-**	assert ()
-**
-**==========================================================
-**
-**	modified copy from 386bsd:/usr/include/sys/assert.h
-**
-**----------------------------------------------------------
+/*
+**	Flags set by the SCRIPT processor for commands 
+**	that have been skipped.
 */
-
-#define	assert(expression) { \
-	if (!(expression)) { \
-		(void)printk(KERN_ERR \
-			"assertion \"%s\" failed: file \"%s\", line %d\n", \
-			#expression, \
-			__FILE__, __LINE__); \
-	} \
-}
+#define HS_SKIPMASK	(0x20)
 
 /*==========================================================
 **
-**	Big/Little endian support.
-**
-**==========================================================
-*/
-
-/*
-**	If the NCR uses big endian addressing mode over the 
-**	PCI, actual io register addresses for byte and word 
-**	accesses must be changed according to lane routing.
-**	Btw, ncr_offb() and ncr_offw() macros only apply to 
-**	constants and so donnot generate bloated code.
-*/
-
-#if	defined(SCSI_NCR_BIG_ENDIAN)
-
-#define ncr_offb(o)	(((o)&~3)+((~((o)&3))&3))
-#define ncr_offw(o)	(((o)&~3)+((~((o)&3))&2))
-
-#else
-
-#define ncr_offb(o)	(o)
-#define ncr_offw(o)	(o)
-
-#endif
-
-/*
-**	If the CPU and the NCR use same endian-ness addressing,
-**	no byte reordering is needed for script patching.
-**	Macro cpu_to_scr() is to be used for script patching.
-**	Macro scr_to_cpu() is to be used for getting a DWORD 
-**	from the script.
-*/
-
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define cpu_to_scr(dw)	cpu_to_le32(dw)
-#define scr_to_cpu(dw)	le32_to_cpu(dw)
-
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define cpu_to_scr(dw)	cpu_to_be32(dw)
-#define scr_to_cpu(dw)	be32_to_cpu(dw)
-
-#else
-
-#define cpu_to_scr(dw)	(dw)
-#define scr_to_cpu(dw)	(dw)
-
-#endif
-
-/*==========================================================
-**
-**	Access to the controller chip.
-**
-**	If NCR_IOMAPPED is defined, the driver will use 
-**	normal IOs instead of the MEMORY MAPPED IO method  
-**	recommended by PCI specifications.
-**	If all PCI bridges, host brigdes and architectures 
-**	would have been correctly designed for PCI, this 
-**	option would be useless.
-**
-**==========================================================
-*/
-
-/*
-**	If the CPU and the NCR use same endian-ness addressing,
-**	no byte reordering is needed for accessing chip io 
-**	registers. Functions suffixed by '_raw' are assumed 
-**	to access the chip over the PCI without doing byte 
-**	reordering. Functions suffixed by '_l2b' are 
-**	assumed to perform little-endian to big-endian byte 
-**	reordering, those suffixed by '_b2l' blah, blah,
-**	blah, ...
-*/
-
-#if defined(NCR_IOMAPPED)
-
-/*
-**	IO mapped only input / ouput
-*/
-
-#define	INB_OFF(o)		inb (np->port + ncr_offb(o))
-#define	OUTB_OFF(o, val)	outb ((val), np->port + ncr_offb(o))
-
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define	INW_OFF(o)		inw_l2b (np->port + ncr_offw(o))
-#define	INL_OFF(o)		inl_l2b (np->port + (o))
-
-#define	OUTW_OFF(o, val)	outw_b2l ((val), np->port + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_b2l ((val), np->port + (o))
-
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define	INW_OFF(o)		inw_b2l (np->port + ncr_offw(o))
-#define	INL_OFF(o)		inl_b2l (np->port + (o))
-
-#define	OUTW_OFF(o, val)	outw_l2b ((val), np->port + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_l2b ((val), np->port + (o))
-
-#else
-
-#define	INW_OFF(o)		inw_raw (np->port + ncr_offw(o))
-#define	INL_OFF(o)		inl_raw (np->port + (o))
-
-#define	OUTW_OFF(o, val)	outw_raw ((val), np->port + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_raw ((val), np->port + (o))
-
-#endif	/* ENDIANs */
-
-#else	/* defined NCR_IOMAPPED */
-
-/*
-**	MEMORY mapped IO input / output
-*/
-
-#define INB_OFF(o)		readb((char *)np->reg + ncr_offb(o))
-#define OUTB_OFF(o, val)	writeb((val), (char *)np->reg + ncr_offb(o))
-
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o)		readw_l2b((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_l2b((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val)	writew_b2l((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_b2l((val), (char *)np->reg + (o))
-
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o)		readw_b2l((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_b2l((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val)	writew_l2b((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_l2b((val), (char *)np->reg + (o))
-
-#else
-
-#define INW_OFF(o)		readw_raw((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_raw((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val)	writew_raw((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_raw((val), (char *)np->reg + (o))
-
-#endif
-
-#endif	/* defined NCR_IOMAPPED */
-
-#define INB(r)		INB_OFF (offsetof(struct ncr_reg,r))
-#define INW(r)		INW_OFF (offsetof(struct ncr_reg,r))
-#define INL(r)		INL_OFF (offsetof(struct ncr_reg,r))
-
-#define OUTB(r, val)	OUTB_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTW(r, val)	OUTW_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTL(r, val)	OUTL_OFF (offsetof(struct ncr_reg,r), (val))
-
-/*
-**	Set bit field ON, OFF 
-*/
-
-#define OUTONB(r, m)	OUTB(r, INB(r) | (m))
-#define OUTOFFB(r, m)	OUTB(r, INB(r) & ~(m))
-#define OUTONW(r, m)	OUTW(r, INW(r) | (m))
-#define OUTOFFW(r, m)	OUTW(r, INW(r) & ~(m))
-#define OUTONL(r, m)	OUTL(r, INL(r) | (m))
-#define OUTOFFL(r, m)	OUTL(r, INL(r) & ~(m))
-
-
-/*==========================================================
-**
-**	Command control block states.
-**
-**==========================================================
-*/
-
-#define HS_IDLE		(0)
-#define HS_BUSY		(1)
-#define HS_NEGOTIATE	(2)	/* sync/wide data transfer*/
-#define HS_DISCONNECT	(3)	/* Disconnected by target */
-
-#define HS_DONEMASK	(0x80)
-#define HS_COMPLETE	(4|HS_DONEMASK)
-#define HS_SEL_TIMEOUT	(5|HS_DONEMASK)	/* Selection timeout      */
-#define HS_RESET	(6|HS_DONEMASK)	/* SCSI reset	          */
-#define HS_ABORTED	(7|HS_DONEMASK)	/* Transfer aborted       */
-#define HS_TIMEOUT	(8|HS_DONEMASK)	/* Software timeout       */
-#define HS_FAIL		(9|HS_DONEMASK)	/* SCSI or PCI bus errors */
-#define HS_UNEXPECTED	(10|HS_DONEMASK)/* Unexpected disconnect  */
-
-/*
-**	Invalid host status values used by the SCRIPTS processor 
-**	when the nexus is not fully identified.
-**	Shall never appear in a CCB.
-*/
-
-#define HS_INVALMASK	(0x40)
-#define	HS_SELECTING	(0|HS_INVALMASK)
-#define	HS_IN_RESELECT	(1|HS_INVALMASK)
-#define	HS_STARTING	(2|HS_INVALMASK)
-
-/*
-**	Flags set by the SCRIPT processor for commands 
-**	that have been skipped.
-*/
-#define HS_SKIPMASK	(0x20)
-
-/*==========================================================
-**
-**	Software Interrupt Codes
+**	Software Interrupt Codes
 **
 **==========================================================
 */
@@ -1758,6 +983,8 @@
 	**----------------------------------------------------------------
 	*/
 	Scsi_Cmnd	*cmd;		/* SCSI command 		*/
+	u_char		cdb_buf[16];	/* Copy of CDB			*/
+	u_char		sense_buf[64];
 	int		data_len;	/* Total data length		*/
 
 	/*----------------------------------------------------------------
@@ -1888,6 +1115,7 @@
 	**	General controller parameters and configuration.
 	**----------------------------------------------------------------
 	*/
+	pcidev_t	pdev;
 	u_short		device_id;	/* PCI device id		*/
 	u_char		revision_id;	/* PCI device revision id	*/
 	u_char		bus;		/* PCI BUS number		*/
@@ -1956,6 +1184,7 @@
 	u_char		order;		/* Tag order to use		*/
 	u_char		verbose;	/* Verbosity for this controller*/
 	int		ncr_cache;	/* Used for cache test at init.	*/
+	u_long		p_ncb;		/* BUS address of this NCB	*/
 
 	/*----------------------------------------------------------------
 	**	Command completion handling.
@@ -1969,6 +1198,9 @@
 	**	Fields that should be removed or changed.
 	**----------------------------------------------------------------
 	*/
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	u_long		ktime;		/* Copy of kernel time		*/
+#endif
 	struct ccb	*ccb;		/* Global CCB			*/
 	struct usrcmd	user;		/* Command from user		*/
 	u_char		release_stage;	/* Synchronisation stage on release  */
@@ -2158,7 +1390,7 @@
 static	void	ncr_script_copy_and_bind
 				(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
 static  void    ncr_script_fill (struct script * scr, struct scripth * scripth);
-static	int	ncr_scatter	(ccb_p cp, Scsi_Cmnd *cmd);
+static	int	ncr_scatter	(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd);
 static	void	ncr_getsync	(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
 static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
 static	void	ncr_setup_tags	(ncb_p np, u_char tn, u_char ln);
@@ -2188,25 +1420,6 @@
 #define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
 #define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
 
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-static  void	ncr_get_nvram		(ncr_device *devp, ncr_nvram *nvp);
-static	int	ncr_get_Symbios_nvram	(ncr_slot *np, Symbios_nvram *nvram);
-static	int	ncr_get_Tekram_nvram	(ncr_slot *np, Tekram_nvram *nvram);
-#endif
-
-/*==========================================================
-**
-**
-**      Global static data.
-**
-**
-**==========================================================
-*/
-
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
-static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
-#endif
-
 static inline char *ncr_name (ncb_p np)
 {
 	return np->inst_name;
@@ -2235,7 +1448,9 @@
 #define	RELOC_SOFTC	0x40000000
 #define	RELOC_LABEL	0x50000000
 #define	RELOC_REGISTER	0x60000000
+#if 0
 #define	RELOC_KVAR	0x70000000
+#endif
 #define	RELOC_LABELH	0x80000000
 #define	RELOC_MASK	0xf0000000
 
@@ -2244,19 +1459,21 @@
 #define PADDRH(label)   (RELOC_LABELH | offsetof(struct scripth, label))
 #define	RADDR(label)	(RELOC_REGISTER | REG(label))
 #define	FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
+#if 0
 #define	KVAR(which)	(RELOC_KVAR | (which))
+#endif
 
+#if 0
 #define	SCRIPT_KVAR_JIFFIES	(0)
-
 #define	SCRIPT_KVAR_FIRST		SCRIPT_KVAR_JIFFIES
 #define	SCRIPT_KVAR_LAST		SCRIPT_KVAR_JIFFIES
-
 /*
  * Kernel variables referenced in the scripts.
  * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
  */
 static void *script_kvars[] __initdata =
 	{ (void *)&jiffies };
+#endif
 
 static	struct script script0 __initdata = {
 /*--------------------------< START >-----------------------*/ {
@@ -2427,7 +1644,7 @@
 	**	... set a timestamp ...
 	*/
 	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
+		NADDR (ktime),
 		NADDR (header.stamp.command),
 #endif
 	/*
@@ -2540,7 +1757,7 @@
 	**	set the timestamp.
 	*/
 	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
+		NADDR (ktime),
 		NADDR (header.stamp.status),
 #endif
 	/*
@@ -2776,7 +1993,7 @@
 	**	and count the disconnects.
 	*/
 	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
+		NADDR (ktime),
 		NADDR (header.stamp.disconnect),
 	SCR_COPY (4),
 		NADDR (disc_phys),
@@ -2932,7 +2149,7 @@
 	**      Set a time stamp for this reselection
 	*/
 	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
+		NADDR (ktime),
 		NADDR (header.stamp.reselect),
 #endif
 	/*
@@ -3825,11 +3042,15 @@
 			*/
 			relocs = 2;
 			tmp1 = src[0];
+#ifdef	RELOC_KVAR
 			if ((tmp1 & RELOC_MASK) == RELOC_KVAR)
 				tmp1 = 0;
+#endif
 			tmp2 = src[1];
+#ifdef	RELOC_KVAR
 			if ((tmp2 & RELOC_MASK) == RELOC_KVAR)
 				tmp2 = 0;
+#endif
 			if ((tmp1 ^ tmp2) & 3) {
 				printk (KERN_ERR"%s: ERROR1 IN SCRIPT at %d.\n",
 					ncr_name(np), (int) (src-start-1));
@@ -3882,7 +3103,7 @@
 				switch (old & RELOC_MASK) {
 				case RELOC_REGISTER:
 					new = (old & ~RELOC_MASK)
-						+ bus_dvma_to_mem(np->paddr);
+						+ pcivtobus(np->paddr);
 					break;
 				case RELOC_LABEL:
 					new = (old & ~RELOC_MASK) + np->p_script;
@@ -3891,8 +3112,9 @@
 					new = (old & ~RELOC_MASK) + np->p_scripth;
 					break;
 				case RELOC_SOFTC:
-					new = (old & ~RELOC_MASK) + vtophys(np);
+					new = (old & ~RELOC_MASK) + np->p_ncb;
 					break;
+#ifdef	RELOC_KVAR
 				case RELOC_KVAR:
 					if (((old & ~RELOC_MASK) <
 					     SCRIPT_KVAR_FIRST) ||
@@ -3902,6 +3124,7 @@
 					new = vtophys(script_kvars[old &
 					    ~RELOC_MASK]);
 					break;
+#endif
 				case 0:
 					/* Don't relocate a 0 address. */
 					if (old == 0) {
@@ -3941,17 +3164,6 @@
 
 struct host_data {
      struct ncb *ncb;
-
-     char ncb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
-     struct ncb _ncb_data;
-
-     char ccb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
-     struct ccb _ccb_data;
-
-     char scr_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
-     struct script script_data;
-
-     struct scripth scripth_data;
 };
 
 /*
@@ -4390,88 +3602,6 @@
 	return 0;
 }
 
-
-#ifdef SCSI_NCR_DEBUG_NVRAM
-
-void __init ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
-{
-	int i;
-
-	/* display Symbios nvram host data */
-	printk(KERN_DEBUG "%s: HOST ID=%d%s%s%s%s%s\n",
-		ncr_name(np), nvram->host_id & 0x0f,
-		(nvram->flags  & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
-		(nvram->flags  & SYMBIOS_PARITY_ENABLE)	? " PARITY"	:"",
-		(nvram->flags  & SYMBIOS_VERBOSE_MSGS)	? " VERBOSE"	:"", 
-		(nvram->flags  & SYMBIOS_CHS_MAPPING)	? " CHS_ALT"	:"", 
-		(nvram->flags1 & SYMBIOS_SCAN_HI_LO)	? " HI_LO"	:"");
-
-	/* display Symbios nvram drive data */
-	for (i = 0 ; i < 15 ; i++) {
-		struct Symbios_target *tn = &nvram->target[i];
-		printk(KERN_DEBUG "%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
-		ncr_name(np), i,
-		(tn->flags & SYMBIOS_DISCONNECT_ENABLE)	? " DISC"	: "",
-		(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)	? " SCAN_BOOT"	: "",
-		(tn->flags & SYMBIOS_SCAN_LUNS)		? " SCAN_LUNS"	: "",
-		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"	: "",
-		tn->bus_width,
-		tn->sync_period / 4,
-		tn->timeout);
-	}
-}
-
-static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
-
-void __init ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
-{
-	int i, tags, boot_delay;
-	char *rem;
-
-	/* display Tekram nvram host data */
-	tags = 2 << nvram->max_tags_index;
-	boot_delay = 0;
-	if (nvram->boot_delay_index < 6)
-		boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
-	switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
-	default:
-	case 0:	rem = "";			break;
-	case 1: rem = " REMOVABLE=boot device";	break;
-	case 2: rem = " REMOVABLE=all";		break;
-	}
-
-	printk(KERN_DEBUG
-		"%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
-		ncr_name(np), nvram->host_id & 0x0f,
-		(nvram->flags1 & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
-		(nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES"	:"",
-		(nvram->flags & TEKRAM_DRIVES_SUP_1GB)	? " >1GB"	:"",
-		(nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"	:"",
-		(nvram->flags & TEKRAM_ACTIVE_NEGATION)	? " ACT_NEG"	:"",
-		(nvram->flags & TEKRAM_IMMEDIATE_SEEK)	? " IMM_SEEK"	:"",
-		(nvram->flags & TEKRAM_SCAN_LUNS)	? " SCAN_LUNS"	:"",
-		(nvram->flags1 & TEKRAM_F2_F6_ENABLED)	? " F2_F6"	:"",
-		rem, boot_delay, tags);
-
-	/* display Tekram nvram drive data */
-	for (i = 0; i <= 15; i++) {
-		int sync, j;
-		struct Tekram_target *tn = &nvram->target[i];
-		j = tn->sync_index & 0xf;
-		sync = Tekram_sync[j];
-		printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
-		ncr_name(np), i,
-		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
-		(tn->flags & TEKRAM_SYNC_NEGO)		? " SYNC"	: "",
-		(tn->flags & TEKRAM_DISCONNECT_ENABLE)	? " DISC"	: "",
-		(tn->flags & TEKRAM_START_CMD)		? " START"	: "",
-		(tn->flags & TEKRAM_TAGGED_COMMANDS)	? " TCQ"	: "",
-		(tn->flags & TEKRAM_WIDE_NEGO)		? " WIDE"	: "",
-		sync);
-	}
-}
-#endif /* SCSI_NCR_DEBUG_NVRAM */
-
 /*
 **	Host attach and initialisations.
 **
@@ -4486,7 +3616,7 @@
 ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
 {
         struct host_data *host_data;
-	ncb_p np;
+	ncb_p np = 0;
         struct Scsi_Host *instance = 0;
 	u_long flags = 0;
 	ncr_nvram *nvram = device->nvram;
@@ -4512,21 +3642,25 @@
 	*/
         if (!(instance = scsi_register(tpnt, sizeof(*host_data))))
 	        goto attach_error;
-
-	/*
-	**	Initialize structure.
-	*/
 	host_data = (struct host_data *) instance->hostdata;
-	bzero (host_data, sizeof(*host_data));
 
 	/*
-	**	Align np and first ccb to 32 boundary for cache line 
-	**	bursting when copying the global header.
+	**	Allocate the host control block.
 	*/
-	np = (ncb_p) (((u_long) &host_data->_ncb_data) & CACHE_LINE_MASK);
+	np = __m_calloc_dma(device->pdev, sizeof(struct ncb), "NCB");
+	if (!np)
+		goto attach_error;
 	NCR_INIT_LOCK_NCB(np);
+	np->pdev  = device->pdev;
+	np->p_ncb = vtobus(np);
 	host_data->ncb = np;
-	np->ccb = (ccb_p) (((u_long) &host_data->_ccb_data) & CACHE_LINE_MASK);
+
+	/*
+	**	Allocate the default CCB.
+	*/
+	np->ccb = (ccb_p) m_calloc_dma(sizeof(struct ccb), "CCB");
+	if (!np->ccb)
+		goto attach_error;
 
 	/*
 	**	Store input informations in the host data structure.
@@ -4545,9 +3679,17 @@
 	np->maxburst	= device->chip.burst_max;
 	np->myaddr	= device->host_id;
 
+	/*
+	**	Allocate SCRIPTS areas.
+	*/
 	np->script0  = (struct script *)
-		(((u_long) &host_data->script_data) & CACHE_LINE_MASK);
-	np->scripth0 = &host_data->scripth_data;
+			m_calloc_dma(sizeof(struct script), "SCRIPT");
+	if (!np->script0)
+		goto attach_error;
+	np->scripth0  = (struct scripth *)
+			m_calloc_dma(sizeof(struct scripth), "SCRIPTH");
+	if (!np->scripth0)
+		goto attach_error;
 
 	/*
 	**    Initialize timer structure
@@ -4599,12 +3741,12 @@
 		switch(nvram->type) {
 		case SCSI_NCR_SYMBIOS_NVRAM:
 #ifdef SCSI_NCR_DEBUG_NVRAM
-			ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+			ncr_display_Symbios_nvram(&nvram->data.Symbios);
 #endif
 			break;
 		case SCSI_NCR_TEKRAM_NVRAM:
 #ifdef SCSI_NCR_DEBUG_NVRAM
-			ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+			ncr_display_Tekram_nvram(&nvram->data.Tekram);
 #endif
 			break;
 		default:
@@ -4656,13 +3798,14 @@
 	ncr_script_fill (&script0, &scripth0);
 
 	np->scripth	= np->scripth0;
-	np->p_scripth	= vtophys(np->scripth);
+	np->p_scripth	= vtobus(np->scripth);
 
-	np->p_script	= (np->paddr2) ? bus_dvma_to_mem(np->paddr2) : vtophys(np->script0);
+	np->p_script	= (np->paddr2) ?
+			  pcivtobus(np->paddr2) : vtobus(np->script0);
 
 	ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
 	ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
-	np->ccb->p_ccb		= vtophys (np->ccb);
+	np->ccb->p_ccb		= vtobus (np->ccb);
 
 	/*
 	**    Patch the script for LED support.
@@ -4798,6 +3941,8 @@
 attach_error:
 	if (!instance) return -1;
 	printk(KERN_INFO "%s: detaching...\n", ncr_name(np));
+	if (!np)
+		goto unregister;
 #ifndef NCR_IOMAPPED
 	if (np->vaddr) {
 #ifdef DEBUG_NCR53C8XX
@@ -4823,6 +3968,15 @@
 #endif
 		free_irq(np->irq, np);
 	}
+	if (np->scripth0)
+		m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+	if (np->script0)
+		m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+	if (np->ccb)
+		m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+	m_free_dma(np, sizeof(struct ncb), "NCB");
+
+unregister:
 	scsi_unregister(instance);
 
         return -1;
@@ -4850,6 +4004,7 @@
 */
 static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
 {
+	unmap_scsi_data(np, cmd);
 	cmd->host_scribble = (char *) np->done_list;
 	np->done_list = cmd;
 }
@@ -5105,7 +4260,7 @@
 	**----------------------------------------------------
 	*/
 
-	segments = ncr_scatter (cp, cp->cmd);
+	segments = ncr_scatter (np, cp, cp->cmd);
 
 	if (segments < 0) {
 		ncr_free_ccb(np, cp);
@@ -5114,47 +4269,24 @@
 
 	/*----------------------------------------------------
 	**
-	**	Guess xfer direction.
-	**	Spare some CPU by testing here frequently opcode.
+	**	Determine xfer direction.
 	**
 	**----------------------------------------------------
 	*/
 	if (!cp->data_len)
-		direction = 0;
-	else {
-		switch((int) cmd->cmnd[0]) {
-		case 0x08:  /*	READ(6)				08 */
-		case 0x28:  /*	READ(10)			28 */
-		case 0xA8:  /*	READ(12)			A8 */
-			direction = XFER_IN;
-			break;
-		case 0x0A:  /*	WRITE(6)			0A */
-		case 0x2A:  /*	WRITE(10)			2A */
-		case 0xAA:  /*	WRITE(12)			AA */
-			direction = XFER_OUT;
-			break;
-		default:
-			direction = (XFER_IN|XFER_OUT);
-			break;
-		}
-	}
-
-	/*----------------------------------------------------
-	**
-	**	Set the SAVED_POINTER.
-	**
-	**----------------------------------------------------
-	*/
-
-	/*
-	**	Default to no data transfer.
-	*/
-	lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+		direction = SCSI_DATA_NONE;
+	else
+		direction = scsi_data_direction(cmd);
 
 	/*
-	**	Compute data out pointers, if needed.
+	**	If data direction is UNKNOWN, speculate DATA_READ 
+	**	but prepare alternate pointers for WRITE in case 
+	**	of our speculation will be just wrong.
+	**	SCRIPTS will swap values if needed.
 	*/
-	if (direction & XFER_OUT) {
+	switch(direction) {
+	case SCSI_DATA_UNKNOWN:
+	case SCSI_DATA_WRITE:
 		goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
 		if (segments <= MAX_SCATTERL)
 			lastp = goalp - 8 - (segments * 16);
@@ -5162,21 +4294,12 @@
 			lastp = NCB_SCRIPTH_PHYS (np, hdata_out2);
 			lastp -= (segments - MAX_SCATTERL) * 16;
 		}
-		/*
-		**	If actual data direction is unknown, save pointers 
-		**	in header. The SCRIPTS will swap them to current 
-		**	if target decision will be data out.
-		*/
-		if (direction & XFER_IN) {
-			cp->phys.header.wgoalp	= cpu_to_scr(goalp);
-			cp->phys.header.wlastp	= cpu_to_scr(lastp);
-		}
-	}
-
-	/*
-	**	Compute data in pointers, if needed.
-	*/
-	if (direction & XFER_IN) {
+		if (direction != SCSI_DATA_UNKNOWN)
+			break;
+		cp->phys.header.wgoalp	= cpu_to_scr(goalp);
+		cp->phys.header.wlastp	= cpu_to_scr(lastp);
+		/* fall through */
+	case SCSI_DATA_READ:
 		goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
 		if (segments <= MAX_SCATTERL)
 			lastp = goalp - 8 - (segments * 16);
@@ -5184,6 +4307,11 @@
 			lastp = NCB_SCRIPTH_PHYS (np, hdata_in2);
 			lastp -= (segments - MAX_SCATTERL) * 16;
 		}
+		break;
+	default:
+	case SCSI_DATA_NONE:
+		lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+		break;
 	}
 
 	/*
@@ -5193,7 +4321,7 @@
 	cp->phys.header.lastp = cpu_to_scr(lastp);
 	cp->phys.header.goalp = cpu_to_scr(goalp);
 
-	if ((direction & (XFER_IN|XFER_OUT)) == (XFER_IN|XFER_OUT))
+	if (direction == SCSI_DATA_UNKNOWN)
 		cp->phys.header.savep = 
 			cpu_to_scr(NCB_SCRIPTH_PHYS (np, data_io));
 	else
@@ -5236,7 +4364,8 @@
 	/*
 	**	command
 	*/
-	cp->phys.cmd.addr		= cpu_to_scr(vtophys (&cmd->cmnd[0]));
+	memcpy(cp->cdb_buf, cmd->cmnd, MIN(cmd->cmd_len, sizeof(cp->cdb_buf)));
+	cp->phys.cmd.addr		= cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
 	cp->phys.cmd.size		= cpu_to_scr(cmd->cmd_len);
 
 	/*
@@ -5684,7 +4813,7 @@
 #ifdef DEBUG_NCR53C8XX
 	printk("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
 #endif
-		m_free(cp, sizeof(*cp));
+		m_free_dma(cp, sizeof(*cp), "CCB");
 	}
 
 	/*
@@ -5700,12 +4829,20 @@
 	printk("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
 #endif
 				if (lp->jump_ccb != &lp->jump_ccb_0)
-					m_free(lp->jump_ccb, 256);
-				m_free(lp, sizeof(*lp));
+					m_free_dma(lp->jump_ccb,256,"JUMP_CCB");
+				m_free_dma(lp, sizeof(*lp), "LCB");
 			}
 		}
 	}
 
+	if (np->scripth0)
+		m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+	if (np->script0)
+		m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+	if (np->ccb)
+		m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+	m_free_dma(np, sizeof(struct ncb), "NCB");
+
 	printk("%s: host resources successfully released\n", ncr_name(np));
 
 	return 1;
@@ -5859,6 +4996,7 @@
 		*/
 		if (cmd->cmnd[0] == 0x12 && !(cmd->cmnd[1] & 0x3) &&
 		    cmd->cmnd[4] >= 7 && !cmd->use_sg) {
+			sync_scsi_data(np, cmd);	/* SYNC the data */
 			ncr_setup_lcb (np, cmd->target, cmd->lun,
 				       (char *) cmd->request_buffer);
 		}
@@ -5885,6 +5023,12 @@
 		*/
 		cmd->result = ScsiResult(DID_OK, S_CHECK_COND);
 
+		/*
+		**	Copy back sense data to caller's buffer.
+		*/
+		memcpy(cmd->sense_buffer, cp->sense_buf,
+		       MIN(sizeof(cmd->sense_buffer), sizeof(cp->sense_buf)));
+
 		if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
 			u_char * p = (u_char*) & cmd->sense_buffer;
 			int i;
@@ -6260,7 +5404,7 @@
 		if (bootverbose)
 			printk ("%s: Downloading SCSI SCRIPTS.\n",
 				ncr_name(np));
-		OUTL (nc_scratcha, vtophys(np->script0));
+		OUTL (nc_scratcha, vtobus(np->script0));
 		OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, start_ram));
 	}
 	else
@@ -6774,7 +5918,12 @@
 		return;
 	}
 
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	np->ktime = thistime;
+	np->timer.expires = ktime_get(1);
+#else
 	np->timer.expires = ktime_get(SCSI_NCR_TIMER_INTERVAL);
+#endif
 	add_timer(&np->timer);
 
 	/*
@@ -7289,6 +6438,7 @@
 	u_int32	dsp;
 	u_int32	dsa;
 	u_int32	nxtdsp;
+	u_int32	newtmp;
 	u_int32	*vdsp;
 	u_int32	oadr, olen;
 	u_int32	*tblp;
@@ -7383,11 +6533,11 @@
 		nxtdsp = dsp;
 	}
 	else if (cp) {
-		if	(dsp == vtophys (&cp->patch[2])) {
+		if	(dsp == CCB_PHYS (cp, patch[2])) {
 			vdsp = &cp->patch[0];
 			nxtdsp = scr_to_cpu(vdsp[3]);
 		}
-		else if (dsp == vtophys (&cp->patch[6])) {
+		else if (dsp == CCB_PHYS (cp, patch[6])) {
 			vdsp = &cp->patch[4];
 			nxtdsp = scr_to_cpu(vdsp[3]);
 		}
@@ -7482,7 +6632,11 @@
 	*/
 
 	newcmd = cp->patch;
-	if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
+	newtmp = CCB_PHYS (cp, patch);
+	if (newtmp == scr_to_cpu(cp->phys.header.savep)) {
+		newcmd = &cp->patch[4];
+		newtmp = CCB_PHYS (cp, patch[4]);
+	}
 
 	/*
 	**	fillin the commands
@@ -7509,7 +6663,7 @@
 #ifdef SCSI_NCR_PROFILE_SUPPORT
 	np->profile.num_break++;
 #endif
-	OUTL (nc_temp, vtophys (newcmd));
+	OUTL (nc_temp, newtmp);
 	OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
 	return;
 
@@ -7678,15 +6832,14 @@
 		*/
 		cp->sensecmd[0]		= 0x03;
 		cp->sensecmd[1]		= cmd->lun << 5;
-		cp->sensecmd[4]		= sizeof(cmd->sense_buffer);
+		cp->sensecmd[4]		= sizeof(cp->sense_buf);
 
 		/*
 		**	sense data
 		*/
-		cp->phys.sense.addr	=
-				cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
-		cp->phys.sense.size	=
-				cpu_to_scr(sizeof(cmd->sense_buffer));
+		bzero(cp->sense_buf, sizeof(cp->sense_buf));
+		cp->phys.sense.addr	= cpu_to_scr(CCB_PHYS(cp,sense_buf[0]));
+		cp->phys.sense.size	= cpu_to_scr(sizeof(cp->sense_buf));
 
 		/*
 		**	requeue the command.
@@ -8395,7 +7548,7 @@
 
 
 #define ncr_reg_bus_addr(r) \
-	(bus_dvma_to_mem(np->paddr) + offsetof (struct ncr_reg, r))
+	(pcivtobus(np->paddr) + offsetof (struct ncr_reg, r))
 
 /*------------------------------------------------------------------------
 **	Initialize the fixed part of a CCB structure.
@@ -8409,7 +7562,7 @@
 	/*
 	**	Remember virtual and bus address of this ccb.
 	*/
-	cp->p_ccb 	   = vtophys(cp);
+	cp->p_ccb 	   = vtobus(cp);
 	cp->phys.header.cp = cp;
 
 	/*
@@ -8424,10 +7577,10 @@
 	**	JUMP @(sched_point)
 	*/
 	cp->start.setup_dsa[0]	 = cpu_to_scr(copy_4);
-	cp->start.setup_dsa[1]	 = cpu_to_scr(vtophys(&cp->start.p_phys));
+	cp->start.setup_dsa[1]	 = cpu_to_scr(CCB_PHYS(cp, start.p_phys));
 	cp->start.setup_dsa[2]	 = cpu_to_scr(ncr_reg_bus_addr(nc_dsa));
 	cp->start.schedule.l_cmd = cpu_to_scr(SCR_JUMP);
-	cp->start.p_phys	 = cpu_to_scr(vtophys(&cp->phys));
+	cp->start.p_phys	 = cpu_to_scr(CCB_PHYS(cp, phys));
 
 	bcopy(&cp->start, &cp->restart, sizeof(cp->restart));
 
@@ -8450,15 +7603,10 @@
 	/*
 	**	Allocate memory for this CCB.
 	*/
-	cp = m_alloc(sizeof(struct ccb), 5);
+	cp = m_calloc_dma(sizeof(struct ccb), "CCB");
 	if (!cp)
 		return;
 
-	if (DEBUG_FLAGS & DEBUG_ALLOC) {
-		PRINT_LUN(np, tn, ln);
-		printk ("new ccb @%p.\n", cp);
-	}
-
 	/*
 	**	Count it and initialyze it.
 	*/
@@ -8516,7 +7664,7 @@
 	**	COPY @(tp->sval), @(sxfer)
 	*/
 	tp->getscr[0] =	cpu_to_scr(copy_1);
-	tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+	tp->getscr[1] = cpu_to_scr(vtobus (&tp->sval));
 	tp->getscr[2] = cpu_to_scr(ncr_reg_bus_addr(nc_sxfer));
   
 	/*
@@ -8524,7 +7672,7 @@
 	**	COPY @(tp->wval), @(scntl3)
 	*/
 	tp->getscr[3] =	cpu_to_scr(copy_1);
-	tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+	tp->getscr[4] = cpu_to_scr(vtobus (&tp->wval));
 	tp->getscr[5] = cpu_to_scr(ncr_reg_bus_addr(nc_scntl3));
 
 	/*
@@ -8549,7 +7697,7 @@
 	/*
 	**	Link this target control block to the JUMP chain.
 	*/
-	np->jump_tcb[th].l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
+	np->jump_tcb[th].l_paddr = cpu_to_scr(vtobus (&tp->jump_tcb));
 
 	/*
 	**	These assert's should be moved at driver initialisations.
@@ -8584,17 +7732,12 @@
 	/*
 	**	Allocate the lcb.
 	*/
-	lp = m_alloc(sizeof(struct lcb), 3);
+	lp = m_calloc_dma(sizeof(struct lcb), "LCB");
 	if (!lp)
 		goto fail;
 	bzero(lp, sizeof(*lp));
 	tp->lp[ln] = lp;
 
-	if (DEBUG_FLAGS & DEBUG_ALLOC) {
-		PRINT_LUN(np, tn, ln);
-		printk ("new lcb @%p.\n", lp);
-	}
-
 	/*
 	**	Initialize the target control block if not yet.
 	*/
@@ -8615,7 +7758,7 @@
 	*/
 	lp->maxnxs	= 1;
 	lp->jump_ccb	= &lp->jump_ccb_0;
-	lp->p_jump_ccb	= cpu_to_scr(vtophys(lp->jump_ccb));
+	lp->p_jump_ccb	= cpu_to_scr(vtobus(lp->jump_ccb));
 
 	/*
 	**	Initilialyze the reselect script:
@@ -8633,7 +7776,7 @@
 	lp->jump_lcb.l_paddr = tp->jump_lcb[lh].l_paddr;
 
 	lp->load_jump_ccb[0] = cpu_to_scr(copy_4);
-	lp->load_jump_ccb[1] = cpu_to_scr(vtophys (&lp->p_jump_ccb));
+	lp->load_jump_ccb[1] = cpu_to_scr(vtobus (&lp->p_jump_ccb));
 	lp->load_jump_ccb[2] = cpu_to_scr(ncr_reg_bus_addr(nc_temp));
 
 	lp->jump_tag.l_cmd   = cpu_to_scr(SCR_JUMP);
@@ -8642,7 +7785,7 @@
 	/*
 	**	Link this lun control block to the JUMP chain.
 	*/
-	tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
+	tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtobus (&lp->jump_lcb));
 
 	/*
 	**	Initialize command queuing control.
@@ -8726,12 +7869,12 @@
 	*/
 	if ((inq_byte7 & INQ7_QUEUE) && lp->jump_ccb == &lp->jump_ccb_0) {
 		int i;
-		lp->jump_ccb = m_alloc(256, 8);
+		lp->jump_ccb = m_calloc_dma(256, "JUMP_CCB");
 		if (!lp->jump_ccb) {
 			lp->jump_ccb = &lp->jump_ccb_0;
 			goto fail;
 		}
-		lp->p_jump_ccb = cpu_to_scr(vtophys(lp->jump_ccb));
+		lp->p_jump_ccb = cpu_to_scr(vtobus(lp->jump_ccb));
 		for (i = 0 ; i < 64 ; i++)
 			lp->jump_ccb[i] =
 				cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q));
@@ -8783,7 +7926,7 @@
 **	sizes to the data segment array.
 */
 
-static	int	ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
+static	int	ncr_scatter(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd)
 {
 	struct scr_tblmove *data;
 	int segment	= 0;
@@ -8794,8 +7937,10 @@
 
 	if (!use_sg) {
 		if (cmd->request_bufflen) {
+			u_long baddr = map_scsi_single_data(np, cmd);
+
 			data = &data[MAX_SCATTER - 1];
-			data[0].addr = cpu_to_scr(vtophys(cmd->request_buffer));
+			data[0].addr = cpu_to_scr(baddr);
 			data[0].size = cpu_to_scr(cmd->request_bufflen);
 			cp->data_len = cmd->request_bufflen;
 			segment = 1;
@@ -8804,13 +7949,16 @@
 	else if (use_sg <= MAX_SCATTER) {
 		struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
 
+		use_sg = map_scsi_sg_data(np, cmd);
 		data = &data[MAX_SCATTER - use_sg];
+
 		while (segment < use_sg) {
-			data[segment].addr =
-				cpu_to_scr(vtophys(scatter[segment].address));
-			data[segment].size =
-				cpu_to_scr(scatter[segment].length);
-			cp->data_len	   += scatter[segment].length;
+			u_long baddr = scsi_sg_dma_address(&scatter[segment]);
+			unsigned int len = scsi_sg_dma_len(&scatter[segment]);
+
+			data[segment].addr = cpu_to_scr(baddr);
+			data[segment].size = cpu_to_scr(len);
+			cp->data_len	  += len;
 			++segment;
 		}
 	}
@@ -9198,866 +8346,43 @@
 
 		(void) ncrgetfreq (np, 11);	/* throw away first result */
 		f1 = ncrgetfreq (np, 11);
-		f2 = ncrgetfreq (np, 11);
-
-		if (bootverbose)
-			printk ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
-
-		if (f1 > f2) f1 = f2;		/* trust lower result	*/
-
-		if	(f1 <	45000)		f1 =  40000;
-		else if (f1 <	55000)		f1 =  50000;
-		else				f1 =  80000;
-
-		if (f1 < 80000 && mult > 1) {
-			if (bootverbose >= 2)
-				printk ("%s: clock multiplier assumed\n", ncr_name(np));
-			np->multiplier	= mult;
-		}
-	} else {
-		if	((scntl3 & 7) == 3)	f1 =  40000;
-		else if	((scntl3 & 7) == 5)	f1 =  80000;
-		else 				f1 = 160000;
-
-		f1 /= np->multiplier;
-	}
-
-	/*
-	**	Compute controller synchronous parameters.
-	*/
-	f1		*= np->multiplier;
-	np->clock_khz	= f1;
-}
-
-/*===================== LINUX ENTRY POINTS SECTION ==========================*/
-
-#ifndef uchar
-#define uchar unsigned char
-#endif
-
-#ifndef ushort
-#define ushort unsigned short
-#endif
-
-#ifndef ulong
-#define ulong unsigned long
-#endif
-
-/* ---------------------------------------------------------------------
-**
-**	Driver setup from the boot command line
-**
-** ---------------------------------------------------------------------
-*/
-
-#ifdef MODULE
-#define	ARG_SEP	' '
-#else
-#define	ARG_SEP	','
-#endif
-
-int __init ncr53c8xx_setup(char *str)
-{
-#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
-	char *cur = str;
-	char *pc, *pv;
-	int val;
-	int base;
-	int c;
-	int xi = 0;
-
-	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
-		char *pe;
-
-		val = 0;
-		pv = pc;
-		c = *++pv;
-
-		if	(c == 'n')
-			val = 0;
-		else if	(c == 'y')
-			val = 1;
-		else {
-			base = 0;
-			val = (int) simple_strtoul(pv, &pe, base);
-		}
-		if	(!strncmp(cur, "tags:", 5)) {
-			int i;
-			driver_setup.default_tags = val;
-			if (pe && *pe == '/') {
-				i = 0;
-				while (*pe && *pe != ARG_SEP && 
-					i < sizeof(driver_setup.tag_ctrl)-1) {
-					driver_setup.tag_ctrl[i++] = *pe++;
-				}
-				driver_setup.tag_ctrl[i] = '\0';
-			}
-		}
-		else if	(!strncmp(cur, "mpar:", 5))
-			driver_setup.master_parity	= val;
-		else if	(!strncmp(cur, "spar:", 5))
-			driver_setup.scsi_parity	= val;
-		else if	(!strncmp(cur, "disc:", 5))
-			driver_setup.disconnection	= val;
-		else if	(!strncmp(cur, "specf:", 6))
-			driver_setup.special_features = val;
-		else if	(!strncmp(cur, "ultra:", 6))
-			driver_setup.ultra_scsi	= val;
-		else if	(!strncmp(cur, "fsn:", 4))
-			driver_setup.force_sync_nego	= val;
-		else if	(!strncmp(cur, "revprob:", 8))
-			driver_setup.reverse_probe	= val;
-		else if	(!strncmp(cur, "sync:", 5))
-			driver_setup.default_sync	= val;
-		else if	(!strncmp(cur, "verb:", 5))
-			driver_setup.verbose	= val;
-		else if	(!strncmp(cur, "debug:", 6))
-			driver_setup.debug	= val;
-		else if	(!strncmp(cur, "burst:", 6))
-			driver_setup.burst_max	= val;
-		else if	(!strncmp(cur, "led:", 4))
-			driver_setup.led_pin	= val;
-		else if	(!strncmp(cur, "wide:", 5))
-			driver_setup.max_wide	= val? 1:0;
-		else if	(!strncmp(cur, "settle:", 7))
-			driver_setup.settle_delay= val;
-		else if	(!strncmp(cur, "diff:", 5))
-			driver_setup.diff_support= val;
-		else if	(!strncmp(cur, "irqm:", 5))
-			driver_setup.irqm	= val;
-		else if	(!strncmp(cur, "pcifix:", 7))
-			driver_setup.pci_fix_up	= val;
-		else if	(!strncmp(cur, "buschk:", 7))
-			driver_setup.bus_check	= val;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-		else if	(!strncmp(cur, "nvram:", 6))
-			driver_setup.use_nvram	= val;
-#endif
-
-		else if	(!strncmp(cur, "safe:", 5) && val)
-			memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
-		else if	(!strncmp(cur, "excl:", 5)) {
-			if (xi < SCSI_NCR_MAX_EXCLUDES)
-				driver_setup.excludes[xi++] = val;
-		}
-		else if	(!strncmp(cur, "hostid:", 7))
-			driver_setup.host_id	= val;
-		else
-			printk("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
-
-		if ((cur = strchr(cur, ARG_SEP)) != NULL)
-			++cur;
-	}
-#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
-	return 0;
-}
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
-#ifndef MODULE
-__setup("ncr53c8xx=", ncr53c8xx_setup);
-#endif
-#endif
-
-static int 
-ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device);
-
-/*
-**   Linux entry point for NCR53C8XX devices detection routine.
-**
-**   Called by the middle-level scsi drivers at initialization time,
-**   or at module installation.
-**
-**   Read the PCI configuration and try to attach each
-**   detected NCR board.
-**
-**   If NVRAM is present, try to attach boards according to 
-**   the used defined boot order.
-**
-**   Returns the number of boards successfully attached.
-*/
-
-static void __init ncr_print_driver_setup(void)
-{
-#define YesNo(y)	y ? 'y' : 'n'
-	printk ("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
-		"burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
-		YesNo(driver_setup.disconnection),
-		driver_setup.special_features,
-		driver_setup.ultra_scsi,
-		driver_setup.default_tags,
-		driver_setup.default_sync,
-		driver_setup.burst_max,
-		YesNo(driver_setup.max_wide),
-		driver_setup.diff_support,
-		YesNo(driver_setup.reverse_probe),
-		driver_setup.bus_check);
-
-	printk ("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
-		"led:%c,settle:%d,irqm:%d,nvram:0x%x,pcifix:0x%x\n",
-		YesNo(driver_setup.master_parity),
-		YesNo(driver_setup.scsi_parity),
-		YesNo(driver_setup.force_sync_nego),
-		driver_setup.verbose,
-		driver_setup.debug,
-		YesNo(driver_setup.led_pin),
-		driver_setup.settle_delay,
-		driver_setup.irqm,
-		driver_setup.use_nvram,
-		driver_setup.pci_fix_up);
-#undef YesNo
-}
-
-/*
-**   NCR53C8XX devices description table and chip ids list.
-*/
-
-static ncr_chip	ncr_chip_table[] __initdata	= SCSI_NCR_CHIP_TABLE;
-static ushort	ncr_chip_ids[]   __initdata	= SCSI_NCR_CHIP_IDS;
-
-
-/*===================================================================
-**    Detect all 53c8xx hosts and then attach them.
-**
-**    If we are using NVRAM, once all hosts are detected, we need to 
-**    check any NVRAM for boot order in case detect and boot order 
-**    differ and attach them using the order in the NVRAM.
-**
-**    If no NVRAM is found or data appears invalid attach boards in 
-**    the the order they are detected.
-**===================================================================
-*/
-int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt)
-{
-	pcidev_t pcidev;
-	int i, j, chips, hosts, count;
-	int attach_count = 0;
-	ncr_device *devtbl, *devp;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	ncr_nvram  nvram0, nvram, *nvp;
-#endif
-
-	/*
-	**    PCI is required.
-	*/
-	if (!pci_present())
-		return 0;
-
-	/*
-	**    Initialize driver general stuff.
-	*/
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
-     tpnt->proc_dir  = &proc_scsi_ncr53c8xx;
-#else
-     tpnt->proc_name = "ncr53c8xx";
-#endif
-     tpnt->proc_info = ncr53c8xx_proc_info;
-#endif
-
-#if	defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
-if (ncr53c8xx)
-	ncr53c8xx_setup(ncr53c8xx);
-#endif
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
-	ncr_debug = driver_setup.debug;
-#endif
-
-	if (initverbose >= 2)
-		ncr_print_driver_setup();
-
-	/*
-	**	Allocate the device table since we donnot want to 
-	**	overflow the kernel stack.
-	**	1 x 4K PAGE is enough for more than 40 devices for i386.
-	*/
-	devtbl = kmalloc(4000, GFP_ATOMIC);
-	if (!devtbl)
-		return 0;
-
-	/* 
-	**    Detect all 53c8xx hosts.
-	**    Save the first Symbios NVRAM content if any 
-	**    for the boot order.
-	*/
-	chips	= sizeof(ncr_chip_ids)	/ sizeof(ncr_chip_ids[0]);
-	hosts	= 4000			/ sizeof(*devtbl);
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
-#endif
-	j = 0;
-	count = 0;
-	pcidev = PCIDEV_NULL; 
-	while (1) {
-		char *msg = "";
-		if (count >= hosts)
-			break;
-		if (j >= chips)
-			break;
-		i = driver_setup.reverse_probe ? chips - 1 - j : j;
-		pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
-					pcidev);
-		if (pcidev == PCIDEV_NULL) {
-			++j;
-			continue;
-		}
-		/* Some HW as the HP LH4 may report twice PCI devices */
-		for (i = 0; i < count ; i++) {
-			if (devtbl[i].slot.bus	     == PciBusNumber(pcidev) && 
-			    devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
-				break;
-		}
-		if (i != count)	/* Ignore this device if we already have it */
-			continue;
-		devp = &devtbl[count];
-		devp->host_id = driver_setup.host_id;
-		devp->attach_done = 0;
-		if (ncr53c8xx_pci_init(tpnt, pcidev, devp)) {
-			continue;
-		}
-		++count;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-		if (nvp) {
-			ncr_get_nvram(devp, nvp);
-			switch(nvp->type) {
-			case SCSI_NCR_SYMBIOS_NVRAM:
-				/*
-				 *   Switch to the other nvram buffer, so that 
-				 *   nvram0 will contain the first Symbios 
-				 *   format NVRAM content with boot order.
-				 */
-				nvp = &nvram;
-				msg = "with Symbios NVRAM";
-				break;
-			case SCSI_NCR_TEKRAM_NVRAM:
-				msg = "with Tekram NVRAM";
-				break;
-			}
-		}
-#endif
-		printk(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
-		       devp->chip.name, msg);
-	}
-
-	/*
-	**    If we have found a SYMBIOS NVRAM, use first the NVRAM boot 
-	**    sequence as device boot order.
-	**    check devices in the boot record against devices detected. 
-	**    attach devices if we find a match. boot table records that 
-	**    do not match any detected devices will be ignored. 
-	**    devices that do not match any boot table will not be attached
-	**    here but will attempt to be attached during the device table 
-	**    rescan.
-	*/
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	if (!nvp || nvram0.type != SCSI_NCR_SYMBIOS_NVRAM)
-		goto next;
-	for (i = 0; i < 4; i++) {
-		Symbios_host *h = &nvram0.data.Symbios.host[i];
-		for (j = 0 ; j < count ; j++) {
-			devp = &devtbl[j];
-			if (h->device_fn != devp->slot.device_fn ||
-			    h->bus_nr	 != devp->slot.bus	 ||
-			    h->device_id != devp->chip.device_id)
-				continue;
-			if (devp->attach_done)
-				continue;
-			ncr_get_nvram(devp, nvp);
-			if (!ncr_attach (tpnt, attach_count, devp))
-				attach_count++;
-			devp->attach_done = 1;
-			break;
-		}
-	}
-next:
-#endif
-
-	/* 
-	**    Rescan device list to make sure all boards attached.
-	**    Devices without boot records will not be attached yet
-	**    so try to attach them here.
-	*/
-	for (i= 0; i < count; i++) {
-		devp = &devtbl[i];
-		if (!devp->attach_done) {
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-			ncr_get_nvram(devp, nvp);
-#endif
-			if (!ncr_attach (tpnt, attach_count, devp))
-				attach_count++;
-		}
-	}
-
-	kfree(devtbl);
-
-	return attach_count;
-}
-
-/*===================================================================
-**    Detect and try to read SYMBIOS and TEKRAM NVRAM.
-**
-**    Data can be used to order booting of boards.
-**
-**    Data is saved in ncr_device structure if NVRAM found. This
-**    is then used to find drive boot order for ncr_attach().
-**
-**    NVRAM data is passed to Scsi_Host_Template later during 
-**    ncr_attach() for any device set up.
-*===================================================================
-*/
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp)
-{
-	devp->nvram = nvp;
-	if (!nvp)
-		return;
-	/*
-	**    Get access to chip IO registers
-	*/
-#ifdef NCR_IOMAPPED
-	request_region(devp->slot.io_port, 128, "ncr53c8xx");
-	devp->slot.port = devp->slot.io_port;
-#else
-	devp->slot.reg = (struct ncr_reg *) remap_pci_mem(devp->slot.base, 128);
-	if (!devp->slot.reg)
-		return;
-#endif
-
-	/*
-	**    Try to read SYMBIOS nvram.
-	**    Try to read TEKRAM nvram if Symbios nvram not found.
-	*/
-	if	(!ncr_get_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
-		nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
-	else if	(!ncr_get_Tekram_nvram(&devp->slot, &nvp->data.Tekram))
-		nvp->type = SCSI_NCR_TEKRAM_NVRAM;
-	else {
-		nvp->type = 0;
-		devp->nvram = 0;
-	}
-
-	/*
-	** Release access to chip IO registers
-	*/
-#ifdef NCR_IOMAPPED
-	release_region(devp->slot.port, 128);
-#else
-	unmap_pci_mem((u_long) devp->slot.reg, 128ul);
-#endif
-
-}
-#endif	/* SCSI_NCR_NVRAM_SUPPORT */
-
-/*
-**   Read and check the PCI configuration for any detected NCR 
-**   boards and save data for attaching after all boards have 
-**   been detected.
-*/
-
-static int __init 
-ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
-{
-	ushort vendor_id, device_id, command;
-	uchar cache_line_size, latency_timer;
-	uchar revision;
-	uint irq;
-	ulong base, base_2, io_port; 
-	int i;
-	ncr_chip *chip;
-
-	/*
-	**    Read info from the PCI config space.
-	**    pci_read_config_xxx() functions are assumed to be used for 
-	**    successfully detected PCI devices.
-	*/
-	vendor_id = PciVendorId(pdev);
-	device_id = PciDeviceId(pdev);
-	irq = PciIrqLine(pdev);
-	i =	0;
-	i =	pci_get_base_address(pdev, i, &io_port);
-	i =	pci_get_base_address(pdev, i, &base);
-	(void)	pci_get_base_address(pdev, i, &base_2);
-	pci_read_config_word(pdev, PCI_COMMAND, &command);
-	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
-	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
-	pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
-
-	/*
-	**	If user excludes this chip, donnot initialize it.
-	*/
-	for (i = 0 ; i < SCSI_NCR_MAX_EXCLUDES ; i++) {
-		if (driver_setup.excludes[i] ==
-				(io_port & PCI_BASE_ADDRESS_IO_MASK))
-			return -1;
-	}
-	/*
-	 *	Check if the chip is supported
-	 */
-	chip = 0;
-	for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
-		if (device_id != ncr_chip_table[i].device_id)
-			continue;
-		if (revision > ncr_chip_table[i].revision_id)
-			continue;
-		chip = &device->chip;
-		memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
-		chip->revision_id = revision;
-		break;
-	}
-
-#if defined(__i386__)
-	/*
-	 *	Ignore Symbios chips controlled by SISL RAID controller.
-	 */
-	if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
-		unsigned int ScriptsSize, MagicValue;
-		vm_offset_t ScriptsRAM;
-
-		if (chip->features & FE_RAM8K)
-			ScriptsSize = 8192;
-		else
-			ScriptsSize = 4096;
-
-		ScriptsRAM = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
-					   ScriptsSize);
-		if (ScriptsRAM) {
-			MagicValue = readl(ScriptsRAM + ScriptsSize - 16);
-			unmap_pci_mem(ScriptsRAM, ScriptsSize);
-			if (MagicValue == 0x52414944)
-				return -1;
-		}
-	}
-#endif
-
-	printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
-		PciBusNumber(pdev),
-		(int) (PciDeviceFn(pdev) & 0xf8) >> 3,
-		(int) (PciDeviceFn(pdev) & 0x7));
-
-	if (!chip) {
-		printk("ncr53c8xx: not initializing, device not supported\n");
-		return -1;
-	}
-
-#ifdef __powerpc__
-	/*
-	 *	Several fix-up for power/pc.
-	 *	Should not be performed by the driver.
-	 */
-	if (!(command & PCI_COMMAND_MASTER)) {
-		printk("ncr53c8xx: attempting to force PCI_COMMAND_MASTER...");
-		command |= PCI_COMMAND_MASTER;
-		pci_write_config_word(pdev, PCI_COMMAND, command);
-		pci_read_config_word(pdev, PCI_COMMAND, &command);
-		if (!(command & PCI_COMMAND_MASTER)) {
-			printk("failed!\n");
-		} else {
-			printk("succeeded.\n");
-		}
-	}
-
-	if (!(command & PCI_COMMAND_IO)) {
-		printk("ncr53c8xx: attempting to force PCI_COMMAND_IO...");
-		command |= PCI_COMMAND_IO;
-		pci_write_config_word(pdev, PCI_COMMAND, command);
-		pci_read_config_word(pdev, PCI_COMMAND, &command);
-		if (!(command & PCI_COMMAND_IO)) {
-			printk("failed!\n");
-		} else {
-			printk("succeeded.\n");
-		}
-	}
-
-	if (!(command & PCI_COMMAND_MEMORY)) {
-		printk("ncr53c8xx: attempting to force PCI_COMMAND_MEMORY...");
-		command |= PCI_COMMAND_MEMORY;
-		pci_write_config_word(pdev, PCI_COMMAND, command);
-		pci_read_config_word(pdev, PCI_COMMAND, &command);
-		if (!(command & PCI_COMMAND_MEMORY)) {
-			printk("failed!\n");
-		} else {
-			printk("succeeded.\n");
-		}
-	}
-
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,140)
-	if ( is_prep ) {
-		if (io_port >= 0x10000000) {
-			printk("ncr53c8xx: reallocating io_port (Wacky IBM)");
-			io_port = (io_port & 0x00FFFFFF) | 0x01000000;
-			pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, io_port);
-		}
-		if (base >= 0x10000000) {
-			printk("ncr53c8xx: reallocating base (Wacky IBM)");
-			base = (base & 0x00FFFFFF) | 0x01000000;
-			pci_write_config_dword(pdev, PCI_BASE_ADDRESS_1, base);
-		}
-		if (base_2 >= 0x10000000) {
-			printk("ncr53c8xx: reallocating base2 (Wacky IBM)");
-			base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
-			pci_write_config_dword(pdev, PCI_BASE_ADDRESS_2, base_2);
-		}
-	}
-#endif
-#endif	/* __powerpc__ */
-
-#ifdef __sparc__
-	/*
-	 *	Severall fix-ups for sparc.
-	 *
-	 *	Should not be performed by the driver, but how can OBP know
-	 *	each and every PCI card, if they don't use Fcode?
-	 */
-
-	base = __pa(base);
-	base_2 = __pa(base_2);
-
-	if (!(command & PCI_COMMAND_MASTER)) {
-		if (initverbose >= 2)
-			printk("ncr53c8xx: setting PCI_COMMAND_MASTER bit (fixup)\n");
-		command |= PCI_COMMAND_MASTER;
-		pci_write_config_word(pdev, PCI_COMMAND, command);
-		pci_read_config_word(pdev, PCI_COMMAND, &command);
-	}
-
-	if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
-		if (initverbose >= 2)
-			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fixup)\n");
-		command |= PCI_COMMAND_INVALIDATE;
-		pci_write_config_word(pdev, PCI_COMMAND, command);
-		pci_read_config_word(pdev, PCI_COMMAND, &command);
-	}
-
-	if ((chip->features & FE_CLSE) && !cache_line_size) {
-		/* PCI_CACHE_LINE_SIZE value is in 32-bit words. */
-		cache_line_size = 64 / sizeof(u_int32);
-		if (initverbose >= 2)
-			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n", cache_line_size);
-		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cache_line_size);
-		pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
-	}
-
-	if (!latency_timer) {
-		latency_timer = 128;
-		if (initverbose >= 2)
-			printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer);
-		pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer);
-		pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
-	}
-#endif	/* __sparc__ */
-
-	/*
-	 * Check availability of IO space, memory space and master capability.
-	 */
-	if (command & PCI_COMMAND_IO)
-		io_port &= PCI_BASE_ADDRESS_IO_MASK;
-	else
-		io_port = 0;
-
-	if (command & PCI_COMMAND_MEMORY)
-		base &= PCI_BASE_ADDRESS_MEM_MASK;
-	else
-		base = 0;
-	
-	if (!io_port && !base) {
-		printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
-		return -1;
-	}
-
-	base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
-
-	if (io_port && check_region (io_port, 128)) {
-#ifdef __sparc__
-		printk("ncr53c8xx: IO region 0x%lx to 0x%lx is in use\n",
-			io_port, (io_port + 127));
-#else
-		printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
-			(int) io_port, (int) (io_port + 127));
-#endif
-		return -1;
-	}
-	
-	if (!(command & PCI_COMMAND_MASTER)) {
-		printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
-		return -1;
-	}
-
-	/*
-	 * Fix some features according to driver setup.
-	 */
-	if (!(driver_setup.special_features & 1))
-		chip->features &= ~FE_SPECIAL_SET;
-	else {
-		if (driver_setup.special_features & 2)
-			chip->features &= ~FE_WRIE;
-	}
-	if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
-		chip->features |=  FE_ULTRA;
-		chip->features &= ~FE_ULTRA2;
-	}
-	if (driver_setup.ultra_scsi < 1)
-		chip->features &= ~FE_ULTRA;
-	if (!driver_setup.max_wide)
-		chip->features &= ~FE_WIDE;
-
-
-#ifdef	SCSI_NCR_PCI_FIX_UP_SUPPORT
-
-	/*
-	 * Try to fix up PCI config according to wished features.
-	 */
-#if defined(__i386__) && !defined(MODULE)
-	if ((driver_setup.pci_fix_up & 1) &&
-	    (chip->features & FE_CLSE) && cache_line_size == 0) {
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
-		extern char x86;
-		switch(x86) {
-#else
-		switch(boot_cpu_data.x86) {
-#endif
-		case 4:	cache_line_size = 4; break;
-		case 6:
-		case 5:	cache_line_size = 8; break;
-		}
-		if (cache_line_size)
-			(void) pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cache_line_size);
-		if (initverbose)
-			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
-	}
-
-	if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
-	    (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
-		command |= PCI_COMMAND_INVALIDATE;
-		(void) pci_write_config_word(pdev, PCI_COMMAND, command);
-		if (initverbose)
-			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
-	}
-#endif
-	/*
-	 * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
-	 * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
-	 *   and donnot enable READ LINE.
-	 * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed). 
-	 */
-
-	if (!(chip->features & FE_CLSE)) {
-		int burst_max = chip->burst_max;
-		if (cache_line_size == 0) {
-			chip->features	&= ~FE_ERL;
-			if (burst_max > 3)
-				burst_max = 3;
-		}
-		else {
-			while (cache_line_size < (1 << burst_max))
-				--burst_max;
-		}
-		chip->burst_max = burst_max;
-	}
-
-	/*
-	 * Tune PCI LATENCY TIMER according to burst max length transfer.
-	 * (latency timer >= burst length + 6, we add 10 to be quite sure)
-	 * If current value is zero, the device has probably been configured 
-	 * for no bursting due to some broken hardware.
-	 */
-
-	if (latency_timer == 0 && chip->burst_max)
-		printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
-
-	if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
-		uchar lt = (1 << chip->burst_max) + 6 + 10;
-		if (latency_timer < lt) {
-			latency_timer = lt;
-			if (initverbose)
-				printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
-			 (void) pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer);
-		}
-	}
+		f2 = ncrgetfreq (np, 11);
 
-	/*
-	 * Fix up for recent chips that support CACHE LINE SIZE.
-	 * If PCI config space is not OK, remove features that shall not be 
-	 * used by the chip. No need to trigger possible chip bugs.
-	 */
+		if (bootverbose)
+			printk ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
 
-	if ((chip->features & FE_CLSE) && cache_line_size == 0) {
-		chip->features &= ~FE_CACHE_SET;
-		printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
-	}
+		if (f1 > f2) f1 = f2;		/* trust lower result	*/
 
-	if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
-		chip->features &= ~FE_WRIE;
-		printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
-	}
+		if	(f1 <	45000)		f1 =  40000;
+		else if (f1 <	55000)		f1 =  50000;
+		else				f1 =  80000;
 
-#endif	/* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+		if (f1 < 80000 && mult > 1) {
+			if (bootverbose >= 2)
+				printk ("%s: clock multiplier assumed\n", ncr_name(np));
+			np->multiplier	= mult;
+		}
+	} else {
+		if	((scntl3 & 7) == 3)	f1 =  40000;
+		else if	((scntl3 & 7) == 5)	f1 =  80000;
+		else 				f1 = 160000;
 
- 	/* initialise ncr_device structure with items required by ncr_attach */
-	device->slot.bus	= PciBusNumber(pdev);
-	device->slot.device_fn	= PciDeviceFn(pdev);
-	device->slot.base	= base;
-	device->slot.base_2	= base_2;
-	device->slot.io_port	= io_port;
-	device->slot.irq	= irq;
-	device->attach_done	= 0;
+		f1 /= np->multiplier;
+	}
 
-	return 0;     
+	/*
+	**	Compute controller synchronous parameters.
+	*/
+	f1		*= np->multiplier;
+	np->clock_khz	= f1;
 }
 
+/*===================== LINUX ENTRY POINTS SECTION ==========================*/
+
 /*
 **   Linux select queue depths function
 */
 
-#define DEF_DEPTH	(driver_setup.default_tags)
-#define ALL_TARGETS	-2
-#define NO_TARGET	-1
-#define ALL_LUNS	-2
-#define NO_LUN		-1
-
-static int device_queue_depth(ncb_p np, int target, int lun)
-{
-	int c, h, t, u, v;
-	char *p = driver_setup.tag_ctrl;
-	char *ep;
-
-	h = -1;
-	t = NO_TARGET;
-	u = NO_LUN;
-	while ((c = *p++) != 0) {
-		v = simple_strtoul(p, &ep, 0);
-		switch(c) {
-		case '/':
-			++h;
-			t = ALL_TARGETS;
-			u = ALL_LUNS;
-			break;
-		case 't':
-			if (t != target)
-				t = (target == v) ? v : NO_TARGET;
-			u = ALL_LUNS;
-			break;
-		case 'u':
-			if (u != lun)
-				u = (lun == v) ? v : NO_LUN;
-			break;
-		case 'q':
-			if (h == np->unit &&
-				(t == ALL_TARGETS || t == target) &&
-				(u == ALL_LUNS    || u == lun))
-				return v;
-			break;
-		case '-':
-			t = ALL_TARGETS;
-			u = ALL_LUNS;
-			break;
-		default:
-			break;
-		}
-		p = ep;
-	}
-	return DEF_DEPTH;
-}
-
 static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist)
 {
 	struct scsi_device *device;
@@ -10081,7 +8406,7 @@
 		**	Use at least 2.
 		**	Donnot use more than our maximum.
 		*/
-		numtags = device_queue_depth(np, device->id, device->lun);
+		numtags = device_queue_depth(np->unit, device->id, device->lun);
 		if (numtags > tp->usrtags)
 			numtags = tp->usrtags;
 		if (!device->tagged_supported)
@@ -10111,14 +8436,6 @@
 }
 
 /*
-**   Linux entry point for info() function
-*/
-const char *ncr53c8xx_info (struct Scsi_Host *host)
-{
-	return SCSI_NCR_DRIVER_NAME;
-}
-
-/*
 **   Linux entry point of queuecommand() function
 */
 
@@ -10134,6 +8451,10 @@
 
      cmd->scsi_done     = done;
      cmd->host_scribble = NULL;
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+     cmd->__data_mapped = 0;
+     cmd->__data_mapping = 0;
+#endif
 
      NCR_LOCK_NCB(np, flags);
 
@@ -10150,8 +8471,10 @@
 
      NCR_UNLOCK_NCB(np, flags);
 
-     if (sts != DID_OK)
+     if (sts != DID_OK) {
+          unmap_scsi_data(np, cmd);
           done(cmd);
+     }
 
      return sts;
 }
@@ -10627,50 +8950,8 @@
 
 #endif	/* SCSI_NCR_USER_COMMAND_SUPPORT */
 
-#ifdef SCSI_NCR_USER_INFO_SUPPORT
-
-struct info_str
-{
-	char *buffer;
-	int length;
-	int offset;
-	int pos;
-};
-
-static void copy_mem_info(struct info_str *info, char *data, int len)
-{
-	if (info->pos + len > info->length)
-		len = info->length - info->pos;
-
-	if (info->pos + len < info->offset) {
-		info->pos += len;
-		return;
-	}
-	if (info->pos < info->offset) {
-		data += (info->offset - info->pos);
-		len  -= (info->offset - info->pos);
-	}
-
-	if (len > 0) {
-		memcpy(info->buffer + info->pos, data, len);
-		info->pos += len;
-	}
-}
-
-static int copy_info(struct info_str *info, char *fmt, ...)
-{
-	va_list args;
-	char buf[81];
-	int len;
-
-	va_start(args, fmt);
-	len = vsprintf(buf, fmt, args);
-	va_end(args);
-
-	copy_mem_info(info, buf, len);
-	return len;
-}
 
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
 /*
 **	Copy formatted profile information into the input buffer.
 */
@@ -10778,7 +9059,6 @@
 	return retv;
 }
 
-
 /*=========================================================================
 **	End of proc file system stuff
 **=========================================================================
@@ -10786,431 +9066,104 @@
 #endif
 
 
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-
-/* ---------------------------------------------------------------------
+/*==========================================================
 **
-**	Try reading Symbios format nvram
+**	/proc directory entry.
 **
-** ---------------------------------------------------------------------
+**==========================================================
+*/
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_ncr53c8xx = {
+    PROC_SCSI_NCR53C8XX, 9, NAME53C8XX,
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+
+/*==========================================================
 **
-** GPOI0 - data in/data out
-** GPIO1 - clock
+**	Boot command line.
 **
-**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
-*/
-
-#define SET_BIT 0
-#define CLR_BIT 1
-#define SET_CLK 2
-#define CLR_CLK 3
-
-static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
-static void nvram_start(ncr_slot *np, u_char *gpreg);
-static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
-static void nvram_stop(ncr_slot *np, u_char *gpreg);
-static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
-
-static int __init ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
-{
-	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
-	u_char	gpcntl, gpreg;
-	u_char	old_gpcntl, old_gpreg;
-	u_short	csum;
-	u_char	ack_data;
-	int	retv = 1;
-
-	/* save current state of GPCNTL and GPREG */
-	old_gpreg	= INB (nc_gpreg);
-	old_gpcntl	= INB (nc_gpcntl);
-	gpcntl		= old_gpcntl & 0xfc;
-
-	/* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
-	OUTB (nc_gpreg,  old_gpreg);
-	OUTB (nc_gpcntl, gpcntl);
-
-	/* this is to set NVRAM into a known state with GPIO0/1 both low */
-	gpreg = old_gpreg;
-	nvram_setBit(np, 0, &gpreg, CLR_CLK);
-	nvram_setBit(np, 0, &gpreg, CLR_BIT);
-		
-	/* now set NVRAM inactive with GPIO0/1 both high */
-	nvram_stop(np, &gpreg);
-	
-	/* activate NVRAM */
-	nvram_start(np, &gpreg);
-
-	/* write device code and random address MSB */
-	nvram_write_byte(np, &ack_data,
-		0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
-	if (ack_data & 0x01)
-		goto out;
-
-	/* write random address LSB */
-	nvram_write_byte(np, &ack_data,
-		(SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
-	if (ack_data & 0x01)
-		goto out;
-
-	/* regenerate START state to set up for reading */
-	nvram_start(np, &gpreg);
-	
-	/* rewrite device code and address MSB with read bit set (lsb = 0x01) */
-	nvram_write_byte(np, &ack_data,
-		0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
-	if (ack_data & 0x01)
-		goto out;
-
-	/* now set up GPIO0 for inputting data */
-	gpcntl |= 0x01;
-	OUTB (nc_gpcntl, gpcntl);
-		
-	/* input all active data - only part of total NVRAM */
-	csum = nvram_read_data(np,
-			(u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
-
-	/* finally put NVRAM back in inactive mode */
-	gpcntl &= 0xfe;
-	OUTB (nc_gpcntl, gpcntl);
-	nvram_stop(np, &gpreg);
-	
-#ifdef SCSI_NCR_DEBUG_NVRAM
-printk("ncr53c8xx: NvRAM type=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
-	nvram->type,
-	nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
-	nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
-	nvram->byte_count, sizeof(*nvram) - 12,
-	nvram->checksum, csum);
+**==========================================================
+*/
+#ifdef	MODULE
+char *ncr53c8xx = 0;	/* command line passed by insmod */
+# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+MODULE_PARM(ncr53c8xx, "s");
+# endif
 #endif
 
-	/* check valid NVRAM signature, verify byte count and checksum */
-	if (nvram->type == 0 &&
-	    !memcmp(nvram->trailer, Symbios_trailer, 6) &&
-	    nvram->byte_count == sizeof(*nvram) - 12 &&
-	    csum == nvram->checksum)
-		retv = 0;
-out:
-	/* return GPIO0/1 to original states after having accessed NVRAM */
-	OUTB (nc_gpcntl, old_gpcntl);
-	OUTB (nc_gpreg,  old_gpreg);
-
-	return retv;
-}
-
-/*
- * Read Symbios NvRAM data and compute checksum.
- */
-static u_short __init 
-nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
-{
-	int	x;
-	u_short	csum;
-
-	for (x = 0; x < len; x++) 
-		nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
-
-	for (x = 6, csum = 0; x < len - 6; x++)
-		csum += data[x];
-
-	return csum;
-}
-
-/*
- * Send START condition to NVRAM to wake it up.
- */
-static void __init nvram_start(ncr_slot *np, u_char *gpreg)
-{
-	nvram_setBit(np, 1, gpreg, SET_BIT);
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	nvram_setBit(np, 0, gpreg, CLR_BIT);
-	nvram_setBit(np, 0, gpreg, CLR_CLK);
-}
-
-/*
- * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
- * GPIO0 must already be set as an output
- */
-static void __init 
-nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
-{
-	int x;
-	
-	for (x = 0; x < 8; x++)
-		nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
-		
-	nvram_readAck(np, ack_data, gpreg, gpcntl);
-}
-
-/*
- * READ a byte from the NVRAM and then send an ACK to say we have got it,
- * GPIO0 must already be set as an input
- */
-static void __init 
-nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
-{
-	int x;
-	u_char read_bit;
-
-	*read_data = 0;
-	for (x = 0; x < 8; x++) {
-		nvram_doBit(np, &read_bit, 1, gpreg);
-		*read_data |= ((read_bit & 0x01) << (7 - x));
-	}
-
-	nvram_writeAck(np, ack_data, gpreg, gpcntl);
-}
-
-/*
- * Output an ACK to the NVRAM after reading,
- * change GPIO0 to output and when done back to an input
- */
-static void __init 
-nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
-{
-	OUTB (nc_gpcntl, *gpcntl & 0xfe);
-	nvram_doBit(np, 0, write_bit, gpreg);
-	OUTB (nc_gpcntl, *gpcntl);
-}
-
-/*
- * Input an ACK from NVRAM after writing,
- * change GPIO0 to input and when done back to an output
- */
-static void __init 
-nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
-{
-	OUTB (nc_gpcntl, *gpcntl | 0x01);
-	nvram_doBit(np, read_bit, 1, gpreg);
-	OUTB (nc_gpcntl, *gpcntl);
-}
-
-/*
- * Read or write a bit to the NVRAM,
- * read if GPIO0 input else write if GPIO0 output
- */
-static void __init nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
-{
-	nvram_setBit(np, write_bit, gpreg, SET_BIT);
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	if (read_bit)
-		*read_bit = INB (nc_gpreg);
-	nvram_setBit(np, 0, gpreg, CLR_CLK);
-	nvram_setBit(np, 0, gpreg, CLR_BIT);
-}
-
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
- */
-static void __init nvram_stop(ncr_slot *np, u_char *gpreg)
-{
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	nvram_setBit(np, 1, gpreg, SET_BIT);
-}
-
-/*
- * Set/clear data/clock bit in GPIO0
- */
-static void __init 
-nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+int __init ncr53c8xx_setup(char *str)
 {
-	UDELAY (5);
-	switch (bit_mode){
-	case SET_BIT:
-		*gpreg |= write_bit;
-		break;
-	case CLR_BIT:
-		*gpreg &= 0xfe;
-		break;
-	case SET_CLK:
-		*gpreg |= 0x02;
-		break;
-	case CLR_CLK:
-		*gpreg &= 0xfd;
-		break;
-
-	}
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (5);
+	return sym53c8xx__setup(str);
 }
 
-#undef SET_BIT 0
-#undef CLR_BIT 1
-#undef SET_CLK 2
-#undef CLR_CLK 3
-
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#ifndef MODULE
+__setup("ncr53c8xx=", ncr53c8xx_setup);
+#endif
+#endif
 
-/* ---------------------------------------------------------------------
-**
-**	Try reading Tekram format nvram
-**
-** ---------------------------------------------------------------------
+/*===================================================================
 **
-** GPOI0 - data in
-** GPIO1 - data out
-** GPIO2 - clock
-** GPIO4 - chip select
+**   SYM53C8XX supported device list
 **
-**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
+**===================================================================
 */
 
-static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
-static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
-static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
-static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
-static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
-
-static int __init ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
-{
-	u_char gpcntl, gpreg;
-	u_char old_gpcntl, old_gpreg;
-	u_short csum;
-
-	/* save current state of GPCNTL and GPREG */
-	old_gpreg	= INB (nc_gpreg);
-	old_gpcntl	= INB (nc_gpcntl);
-
-	/* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
-	   1/2/4 out */
-	gpreg = old_gpreg & 0xe9;
-	OUTB (nc_gpreg, gpreg);
-	gpcntl = (old_gpcntl & 0xe9) | 0x09;
-	OUTB (nc_gpcntl, gpcntl);
-
-	/* input all of NVRAM, 64 words */
-	csum = Tnvram_read_data(np, (u_short *) nvram,
-			sizeof(*nvram) / sizeof(short), &gpreg);
-	
-	/* return GPIO0/1/2/4 to original states after having accessed NVRAM */
-	OUTB (nc_gpcntl, old_gpcntl);
-	OUTB (nc_gpreg,  old_gpreg);
-
-	/* check data valid */
-	if (csum != 0x1234)
-		return 1;
-
-	return 0;
-}
-
-/*
- * Read Tekram NvRAM data and compute checksum.
- */
-static u_short __init 
-Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
-{
-	u_char	read_bit;
-	u_short	csum;
-	int	x;
-
-	for (x = 0, csum = 0; x < len; x++)  {
-
-		/* output read command and address */
-		Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
-		if (read_bit & 0x01)
-			return 0; /* Force bad checksum */
-
-		Tnvram_Read_Word(np, &data[x], gpreg);
-		csum += data[x];
-
-		Tnvram_Stop(np, gpreg);
-	}
-
-	return csum;
-}
-
-/*
- * Send read command and address to NVRAM
- */
-static void __init Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
-{
-	int x;
-
-	/* send 9 bits, start bit (1), command (2), address (6)  */
-	for (x = 0; x < 9; x++)
-		Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
-
-	*read_bit = INB (nc_gpreg);
-}
-
-/*
- * READ a byte from the NVRAM
- */
-static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
-{
-	int x;
-	u_char read_bit;
-
-	*nvram_data = 0;
-	for (x = 0; x < 16; x++) {
-		Tnvram_Read_Bit(np, &read_bit, gpreg);
-
-		if (read_bit & 0x01)
-			*nvram_data |=  (0x01 << (15 - x));
-		else
-			*nvram_data &= ~(0x01 << (15 - x));
-	}
-}
-
-/* 
- * Read bit from NVRAM
- */
-static void __init 
-Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
-{
-	UDELAY (2);
-	Tnvram_Clk(np, gpreg);
-	*read_bit = INB (nc_gpreg);
-}
+static u_short	ncr_chip_ids[]   __initdata = {
+	PCI_DEVICE_ID_NCR_53C810,
+	PCI_DEVICE_ID_NCR_53C815,
+	PCI_DEVICE_ID_NCR_53C820,
+	PCI_DEVICE_ID_NCR_53C825,
+	PCI_DEVICE_ID_NCR_53C860,
+	PCI_DEVICE_ID_NCR_53C875,
+	PCI_DEVICE_ID_NCR_53C875J,
+	PCI_DEVICE_ID_NCR_53C885,
+	PCI_DEVICE_ID_NCR_53C895,
+	PCI_DEVICE_ID_NCR_53C896,
+	PCI_DEVICE_ID_NCR_53C895A,
+	PCI_DEVICE_ID_NCR_53C1510D
+};
 
-/*
- * Write bit to GPIO0
- */
-static void __init 
-Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+/*==========================================================
+**
+**	Chip detection entry point.
+**
+**==========================================================
+*/
+int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 {
-	if (write_bit & 0x01)
-		*gpreg |= 0x02;
-	else
-		*gpreg &= 0xfd;
-		
-	*gpreg |= 0x10;
-		
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (2);
-
-	Tnvram_Clk(np, gpreg);
-}
+	/*
+	**    Initialize driver general stuff.
+	*/
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+     tpnt->proc_dir  = &proc_scsi_ncr53c8xx;
+#else
+     tpnt->proc_name = NAME53C8XX;
+#endif
+     tpnt->proc_info = ncr53c8xx_proc_info;
+#endif
 
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
- */
-static void __init Tnvram_Stop(ncr_slot *np, u_char *gpreg)
-{
-	*gpreg &= 0xef;
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (2);
+#if	defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+	ncr53c8xx_setup(ncr53c8xx);
+#endif
 
-	Tnvram_Clk(np, gpreg);
+	return sym53c8xx__detect(tpnt, ncr_chip_ids,
+				 sizeof(ncr_chip_ids)/sizeof(ncr_chip_ids[0]));
 }
 
-/*
- * Pulse clock bit in GPIO0
- */
-static void __init Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+/*==========================================================
+**
+**   Entry point for info() function
+**
+**==========================================================
+*/
+const char *ncr53c8xx_info (struct Scsi_Host *host)
 {
-	OUTB (nc_gpreg, *gpreg | 0x04);
-	UDELAY (2);
-	OUTB (nc_gpreg, *gpreg);
+	return SCSI_NCR_DRIVER_NAME;
 }
-
-#endif	/* SCSI_NCR_NVRAM_SUPPORT */
 
 /*
 **	Module stuff
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.3.51/linux/drivers/scsi/scsi.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/scsi.c	Mon Mar 13 22:15:03 2000
@@ -218,23 +218,6 @@
 	}
 }
 
-void scsi_wait_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
-		  void *buffer, unsigned bufflen, 
-		  int timeout, int retries)
-{
-	DECLARE_MUTEX_LOCKED(sem);
-	
-        if (buffer != NULL && SCpnt->sc_data_direction == SCSI_DATA_NONE)
-                BUG();
-	SCpnt->request.sem = &sem;
-	SCpnt->request.rq_status = RQ_SCSI_BUSY;
-	scsi_do_cmd (SCpnt, (void *) cmnd,
-		buffer, bufflen, scsi_wait_done, timeout, retries);
-	down (&sem);
-	SCpnt->request.sem = NULL;
-}
-
-
 /*
  * This lock protects the freelist for all devices on the system.
  * We could make this finer grained by having a single lock per
@@ -2499,7 +2482,6 @@
 		       atomic_read(&shpnt->host_active),
 		       shpnt->host_blocked,
 		       shpnt->host_self_blocked);
-
 	}
 
 	printk("\n\n");
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.3.51/linux/drivers/scsi/scsi.h	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/scsi.h	Tue Mar 14 18:38:26 2000
@@ -497,9 +497,6 @@
 			void *buffer, unsigned bufflen,
 			void (*done) (struct scsi_cmnd *),
 			int timeout, int retries);
-extern void scsi_wait_cmd(Scsi_Cmnd *, const void *cmnd,
-  			  void *buffer, unsigned bufflen,
-  			  int timeout, int retries);
 extern int scsi_dev_init(void);
 
 /*
@@ -571,7 +568,7 @@
 	Scsi_Cmnd *device_queue;	/* queue of SCSI Command structures */
 
 /* public: */
-	unsigned char id, lun, channel;
+	unsigned int id, lun, channel;
 
 	unsigned int manufacturer;	/* Manufacturer of device, for using 
 					 * vendor-specific cmd's */
@@ -731,9 +728,9 @@
 
 /* public: */
 
-	unsigned char target;
-	unsigned char lun;
-	unsigned char channel;
+	unsigned int target;
+	unsigned int lun;
+	unsigned int channel;
 	unsigned char cmd_len;
 	unsigned char old_cmd_len;
 	unsigned char sc_data_direction;
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/scsi_debug.c linux/drivers/scsi/scsi_debug.c
--- v2.3.51/linux/drivers/scsi/scsi_debug.c	Fri Jan 28 15:09:08 2000
+++ linux/drivers/scsi/scsi_debug.c	Mon Mar 13 22:15:03 2000
@@ -549,24 +549,24 @@
 	static unsigned char cmd[6] =
 	{TEST_UNIT_READY, 0, 0, 0, 0, 0};
 
-        Scsi_Cmnd     * scp;
+        Scsi_Request  * scp;
         Scsi_Device   * sdev;
         
         printk("Allocating host dev\n");
         sdev = scsi_get_host_dev(shpnt);
         printk("Got %p. Allocating command block\n", sdev);
-        scp  = scsi_allocate_device(sdev, 1, FALSE);
+        scp  = scsi_allocate_request(sdev);
         printk("Got %p\n", scp);
 
-        scp->cmd_len = 6;
-        scp->use_sg = 0;
+        scp->sr_cmd_len = 6;
+        scp->sr_use_sg = 0;
         
         printk("Sending command\n");
-        scsi_wait_cmd (scp, (void *) cmd, (void *) NULL,
+        scsi_wait_req (scp, (void *) cmd, (void *) NULL,
                        0, 100, 3);
         
         printk("Releasing command\n");
-        scsi_release_command(scp);
+        scsi_release_request(scp);
         printk("Freeing device\n");
         scsi_free_host_dev(sdev);
 }
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v2.3.51/linux/drivers/scsi/scsi_ioctl.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/scsi_ioctl.c	Mon Mar 13 22:15:03 2000
@@ -94,24 +94,20 @@
 				  int timeout, int retries)
 {
 	int result;
-	Scsi_Cmnd *SCpnt;
+	Scsi_Request *SRpnt;
 	Scsi_Device *SDpnt;
 
 
 	SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", cmd[0]));
-	SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
-        if( SCpnt == NULL )
-        {
-                return -EINTR;
-        }
+	SRpnt = scsi_allocate_request(dev);
 
-	SCpnt->sc_data_direction = SCSI_DATA_NONE;
-        scsi_wait_cmd(SCpnt, cmd, NULL, 0, timeout, retries);
+	SRpnt->sr_data_direction = SCSI_DATA_NONE;
+        scsi_wait_req(SRpnt, cmd, NULL, 0, timeout, retries);
 
-	SCSI_LOG_IOCTL(2, printk("Ioctl returned  0x%x\n", SCpnt->result));
+	SCSI_LOG_IOCTL(2, printk("Ioctl returned  0x%x\n", SRpnt->sr_result));
 
-	if (driver_byte(SCpnt->result) != 0)
-		switch (SCpnt->sense_buffer[2] & 0xf) {
+	if (driver_byte(SRpnt->sr_result) != 0)
+		switch (SRpnt->sr_sense_buffer[2] & 0xf) {
 		case ILLEGAL_REQUEST:
 			if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
 				dev->lockable = 0;
@@ -126,7 +122,7 @@
 		case UNIT_ATTENTION:
 			if (dev->removable) {
 				dev->changed = 1;
-				SCpnt->result = 0;	/* This is no longer considered an error */
+				SRpnt->sr_result = 0;	/* This is no longer considered an error */
 				/* gag this error, VFS will log it anyway /axboe */
 				/* printk(KERN_INFO "Disc change detected.\n"); */
 				break;
@@ -136,20 +132,20 @@
 			       dev->host->host_no,
 			       dev->id,
 			       dev->lun,
-			       SCpnt->result);
+			       SRpnt->sr_result);
 			printk("\tSense class %x, sense error %x, extended sense %x\n",
-			       sense_class(SCpnt->sense_buffer[0]),
-			       sense_error(SCpnt->sense_buffer[0]),
-			       SCpnt->sense_buffer[2] & 0xf);
+			       sense_class(SRpnt->sr_sense_buffer[0]),
+			       sense_error(SRpnt->sr_sense_buffer[0]),
+			       SRpnt->sr_sense_buffer[2] & 0xf);
 
 		};
 
-	result = SCpnt->result;
+	result = SRpnt->sr_result;
 
 	SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n"));
-	SDpnt = SCpnt->device;
-	scsi_release_command(SCpnt);
-	SCpnt = NULL;
+	SDpnt = SRpnt->sr_device;
+	scsi_release_request(SRpnt);
+	SRpnt = NULL;
 
 	return result;
 }
@@ -192,7 +188,7 @@
 	char *buf;
 	unsigned char cmd[MAX_COMMAND_SIZE];
 	char *cmd_in;
-	Scsi_Cmnd *SCpnt;
+	Scsi_Request *SRpnt;
 	Scsi_Device *SDpnt;
 	unsigned char opcode;
 	int inlen, outlen, cmdlen;
@@ -235,9 +231,9 @@
 			return -ENOMEM;
 		memset(buf, 0, buf_needed);
 		if( inlen == 0 ) {
-			data_direction = SCSI_DATA_WRITE;
-		} else if (outlen == 0 ) {
 			data_direction = SCSI_DATA_READ;
+		} else if (outlen == 0 ) {
+			data_direction = SCSI_DATA_WRITE;
 		} else {
 			/*
 			 * Can this ever happen?
@@ -297,38 +293,38 @@
 #ifndef DEBUG_NO_CMD
 
 
-	SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
-        if( SCpnt == NULL )
+	SRpnt = scsi_allocate_request(dev);
+        if( SRpnt == NULL )
         {
                 return -EINTR;
         }
 
-	SCpnt->sc_data_direction = data_direction;
-        scsi_wait_cmd(SCpnt, cmd, buf, needed, timeout, retries);
+	SRpnt->sr_data_direction = data_direction;
+        scsi_wait_req(SRpnt, cmd, buf, needed, timeout, retries);
 
 	/* 
 	 * If there was an error condition, pass the info back to the user. 
 	 */
-	if (SCpnt->result) {
-		int sb_len = sizeof(SCpnt->sense_buffer);
+	if (SRpnt->sr_result) {
+		int sb_len = sizeof(SRpnt->sr_sense_buffer);
 
 		sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len;
 		result = verify_area(VERIFY_WRITE, cmd_in, sb_len);
 		if (result)
 			return result;
-		copy_to_user(cmd_in, SCpnt->sense_buffer, sb_len);
+		copy_to_user(cmd_in, SRpnt->sr_sense_buffer, sb_len);
 	} else {
 		result = verify_area(VERIFY_WRITE, cmd_in, outlen);
 		if (result)
 			return result;
 		copy_to_user(cmd_in, buf, outlen);
 	}
-	result = SCpnt->result;
+	result = SRpnt->sr_result;
 
 
-	SDpnt = SCpnt->device;
-	scsi_release_command(SCpnt);
-	SCpnt = NULL;
+	SDpnt = SRpnt->sr_device;
+	scsi_release_request(SRpnt);
+	SRpnt = NULL;
 
 	if (buf)
 		scsi_free(buf, buf_needed);
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/scsi_lib.c linux/drivers/scsi/scsi_lib.c
--- v2.3.51/linux/drivers/scsi/scsi_lib.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/scsi_lib.c	Mon Mar 13 22:15:03 2000
@@ -87,6 +87,7 @@
 	SCpnt->request.cmd = SPECIAL;
 	SCpnt->request.special = (void *) SCpnt;
 	SCpnt->request.q = NULL;
+	SCpnt->request.nr_segments = 0;
 
 	/*
 	 * We have the option of inserting the head or the tail of the queue.
@@ -155,6 +156,8 @@
 	q = &SRpnt->sr_device->request_queue;
 	SRpnt->sr_request.cmd = SPECIAL;
 	SRpnt->sr_request.special = (void *) SRpnt;
+	SRpnt->sr_request.q = NULL;
+	SRpnt->sr_request.nr_segments = 0;
 
 	/*
 	 * We have the option of inserting the head or the tail of the queue.
@@ -909,6 +912,9 @@
 			 * be in an interrupt handler.  Only do this
 			 * from user space, since we do not want to
 			 * sleep from an interrupt.
+			 *
+			 * FIXME(eric) - have the error handler thread do
+			 * this work.
 			 */
 			SDpnt->was_reset = 0;
 			if (SDpnt->removable && !in_interrupt()) {
@@ -950,6 +956,9 @@
 			if( SRpnt->sr_magic == SCSI_REQ_MAGIC ) {
 				SCpnt = scsi_allocate_device(SRpnt->sr_device, 
 							     FALSE, FALSE);
+				if( !SCpnt ) {
+					break;
+				}
 				scsi_init_cmd_from_req(SCpnt, SRpnt);
 			}
 
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/scsi_merge.c linux/drivers/scsi/scsi_merge.c
--- v2.3.51/linux/drivers/scsi/scsi_merge.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/scsi/scsi_merge.c	Sun Mar 12 19:32:58 2000
@@ -324,7 +324,7 @@
 	    req->nr_segments >= SHpnt->sg_tablesize)
 		return 0;
 	req->nr_segments++;
-	q->nr_segments++;
+	q->elevator.nr_segments++;
 	return 1;
 }
 
@@ -346,7 +346,7 @@
 		return 0;
 	req->nr_hw_segments++;
 	req->nr_segments++;
-	q->nr_segments++;
+	q->elevator.nr_segments++;
 	return 1;
 }
 #else
@@ -362,7 +362,7 @@
 		 * counter.
 		 */
 		req->nr_segments++;
-		q->nr_segments++;
+		q->elevator.nr_segments++;
 		return 1;
 	} else {
 		return 0;
@@ -665,7 +665,7 @@
 			 * This one is OK.  Let it go.
 			 */
 			req->nr_segments += next->nr_segments - 1;
-			q->nr_segments--;
+			q->elevator.nr_segments--;
 #ifdef DMA_CHUNK_SIZE
 			req->nr_hw_segments += next->nr_hw_segments - 1;
 #endif
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/scsi_scan.c linux/drivers/scsi/scsi_scan.c
--- v2.3.51/linux/drivers/scsi/scsi_scan.c	Thu Mar  2 14:36:23 2000
+++ linux/drivers/scsi/scsi_scan.c	Mon Mar 13 22:15:03 2000
@@ -252,12 +252,12 @@
  *  devices to the disk driver.
  */
 void scan_scsis(struct Scsi_Host *shpnt,
-		       unchar hardcoded,
-		       unchar hchannel,
-		       unchar hid,
-		       unchar hlun)
+		       uint hardcoded,
+		       uint hchannel,
+		       uint hid,
+		       uint hlun)
 {
-	int channel;
+	uint channel;
 	int dev;
 	int lun;
 	int max_dev_lun;
@@ -299,8 +299,6 @@
 	SDpnt->host = shpnt;
 	SDpnt->online = TRUE;
 
-	scsi_build_commandblocks(SDpnt);
-
 	initialize_merge_fn(SDpnt);
 
         /*
@@ -405,7 +403,7 @@
 
       leave:
 
-	{			/* Unchain SCpnt from host_queue */
+	{			/* Unchain SRpnt from host_queue */
 		Scsi_Device *prev, *next;
 		Scsi_Device *dqptr;
 
@@ -423,8 +421,6 @@
 		}
 	}
 
-	scsi_release_commandblocks(SDpnt);
-
 	/* Last device block does not exist.  Free memory. */
 	if (SDpnt != NULL)
 		kfree((char *) SDpnt);
@@ -460,7 +456,7 @@
 	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
 	struct Scsi_Device_Template *sdtpnt;
 	Scsi_Device *SDtail, *SDpnt = *SDpnt2;
-	Scsi_Cmnd * SCpnt;
+	Scsi_Request * SRpnt;
 	int bflags, type = -1;
 	static int ghost_channel=-1, ghost_dev=-1;
 	int org_lun = lun;
@@ -472,6 +468,7 @@
 	SDpnt->channel = channel;
 	SDpnt->online = TRUE;
 
+	scsi_build_commandblocks(SDpnt);
  
 	if ((channel == ghost_channel) && (dev == ghost_dev) && (lun == 1)) {
 		SDpnt->lun = 0;
@@ -496,37 +493,32 @@
 	scsi_cmd[1] = lun << 5;
 	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
 
-	SCpnt = scsi_allocate_device(SDpnt, 0, 0);
+	SRpnt = scsi_allocate_request(SDpnt);
 
-	SCpnt->host = SDpnt->host;
-	SCpnt->device = SDpnt;
-	SCpnt->target = SDpnt->id;
-	SCpnt->lun = SDpnt->lun;
-	SCpnt->channel = SDpnt->channel;
-	SCpnt->sc_data_direction = SCSI_DATA_NONE;
+	SRpnt->sr_data_direction = SCSI_DATA_NONE;
 
-	scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+	scsi_wait_req (SRpnt, (void *) scsi_cmd,
 	          (void *) NULL,
 	          0, SCSI_TIMEOUT + 4 * HZ, 5);
 
 	SCSI_LOG_SCAN_BUS(3, printk("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
-				    dev, lun, SCpnt->result));
-	SCSI_LOG_SCAN_BUS(3, print_driverbyte(SCpnt->result));
-	SCSI_LOG_SCAN_BUS(3, print_hostbyte(SCpnt->result));
+				    dev, lun, SRpnt->sr_result));
+	SCSI_LOG_SCAN_BUS(3, print_driverbyte(SRpnt->sr_result));
+	SCSI_LOG_SCAN_BUS(3, print_hostbyte(SRpnt->sr_result));
 	SCSI_LOG_SCAN_BUS(3, printk("\n"));
 
-	if (SCpnt->result) {
-		if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
-		     (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
-		    ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
-			if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
-			    ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
-			    ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
-				scsi_release_command(SCpnt);
+	if (SRpnt->sr_result) {
+		if (((driver_byte(SRpnt->sr_result) & DRIVER_SENSE) ||
+		     (status_byte(SRpnt->sr_result) & CHECK_CONDITION)) &&
+		    ((SRpnt->sr_sense_buffer[0] & 0x70) >> 4) == 7) {
+			if (((SRpnt->sr_sense_buffer[2] & 0xf) != NOT_READY) &&
+			    ((SRpnt->sr_sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+			    ((SRpnt->sr_sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
+				scsi_release_request(SRpnt);
 				return 1;
 			}
 		} else {
-			scsi_release_command(SCpnt);
+			scsi_release_request(SRpnt);
 			return 0;
 		}
 	}
@@ -540,18 +532,18 @@
 	scsi_cmd[3] = 0;
 	scsi_cmd[4] = 255;
 	scsi_cmd[5] = 0;
-	SCpnt->cmd_len = 0;
-	SCpnt->sc_data_direction = SCSI_DATA_READ;
+	SRpnt->sr_cmd_len = 0;
+	SRpnt->sr_data_direction = SCSI_DATA_READ;
 
-	scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+	scsi_wait_req (SRpnt, (void *) scsi_cmd,
 	          (void *) scsi_result,
 	          256, SCSI_TIMEOUT, 3);
 
 	SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n",
-		SCpnt->result ? "failed" : "successful", SCpnt->result));
+		SRpnt->sr_result ? "failed" : "successful", SRpnt->sr_result));
 
-	if (SCpnt->result) {
-		scsi_release_command(SCpnt);
+	if (SRpnt->sr_result) {
+		scsi_release_request(SRpnt);
 		return 0;	/* assume no peripheral if any sort of error */
 	}
 
@@ -560,7 +552,7 @@
 	 * are supported here or not.
 	 */
 	if ((scsi_result[0] >> 5) == 3) {
-		scsi_release_command(SCpnt);
+		scsi_release_request(SRpnt);
 		return 0;	/* assume no peripheral if any sort of error */
 	}
 
@@ -705,15 +697,15 @@
 		scsi_cmd[3] = 0;
 		scsi_cmd[4] = 0x2a;
 		scsi_cmd[5] = 0;
-		SCpnt->cmd_len = 0;
-		SCpnt->sc_data_direction = SCSI_DATA_READ;
-		scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+		SRpnt->sr_cmd_len = 0;
+		SRpnt->sr_data_direction = SCSI_DATA_READ;
+		scsi_wait_req (SRpnt, (void *) scsi_cmd,
 	        	(void *) scsi_result, 0x2a,
 	        	SCSI_TIMEOUT, 3);
 	}
 
-	scsi_release_command(SCpnt);
-	SCpnt = NULL;
+	scsi_release_request(SRpnt);
+	SRpnt = NULL;
 
 	scsi_release_commandblocks(SDpnt);
 
@@ -733,8 +725,6 @@
 	SDpnt->queue_depth = 1;
 	SDpnt->host = shpnt;
 	SDpnt->online = TRUE;
-
-	scsi_build_commandblocks(SDpnt);
 
 	/*
 	 * Register the queue for the device.  All I/O requests will come
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v2.3.51/linux/drivers/scsi/scsi_syms.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/scsi_syms.c	Mon Mar 13 22:15:03 2000
@@ -43,7 +43,6 @@
 EXPORT_SYMBOL(scsi_partsize);
 EXPORT_SYMBOL(scsi_allocate_device);
 EXPORT_SYMBOL(scsi_do_cmd);
-EXPORT_SYMBOL(scsi_wait_cmd);
 EXPORT_SYMBOL(scsi_command_size);
 EXPORT_SYMBOL(scsi_ioctl);
 EXPORT_SYMBOL(print_command);
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.3.51/linux/drivers/scsi/sd.c	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/sd.c	Mon Mar 13 22:15:03 2000
@@ -176,6 +176,8 @@
 		case BLKFLSBUF:
 		case BLKSSZGET:
 		case BLKPG:
+                case BLKELVGET:
+                case BLKELVSET:
 			return blk_ioctl(inode->i_rdev, cmd, arg);
 
 		case BLKRRPART: /* Re-read partition tables */
@@ -660,7 +662,7 @@
 	unsigned long spintime_value = 0;
 	int the_result, retries, spintime;
 	int sector_size;
-	Scsi_Cmnd *SCpnt;
+	Scsi_Request *SRpnt;
 
 	/*
 	 * Get the name of the disk, in case we need to log it somewhere.
@@ -679,7 +681,7 @@
 	 * just after a scsi bus reset.
 	 */
 
-	SCpnt = scsi_allocate_device(rscsi_disks[i].device, 1, FALSE);
+	SRpnt = scsi_allocate_request(rscsi_disks[i].device);
 
 	buffer = (unsigned char *) scsi_malloc(512);
 
@@ -694,18 +696,18 @@
 			cmd[0] = TEST_UNIT_READY;
 			cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
 			memset((void *) &cmd[2], 0, 8);
-			SCpnt->cmd_len = 0;
-			SCpnt->sense_buffer[0] = 0;
-			SCpnt->sense_buffer[2] = 0;
-			SCpnt->sc_data_direction = SCSI_DATA_READ;
+			SRpnt->sr_cmd_len = 0;
+			SRpnt->sr_sense_buffer[0] = 0;
+			SRpnt->sr_sense_buffer[2] = 0;
+			SRpnt->sr_data_direction = SCSI_DATA_READ;
 
-			scsi_wait_cmd (SCpnt, (void *) cmd, (void *) buffer,
+			scsi_wait_req (SRpnt, (void *) cmd, (void *) buffer,
 				0/*512*/, SD_TIMEOUT, MAX_RETRIES);
 
-			the_result = SCpnt->result;
+			the_result = SRpnt->sr_result;
 			retries++;
 			if (the_result == 0
-			    || SCpnt->sense_buffer[2] != UNIT_ATTENTION)
+			    || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION)
 				break;
 		}
 
@@ -716,8 +718,8 @@
 		 */
 		if( the_result != 0
 		    && ((driver_byte(the_result) & DRIVER_SENSE) != 0)
-		    && SCpnt->sense_buffer[2] == UNIT_ATTENTION
-		    && SCpnt->sense_buffer[12] == 0x3A ) {
+		    && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION
+		    && SRpnt->sr_sense_buffer[12] == 0x3A ) {
 			rscsi_disks[i].capacity = 0x1fffff;
 			sector_size = 512;
 			rscsi_disks[i].device->changed = 1;
@@ -728,7 +730,7 @@
 		/* Look for non-removable devices that return NOT_READY.
 		 * Issue command to spin up drive for these cases. */
 		if (the_result && !rscsi_disks[i].device->removable &&
-		    SCpnt->sense_buffer[2] == NOT_READY) {
+		    SRpnt->sr_sense_buffer[2] == NOT_READY) {
 			unsigned long time1;
 			if (!spintime) {
 				printk("%s: Spinning up disk...", nbuff);
@@ -737,12 +739,12 @@
 				cmd[1] |= 1;	/* Return immediately */
 				memset((void *) &cmd[2], 0, 8);
 				cmd[4] = 1;	/* Start spin cycle */
-				SCpnt->cmd_len = 0;
-				SCpnt->sense_buffer[0] = 0;
-				SCpnt->sense_buffer[2] = 0;
+				SRpnt->sr_cmd_len = 0;
+				SRpnt->sr_sense_buffer[0] = 0;
+				SRpnt->sr_sense_buffer[2] = 0;
 
-				SCpnt->sc_data_direction = SCSI_DATA_READ;
-				scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+				SRpnt->sr_data_direction = SCSI_DATA_READ;
+				scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
 					    0/*512*/, SD_TIMEOUT, MAX_RETRIES);
 			}
 			spintime = 1;
@@ -768,15 +770,15 @@
 		cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
 		memset((void *) &cmd[2], 0, 8);
 		memset((void *) buffer, 0, 8);
-		SCpnt->cmd_len = 0;
-		SCpnt->sense_buffer[0] = 0;
-		SCpnt->sense_buffer[2] = 0;
+		SRpnt->sr_cmd_len = 0;
+		SRpnt->sr_sense_buffer[0] = 0;
+		SRpnt->sr_sense_buffer[2] = 0;
 
-		SCpnt->sc_data_direction = SCSI_DATA_READ;
-		scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+		SRpnt->sr_data_direction = SCSI_DATA_READ;
+		scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
 			    8, SD_TIMEOUT, MAX_RETRIES);
 
-		the_result = SCpnt->result;
+		the_result = SRpnt->sr_result;
 		retries--;
 
 	} while (the_result && retries);
@@ -806,7 +808,7 @@
 		    );
 		if (driver_byte(the_result) & DRIVER_SENSE)
 			printk("%s : extended sense code = %1x \n",
-			       nbuff, SCpnt->sense_buffer[2] & 0xf);
+			       nbuff, SRpnt->sr_sense_buffer[2] & 0xf);
 		else
 			printk("%s : sense not available. \n", nbuff);
 
@@ -818,7 +820,7 @@
 		/* Set dirty bit for removable devices if not ready - sometimes drives
 		 * will not report this properly. */
 		if (rscsi_disks[i].device->removable &&
-		    SCpnt->sense_buffer[2] == NOT_READY)
+		    SRpnt->sr_sense_buffer[2] == NOT_READY)
 			rscsi_disks[i].device->changed = 1;
 
 	} else {
@@ -919,16 +921,16 @@
 		cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
 		cmd[2] = 1;	/* page code 1 ?? */
 		cmd[4] = 12;
-		SCpnt->cmd_len = 0;
-		SCpnt->sense_buffer[0] = 0;
-		SCpnt->sense_buffer[2] = 0;
+		SRpnt->sr_cmd_len = 0;
+		SRpnt->sr_sense_buffer[0] = 0;
+		SRpnt->sr_sense_buffer[2] = 0;
 
 		/* same code as READCAPA !! */
-		SCpnt->sc_data_direction = SCSI_DATA_READ;
-		scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+		SRpnt->sr_data_direction = SCSI_DATA_READ;
+		scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
 			    512, SD_TIMEOUT, MAX_RETRIES);
 
-		the_result = SCpnt->result;
+		the_result = SRpnt->sr_result;
 
 		if (the_result) {
 			printk("%s: test WP failed, assume Write Protected\n", nbuff);
@@ -940,12 +942,12 @@
 		}
 
 	}			/* check for write protect */
-	SCpnt->device->ten = 1;
-	SCpnt->device->remap = 1;
-	SCpnt->device->sector_size = sector_size;
+	SRpnt->sr_device->ten = 1;
+	SRpnt->sr_device->remap = 1;
+	SRpnt->sr_device->sector_size = sector_size;
 	/* Wake up a process waiting for device */
-	scsi_release_command(SCpnt);
-	SCpnt = NULL;
+	scsi_release_request(SRpnt);
+	SRpnt = NULL;
 
 	scsi_free(buffer, 512);
 	return i;
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.3.51/linux/drivers/scsi/sr.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/scsi/sr.c	Tue Mar 14 17:45:07 2000
@@ -486,7 +486,7 @@
 
 		SRpnt->sr_data_direction = SCSI_DATA_READ;
 		scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
-			      512, SR_TIMEOUT, MAX_RETRIES);
+			      8, SR_TIMEOUT, MAX_RETRIES);
 
 		the_result = SRpnt->sr_result;
 		retries--;
@@ -663,7 +663,7 @@
 
 	/* do the locking and issue the command */
 	SRpnt->sr_request.rq_dev = cdi->dev;
-	/* scsi_wait_cmd sets the command length */
+	/* scsi_wait_req sets the command length */
 	SRpnt->sr_cmd_len = 0;
 
 	SRpnt->sr_data_direction = cgc->data_direction;
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.3.51/linux/drivers/scsi/st.c	Thu Mar  2 14:36:23 2000
+++ linux/drivers/scsi/st.c	Mon Mar 13 12:32:22 2000
@@ -6,12 +6,13 @@
    Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
    Contribution and ideas from several people including (in alphabetical
    order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
-   Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
+   Michael Leodolter, Eyal Lebedinsky, Michael Schaefer, J"org Weule, and
+   Eric Youngdale.
 
    Copyright 1992 - 2000 Kai Makisara
    email Kai.Makisara@metla.fi
 
-   Last modified: Tue Feb 29 20:47:03 2000 by makisara@kai.makisara.local
+   Last modified: Mon Mar 13 21:15:29 2000 by makisara@kai.makisara.local
    Some small formal changes - aeb, 950809
 
    Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
@@ -125,14 +126,17 @@
    24 bits) */
 #define SET_DENS_AND_BLK 0x10001
 
+#define ST_DEV_ARR_LUMP  6
+static rwlock_t st_dev_arr_lock = RW_LOCK_UNLOCKED;
+
 static int st_nbr_buffers;
-static ST_buffer **st_buffers;
+static ST_buffer **st_buffers = NULL;
 static int st_buffer_size = ST_BUFFER_SIZE;
 static int st_write_threshold = ST_WRITE_THRESHOLD;
 static int st_max_buffers = ST_MAX_BUFFERS;
 static int st_max_sg_segs = ST_MAX_SG;
 
-static Scsi_Tape *scsi_tapes = NULL;
+static Scsi_Tape **scsi_tapes = NULL;
 
 static int modes_defined = FALSE;
 
@@ -161,16 +165,14 @@
 
 static int st_compression(Scsi_Tape *, int);
 
-static int find_partition(struct inode *);
-static int update_partition(struct inode *);
+static int find_partition(Scsi_Tape *);
+static int update_partition(Scsi_Tape *);
 
-static int st_int_ioctl(struct inode *inode, unsigned int cmd_in,
-			unsigned long arg);
+static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long);
 
 
-
 /* Convert the result to success code */
-static int st_chk_result(Scsi_Request * SRpnt)
+static int st_chk_result(Scsi_Tape *STp, Scsi_Request * SRpnt)
 {
 	int dev;
 	int result = SRpnt->sr_result;
@@ -184,8 +186,10 @@
 
 	if (driver_byte(result) & DRIVER_SENSE)
 		scode = sense[2] & 0x0f;
-	else
+	else {
+		sense[0] = 0;
 		scode = 0;
+	}
 
 	dev = TAPE_NR(SRpnt->sr_request.rq_dev);
         DEB(
@@ -224,8 +228,8 @@
 	    && SRpnt->sr_cmnd[0] != WRITE_FILEMARKS
 #endif
 	    ) {
-		scsi_tapes[dev].recover_count++;
-		scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
+		STp->recover_count++;
+		STp->recover_reg++;
 
                 DEB(
 		if (debugging) {
@@ -236,7 +240,7 @@
 			else
 				stp = "ioctl";
 			printk(ST_DEB_MSG "st%d: Recovered %s error (%d).\n", dev, stp,
-			       scsi_tapes[dev].recover_count);
+			       STp->recover_count);
 		} ) /* end DEB */
 
 		if ((sense[2] & 0xe0) == 0)
@@ -254,7 +258,9 @@
 	Scsi_Tape *STp;
 
 	if ((st_nbr = TAPE_NR(SCpnt->request.rq_dev)) < st_template.nr_dev) {
-		STp = &(scsi_tapes[st_nbr]);
+		read_lock(&st_dev_arr_lock);
+		STp = scsi_tapes[st_nbr];
+		read_unlock(&st_dev_arr_lock);
 		if ((STp->buffer)->writing &&
 		    (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
 		    (SCpnt->sense_buffer[2] & 0x40)) {
@@ -330,7 +336,7 @@
 	if (do_wait) {
 		down(SRpnt->sr_request.sem);
 		SRpnt->sr_request.sem = NULL;
-		(STp->buffer)->syscall_result = st_chk_result(SRpnt);
+		(STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
 	}
 	return SRpnt;
 }
@@ -354,7 +360,7 @@
 	down(&(STp->sem));
 	(STp->buffer)->last_SRpnt->sr_request.sem = NULL;
 
-	(STp->buffer)->syscall_result = st_chk_result((STp->buffer)->last_SRpnt);
+	(STp->buffer)->syscall_result = st_chk_result(STp, (STp->buffer)->last_SRpnt);
 	scsi_release_request((STp->buffer)->last_SRpnt);
 
 	if (STbuffer->writing < STbuffer->buffer_bytes)
@@ -491,15 +497,12 @@
 
 /* Flush the tape buffer. The tape will be positioned correctly unless
    seek_next is true. */
-static int flush_buffer(struct inode *inode, struct file *filp, int seek_next)
+static int flush_buffer(Scsi_Tape *STp, int seek_next)
 {
 	int backspace, result;
-	Scsi_Tape *STp;
 	ST_buffer *STbuffer;
 	ST_partstat *STps;
-	int dev = TAPE_NR(inode->i_rdev);
 
-	STp = &(scsi_tapes[dev]);
 	STbuffer = STp->buffer;
 
 	/*
@@ -538,7 +541,7 @@
 			}
 		}
 		if (!result && backspace > 0)
-			result = st_int_ioctl(inode, MTBSR, backspace);
+			result = st_int_ioctl(STp, MTBSR, backspace);
 	} else if (STps->eof == ST_FM_HIT) {
 		if (STps->drv_file >= 0)
 			STps->drv_file++;
@@ -550,11 +553,11 @@
 }
 
 /* Set the mode parameters */
-static int set_mode_densblk(struct inode *inode, Scsi_Tape * STp, ST_mode * STm)
+static int set_mode_densblk(Scsi_Tape * STp, ST_mode * STm)
 {
 	int set_it = FALSE;
 	unsigned long arg;
-	int dev = TAPE_NR(inode->i_rdev);
+	int dev = TAPE_NR(STp->devt);
 
 	if (!STp->density_changed &&
 	    STm->default_density >= 0 &&
@@ -572,7 +575,7 @@
 	} else
 		arg |= STp->block_size;
 	if (set_it &&
-	    st_int_ioctl(inode, SET_DENS_AND_BLK, arg)) {
+	    st_int_ioctl(STp, SET_DENS_AND_BLK, arg)) {
 		printk(KERN_WARNING
 		       "st%d: Can't set default block size to %d bytes and density %x.\n",
 		       dev, STm->default_blksize, STm->default_density);
@@ -597,22 +600,26 @@
 	int dev = TAPE_NR(inode->i_rdev);
 	int mode = TAPE_MODE(inode->i_rdev);
 
-	if (dev >= st_template.dev_max || !scsi_tapes[dev].device)
+	read_lock(&st_dev_arr_lock);
+	STp = scsi_tapes[dev];
+	if (dev >= st_template.dev_max || STp == NULL) {
+		read_unlock(&st_dev_arr_lock);
 		return (-ENXIO);
+	}
+	read_unlock(&st_dev_arr_lock);
 
-	if (!scsi_block_when_processing_errors(scsi_tapes[dev].device)) {
+	if (!scsi_block_when_processing_errors(STp->device)) {
 		return -ENXIO;
 	}
-	STp = &(scsi_tapes[dev]);
 	if (STp->in_use) {
 		DEB( printk(ST_DEB_MSG "st%d: Device already in use.\n", dev); )
 		return (-EBUSY);
 	}
 	STp->in_use = 1;
-	STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+	STp->rew_at_close = STp->autorew_dev = (MINOR(inode->i_rdev) & 0x80) == 0;
 
-	if (scsi_tapes[dev].device->host->hostt->module)
-		__MOD_INC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+	if (STp->device->host->hostt->module)
+		__MOD_INC_USE_COUNT(STp->device->host->hostt->module);
 	if (st_template.module)
 		__MOD_INC_USE_COUNT(st_template.module);
 
@@ -626,10 +633,14 @@
 
 	/* Allocate a buffer for this user */
 	need_dma_buffer = STp->restr_dma;
+	read_lock(&st_dev_arr_lock);
 	for (i = 0; i < st_nbr_buffers; i++)
 		if (!st_buffers[i]->in_use &&
-		    (!need_dma_buffer || st_buffers[i]->dma))
+		    (!need_dma_buffer || st_buffers[i]->dma)) {
+			STp->buffer = st_buffers[i];
 			break;
+		}
+	read_unlock(&st_dev_arr_lock);
 	if (i >= st_nbr_buffers) {
 		STp->buffer = new_tape_buffer(FALSE, need_dma_buffer);
 		if (STp->buffer == NULL) {
@@ -637,8 +648,8 @@
 			retval = (-EBUSY);
 			goto err_out;
 		}
-	} else
-		STp->buffer = st_buffers[i];
+	}
+
 	(STp->buffer)->in_use = 1;
 	(STp->buffer)->writing = 0;
 	(STp->buffer)->syscall_result = 0;
@@ -824,7 +835,7 @@
 		   partition support has been enabled. */
                 DEBC(printk(ST_DEB_MSG
                             "st%d: Updating partition number in status.\n", dev));
-		if ((STp->partition = find_partition(inode)) < 0) {
+		if ((STp->partition = find_partition(STp)) < 0) {
 			retval = STp->partition;
 			goto err_out;
 		}
@@ -836,11 +847,11 @@
 		STp->density_changed = STp->blksize_changed = FALSE;
 		STp->compression_changed = FALSE;
 		if (!(STm->defaults_for_writes) &&
-		    (retval = set_mode_densblk(inode, STp, STm)) < 0)
+		    (retval = set_mode_densblk(STp, STm)) < 0)
 		    goto err_out;
 
 		if (STp->default_drvbuffer != 0xff) {
-			if (st_int_ioctl(inode, MTSETDRVBUFFER, STp->default_drvbuffer))
+			if (st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer))
 				printk(KERN_WARNING
                                        "st%d: Can't set default drive buffering to %d.\n",
 				       dev, STp->default_drvbuffer);
@@ -855,8 +866,8 @@
 		STp->buffer = NULL;
 	}
 	STp->in_use = 0;
-	if (scsi_tapes[dev].device->host->hostt->module)
-	    __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+	if (STp->device->host->hostt->module)
+	    __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
 	if (st_template.module)
 	    __MOD_DEC_USE_COUNT(st_template.module);
 	return retval;
@@ -882,7 +893,9 @@
 		return 0;
 
 	dev = TAPE_NR(devt);
-	STp = &(scsi_tapes[dev]);
+	read_lock(&st_dev_arr_lock);
+	STp = scsi_tapes[dev];
+	read_unlock(&st_dev_arr_lock);
 	STm = &(STp->modes[STp->current_mode]);
 	STps = &(STp->ps[STp->partition]);
 
@@ -893,7 +906,7 @@
 	}
 
 	if (STp->can_partitions &&
-	    (result2 = update_partition(inode)) < 0) {
+	    (result2 = update_partition(STp)) < 0) {
                 DEBC(printk(ST_DEB_MSG
                                "st%d: update_partition at close failed.\n", dev));
 		if (result == 0)
@@ -950,7 +963,7 @@
 		STps = &(STp->ps[STp->partition]);
 		if (!STm->sysv || STps->rw != ST_READING) {
 			if (STp->can_bsr)
-				result = flush_buffer(inode, filp, 0);
+				result = flush_buffer(STp, 0);
 			else if (STps->eof == ST_FM_HIT) {
 				result = cross_eof(STp, FALSE);
 				if (result) {
@@ -973,7 +986,7 @@
 
       out:
 	if (STp->rew_at_close) {
-		result2 = st_int_ioctl(inode, MTREW, 1);
+		result2 = st_int_ioctl(STp, MTREW, 1);
 		if (result == 0)
 			result = result2;
 	}
@@ -991,10 +1004,12 @@
 	int dev;
 
 	dev = TAPE_NR(devt);
-	STp = &(scsi_tapes[dev]);
+	read_lock(&st_dev_arr_lock);
+	STp = scsi_tapes[dev];
+	read_unlock(&st_dev_arr_lock);
 
 	if (STp->door_locked == ST_LOCKED_AUTO)
-		st_int_ioctl(inode, MTUNLOCK, 0);
+		st_int_ioctl(STp, MTUNLOCK, 0);
 
 	if (STp->buffer != NULL) {
 		normalize_buffer(STp->buffer);
@@ -1002,8 +1017,8 @@
 	}
 
 	STp->in_use = 0;
-	if (scsi_tapes[dev].device->host->hostt->module)
-		__MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+	if (STp->device->host->hostt->module)
+		__MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
 	if (st_template.module)
 		__MOD_DEC_USE_COUNT(st_template.module);
 
@@ -1028,7 +1043,9 @@
 	ST_partstat *STps;
 	int dev = TAPE_NR(inode->i_rdev);
 
-	STp = &(scsi_tapes[dev]);
+	read_lock(&st_dev_arr_lock);
+	STp = scsi_tapes[dev];
+	read_unlock(&st_dev_arr_lock);
 
 	/*
 	 * If we are in the middle of error recovery, don't let anyone
@@ -1079,7 +1096,7 @@
 	}
 
 	if (STp->can_partitions &&
-	    (retval = update_partition(inode)) < 0)
+	    (retval = update_partition(STp)) < 0)
 		return retval;
 	STps = &(STp->ps[STp->partition]);
 
@@ -1092,17 +1109,17 @@
 		return (-EOVERFLOW);
 
 	if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
-	    !st_int_ioctl(inode, MTLOCK, 0))
+	    !st_int_ioctl(STp, MTLOCK, 0))
 		STp->door_locked = ST_LOCKED_AUTO;
 
 	if (STps->rw == ST_READING) {
-		retval = flush_buffer(inode, filp, 0);
+		retval = flush_buffer(STp, 0);
 		if (retval)
 			return retval;
 		STps->rw = ST_WRITING;
 	} else if (STps->rw != ST_WRITING &&
 		   STps->drv_file == 0 && STps->drv_block == 0) {
-		if ((retval = set_mode_densblk(inode, STp, STm)) < 0)
+		if ((retval = set_mode_densblk(STp, STm)) < 0)
 			return retval;
 		if (STm->default_compression != ST_DONT_TOUCH &&
 		    !(STp->compression_changed)) {
@@ -1328,21 +1345,19 @@
 /* Read data from the tape. Returns zero in the normal case, one if the
    eof status has changed, and the negative error code in case of a
    fatal error. Otherwise updates the buffer and the eof state. */
-static long read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt)
+static long read_tape(Scsi_Tape *STp, long count, Scsi_Request ** aSRpnt)
 {
 	int transfer, blks, bytes;
 	static unsigned char cmd[MAX_COMMAND_SIZE];
 	Scsi_Request *SRpnt;
-	Scsi_Tape *STp;
 	ST_mode *STm;
 	ST_partstat *STps;
-	int dev = TAPE_NR(inode->i_rdev);
+	int dev = TAPE_NR(STp->devt);
 	int retval = 0;
 
 	if (count == 0)
 		return 0;
 
-	STp = &(scsi_tapes[dev]);
 	STm = &(STp->modes[STp->current_mode]);
 	STps = &(STp->ps[STp->partition]);
 	if (STps->eof == ST_FM_HIT)
@@ -1418,7 +1433,7 @@
 							printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
 							if (STps->drv_block >= 0)
 								STps->drv_block += blks - transfer + 1;
-							st_int_ioctl(inode, MTBSR, 1);
+							st_int_ioctl(STp, MTBSR, 1);
 							return (-EIO);
 						}
 						/* We have some data, deliver it */
@@ -1429,7 +1444,7 @@
                                                             dev, count, (STp->buffer)->buffer_bytes));
 						if (STps->drv_block >= 0)
 							STps->drv_block += 1;
-						if (st_int_ioctl(inode, MTBSR, 1))
+						if (st_int_ioctl(STp, MTBSR, 1))
 							return (-EIO);
 					}
 				} else if (SRpnt->sr_sense_buffer[2] & 0x80) {	/* FM overrides EOM */
@@ -1509,7 +1524,9 @@
 	ST_partstat *STps;
 	int dev = TAPE_NR(inode->i_rdev);
 
-	STp = &(scsi_tapes[dev]);
+	read_lock(&st_dev_arr_lock);
+	STp = scsi_tapes[dev];
+	read_unlock(&st_dev_arr_lock);
 
 	/*
 	 * If we are in the middle of error recovery, don't let anyone
@@ -1542,7 +1559,7 @@
 	} ) /* end DEB */
 
 	if (STp->can_partitions &&
-	    (total = update_partition(inode)) < 0)
+	    (total = update_partition(STp)) < 0)
 		return total;
 
 	if (STp->block_size == 0 &&
@@ -1555,12 +1572,12 @@
 		return (-EIO);	/* Read must be integral number of blocks */
 
 	if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
-	    !st_int_ioctl(inode, MTLOCK, 0))
+	    !st_int_ioctl(STp, MTLOCK, 0))
 		STp->door_locked = ST_LOCKED_AUTO;
 
 	STps = &(STp->ps[STp->partition]);
 	if (STps->rw == ST_WRITING) {
-		transfer = flush_buffer(inode, filp, 0);
+		transfer = flush_buffer(STp, 0);
 		if (transfer)
 			return transfer;
 		STps->rw = ST_READING;
@@ -1596,7 +1613,7 @@
 
 		/* Get new data if the buffer is empty */
 		if ((STp->buffer)->buffer_bytes == 0) {
-			special = read_tape(inode, count - total, &SRpnt);
+			special = read_tape(STp, count - total, &SRpnt);
 			if (special < 0) {	/* No need to continue read */
 				if (SRpnt != NULL) {
 					scsi_release_request(SRpnt);
@@ -1684,15 +1701,13 @@
 }
 
 
-static int st_set_options(struct inode *inode, long options)
+static int st_set_options(Scsi_Tape *STp, long options)
 {
 	int value;
 	long code;
-	Scsi_Tape *STp;
 	ST_mode *STm;
-	int dev = TAPE_NR(inode->i_rdev);
+	int dev = TAPE_NR(STp->devt);
 
-	STp = &(scsi_tapes[dev]);
 	STm = &(STp->modes[STp->current_mode]);
 	if (!STm->defined) {
 		memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
@@ -1823,95 +1838,146 @@
 	return 0;
 }
 
+#define MODE_HEADER_LENGTH  4
+
+/* Mode header and page byte offsets */
+#define MH_OFF_DATA_LENGTH     0
+#define MH_OFF_MEDIUM_TYPE     1
+#define MH_OFF_DEV_SPECIFIC    2
+#define MH_OFF_BDESCS_LENGTH   3
+#define MP_OFF_PAGE_NBR        0
+#define MP_OFF_PAGE_LENGTH     1
+
+/* Mode header and page bit masks */
+#define MH_BIT_WP              0x80
+#define MP_MSK_PAGE_NBR        0x3f
+
+/* Don't return block descriptors */
+#define MODE_SENSE_OMIT_BDESCS 0x08
+
+#define MODE_SELECT_PAGE_FORMAT 0x10
+
+/* Read a mode page into the tape buffer. The block descriptors are included
+   if incl_block_descs is true. */
+static int read_mode_page(Scsi_Tape *STp, int page, int omit_block_descs)
+{
+	unsigned char cmd[MAX_COMMAND_SIZE];
+	Scsi_Request *SRpnt = NULL;
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = MODE_SENSE;
+	if (omit_block_descs)
+		cmd[1] = MODE_SENSE_OMIT_BDESCS;
+	cmd[2] = page;
+	cmd[4] = 255;
+
+	SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ,
+			   STp->timeout, 0, TRUE);
+	if (SRpnt == NULL)
+		return (STp->buffer)->syscall_result;
+
+	scsi_release_request(SRpnt);
+
+	return (STp->buffer)->syscall_result;
+}
+
+
+/* Send the mode page in the tape buffer to the drive. Assumes that the mode data
+   in the buffer is correctly formatted. */
+static int write_mode_page(Scsi_Tape *STp, int page)
+{
+	int pgo;
+	unsigned char cmd[MAX_COMMAND_SIZE];
+	Scsi_Request *SRpnt = NULL;
+
+	memset(cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = MODE_SELECT;
+	cmd[1] = MODE_SELECT_PAGE_FORMAT;
+	pgo = MODE_HEADER_LENGTH + (STp->buffer)->b_data[MH_OFF_BDESCS_LENGTH];
+	cmd[4] = pgo + (STp->buffer)->b_data[pgo + MP_OFF_PAGE_LENGTH] + 2;
+
+	/* Clear reserved fields */
+	(STp->buffer)->b_data[MH_OFF_DATA_LENGTH] = 0;
+	(STp->buffer)->b_data[MH_OFF_MEDIUM_TYPE] = 0;
+	(STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
+	(STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
+
+	SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
+			   STp->timeout, 0, TRUE);
+	if (SRpnt == NULL)
+		return (STp->buffer)->syscall_result;
+
+	scsi_release_request(SRpnt);
+
+	return (STp->buffer)->syscall_result;
+}
+
 
 #define COMPRESSION_PAGE        0x0f
 #define COMPRESSION_PAGE_LENGTH 16
 
-#define MODE_HEADER_LENGTH  4
+#define CP_OFF_DCE_DCC          2
 
 #define DCE_MASK  0x80
 #define DCC_MASK  0x40
 #define RED_MASK  0x60
 
 
-/* Control the compression with mode page 15. Algorithm not changed if zero. */
+/* Control the compression with mode page 15. Algorithm not changed if zero.
+
+   The block descriptors are read and written because Sony SDT-7000 does not
+   work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
+   Including block descriptors should not cause any harm to other drives. */
+
 static int st_compression(Scsi_Tape * STp, int state)
 {
-	int dev;
-	unsigned char cmd[MAX_COMMAND_SIZE];
-	Scsi_Request *SRpnt = NULL;
+	int retval;
+	int mpoffs;  /* Offset to mode page start */
+	unsigned char *b_data = (STp->buffer)->b_data;
+	DEB( int dev = TAPE_NR(STp->devt); )
 
 	if (STp->ready != ST_READY)
 		return (-EIO);
 
 	/* Read the current page contents */
-	memset(cmd, 0, MAX_COMMAND_SIZE);
-	cmd[0] = MODE_SENSE;
-	cmd[1] = 8;
-	cmd[2] = COMPRESSION_PAGE;
-	cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
-	SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ,
-			   STp->timeout, 0, TRUE);
-	if (SRpnt == NULL)
-		return (STp->buffer)->syscall_result;
-
-	dev = TAPE_NR(SRpnt->sr_request.rq_dev);
-
-	if ((STp->buffer)->syscall_result != 0) {
+	retval = read_mode_page(STp, COMPRESSION_PAGE, FALSE);
+	if (retval) {
                 DEBC(printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n",
                             dev));
-		scsi_release_request(SRpnt);
-		SRpnt = NULL;
 		return (-EIO);
 	}
+
+	mpoffs = MODE_HEADER_LENGTH + b_data[MH_OFF_BDESCS_LENGTH];
         DEBC(printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev,
-                    ((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCE_MASK ? 1 : 0)));
+                    (b_data[mpoffs + CP_OFF_DCE_DCC] & DCE_MASK ? 1 : 0)));
 
 	/* Check if compression can be changed */
-	if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) {
+	if ((b_data[mpoffs + 2] & DCC_MASK) == 0) {
                 DEBC(printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev));
-		scsi_release_request(SRpnt);
-		SRpnt = NULL;
 		return (-EIO);
 	}
 
 	/* Do the change */
 	if (state)
-		(STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] |= DCE_MASK;
+		b_data[mpoffs + CP_OFF_DCE_DCC] |= DCE_MASK;
 	else
-		(STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] &= ~DCE_MASK;
-
-	memset(cmd, 0, MAX_COMMAND_SIZE);
-	cmd[0] = MODE_SELECT;
-	cmd[1] = 0x10;
-	cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
-	(STp->buffer)->b_data[0] = 0;	/* Reserved data length */
-	(STp->buffer)->b_data[1] = 0;	/* Reserved media type byte */
-	(STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
-	SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
-			   STp->timeout, 0, TRUE);
+		b_data[mpoffs + CP_OFF_DCE_DCC] &= ~DCE_MASK;
 
-	if ((STp->buffer)->syscall_result != 0) {
+	retval = write_mode_page(STp, COMPRESSION_PAGE);
+	if (retval) {
                 DEBC(printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev));
-		scsi_release_request(SRpnt);
-		SRpnt = NULL;
 		return (-EIO);
 	}
         DEBC(printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n",
 		       dev, state));
 
-	scsi_release_request(SRpnt);
-	SRpnt = NULL;
 	STp->compression_changed = TRUE;
 	return 0;
 }
 
 
 /* Internal ioctl function */
-static int st_int_ioctl(struct inode *inode,
-			unsigned int cmd_in, unsigned long arg)
+static int st_int_ioctl(Scsi_Tape *STp, unsigned int cmd_in, unsigned long arg)
 {
 	int timeout;
 	long ltmp;
@@ -1919,13 +1985,11 @@
 	int chg_eof = TRUE;
 	unsigned char cmd[MAX_COMMAND_SIZE];
 	Scsi_Request *SRpnt;
-	Scsi_Tape *STp;
 	ST_partstat *STps;
 	int fileno, blkno, at_sm, undone;
 	int datalen = 0, direction = SCSI_DATA_NONE;
-	int dev = TAPE_NR(inode->i_rdev);
+	int dev = TAPE_NR(STp->devt);
 
-	STp = &(scsi_tapes[dev]);
 	if (STp->ready != ST_READY && cmd_in != MTLOAD) {
 		if (STp->ready == ST_NO_TAPE)
 			return (-ENOMEDIUM);
@@ -2120,7 +2184,7 @@
 	case MTEOM:
 		if (!STp->fast_mteom) {
 			/* space to the end of tape */
-			ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff);
+			ioctl_result = st_int_ioctl(STp, MTFSF, 0x3fff);
 			fileno = STps->drv_file;
 			if (STps->eof >= ST_EOD_1)
 				return 0;
@@ -2247,9 +2311,9 @@
 			STp->door_locked = ST_UNLOCKED;
 
 		if (cmd_in == MTBSFM)
-			ioctl_result = st_int_ioctl(inode, MTFSF, 1);
+			ioctl_result = st_int_ioctl(STp, MTFSF, 1);
 		else if (cmd_in == MTFSFM)
-			ioctl_result = st_int_ioctl(inode, MTBSF, 1);
+			ioctl_result = st_int_ioctl(STp, MTBSF, 1);
 
 		if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
 			STp->block_size = arg & MT_ST_BLKSIZE_MASK;
@@ -2275,7 +2339,7 @@
 		if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
 			STp->rew_at_close = 0;
 		else if (cmd_in == MTLOAD) {
-			STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+			STp->rew_at_close = STp->autorew_dev;
 			for (i = 0; i < ST_NBR_PARTITIONS; i++) {
 				STp->ps[i].rw = ST_IDLE;
 				STp->ps[i].last_block_valid = FALSE;
@@ -2368,16 +2432,14 @@
 /* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
    structure. */
 
-static int get_location(struct inode *inode, unsigned int *block, int *partition,
+static int get_location(Scsi_Tape *STp, unsigned int *block, int *partition,
 			int logical)
 {
-	Scsi_Tape *STp;
-	int dev = TAPE_NR(inode->i_rdev);
 	int result;
 	unsigned char scmd[MAX_COMMAND_SIZE];
 	Scsi_Request *SRpnt;
+	DEB( int dev = TAPE_NR(STp->devt); )
 
-	STp = &(scsi_tapes[dev]);
 	if (STp->ready != ST_READY)
 		return (-EIO);
 
@@ -2430,19 +2492,17 @@
 
 /* Set the tape block and partition. Negative partition means that only the
    block should be set in vendor specific way. */
-static int set_location(struct inode *inode, unsigned int block, int partition,
+static int set_location(Scsi_Tape *STp, unsigned int block, int partition,
 			int logical)
 {
-	Scsi_Tape *STp;
 	ST_partstat *STps;
-	int dev = TAPE_NR(inode->i_rdev);
 	int result, p;
 	unsigned int blk;
 	int timeout;
 	unsigned char scmd[MAX_COMMAND_SIZE];
 	Scsi_Request *SRpnt;
+	DEB( int dev = TAPE_NR(STp->devt); )
 
-	STp = &(scsi_tapes[dev]);
 	if (STp->ready != ST_READY)
 		return (-EIO);
 	timeout = STp->long_timeout;
@@ -2458,7 +2518,7 @@
 	    partition >= ST_NBR_PARTITIONS)
 		return (-EINVAL);
 	if (partition != STp->partition) {
-		if (get_location(inode, &blk, &p, 1))
+		if (get_location(STp, &blk, &p, 1))
 			STps->last_block_valid = FALSE;
 		else {
 			STps->last_block_valid = TRUE;
@@ -2508,7 +2568,7 @@
 		result = (-EIO);
 		if (STp->can_partitions &&
 		    (STp->device)->scsi_level >= SCSI_2 &&
-		    (p = find_partition(inode)) >= 0)
+		    (p = find_partition(STp)) >= 0)
 			STp->partition = p;
 	} else {
 		if (STp->can_partitions) {
@@ -2535,12 +2595,12 @@
 
 /* Find the current partition number for the drive status. Called from open and
    returns either partition number of negative error code. */
-static int find_partition(struct inode *inode)
+static int find_partition(Scsi_Tape *STp)
 {
 	int i, partition;
 	unsigned int block;
 
-	if ((i = get_location(inode, &block, &partition, 1)) < 0)
+	if ((i = get_location(STp, &block, &partition, 1)) < 0)
 		return i;
 	if (partition >= ST_NBR_PARTITIONS)
 		return (-EIO);
@@ -2549,60 +2609,52 @@
 
 
 /* Change the partition if necessary */
-static int update_partition(struct inode *inode)
+static int update_partition(Scsi_Tape *STp)
 {
-	int dev = TAPE_NR(inode->i_rdev);
-	Scsi_Tape *STp;
 	ST_partstat *STps;
 
-	STp = &(scsi_tapes[dev]);
 	if (STp->partition == STp->new_partition)
 		return 0;
 	STps = &(STp->ps[STp->new_partition]);
 	if (!STps->last_block_valid)
 		STps->last_block_visited = 0;
-	return set_location(inode, STps->last_block_visited, STp->new_partition, 1);
+	return set_location(STp, STps->last_block_visited, STp->new_partition, 1);
 }
 
 /* Functions for reading and writing the medium partition mode page. These
    seem to work with Wangtek 6200HS and HP C1533A. */
 
 #define PART_PAGE   0x11
-#define PART_PAGE_LENGTH 10
+#define PART_PAGE_FIXED_LENGTH 8
+
+#define PP_OFF_MAX_ADD_PARTS   2
+#define PP_OFF_NBR_ADD_PARTS   3
+#define PP_OFF_FLAGS           4
+#define PP_OFF_PART_UNITS      6
+#define PP_OFF_RESERVED        7
+
+#define PP_BIT_IDP             0x20
+#define PP_MSK_PSUM_MB         0x10
 
 /* Get the number of partitions on the tape. As a side effect reads the
    mode page into the tape buffer. */
-static int nbr_partitions(struct inode *inode)
+static int nbr_partitions(Scsi_Tape *STp)
 {
-	int dev = TAPE_NR(inode->i_rdev), result;
-	Scsi_Tape *STp;
-	Scsi_Request *SRpnt = NULL;
-	unsigned char cmd[MAX_COMMAND_SIZE];
+	int result;
+	DEB( int dev = TAPE_NR(STp->devt) );
 
-	STp = &(scsi_tapes[dev]);
 	if (STp->ready != ST_READY)
 		return (-EIO);
 
-	memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
-	cmd[0] = MODE_SENSE;
-	cmd[1] = 8;		/* Page format */
-	cmd[2] = PART_PAGE;
-	cmd[4] = 200;
-
-	SRpnt = st_do_scsi(SRpnt, STp, cmd, 200, SCSI_DATA_READ, STp->timeout,
-			   MAX_READY_RETRIES, TRUE);
-	if (SRpnt == NULL)
-		return (STp->buffer)->syscall_result;
-
-	scsi_release_request(SRpnt);
-	SRpnt = NULL;
+	result = read_mode_page(STp, PART_PAGE, TRUE);
 
-	if ((STp->buffer)->syscall_result != 0) {
+	if (result) {
                 DEBC(printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n",
                             dev));
 		result = (-EIO);
 	} else {
-		result = (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] + 1;
+		result = (STp->buffer)->b_data[MODE_HEADER_LENGTH +
+					      PP_OFF_NBR_ADD_PARTS] + 1;
                 DEBC(printk(ST_DEB_MSG "st%d: Number of partitions %d.\n", dev, result));
 	}
 
@@ -2611,62 +2663,69 @@
 
 
 /* Partition the tape into two partitions if size > 0 or one partition if
-   size == 0 */
-static int partition_tape(struct inode *inode, int size)
+   size == 0.
+
+   The block descriptors are read and written because Sony SDT-7000 does not
+   work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
+
+   My HP C1533A drive returns only one partition size field. This is used to
+   set the size of partition 1. There is no size field for the default partition.
+   Michael Schaefer's Sony SDT-7000 returns two descriptors and the second is
+   used to set the size of partition 1 (this is what the SCSI-3 standard specifies).
+   The following algorithm is used to accomodate both drives: if the number of
+   partition size fields is greater than the maximum number of additional partitions
+   in the mode page, the second field is used. Otherwise the first field is used.
+ */
+static int partition_tape(Scsi_Tape *STp, int size)
 {
-	int dev = TAPE_NR(inode->i_rdev), result;
-	int length;
-	Scsi_Tape *STp;
-	Scsi_Request *SRpnt = NULL;
-	unsigned char cmd[MAX_COMMAND_SIZE], *bp;
+	int dev = TAPE_NR(STp->devt), result;
+	int pgo, psd_cnt, psdo;
+	unsigned char *bp;
 
-	if ((result = nbr_partitions(inode)) < 0)
+	result = read_mode_page(STp, PART_PAGE, FALSE);
+	if (result) {
+		DEBC(printk(ST_DEB_MSG "st%d: Can't read partition mode page.\n", dev));
 		return result;
-	STp = &(scsi_tapes[dev]);
-
+	}
 	/* The mode page is in the buffer. Let's modify it and write it. */
-	bp = &((STp->buffer)->b_data[0]);
+	bp = (STp->buffer)->b_data;
+	pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
+	DEBC(printk(ST_DEB_MSG "st%d: Partition page length is %d bytes.\n",
+		    dev, bp[pgo + MP_OFF_PAGE_LENGTH] + 2));
+
+	psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 2;
+	psdo = pgo + PART_PAGE_FIXED_LENGTH;
+	if (psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS]) {
+		bp[psdo] = bp[psdo + 1] = 0xff;  /* Rest of the tape */
+		psdo += 2;
+	}
+	memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2);
+
+	DEBC(printk("st%d: psd_cnt %d, max.parts %d, nbr_parts %d\n", dev,
+		    psd_cnt, bp[pgo + PP_OFF_MAX_ADD_PARTS],
+		    bp[pgo + PP_OFF_NBR_ADD_PARTS]));
+
 	if (size <= 0) {
-		length = 8;
-		bp[MODE_HEADER_LENGTH + 3] = 0;
+		bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0;
                 DEBC(printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n",
                             dev));
 	} else {
-		length = 10;
-		bp[MODE_HEADER_LENGTH + 3] = 1;
-		bp[MODE_HEADER_LENGTH + 8] = (size >> 8) & 0xff;
-		bp[MODE_HEADER_LENGTH + 9] = size & 0xff;
+		bp[psdo] = (size >> 8) & 0xff;
+		bp[psdo + 1] = size & 0xff;
+		bp[pgo + 3] = 1;
                 DEBC(printk(ST_DEB_MSG
-                            "st%d: Formatting tape with two partition (1 = %d MB).\n",
+                            "st%d: Formatting tape with two partitions (1 = %d MB).\n",
                             dev, size));
 	}
-	bp[MODE_HEADER_LENGTH + 6] = 0;
-	bp[MODE_HEADER_LENGTH + 7] = 0;
-	bp[MODE_HEADER_LENGTH + 4] = 0x30;	/* IDP | PSUM = MB */
-
-	bp[0] = 0;
-	bp[1] = 0;
-	bp[MODE_HEADER_LENGTH] &= 0x3f;
-	bp[MODE_HEADER_LENGTH + 1] = length - 2;
-
-	memset(cmd, 0, MAX_COMMAND_SIZE);
-	cmd[0] = MODE_SELECT;
-	cmd[1] = 0x10;
-	cmd[4] = length + MODE_HEADER_LENGTH;
-
-	SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
-			   STp->long_timeout, MAX_READY_RETRIES, TRUE);
-	if (SRpnt == NULL)
-		return (STp->buffer)->syscall_result;
-
-	scsi_release_request(SRpnt);
-	SRpnt = NULL;
+	bp[pgo + PP_OFF_PART_UNITS] = 0;
+	bp[pgo + PP_OFF_RESERVED] = 0;
+	bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | PP_MSK_PSUM_MB;
 
-	if ((STp->buffer)->syscall_result != 0) {
+	result = write_mode_page(STp, PART_PAGE);
+	if (result) {
 		printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev);
 		result = (-EIO);
-	} else
-		result = 0;
+	}
 
 	return result;
 }
@@ -2679,14 +2738,15 @@
 {
 	int i, cmd_nr, cmd_type, bt;
 	unsigned int blk;
-	struct mtop mtc;
-	struct mtpos mt_pos;
 	Scsi_Tape *STp;
 	ST_mode *STm;
 	ST_partstat *STps;
 	int dev = TAPE_NR(inode->i_rdev);
 
-	STp = &(scsi_tapes[dev]);
+	read_lock(&st_dev_arr_lock);
+	STp = scsi_tapes[dev];
+	read_unlock(&st_dev_arr_lock);
+
         DEB(
 	if (debugging && !STp->in_use) {
 		printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
@@ -2709,6 +2769,8 @@
 	cmd_nr = _IOC_NR(cmd_in);
 
 	if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
+		struct mtop mtc;
+
 		if (_IOC_SIZE(cmd_in) != sizeof(mtc))
 			return (-EINVAL);
 
@@ -2751,7 +2813,7 @@
 				    mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
 				    mtc.mt_op == MTCOMPRESSION;
 			}
-			i = flush_buffer(inode, file, i);
+			i = flush_buffer(STp, i);
 			if (i < 0)
 				return i;
 		} else {
@@ -2770,7 +2832,7 @@
 			STp->device->was_reset = 0;
 			if (STp->door_locked != ST_UNLOCKED &&
 			    STp->door_locked != ST_LOCK_FAILS) {
-				if (st_int_ioctl(inode, MTLOCK, 0)) {
+				if (st_int_ioctl(STp, MTLOCK, 0)) {
 					printk(KERN_NOTICE
                                                "st%d: Could not relock door after bus reset.\n",
 					       dev);
@@ -2785,18 +2847,18 @@
 			STps->rw = ST_IDLE;	/* Prevent automatic WEOF and fsf */
 
 		if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
-			st_int_ioctl(inode, MTUNLOCK, 0);	/* Ignore result! */
+			st_int_ioctl(STp, MTUNLOCK, 0);	/* Ignore result! */
 
 		if (mtc.mt_op == MTSETDRVBUFFER &&
 		    (mtc.mt_count & MT_ST_OPTIONS) != 0)
-			return st_set_options(inode, mtc.mt_count);
+			return st_set_options(STp, mtc.mt_count);
 
 		if (mtc.mt_op == MTSETPART) {
 			if (!STp->can_partitions ||
 			    mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
 				return (-EINVAL);
 			if (mtc.mt_count >= STp->nbr_partitions &&
-			(STp->nbr_partitions = nbr_partitions(inode)) < 0)
+			(STp->nbr_partitions = nbr_partitions(STp)) < 0)
 				return (-EIO);
 			if (mtc.mt_count >= STp->nbr_partitions)
 				return (-EINVAL);
@@ -2807,8 +2869,8 @@
 		if (mtc.mt_op == MTMKPART) {
 			if (!STp->can_partitions)
 				return (-EINVAL);
-			if ((i = st_int_ioctl(inode, MTREW, 0)) < 0 ||
-			    (i = partition_tape(inode, mtc.mt_count)) < 0)
+			if ((i = st_int_ioctl(STp, MTREW, 0)) < 0 ||
+			    (i = partition_tape(STp, mtc.mt_count)) < 0)
 				return i;
 			for (i = 0; i < ST_NBR_PARTITIONS; i++) {
 				STp->ps[i].rw = ST_IDLE;
@@ -2822,93 +2884,97 @@
 		}
 
 		if (mtc.mt_op == MTSEEK) {
-			i = set_location(inode, mtc.mt_count, STp->new_partition, 0);
+			i = set_location(STp, mtc.mt_count, STp->new_partition, 0);
 			if (!STp->can_partitions)
 				STp->ps[0].rw = ST_IDLE;
 			return i;
 		}
 
 		if (STp->can_partitions && STp->ready == ST_READY &&
-		    (i = update_partition(inode)) < 0)
+		    (i = update_partition(STp)) < 0)
 			return i;
 
 		if (mtc.mt_op == MTCOMPRESSION)
 			return st_compression(STp, (mtc.mt_count & 1));
 		else
-			return st_int_ioctl(inode, mtc.mt_op, mtc.mt_count);
+			return st_int_ioctl(STp, mtc.mt_op, mtc.mt_count);
 	}
 	if (!STm->defined)
 		return (-ENXIO);
 
-	if ((i = flush_buffer(inode, file, FALSE)) < 0)
+	if ((i = flush_buffer(STp, FALSE)) < 0)
 		return i;
 	if (STp->can_partitions &&
-	    (i = update_partition(inode)) < 0)
+	    (i = update_partition(STp)) < 0)
 		return i;
 
 	if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+		struct mtget mt_status;
 
 		if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
 			 return (-EINVAL);
 
-		(STp->mt_status)->mt_dsreg =
+		mt_status.mt_type = STp->tape_type;
+		mt_status.mt_dsreg =
 		    ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
 		    ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
-		(STp->mt_status)->mt_blkno = STps->drv_block;
-		(STp->mt_status)->mt_fileno = STps->drv_file;
+		mt_status.mt_blkno = STps->drv_block;
+		mt_status.mt_fileno = STps->drv_file;
 		if (STp->block_size != 0) {
 			if (STps->rw == ST_WRITING)
-				(STp->mt_status)->mt_blkno +=
+				mt_status.mt_blkno +=
 				    (STp->buffer)->buffer_bytes / STp->block_size;
 			else if (STps->rw == ST_READING)
-				(STp->mt_status)->mt_blkno -=
+				mt_status.mt_blkno -=
                                         ((STp->buffer)->buffer_bytes +
                                          STp->block_size - 1) / STp->block_size;
 		}
 
-		(STp->mt_status)->mt_gstat = 0;
+		mt_status.mt_gstat = 0;
 		if (STp->drv_write_prot)
-			(STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff);
-		if ((STp->mt_status)->mt_blkno == 0) {
-			if ((STp->mt_status)->mt_fileno == 0)
-				(STp->mt_status)->mt_gstat |= GMT_BOT(0xffffffff);
+			mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
+		if (mt_status.mt_blkno == 0) {
+			if (mt_status.mt_fileno == 0)
+				mt_status.mt_gstat |= GMT_BOT(0xffffffff);
 			else
-				(STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff);
+				mt_status.mt_gstat |= GMT_EOF(0xffffffff);
 		}
-		(STp->mt_status)->mt_resid = STp->partition;
+		mt_status.mt_erreg = (STp->recover_reg << MT_ST_SOFTERR_SHIFT);
+		mt_status.mt_resid = STp->partition;
 		if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
-			(STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
+			mt_status.mt_gstat |= GMT_EOT(0xffffffff);
 		else if (STps->eof >= ST_EOM_OK)
-			(STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
+			mt_status.mt_gstat |= GMT_EOD(0xffffffff);
 		if (STp->density == 1)
-			(STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
+			mt_status.mt_gstat |= GMT_D_800(0xffffffff);
 		else if (STp->density == 2)
-			(STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff);
+			mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
 		else if (STp->density == 3)
-			(STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff);
+			mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
 		if (STp->ready == ST_READY)
-			(STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
+			mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
 		if (STp->ready == ST_NO_TAPE)
-			(STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
+			mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
 		if (STps->at_sm)
-			(STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
+			mt_status.mt_gstat |= GMT_SM(0xffffffff);
 		if (STm->do_async_writes ||
                     (STm->do_buffer_writes && STp->block_size != 0) ||
 		    STp->drv_buffer != 0)
-			(STp->mt_status)->mt_gstat |= GMT_IM_REP_EN(0xffffffff);
+			mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
 
-		i = copy_to_user((char *) arg, (char *) (STp->mt_status),
+		i = copy_to_user((char *) arg, (char *) &(mt_status),
 				 sizeof(struct mtget));
 		if (i)
 			return (-EFAULT);
 
-		(STp->mt_status)->mt_erreg = 0;		/* Clear after read */
+		STp->recover_reg = 0;		/* Clear after read */
 		return 0;
 	}			/* End of MTIOCGET */
 	if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
+		struct mtpos mt_pos;
 		if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos))
 			 return (-EINVAL);
-		if ((i = get_location(inode, &blk, &bt, 0)) < 0)
+		if ((i = get_location(STp, &blk, &bt, 0)) < 0)
 			return i;
 		mt_pos.mt_blkno = blk;
 		i = copy_to_user((char *) arg, (char *) (&mt_pos), sizeof(struct mtpos));
@@ -2920,15 +2986,21 @@
 }
 
 
-/* Try to allocate a new tape buffer */
+/* Try to allocate a new tape buffer. Calling function must not hold
+   dev_arr_lock. */
 static ST_buffer *
  new_tape_buffer(int from_initialization, int need_dma)
 {
 	int i, priority, b_size, order, got = 0, segs = 0;
+	unsigned long flags;
 	ST_buffer *tb;
 
-	if (st_nbr_buffers >= st_template.dev_max)
+	read_lock(&st_dev_arr_lock);
+	if (st_nbr_buffers >= st_template.dev_max) {
+		read_unlock(&st_dev_arr_lock);
 		return NULL;	/* Should never happen */
+	}
+	read_unlock(&st_dev_arr_lock);
 
 	if (from_initialization)
 		priority = GFP_ATOMIC;
@@ -3014,7 +3086,10 @@
 	tb->dma = need_dma;
 	tb->buffer_size = got;
 	tb->writing = 0;
+
+	write_lock_irqsave(&st_dev_arr_lock, flags);
 	st_buffers[st_nbr_buffers++] = tb;
+	write_unlock_irqrestore(&st_dev_arr_lock, flags);
 
 	return tb;
 }
@@ -3039,7 +3114,8 @@
 		priority |= GFP_DMA;
 	for (b_size = PAGE_SIZE, order=0;
 	     b_size * nbr < new_size - STbuffer->buffer_size;
-	     order++, b_size *= 2);
+	     order++, b_size *= 2)
+		;  /* empty */
 
 	for (segs = STbuffer->sg_segs, got = STbuffer->buffer_size;
 	     segs < max_segs && got < new_size;) {
@@ -3080,7 +3156,7 @@
 	for (i = STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
 		for (b_size=PAGE_SIZE, order=0; b_size < STbuffer->sg[i].length;
 		     order++, b_size *= 2)
-			;
+			; /* empty */
 		free_pages((unsigned long)(STbuffer->sg[i].address), order);
 		STbuffer->buffer_size -= STbuffer->sg[i].length;
 	}
@@ -3239,23 +3315,77 @@
 	Scsi_Tape *tpnt;
 	ST_mode *STm;
 	ST_partstat *STps;
-	int i, mode;
+	int i, mode, target_nbr;
+	unsigned long flags = 0;
 
 	if (SDp->type != TYPE_TAPE)
 		return 1;
 
+	write_lock_irqsave(&st_dev_arr_lock, flags);
 	if (st_template.nr_dev >= st_template.dev_max) {
-		SDp->attached--;
-		return 1;
+		Scsi_Tape **tmp_da;
+		ST_buffer **tmp_ba;
+		int tmp_dev_max;
+
+		tmp_dev_max = st_template.nr_dev + ST_DEV_ARR_LUMP;
+		if (tmp_dev_max > ST_MAX_TAPES)
+			tmp_dev_max = ST_MAX_TAPES;
+		if (tmp_dev_max <= st_template.nr_dev) {
+			SDp->attached--;
+			write_unlock_irqrestore(&st_dev_arr_lock, flags);
+			printk(KERN_ERR "st: Too many tape devices (max. %d).\n",
+			       ST_MAX_TAPES);
+			return 1;
+		}
+
+		tmp_da = (Scsi_Tape **) kmalloc(tmp_dev_max * sizeof(Scsi_Tape *),
+						GFP_ATOMIC);
+		tmp_ba = (ST_buffer **) kmalloc(tmp_dev_max * sizeof(ST_buffer *),
+						GFP_ATOMIC);
+		if (tmp_da == NULL || tmp_ba == NULL) {
+			if (tmp_da != NULL)
+				kfree(tmp_da);
+			SDp->attached--;
+			write_unlock_irqrestore(&st_dev_arr_lock, flags);
+			printk(KERN_ERR "st: Can't extend device array.\n");
+			return 1;
+		}
+
+		memset(tmp_da, 0, tmp_dev_max * sizeof(Scsi_Tape *));
+		if (scsi_tapes != NULL) {
+			memcpy(tmp_da, scsi_tapes,
+			       st_template.dev_max * sizeof(Scsi_Tape *));
+			kfree(scsi_tapes);
+		}
+		scsi_tapes = tmp_da;
+
+		memset(tmp_ba, 0, tmp_dev_max * sizeof(ST_buffer *));
+		if (st_buffers != NULL) {
+			memcpy(tmp_ba, st_buffers,
+			       st_template.dev_max * sizeof(ST_buffer *));
+			kfree(st_buffers);
+		}
+		st_buffers = tmp_ba;
+
+		st_template.dev_max = tmp_dev_max;
 	}
 
-	for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
-		if (!tpnt->device)
+	for (i = 0; i < st_template.dev_max; i++)
+		if (scsi_tapes[i] == NULL)
 			break;
-
 	if (i >= st_template.dev_max)
 		panic("scsi_devices corrupt (st)");
 
+	tpnt = (Scsi_Tape *)kmalloc(sizeof(Scsi_Tape), GFP_ATOMIC);
+	if (tpnt == NULL) {
+		SDp->attached--;
+		write_unlock_irqrestore(&st_dev_arr_lock, flags);
+		printk(KERN_ERR "st: Can't allocate device descriptor.\n");
+		return 1;
+	}
+	memset(tpnt, 0, sizeof(Scsi_Tape));
+	scsi_tapes[i] = tpnt;
+
 	for (mode = 0; mode < ST_NBR_MODES; ++mode) {
 	    char name[8];
 	    static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"};
@@ -3276,11 +3406,11 @@
 				0, 0, &st_fops, NULL);
 	}
 	devfs_register_tape (tpnt->de_r[0]);
-	scsi_tapes[i].device = SDp;
+	tpnt->device = SDp;
 	if (SDp->scsi_level <= 2)
-		scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1;
+		tpnt->tape_type = MT_ISSCSI1;
 	else
-		scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2;
+		tpnt->tape_type = MT_ISSCSI2;
 
         tpnt->inited = 0;
 	tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
@@ -3333,6 +3463,20 @@
 	    tpnt->blksize_changed = FALSE;
 
 	st_template.nr_dev++;
+	write_unlock_irqrestore(&st_dev_arr_lock, flags);
+
+	/* See if we need to allocate more static buffers */
+	target_nbr = st_template.nr_dev;
+	if (target_nbr > st_max_buffers)
+		target_nbr = st_max_buffers;
+	for (i=st_nbr_buffers; i < target_nbr; i++)
+		if (!new_tape_buffer(TRUE, TRUE)) {
+			printk(KERN_INFO "st: Unable to allocate new static buffer.\n");
+			break;
+		}
+	/* If the previous allocation fails, we will try again when the buffer is
+	   really needed. */
+
 	return 0;
 };
 
@@ -3354,90 +3498,28 @@
 /* Driver initialization (not __init because may be called later) */
 static int st_init()
 {
-	int i, j;
-	Scsi_Tape *STp;
-	int target_nbr;
+	unsigned long flags;
 
-	if (st_template.dev_noticed == 0)
+	if (st_template.dev_noticed == 0 || st_registered)
 		return 0;
 
 	printk(KERN_INFO "st: bufsize %d, wrt %d, max init. buffers %d, s/g segs %d.\n",
 	       st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs);
 
+	write_lock_irqsave(&st_dev_arr_lock, flags);
 	if (!st_registered) {
 		if (devfs_register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) {
+			write_unlock_irqrestore(&st_dev_arr_lock, flags);
 			printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
                                MAJOR_NR);
 			return 1;
 		}
 		st_registered++;
 	}
-	if (scsi_tapes)
-		return 0;
-	st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
-	if (st_template.dev_max < ST_MAX_TAPES)
-		st_template.dev_max = ST_MAX_TAPES;
-	if (st_template.dev_max > 128 / ST_NBR_MODES)
-		printk(KERN_INFO "st: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
-	scsi_tapes =
-		(Scsi_Tape *) kmalloc(st_template.dev_max * sizeof(Scsi_Tape),
-				      GFP_ATOMIC);
-	if (scsi_tapes == NULL) {
-		printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n");
-		devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
-		return 1;
-	}
-
-        DEB(printk(ST_DEB_MSG "st: Buffer size %d bytes, write threshold %d bytes.\n",
-                   st_buffer_size, st_write_threshold));
-
-	memset(scsi_tapes, 0, st_template.dev_max * sizeof(Scsi_Tape));
-	for (i = 0; i < st_template.dev_max; ++i) {
-		STp = &(scsi_tapes[i]);
-		STp->capacity = 0xfffff;
-		STp->mt_status = (struct mtget *) kmalloc(sizeof(struct mtget),
-							  GFP_ATOMIC);
-		if (STp->mt_status == NULL) {
-			for (j=0; j < i; j++)
-				kfree(scsi_tapes[j].mt_status);
-			kfree(scsi_tapes);
-			devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
-			return 1;
-		}
-		/* Initialize status */
-		memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
-	}
-
-	/* Allocate the buffers */
-	st_buffers =
-	    (ST_buffer **) kmalloc(st_template.dev_max * sizeof(ST_buffer *),
-				   GFP_ATOMIC);
-	if (st_buffers == NULL) {
-		printk(KERN_ERR "Unable to allocate tape buffer pointers.\n");
-		devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
-		for (i=0; i < st_template.dev_max; i++)
-			kfree(scsi_tapes[i].mt_status);
-		kfree(scsi_tapes);
-		unregister_chrdev(SCSI_TAPE_MAJOR, "st");
-		return 1;
-	}
-	target_nbr = st_template.dev_noticed;
-	if (target_nbr < ST_EXTRA_DEVS)
-		target_nbr = ST_EXTRA_DEVS;
-	if (target_nbr > st_max_buffers)
-		target_nbr = st_max_buffers;
 
-	for (i = st_nbr_buffers = 0; i < target_nbr; i++) {
-		if (!new_tape_buffer(TRUE, TRUE)) {
-			if (i == 0) {
-				printk(KERN_INFO
-                                       "No tape buffers allocated at initialization.\n");
-				break;
-			}
-			printk(KERN_INFO "Number of tape buffers adjusted.\n");
-			break;
-		}
-	}
+	st_template.dev_max = 0;
+	st_nbr_buffers = 0;
+	write_unlock_irqrestore(&st_dev_arr_lock, flags);
 
 	return 0;
 }
@@ -3446,9 +3528,12 @@
 {
 	Scsi_Tape *tpnt;
 	int i, mode;
+	unsigned long flags;
 
-	for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
-		if (tpnt->device == SDp) {
+	write_lock_irqsave(&st_dev_arr_lock, flags);
+	for (i = 0; i < st_template.dev_max; i++) {
+		tpnt = scsi_tapes[i];
+		if (tpnt != NULL && tpnt->device == SDp) {
 			tpnt->device = NULL;
 			for (mode = 0; mode < ST_NBR_MODES; ++mode) {
 				devfs_unregister (tpnt->de_r[mode]);
@@ -3456,11 +3541,17 @@
 				devfs_unregister (tpnt->de_n[mode]);
 				tpnt->de_n[mode] = NULL;
 			}
+			kfree(tpnt);
+			scsi_tapes[i] = 0;
 			SDp->attached--;
 			st_template.nr_dev--;
 			st_template.dev_noticed--;
+			write_unlock_irqrestore(&st_dev_arr_lock, flags);
 			return;
 		}
+	}
+
+	write_unlock_irqrestore(&st_dev_arr_lock, flags);
 	return;
 }
 
@@ -3484,7 +3575,8 @@
 	st_registered--;
 	if (scsi_tapes != NULL) {
 		for (i=0; i < st_template.dev_max; ++i)
-			kfree(scsi_tapes[i].mt_status);
+			if (scsi_tapes[i])
+				kfree(scsi_tapes[i]);
 		kfree(scsi_tapes);
 		if (st_buffers != NULL) {
 			for (i = 0; i < st_nbr_buffers; i++) {
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v2.3.51/linux/drivers/scsi/st.h	Sun Feb 20 21:12:39 2000
+++ linux/drivers/scsi/st.h	Mon Mar 13 12:32:22 2000
@@ -47,6 +47,7 @@
 #define ST_NBR_MODES (1 << ST_NBR_MODE_BITS)
 #define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS)
 #define ST_MODE_MASK ((ST_NBR_MODES - 1) << ST_MODE_SHIFT)
+#define ST_MAX_TAPES (1 << ST_MODE_SHIFT)
 
 /* The status related to each partition */
 typedef struct {
@@ -64,7 +65,6 @@
 /* The tape drive descriptor */
 typedef struct {
 	kdev_t devt;
-	unsigned capacity;
 	Scsi_Device *device;
 	struct semaphore sem;
 	ST_buffer *buffer;
@@ -79,6 +79,7 @@
 	unsigned char restr_dma;
 	unsigned char scsi2_logical;
 	unsigned char default_drvbuffer;	/* 0xff = don't touch, value 3 bits */
+	int tape_type;
 	int write_threshold;
 	int timeout;		/* timeout for normal commands */
 	int long_timeout;	/* timeout for commands known to take long time */
@@ -105,13 +106,14 @@
 	unsigned char drv_buffer;
 	unsigned char density;
 	unsigned char door_locked;
-	unsigned char rew_at_close;
+	unsigned char autorew_dev;   /* auto-rewind device */
+	unsigned char rew_at_close;  /* rewind necessary at close */
 	unsigned char inited;
 	int block_size;
 	int min_block;
 	int max_block;
-	int recover_count;
-	struct mtget *mt_status;
+	int recover_count;     /* From tape opening */
+	int recover_reg;       /* From last status call */
 
 #if DEBUG
 	unsigned char write_pending;
@@ -122,7 +124,6 @@
 #endif
 } Scsi_Tape;
 
-extern Scsi_Tape *scsi_tapes;
 
 /* Values of eof */
 #define	ST_NOEOF	0
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/st_options.h linux/drivers/scsi/st_options.h
--- v2.3.51/linux/drivers/scsi/st_options.h	Tue Jan  4 13:57:17 2000
+++ linux/drivers/scsi/st_options.h	Mon Mar 13 12:32:22 2000
@@ -3,17 +3,12 @@
 
    Copyright 1995-2000 Kai Makisara.
 
-   Last modified: Sat Jan  1 18:34:38 2000 by makisara@kai.makisara.local
+   Last modified: Sat Mar 11 10:32:00 2000 by makisara@kai.makisara.local
 */
 
 #ifndef _ST_OPTIONS_H
 #define _ST_OPTIONS_H
 
-/* The minimum limit for the number of SCSI tape devices is determined by
-   ST_MAX_TAPES. If the number of tape devices and the "slack" defined by
-   ST_EXTRA_DEVS exceeds ST_MAX_TAPES, the large number is used. */
-#define ST_MAX_TAPES 4
-
 /* The driver does not wait for some operations to finish before returning
    to the user program if ST_NOWAIT is non-zero. This helps if the SCSI
    adapter does not support multiple outstanding commands. However, the user
@@ -47,7 +42,7 @@
    driver initialisation. The number is also constrained by the number
    of drives detected. If more buffers are needed, they are allocated
    at run time and freed after use. */
-#define ST_MAX_BUFFERS (2 + ST_EXTRA_DEVS)
+#define ST_MAX_BUFFERS 4
 
 /* Maximum number of scatter/gather segments */
 #define ST_MAX_SG      16
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/sym53c8xx.c linux/drivers/scsi/sym53c8xx.c
--- v2.3.51/linux/drivers/scsi/sym53c8xx.c	Sat Feb 26 22:31:48 2000
+++ linux/drivers/scsi/sym53c8xx.c	Sun Mar 12 20:09:55 2000
@@ -55,7 +55,7 @@
 */
 
 /*
-**	February 20 2000, sym53c8xx 1.5j
+**	March 6 2000, sym53c8xx 1.5k
 **
 **	Supported SCSI features:
 **	    Synchronous data transfers
@@ -84,7 +84,7 @@
 /*
 **	Name and version of the driver
 */
-#define SCSI_NCR_DRIVER_NAME	"sym53c8xx - version 1.5j"
+#define SCSI_NCR_DRIVER_NAME	"sym53c8xx - version 1.5k"
 
 /* #define DEBUG_896R1 */
 #define SCSI_NCR_OPTIMIZE_896
@@ -174,6 +174,9 @@
 
 #include "sym53c8xx.h"
 
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
+
 /*
 **	Hmmm... What complex some PCI-HOST bridges actually are, 
 **	despite the fact that the PCI specifications are looking 
@@ -1000,8 +1003,9 @@
 			++mp->nump;
 			return vp;
 		}
+		else
+			__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
 	}
-	__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
 	return 0;
 }
 
@@ -1253,7 +1257,7 @@
 #define	SCSI_DATA_READ		2
 #define	SCSI_DATA_NONE		3
 
-static __inline__ scsi_data_direction(Scsi_Cmnd *cmd)
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
 {
 	int direction;
 
@@ -2043,7 +2047,7 @@
 #define HF_ACT_PM	(1u<<2)
 #define HF_DP_SAVED	(1u<<3)
 #define HF_AUTO_SENSE	(1u<<4)
-#define HF_DATA_ST	(1u<<5)
+#define HF_DATA_IN	(1u<<5)
 #define HF_PM_TO_C	(1u<<6)
 
 #ifdef SCSI_NCR_IARB_SUPPORT
@@ -2051,6 +2055,11 @@
 #endif
 
 /*
+**	This one is stolen from QU_REG.:)
+*/
+#define HF_DATA_ST	(1u<<7)
+
+/*
 **	First four bytes (script)
 */
 #define  xerr_st       header.scr_st[0]
@@ -2568,8 +2577,12 @@
 	ncrcmd  data_in2	[  4];
 	ncrcmd  data_out	[MAX_SCATTER * SCR_SG_SIZE];
 	ncrcmd  data_out2	[  4];
-	ncrcmd  pm0_data	[ 16];
-	ncrcmd  pm1_data	[ 16];
+	ncrcmd  pm0_data	[ 12];
+	ncrcmd  pm0_data_out	[  6];
+	ncrcmd  pm0_data_end	[  6];
+	ncrcmd  pm1_data	[ 12];
+	ncrcmd  pm1_data_out	[  6];
+	ncrcmd  pm1_data_end	[  6];
 };
 
 /*
@@ -2607,7 +2620,7 @@
 	ncrcmd	sdata_in	[  6];
 	ncrcmd  data_io		[  2];
 	ncrcmd  data_io_com	[  8];
-	ncrcmd  data_io_out	[ 10];
+	ncrcmd  data_io_out	[ 12];
 	ncrcmd	bad_identify	[ 12];
 	ncrcmd	bad_i_t_l	[  4];
 	ncrcmd	bad_i_t_l_q	[  4];
@@ -3146,12 +3159,11 @@
 
 }/*-------------------------< DATAPHASE >------------------*/,{
 #ifdef SCSI_NCR_PROFILE_SUPPORT
-	SCR_REG_REG (HF_REG, SCR_OR, HF_DATA_ST),
+	SCR_REG_REG (QU_REG, SCR_OR, HF_DATA_ST),
 		0,
 #endif
 	SCR_RETURN,
- 		0,
-
+		0,
 }/*-------------------------< MSG_IN >--------------------*/,{
 	/*
 	**	Get the first byte of the message.
@@ -3390,7 +3402,7 @@
 	*/
 	SCR_LOAD_REL (scratcha, 4),
 		offsetof (struct ccb, phys.num_disc),
-	SCR_FROM_REG (HF_REG),
+	SCR_FROM_REG (QU_REG),
 		0,
 	SCR_JUMPR ^ IFTRUE (MASK (HF_DATA_ST, HF_DATA_ST)),
 		8,
@@ -3692,26 +3704,57 @@
 
 }/*-------------------------< PM0_DATA >--------------------*/,{
 	/*
-	**	Keep track we are executing the PM0 DATA 
-	**	mini-script.
+	**	Read our host flags to SFBR, so we will be able 
+	**	to check against the data direction we expect.
+	*/
+	SCR_FROM_REG (HF_REG),
+		0,
+	/*
+	**	Check against actual DATA PHASE.
+	*/
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+		PADDR (pm0_data_out),
+	/*
+	**	Actual phase is DATA IN.
+	**	Check against expected direction.
+	*/
+	SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+		PADDRH (no_data),
+	/*
+	**	Keep track we are moving data from the 
+	**	PM0 DATA mini-script.
 	*/
 	SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
 		0,
 	/*
-	**	MOVE the data according to the actual 
-	**	DATA direction.
+	**	Move the data to memory.
 	*/
-	SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
-		16,
 	SCR_CHMOV_TBL ^ SCR_DATA_IN,
 		offsetof (struct ccb, phys.pm0.sg),
-	SCR_JUMPR,
-		8,
+	SCR_JUMP,
+		PADDR (pm0_data_end),
+}/*-------------------------< PM0_DATA_OUT >----------------*/,{
+	/*
+	**	Actual phase is DATA OUT.
+	**	Check against expected direction.
+	*/
+	SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+		PADDRH (no_data),
+	/*
+	**	Keep track we are moving data from the 
+	**	PM0 DATA mini-script.
+	*/
+	SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+		0,
+	/*
+	**	Move the data from memory.
+	*/
 	SCR_CHMOV_TBL ^ SCR_DATA_OUT,
 		offsetof (struct ccb, phys.pm0.sg),
+}/*-------------------------< PM0_DATA_END >----------------*/,{
 	/*
-	**	Clear the flag that told we were in 
-	**	the PM0 DATA mini-script.
+	**	Clear the flag that told we were moving  
+	**	data from the PM0 DATA mini-script.
 	*/
 	SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM0)),
 		0,
@@ -3726,26 +3769,57 @@
 		0,
 }/*-------------------------< PM1_DATA >--------------------*/,{
 	/*
-	**	Keep track we are executing the PM1 DATA 
-	**	mini-script.
+	**	Read our host flags to SFBR, so we will be able 
+	**	to check against the data direction we expect.
+	*/
+	SCR_FROM_REG (HF_REG),
+		0,
+	/*
+	**	Check against actual DATA PHASE.
+	*/
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+		PADDR (pm1_data_out),
+	/*
+	**	Actual phase is DATA IN.
+	**	Check against expected direction.
+	*/
+	SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+		PADDRH (no_data),
+	/*
+	**	Keep track we are moving data from the 
+	**	PM1 DATA mini-script.
 	*/
 	SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
 		0,
 	/*
-	**	MOVE the data according to the actual 
-	**	DATA direction.
+	**	Move the data to memory.
 	*/
-	SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
-		16,
 	SCR_CHMOV_TBL ^ SCR_DATA_IN,
 		offsetof (struct ccb, phys.pm1.sg),
-	SCR_JUMPR,
-		8,
+	SCR_JUMP,
+		PADDR (pm1_data_end),
+}/*-------------------------< PM1_DATA_OUT >----------------*/,{
+	/*
+	**	Actual phase is DATA OUT.
+	**	Check against expected direction.
+	*/
+	SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+		PADDRH (no_data),
+	/*
+	**	Keep track we are moving data from the 
+	**	PM1 DATA mini-script.
+	*/
+	SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+		0,
+	/*
+	**	Move the data from memory.
+	*/
 	SCR_CHMOV_TBL ^ SCR_DATA_OUT,
 		offsetof (struct ccb, phys.pm1.sg),
+}/*-------------------------< PM1_DATA_END >----------------*/,{
 	/*
-	**	Clear the flag that told we were in 
-	**	the PM1 DATA mini-script.
+	**	Clear the flag that told we were moving  
+	**	data from the PM1 DATA mini-script.
 	*/
 	SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM1)),
 		0,
@@ -4193,6 +4267,8 @@
 	/*
 	**	Direction is DATA OUT.
 	*/
+	SCR_REG_REG (HF_REG, SCR_AND, (~HF_DATA_IN)),
+		0,
 	SCR_LOAD_REL  (scratcha, 4),
 		offsetof (struct ccb, phys.header.wlastp),
 	SCR_STORE_REL (scratcha, 4),
@@ -5550,7 +5626,7 @@
 		goto attach_error;
 	NCR_INIT_LOCK_NCB(np);
 	np->pdev  = device->pdev;
-	np->p_ncb = __vtobus(device->pdev, np);
+	np->p_ncb = vtobus(np);
 	host_data->ncb = np;
 
 	/*
@@ -6119,18 +6195,18 @@
 */
 static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
 {
+	unmap_scsi_data(np, cmd);
 	cmd->host_scribble = (char *) np->done_list;
 	np->done_list = cmd;
 }
 
-static inline void ncr_flush_done_cmds(pcidev_t pdev, Scsi_Cmnd *lcmd)
+static inline void ncr_flush_done_cmds(Scsi_Cmnd *lcmd)
 {
 	Scsi_Cmnd *cmd;
 
 	while (lcmd) {
 		cmd = lcmd;
 		lcmd = (Scsi_Cmnd *) cmd->host_scribble;
-		__unmap_scsi_data(pdev, cmd);
 		cmd->scsi_done(cmd);
 	}
 }
@@ -6411,6 +6487,7 @@
 		cp->phys.header.wlastp	= cpu_to_scr(lastp);
 		/* fall through */
 	case SCSI_DATA_READ:
+		cp->host_flags |= HF_DATA_IN;
 		goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
 		lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4));
 		break;
@@ -6488,7 +6565,7 @@
 	/*
 	**	command
 	*/
-	memcpy(cp->cdb_buf, cmd->cmnd, cmd->cmd_len);
+	memcpy(cp->cdb_buf, cmd->cmnd, MIN(cmd->cmd_len, sizeof(cp->cdb_buf)));
 	cp->phys.cmd.addr	= cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
 	cp->phys.cmd.size	= cpu_to_scr(cmd->cmd_len);
 
@@ -9154,7 +9231,7 @@
 		cp->scsi_status 	= S_ILLEGAL;
 		cp->xerr_status		= 0;
 		cp->phys.extra_bytes	= 0;
-		cp->host_flags		&= HF_PM_TO_C;
+		cp->host_flags		&= (HF_PM_TO_C|HF_DATA_IN);
 
 		break;
 
@@ -9221,7 +9298,7 @@
 		*/
 		cp->sensecmd[0]		= 0x03;
 		cp->sensecmd[1]		= cp->lun << 5;
-		cp->sensecmd[4]		= sizeof(cmd->sense_buffer);
+		cp->sensecmd[4]		= sizeof(cp->sense_buf);
 
 		/*
 		**	sense data
@@ -9243,7 +9320,7 @@
 
 		cp->host_status	= cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
 		cp->scsi_status = S_ILLEGAL;
-		cp->host_flags	= HF_AUTO_SENSE;
+		cp->host_flags	= (HF_AUTO_SENSE|HF_DATA_IN);
 
 		cp->phys.header.go.start =
 			cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
@@ -12616,8 +12693,10 @@
 
      NCR_UNLOCK_NCB(np, flags);
 
-     if (sts != DID_OK)
+     if (sts != DID_OK) {
+          unmap_scsi_data(np, cmd);
           done(cmd);
+     }
 
      return sts;
 }
@@ -12635,7 +12714,6 @@
      unsigned long flags;
      ncb_p np = (ncb_p) dev_id;
      Scsi_Cmnd *done_list;
-     pcidev_t pdev;
 
 #ifdef DEBUG_SYM53C8XX
      printk("sym53c8xx : interrupt received\n");
@@ -12645,7 +12723,6 @@
 
      NCR_LOCK_NCB(np, flags);
      ncr_exception(np);
-     pdev = np->pdev;
      done_list     = np->done_list;
      np->done_list = 0;
      NCR_UNLOCK_NCB(np, flags);
@@ -12654,7 +12731,7 @@
 
      if (done_list) {
           NCR_LOCK_SCSI_DONE(np, flags);
-          ncr_flush_done_cmds(pdev, done_list);
+          ncr_flush_done_cmds(done_list);
           NCR_UNLOCK_SCSI_DONE(np, flags);
      }
 }
@@ -12667,19 +12744,17 @@
 {
      ncb_p np = (ncb_p) npref;
      unsigned long flags;
-     pcidev_t pdev;
      Scsi_Cmnd *done_list;
 
      NCR_LOCK_NCB(np, flags);
      ncr_timeout((ncb_p) np);
-     pdev = np->pdev;
      done_list     = np->done_list;
      np->done_list = 0;
      NCR_UNLOCK_NCB(np, flags);
 
      if (done_list) {
           NCR_LOCK_SCSI_DONE(np, flags);
-          ncr_flush_done_cmds(pdev, done_list);
+          ncr_flush_done_cmds(done_list);
           NCR_UNLOCK_SCSI_DONE(np, flags);
      }
 }
@@ -12697,7 +12772,6 @@
 	ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
 	int sts;
 	unsigned long flags;
-	pcidev_t pdev;
 	Scsi_Cmnd *done_list;
 
 #if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
@@ -12742,12 +12816,11 @@
 #endif
 
 out:
-	pdev = np->pdev;
 	done_list     = np->done_list;
 	np->done_list = 0;
 	NCR_UNLOCK_NCB(np, flags);
 
-	ncr_flush_done_cmds(pdev, done_list);
+	ncr_flush_done_cmds(done_list);
 
 	return sts;
 }
@@ -12761,7 +12834,6 @@
 	ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
 	int sts;
 	unsigned long flags;
-	pcidev_t pdev;
 	Scsi_Cmnd *done_list;
 
 #if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
@@ -12785,12 +12857,11 @@
 
 	sts = ncr_abort_command(np, cmd);
 out:
-	pdev = np->pdev;
 	done_list     = np->done_list;
 	np->done_list = 0;
 	NCR_UNLOCK_NCB(np, flags);
 
-	ncr_flush_done_cmds(pdev, done_list);
+	ncr_flush_done_cmds(done_list);
 
 	return sts;
 }
diff -u --recursive --new-file v2.3.51/linux/drivers/scsi/sym53c8xx_comm.h linux/drivers/scsi/sym53c8xx_comm.h
--- v2.3.51/linux/drivers/scsi/sym53c8xx_comm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/sym53c8xx_comm.h	Sun Mar 12 20:10:20 2000
@@ -0,0 +1,2863 @@
+/******************************************************************************
+**  High Performance device driver for the Symbios 53C896 controller.
+**
+**  Copyright (C) 1998-2000  Gerard Roudier <groudier@club-internet.fr>
+**
+**  This driver also supports all the Symbios 53C8XX controller family, 
+**  except 53C810 revisions < 16, 53C825 revisions < 16 and all 
+**  revisions of 53C815 controllers.
+**
+**  This driver is based on the Linux port of the FreeBSD ncr driver.
+** 
+**  Copyright (C) 1994  Wolfgang Stanglmeier
+**  
+**-----------------------------------------------------------------------------
+**  
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+**
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+**
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+**
+**  The Linux port of the FreeBSD ncr driver has been achieved in 
+**  november 1995 by:
+**
+**          Gerard Roudier              <groudier@club-internet.fr>
+**
+**  Being given that this driver originates from the FreeBSD version, and
+**  in order to keep synergy on both, any suggested enhancements and corrections
+**  received on Linux are automatically a potential candidate for the FreeBSD 
+**  version.
+**
+**  The original driver has been written for 386bsd and FreeBSD by
+**          Wolfgang Stanglmeier        <wolf@cologne.de>
+**          Stefan Esser                <se@mi.Uni-Koeln.de>
+**
+**-----------------------------------------------------------------------------
+**
+**  Major contributions:
+**  --------------------
+**
+**  NVRAM detection and reading.
+**    Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+**
+*******************************************************************************
+*/
+
+/*
+**	This file contains definitions and code that the 
+**	sym53c8xx and ncr53c8xx drivers should share.
+**	The sharing will be achieved in a further version  
+**	of the driver bundle. For now, only the ncr53c8xx 
+**	driver includes	this file.
+*/
+
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
+
+/*==========================================================
+**
+**	Hmmm... What complex some PCI-HOST bridges actually 
+**	are, despite the fact that the PCI specifications 
+**	are looking so smart and simple! ;-)
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,47)
+#define SCSI_NCR_DYNAMIC_DMA_MAPPING
+#endif
+
+/*==========================================================
+**
+**    Io mapped versus memory mapped.
+**
+**==========================================================
+*/
+
+#if defined(SCSI_NCR_IOMAPPED) || defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED)
+#define NCR_IOMAPPED
+#endif
+
+/*==========================================================
+**
+**	Miscallaneous defines.
+**
+**==========================================================
+*/
+
+#define u_char		unsigned char
+#define u_short		unsigned short
+#define u_int		unsigned int
+#define u_long		unsigned long
+
+#ifndef bcopy
+#define bcopy(s, d, n)	memcpy((d), (s), (n))
+#endif
+
+#ifndef bcmp
+#define bcmp(s, d, n)	memcmp((d), (s), (n))
+#endif
+
+#ifndef bzero
+#define bzero(d, n)	memset((d), 0, (n))
+#endif
+ 
+#ifndef offsetof
+#define offsetof(t, m)	((size_t) (&((t *)0)->m))
+#endif
+
+/*==========================================================
+**
+**	assert ()
+**
+**==========================================================
+**
+**	modified copy from 386bsd:/usr/include/sys/assert.h
+**
+**----------------------------------------------------------
+*/
+
+#define	assert(expression) { \
+	if (!(expression)) { \
+		(void)panic( \
+			"assertion \"%s\" failed: file \"%s\", line %d\n", \
+			#expression, \
+			__FILE__, __LINE__); \
+	} \
+}
+
+/*==========================================================
+**
+**	Debugging tags
+**
+**==========================================================
+*/
+
+#define DEBUG_ALLOC    (0x0001)
+#define DEBUG_PHASE    (0x0002)
+#define DEBUG_QUEUE    (0x0008)
+#define DEBUG_RESULT   (0x0010)
+#define DEBUG_POINTER  (0x0020)
+#define DEBUG_SCRIPT   (0x0040)
+#define DEBUG_TINY     (0x0080)
+#define DEBUG_TIMING   (0x0100)
+#define DEBUG_NEGO     (0x0200)
+#define DEBUG_TAGS     (0x0400)
+#define DEBUG_SCATTER  (0x0800)
+
+/*
+**    Enable/Disable debug messages.
+**    Can be changed at runtime too.
+*/
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
+	#define DEBUG_FLAGS ncr_debug
+#else
+	#define DEBUG_FLAGS	SCSI_NCR_DEBUG_FLAGS
+#endif
+
+/*==========================================================
+**
+**	A la VMS/CAM-3 queue management.
+**	Implemented from linux list management.
+**
+**==========================================================
+*/
+
+typedef struct xpt_quehead {
+	struct xpt_quehead *flink;	/* Forward  pointer */
+	struct xpt_quehead *blink;	/* Backward pointer */
+} XPT_QUEHEAD;
+
+#define xpt_que_init(ptr) do { \
+	(ptr)->flink = (ptr); (ptr)->blink = (ptr); \
+} while (0)
+
+static inline void __xpt_que_add(struct xpt_quehead * new,
+	struct xpt_quehead * blink,
+	struct xpt_quehead * flink)
+{
+	flink->blink	= new;
+	new->flink	= flink;
+	new->blink	= blink;
+	blink->flink	= new;
+}
+
+static inline void __xpt_que_del(struct xpt_quehead * blink,
+	struct xpt_quehead * flink)
+{
+	flink->blink = blink;
+	blink->flink = flink;
+}
+
+static inline int xpt_que_empty(struct xpt_quehead *head)
+{
+	return head->flink == head;
+}
+
+static inline void xpt_que_splice(struct xpt_quehead *list,
+	struct xpt_quehead *head)
+{
+	struct xpt_quehead *first = list->flink;
+
+	if (first != list) {
+		struct xpt_quehead *last = list->blink;
+		struct xpt_quehead *at   = head->flink;
+
+		first->blink = head;
+		head->flink  = first;
+
+		last->flink = at;
+		at->blink   = last;
+	}
+}
+
+#define xpt_que_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+
+#define xpt_insque(new, pos)		__xpt_que_add(new, pos, (pos)->flink)
+
+#define xpt_remque(el)			__xpt_que_del((el)->blink, (el)->flink)
+
+#define xpt_insque_head(new, head)	__xpt_que_add(new, head, (head)->flink)
+
+static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
+{
+	struct xpt_quehead *elem = head->flink;
+
+	if (elem != head)
+		__xpt_que_del(head, elem->flink);
+	else
+		elem = 0;
+	return elem;
+}
+
+#define xpt_insque_tail(new, head)	__xpt_que_add(new, (head)->blink, head)
+
+static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
+{
+	struct xpt_quehead *elem = head->blink;
+
+	if (elem != head)
+		__xpt_que_del(elem->blink, head);
+	else
+		elem = 0;
+	return elem;
+}
+
+/*==========================================================
+**
+**	On x86 architecture, write buffers management does 
+**	not reorder writes to memory. So, using compiler 
+**	optimization barriers is enough to guarantee some 
+**	ordering when the CPU is writing data accessed by 
+**	the NCR.
+**	On Alpha architecture, explicit memory barriers have 
+**	to be used.
+**	Other architectures are defaulted to mb() macro if  
+**	defined, otherwise use compiler barrier.
+**
+**==========================================================
+*/
+
+#if defined(__i386__)
+#define MEMORY_BARRIER()	barrier()
+#elif defined(__alpha__)
+#define MEMORY_BARRIER()	mb()
+#else
+#  ifdef mb
+#  define MEMORY_BARRIER()	mb()
+#  else
+#  define MEMORY_BARRIER()	barrier()
+#  endif
+#endif
+
+/*==========================================================
+**
+**	Simple Wrapper to kernel PCI bus interface.
+**
+**	This wrapper allows to get rid of old kernel PCI 
+**	interface and still allows to preserve linux-2.0 
+**	compatibilty. In fact, it is mostly an incomplete 
+**	emulation of the new PCI code for pre-2.2 kernels.
+**	When kernel-2.0 support will be dropped, we will 
+**	just have to remove most of this code.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
+
+typedef struct pci_dev *pcidev_t;
+#define PCIDEV_NULL		(0)
+#define PciBusNumber(d)		(d)->bus->number
+#define PciDeviceFn(d)		(d)->devfn
+#define PciVendorId(d)		(d)->vendor
+#define PciDeviceId(d)		(d)->device
+#define PciIrqLine(d)		(d)->irq
+
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
+
+static int __init 
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+	*base = pdev->resource[index].start;
+	if ((pdev->resource[index].flags & 0x7) == 0x4)
+		++index;
+	return ++index;
+}
+#else
+static int __init 
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+	*base = pdev->base_address[index++];
+	if ((*base & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+		*base |= (((u_long)pdev->base_address[index]) << 32);
+#endif
+		++index;
+	}
+	return index;
+}
+#endif
+
+#else	/* Incomplete emulation of current PCI code for pre-2.2 kernels */
+
+typedef unsigned int pcidev_t;
+#define PCIDEV_NULL		(~0u)
+#define PciBusNumber(d)		((d)>>8)
+#define PciDeviceFn(d)		((d)&0xff)
+#define __PciDev(busn, devfn)	(((busn)<<8)+(devfn))
+
+#define pci_present pcibios_present
+
+#define pci_read_config_byte(d, w, v) \
+	pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_word(d, w, v) \
+	pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_dword(d, w, v) \
+	pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+#define pci_write_config_byte(d, w, v) \
+	pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_word(d, w, v) \
+	pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_dword(d, w, v) \
+	pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+static pcidev_t __init
+pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
+{
+	static unsigned short pci_index;
+	int retv;
+	unsigned char bus_number, device_fn;
+
+	if (prev == PCIDEV_NULL)
+		pci_index = 0;
+	else
+		++pci_index;
+	retv = pcibios_find_device (vendor, device, pci_index,
+				    &bus_number, &device_fn);
+	return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
+}
+
+static u_short __init PciVendorId(pcidev_t dev)
+{
+	u_short vendor_id;
+	pci_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
+	return vendor_id;
+}
+
+static u_short __init PciDeviceId(pcidev_t dev)
+{
+	u_short device_id;
+	pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
+	return device_id;
+}
+
+static u_int __init PciIrqLine(pcidev_t dev)
+{
+	u_char irq;
+	pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+	return irq;
+}
+
+static int __init 
+pci_get_base_address(pcidev_t dev, int offset, u_long *base)
+{
+	u_int32 tmp;
+	
+	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+	*base = tmp;
+	offset += sizeof(u_int32);
+	if ((tmp & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+		pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+		*base |= (((u_long)tmp) << 32);
+#endif
+		offset += sizeof(u_int32);
+	}
+	return offset;
+}
+
+#endif	/* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
+
+/*==========================================================
+**
+**	SMP threading.
+**
+**	Assuming that SMP systems are generally high end 
+**	systems and may use several SCSI adapters, we are 
+**	using one lock per controller instead of some global 
+**	one. For the moment (linux-2.1.95), driver's entry 
+**	points are called with the 'io_request_lock' lock 
+**	held, so:
+**	- We are uselessly loosing a couple of micro-seconds 
+**	  to lock the controller data structure.
+**	- But the driver is not broken by design for SMP and 
+**	  so can be more resistant to bugs or bad changes in 
+**	  the IO sub-system code.
+**	- A small advantage could be that the interrupt code 
+**	  is grained as wished (e.g.: by controller).
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+spinlock_t DRIVER_SMP_LOCK = SPIN_LOCK_UNLOCKED;
+#define	NCR_LOCK_DRIVER(flags)     spin_lock_irqsave(&DRIVER_SMP_LOCK, flags)
+#define	NCR_UNLOCK_DRIVER(flags)   \
+		spin_unlock_irqrestore(&DRIVER_SMP_LOCK, flags)
+
+#define NCR_INIT_LOCK_NCB(np)      spin_lock_init(&np->smp_lock)
+#define	NCR_LOCK_NCB(np, flags)    spin_lock_irqsave(&np->smp_lock, flags)
+#define	NCR_UNLOCK_NCB(np, flags)  spin_unlock_irqrestore(&np->smp_lock, flags)
+
+#define	NCR_LOCK_SCSI_DONE(np, flags) \
+		spin_lock_irqsave(&io_request_lock, flags)
+#define	NCR_UNLOCK_SCSI_DONE(np, flags) \
+		spin_unlock_irqrestore(&io_request_lock, flags)
+
+#else
+
+#define	NCR_LOCK_DRIVER(flags)     do { save_flags(flags); cli(); } while (0)
+#define	NCR_UNLOCK_DRIVER(flags)   do { restore_flags(flags); } while (0)
+
+#define	NCR_INIT_LOCK_NCB(np)      do { } while (0)
+#define	NCR_LOCK_NCB(np, flags)    do { save_flags(flags); cli(); } while (0)
+#define	NCR_UNLOCK_NCB(np, flags)  do { restore_flags(flags); } while (0)
+
+#define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
+#define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
+
+#endif
+
+/*==========================================================
+**
+**	Memory mapped IO
+**
+**	Since linux-2.1, we must use ioremap() to map the io 
+**	memory space and iounmap() to unmap it. This allows 
+**	portability. Linux 1.3.X and 2.0.X allow to remap 
+**	physical pages addresses greater than the highest 
+**	physical memory address to kernel virtual pages with 
+**	vremap() / vfree(). That was not portable but worked 
+**	with i386 architecture.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#define ioremap vremap
+#define iounmap vfree
+#endif
+
+#ifdef __sparc__
+#  include <asm/irq.h>
+#  define pcivtobus(p)			bus_dvma_to_mem(p)
+#  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
+#elif defined(__alpha__)
+#  define pcivtobus(p)			((p) & 0xfffffffful)
+#  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
+#else	/* others */
+#  define pcivtobus(p)			(p)
+#  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
+#endif
+
+#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED
+static u_long __init remap_pci_mem(u_long base, u_long size)
+{
+	u_long page_base	= ((u_long) base) & PAGE_MASK;
+	u_long page_offs	= ((u_long) base) - page_base;
+	u_long page_remapped	= (u_long) ioremap(page_base, page_offs+size);
+
+	return page_remapped? (page_remapped + page_offs) : 0UL;
+}
+
+static void __init unmap_pci_mem(u_long vaddr, u_long size)
+{
+	if (vaddr)
+		iounmap((void *) (vaddr & PAGE_MASK));
+}
+
+#endif /* not def SCSI_NCR_PCI_MEM_NOT_SUPPORTED */
+
+/*==========================================================
+**
+**	Insert a delay in micro-seconds and milli-seconds.
+**
+**	Under Linux, udelay() is restricted to delay < 
+**	1 milli-second. In fact, it generally works for up 
+**	to 1 second delay. Since 2.1.105, the mdelay() function 
+**	is provided for delays in milli-seconds.
+**	Under 2.0 kernels, udelay() is an inline function 
+**	that is very inaccurate on Pentium processors.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
+#define UDELAY udelay
+#define MDELAY mdelay
+#else
+static void UDELAY(long us) { udelay(us); }
+static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
+#endif
+
+/*==========================================================
+**
+**	Simple power of two buddy-like allocator.
+**
+**	This simple code is not intended to be fast, but to 
+**	provide power of 2 aligned memory allocations.
+**	Since the SCRIPTS processor only supplies 8 bit 
+**	arithmetic, this allocator allows simple and fast 
+**	address calculations  from the SCRIPTS code.
+**	In addition, cache line alignment is guaranteed for 
+**	power of 2 cache line size.
+**	Enhanced in linux-2.3.44 to provide a memory pool 
+**	per pcidev to support dynamic dma mapping. (I would 
+**	have preferred a real bus astraction, btw).
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+#define __GetFreePages(flags, order) __get_free_pages(flags, order)
+#else
+#define __GetFreePages(flags, order) __get_free_pages(flags, order, 0)
+#endif
+
+#define MEMO_SHIFT	4	/* 16 bytes minimum memory chunk */
+#if PAGE_SIZE >= 8192
+#define MEMO_PAGE_ORDER	0	/* 1 PAGE  maximum */
+#else
+#define MEMO_PAGE_ORDER	1	/* 2 PAGES maximum */
+#endif
+#define MEMO_FREE_UNUSED	/* Free unused pages immediately */
+#define MEMO_WARN	1
+#define MEMO_GFP_FLAGS	GFP_ATOMIC
+#define MEMO_CLUSTER_SHIFT	(PAGE_SHIFT+MEMO_PAGE_ORDER)
+#define MEMO_CLUSTER_SIZE	(1UL << MEMO_CLUSTER_SHIFT)
+#define MEMO_CLUSTER_MASK	(MEMO_CLUSTER_SIZE-1)
+
+typedef u_long m_addr_t;	/* Enough bits to bit-hack addresses */
+typedef pcidev_t m_bush_t;	/* Something that addresses DMAable */
+
+typedef struct m_link {		/* Link between free memory chunks */
+	struct m_link *next;
+} m_link_s;
+
+#ifdef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+typedef struct m_vtob {		/* Virtual to Bus address translation */
+	struct m_vtob *next;
+	m_addr_t vaddr;
+	m_addr_t baddr;
+} m_vtob_s;
+#define VTOB_HASH_SHIFT		5
+#define VTOB_HASH_SIZE		(1UL << VTOB_HASH_SHIFT)
+#define VTOB_HASH_MASK		(VTOB_HASH_SIZE-1)
+#define VTOB_HASH_CODE(m)	\
+	((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK)
+#endif
+
+typedef struct m_pool {		/* Memory pool of a given kind */
+#ifdef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+	m_bush_t bush;
+	m_addr_t (*getp)(struct m_pool *);
+	void (*freep)(struct m_pool *, m_addr_t);
+#define M_GETP()		mp->getp(mp)
+#define M_FREEP(p)		mp->freep(mp, p)
+#define GetPages()		__GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define FreePages(p)		free_pages(p, MEMO_PAGE_ORDER)
+	int nump;
+	m_vtob_s *(vtob[VTOB_HASH_SIZE]);
+	struct m_pool *next;
+#else
+#define M_GETP()		__GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define M_FREEP(p)		free_pages(p, MEMO_PAGE_ORDER)
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+	struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1];
+} m_pool_s;
+
+static void *___m_alloc(m_pool_s *mp, int size)
+{
+	int i = 0;
+	int s = (1 << MEMO_SHIFT);
+	int j;
+	m_addr_t a;
+	m_link_s *h = mp->h;
+
+	if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+		return 0;
+
+	while (size > s) {
+		s <<= 1;
+		++i;
+	}
+
+	j = i;
+	while (!h[j].next) {
+		if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+			h[j].next = (m_link_s *) M_GETP();
+			if (h[j].next)
+				h[j].next->next = 0;
+			break;
+		}
+		++j;
+		s <<= 1;
+	}
+	a = (m_addr_t) h[j].next;
+	if (a) {
+		h[j].next = h[j].next->next;
+		while (j > i) {
+			j -= 1;
+			s >>= 1;
+			h[j].next = (m_link_s *) (a+s);
+			h[j].next->next = 0;
+		}
+	}
+#ifdef DEBUG
+	printk("___m_alloc(%d) = %p\n", size, (void *) a);
+#endif
+	return (void *) a;
+}
+
+static void ___m_free(m_pool_s *mp, void *ptr, int size)
+{
+	int i = 0;
+	int s = (1 << MEMO_SHIFT);
+	m_link_s *q;
+	m_addr_t a, b;
+	m_link_s *h = mp->h;
+
+#ifdef DEBUG
+	printk("___m_free(%p, %d)\n", ptr, size);
+#endif
+
+	if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+		return;
+
+	while (size > s) {
+		s <<= 1;
+		++i;
+	}
+
+	a = (m_addr_t) ptr;
+
+	while (1) {
+#ifdef MEMO_FREE_UNUSED
+		if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+			M_FREEP(a);
+			break;
+		}
+#endif
+		b = a ^ s;
+		q = &h[i];
+		while (q->next && q->next != (m_link_s *) b) {
+			q = q->next;
+		}
+		if (!q->next) {
+			((m_link_s *) a)->next = h[i].next;
+			h[i].next = (m_link_s *) a;
+			break;
+		}
+		q->next = q->next->next;
+		a = a & b;
+		s <<= 1;
+		++i;
+	}
+}
+
+static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags)
+{
+	void *p;
+
+	p = ___m_alloc(mp, size);
+
+	if (DEBUG_FLAGS & DEBUG_ALLOC)
+		printk ("new %-10s[%4d] @%p.\n", name, size, p);
+
+	if (p)
+		bzero(p, size);
+	else if (uflags & MEMO_WARN)
+		printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size);
+
+	return p;
+}
+
+#define __m_calloc(mp, s, n)	__m_calloc2(mp, s, n, MEMO_WARN)
+
+static void __m_free(m_pool_s *mp, void *ptr, int size, char *name)
+{
+	if (DEBUG_FLAGS & DEBUG_ALLOC)
+		printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
+
+	___m_free(mp, ptr, size);
+
+}
+
+/*
+ * With pci bus iommu support, we use a default pool of unmapped memory 
+ * for memory we donnot need to DMA from/to and one pool per pcidev for 
+ * memory accessed by the PCI chip. `mp0' is the default not DMAable pool.
+ */
+
+#ifndef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+static m_pool_s mp0;
+
+#else
+
+static m_addr_t ___mp0_getp(m_pool_s *mp)
+{
+	m_addr_t m = GetPages();
+	if (m)
+		++mp->nump;
+	return m;
+}
+
+static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
+{
+	FreePages(m);
+	--mp->nump;
+}
+
+static m_pool_s mp0 = {0, ___mp0_getp, ___mp0_freep};
+
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+static void *m_calloc(int size, char *name)
+{
+	u_long flags;
+	void *m;
+	NCR_LOCK_DRIVER(flags);
+	m = __m_calloc(&mp0, size, name);
+	NCR_UNLOCK_DRIVER(flags);
+	return m;
+}
+
+static void m_free(void *ptr, int size, char *name)
+{
+	u_long flags;
+	NCR_LOCK_DRIVER(flags);
+	__m_free(&mp0, ptr, size, name);
+	NCR_UNLOCK_DRIVER(flags);
+}
+
+/*
+ * DMAable pools.
+ */
+
+#ifndef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+/* Without pci bus iommu support, all the memory is assumed DMAable */
+
+#define __m_calloc_dma(b, s, n)		m_calloc(s, n)
+#define __m_free_dma(b, p, s, n)	m_free(p, s, n)
+#define __vtobus(b, p)			virt_to_bus(p)
+
+#else
+
+/*
+ * With pci bus iommu support, we maintain one pool per pcidev and a 
+ * hashed reverse table for virtual to bus physical address translations.
+ */
+static m_addr_t ___dma_getp(m_pool_s *mp)
+{
+	m_addr_t vp;
+	m_vtob_s *vbp;
+
+	vbp = __m_calloc(&mp0, sizeof(*vbp), "VTOB");
+	if (vbp) {
+		dma_addr_t daddr;
+		vp = (m_addr_t) pci_alloc_consistent(mp->bush,
+						PAGE_SIZE<<MEMO_PAGE_ORDER,
+						&daddr);
+		if (vp) {
+			int hc = VTOB_HASH_CODE(vp);
+			vbp->vaddr = vp;
+			vbp->baddr = daddr;
+			vbp->next = mp->vtob[hc];
+			mp->vtob[hc] = vbp;
+			++mp->nump;
+			return vp;
+		}
+	}
+	if (vbp)
+		__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+	return 0;
+}
+
+static void ___dma_freep(m_pool_s *mp, m_addr_t m)
+{
+	m_vtob_s **vbpp, *vbp;
+	int hc = VTOB_HASH_CODE(m);
+
+	vbpp = &mp->vtob[hc];
+	while (*vbpp && (*vbpp)->vaddr != m)
+		vbpp = &(*vbpp)->next;
+	if (*vbpp) {
+		vbp = *vbpp;
+		*vbpp = (*vbpp)->next;
+		pci_free_consistent(mp->bush, PAGE_SIZE<<MEMO_PAGE_ORDER,
+				    (void *)vbp->vaddr, (dma_addr_t)vbp->baddr);
+		__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+		--mp->nump;
+	}
+}
+
+static inline m_pool_s *___get_dma_pool(m_bush_t bush)
+{
+	m_pool_s *mp;
+	for (mp = mp0.next; mp && mp->bush != bush; mp = mp->next);
+	return mp;
+}
+
+static m_pool_s *___cre_dma_pool(m_bush_t bush)
+{
+	m_pool_s *mp;
+	mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL");
+	if (mp) {
+		bzero(mp, sizeof(*mp));
+		mp->bush = bush;
+		mp->getp = ___dma_getp;
+		mp->freep = ___dma_freep;
+		mp->next = mp0.next;
+		mp0.next = mp;
+	}
+	return mp;
+}
+
+static void ___del_dma_pool(m_pool_s *p)
+{
+	struct m_pool **pp = &mp0.next;
+
+	while (*pp && *pp != p)
+		pp = &(*pp)->next;
+	if (*pp) {
+		*pp = (*pp)->next;
+		__m_free(&mp0, p, sizeof(*p), "MPOOL");
+	}
+}
+
+static void *__m_calloc_dma(m_bush_t bush, int size, char *name)
+{
+	u_long flags;
+	struct m_pool *mp;
+	void *m = 0;
+
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (!mp)
+		mp = ___cre_dma_pool(bush);
+	if (mp)
+		m = __m_calloc(mp, size, name);
+	if (mp && !mp->nump)
+		___del_dma_pool(mp);
+	NCR_UNLOCK_DRIVER(flags);
+
+	return m;
+}
+
+static void __m_free_dma(m_bush_t bush, void *m, int size, char *name)
+{
+	u_long flags;
+	struct m_pool *mp;
+
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (mp)
+		__m_free(mp, m, size, name);
+	if (mp && !mp->nump)
+		___del_dma_pool(mp);
+	NCR_UNLOCK_DRIVER(flags);
+}
+
+static m_addr_t __vtobus(m_bush_t bush, void *m)
+{
+	u_long flags;
+	m_pool_s *mp;
+	int hc = VTOB_HASH_CODE(m);
+	m_vtob_s *vp = 0;
+	m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK;
+
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (mp) {
+		vp = mp->vtob[hc];
+		while (vp && (m_addr_t) vp->vaddr != a)
+			vp = vp->next;
+	}
+	NCR_UNLOCK_DRIVER(flags);
+	return vp ? vp->baddr + (((m_addr_t) m) - a) : 0;
+}
+
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define _m_calloc_dma(np, s, n)		__m_calloc_dma(np->pdev, s, n)
+#define _m_free_dma(np, p, s, n)	__m_free_dma(np->pdev, p, s, n)
+#define m_calloc_dma(s, n)		_m_calloc_dma(np, s, n)
+#define m_free_dma(p, s, n)		_m_free_dma(np, p, s, n)
+#define _vtobus(np, p)			__vtobus(np->pdev, p)
+#define vtobus(p)			_vtobus(np, p)
+
+/*
+ *  Deal with DMA mapping/unmapping.
+ */
+
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+/* Linux versions prior to pci bus iommu kernel interface */
+
+#define __unmap_scsi_data(pdev, cmd)	do {; } while (0)
+#define __map_scsi_single_data(pdev, cmd) (__vtobus(pdev,(cmd)->request_buffer))
+#define __map_scsi_sg_data(pdev, cmd)	((cmd)->use_sg)
+#define __sync_scsi_data(pdev, cmd)	do {; } while (0)
+
+#define scsi_sg_dma_address(sc)		vtobus((sc)->address)
+#define scsi_sg_dma_len(sc)		((sc)->length)
+
+#else
+
+/* Linux version with pci bus iommu kernel interface */
+
+/* To keep track of the dma mapping (sg/single) that has been set */
+#define __data_mapped	SCp.phase
+#define __data_mapping	SCp.have_data_in
+
+static void __unmap_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	switch(cmd->__data_mapped) {
+	case 2:
+		pci_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+		break;
+	case 1:
+		pci_unmap_single(pdev, cmd->__data_mapping,
+				 cmd->request_bufflen, dma_dir);
+		break;
+	}
+	cmd->__data_mapped = 0;
+}
+
+static u_long __map_scsi_single_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	dma_addr_t mapping;
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	if (cmd->request_bufflen == 0)
+		return 0;
+
+	mapping = pci_map_single(pdev, cmd->request_buffer,
+				 cmd->request_bufflen, dma_dir);
+	cmd->__data_mapped = 1;
+	cmd->__data_mapping = mapping;
+
+	return mapping;
+}
+
+static int __map_scsi_sg_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int use_sg;
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	if (cmd->use_sg == 0)
+		return 0;
+
+	use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+	cmd->__data_mapped = 2;
+	cmd->__data_mapping = use_sg;
+
+	return use_sg;
+}
+
+static void __sync_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	switch(cmd->__data_mapped) {
+	case 2:
+		pci_dma_sync_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+		break;
+	case 1:
+		pci_dma_sync_single(pdev, cmd->__data_mapping,
+				    cmd->request_bufflen, dma_dir);
+		break;
+	}
+}
+
+#define scsi_sg_dma_address(sc)		sg_dma_address(sc)
+#define scsi_sg_dma_len(sc)		sg_dma_len(sc)
+
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define unmap_scsi_data(np, cmd)	__unmap_scsi_data(np->pdev, cmd)
+#define map_scsi_single_data(np, cmd)	__map_scsi_single_data(np->pdev, cmd)
+#define map_scsi_sg_data(np, cmd)	__map_scsi_sg_data(np->pdev, cmd)
+#define sync_scsi_data(np, cmd)		__sync_scsi_data(np->pdev, cmd)
+
+/*==========================================================
+**
+**	SCSI data transfer direction
+**
+**	Until some linux kernel version near 2.3.40, 
+**	low-level scsi drivers were not told about data 
+**	transfer direction. We check the existence of this 
+**	feature that has been expected for a _long_ time by 
+**	all SCSI driver developers by just testing against 
+**	the definition of SCSI_DATA_UNKNOWN. Indeed this is 
+**	a hack, but testing against a kernel version would 
+**	have been a shame. ;-)
+**
+**==========================================================
+*/
+#ifdef	SCSI_DATA_UNKNOWN
+
+#define scsi_data_direction(cmd)	(cmd->sc_data_direction)
+
+#else
+
+#define	SCSI_DATA_UNKNOWN	0
+#define	SCSI_DATA_WRITE		1
+#define	SCSI_DATA_READ		2
+#define	SCSI_DATA_NONE		3
+
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
+{
+	int direction;
+
+	switch((int) cmd->cmnd[0]) {
+	case 0x08:  /*	READ(6)				08 */
+	case 0x28:  /*	READ(10)			28 */
+	case 0xA8:  /*	READ(12)			A8 */
+		direction = SCSI_DATA_READ;
+		break;
+	case 0x0A:  /*	WRITE(6)			0A */
+	case 0x2A:  /*	WRITE(10)			2A */
+	case 0xAA:  /*	WRITE(12)			AA */
+		direction = SCSI_DATA_WRITE;
+		break;
+	default:
+		direction = SCSI_DATA_UNKNOWN;
+		break;
+	}
+
+	return direction;
+}
+
+#endif	/* SCSI_DATA_UNKNOWN */
+
+/*==========================================================
+**
+**	Driver setup.
+**
+**	This structure is initialized from linux config 
+**	options. It can be overridden at boot-up by the boot 
+**	command line.
+**
+**==========================================================
+*/
+static struct ncr_driver_setup
+	driver_setup			= SCSI_NCR_DRIVER_SETUP;
+
+#ifdef	SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+	driver_safe_setup __initdata	= SCSI_NCR_DRIVER_SAFE_SETUP;
+#endif
+
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+/*==========================================================
+**
+**	Big/Little endian support.
+**
+**	If the NCR uses big endian addressing mode over the 
+**	PCI, actual io register addresses for byte and word 
+**	accesses must be changed according to lane routing.
+**	Btw, ncr_offb() and ncr_offw() macros only apply to 
+**	constants and so donnot generate bloated code.
+**
+**	If the CPU and the NCR use same endian-ness adressing,
+**	no byte reordering is needed for script patching.
+**	Macro cpu_to_scr() is to be used for script patching.
+**	Macro scr_to_cpu() is to be used for getting a DWORD 
+**	from the script.
+**
+**==========================================================
+*/
+
+#if	defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o)	(((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o)	(((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define ncr_offb(o)	(o)
+#define ncr_offw(o)	(o)
+
+#endif
+
+#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw)	cpu_to_le32(dw)
+#define scr_to_cpu(dw)	le32_to_cpu(dw)
+
+#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw)	cpu_to_be32(dw)
+#define scr_to_cpu(dw)	be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw)	(dw)
+#define scr_to_cpu(dw)	(dw)
+
+#endif
+
+/*==========================================================
+**
+**	Access to the controller chip.
+**
+**	If NCR_IOMAPPED is defined, the driver will use 
+**	normal IOs instead of the MEMORY MAPPED IO method  
+**	recommended by PCI specifications.
+**	If all PCI bridges, host brigdes and architectures 
+**	would have been correctly designed for PCI, this 
+**	option would be useless.
+**
+**	If the CPU and the NCR use same endian-ness adressing,
+**	no byte reordering is needed for accessing chip io 
+**	registers. Functions suffixed by '_raw' are assumed 
+**	to access the chip over the PCI without doing byte 
+**	reordering. Functions suffixed by '_l2b' are 
+**	assumed to perform little-endian to big-endian byte 
+**	reordering, those suffixed by '_b2l' blah, blah,
+**	blah, ...
+**
+**==========================================================
+*/
+
+#if defined(NCR_IOMAPPED)
+
+/*
+**	IO mapped only input / ouput
+*/
+
+#define	INB_OFF(o)		inb (np->base_io + ncr_offb(o))
+#define	OUTB_OFF(o, val)	outb ((val), np->base_io + ncr_offb(o))
+
+#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define	INW_OFF(o)		inw_l2b (np->base_io + ncr_offw(o))
+#define	INL_OFF(o)		inl_l2b (np->base_io + (o))
+
+#define	OUTW_OFF(o, val)	outw_b2l ((val), np->base_io + ncr_offw(o))
+#define	OUTL_OFF(o, val)	outl_b2l ((val), np->base_io + (o))
+
+#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define	INW_OFF(o)		inw_b2l (np->base_io + ncr_offw(o))
+#define	INL_OFF(o)		inl_b2l (np->base_io + (o))
+
+#define	OUTW_OFF(o, val)	outw_l2b ((val), np->base_io + ncr_offw(o))
+#define	OUTL_OFF(o, val)	outl_l2b ((val), np->base_io + (o))
+
+#else
+
+#define	INW_OFF(o)		inw_raw (np->base_io + ncr_offw(o))
+#define	INL_OFF(o)		inl_raw (np->base_io + (o))
+
+#define	OUTW_OFF(o, val)	outw_raw ((val), np->base_io + ncr_offw(o))
+#define	OUTL_OFF(o, val)	outl_raw ((val), np->base_io + (o))
+
+#endif	/* ENDIANs */
+
+#else	/* defined NCR_IOMAPPED */
+
+/*
+**	MEMORY mapped IO input / output
+*/
+
+#define INB_OFF(o)		readb((char *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val)	writeb((val), (char *)np->reg + ncr_offb(o))
+
+#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o)		readw_l2b((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)		readl_l2b((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val)	writew_b2l((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)	writel_b2l((val), (char *)np->reg + (o))
+
+#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o)		readw_b2l((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)		readl_b2l((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val)	writew_l2b((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)	writel_l2b((val), (char *)np->reg + (o))
+
+#else
+
+#define INW_OFF(o)		readw_raw((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)		readl_raw((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val)	writew_raw((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)	writel_raw((val), (char *)np->reg + (o))
+
+#endif
+
+#endif	/* defined NCR_IOMAPPED */
+
+#define INB(r)		INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r)		INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r)		INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val)	OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val)	OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val)	OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
+/*
+**	Set bit field ON, OFF 
+*/
+
+#define OUTONB(r, m)	OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m)	OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m)	OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m)	OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m)	OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m)	OUTL(r, INL(r) & ~(m))
+
+
+/*==========================================================
+**
+**	Structures used by the detection routine to transmit 
+**	device configuration to the attach function.
+**
+**==========================================================
+*/
+typedef struct {
+	int	bus;
+	u_char	device_fn;
+	u_long	base;
+	u_long	base_2;
+	u_long	io_port;
+	int	irq;
+/* port and reg fields to use INB, OUTB macros */
+	u_long	base_io;
+	volatile struct ncr_reg	*reg;
+} ncr_slot;
+
+/*==========================================================
+**
+**	Structure used to store the NVRAM content.
+**
+**==========================================================
+*/
+typedef struct {
+	int type;
+#define	SCSI_NCR_SYMBIOS_NVRAM	(1)
+#define	SCSI_NCR_TEKRAM_NVRAM	(2)
+#ifdef	SCSI_NCR_NVRAM_SUPPORT
+	union {
+		Symbios_nvram Symbios;
+		Tekram_nvram Tekram;
+	} data;
+#endif
+} ncr_nvram;
+
+/*==========================================================
+**
+**	Structure used by detection routine to save data on 
+**	each detected board for attach.
+**
+**==========================================================
+*/
+typedef struct {
+	pcidev_t  pdev;
+	ncr_slot  slot;
+	ncr_chip  chip;
+	ncr_nvram *nvram;
+	u_char host_id;
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+	u_char pqs_pds;
+#endif
+	int attach_done;
+} ncr_device;
+
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
+
+/*==========================================================
+**
+**	NVRAM detection and reading.
+**	 
+**	Currently supported:
+**	- 24C16 EEPROM with both Symbios and Tekram layout.
+**	- 93C46 EEPROM with Tekram layout.
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+ *  24C16 EEPROM reading.
+ *
+ *  GPOI0 - data in/data out
+ *  GPIO1 - clock
+ *  Symbios NVRAM wiring now also used by Tekram.
+ */
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+/*
+ *  Set/clear data/clock bit in GPIO0
+ */
+static void __init
+S24C16_set_bit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+{
+	UDELAY (5);
+	switch (bit_mode){
+	case SET_BIT:
+		*gpreg |= write_bit;
+		break;
+	case CLR_BIT:
+		*gpreg &= 0xfe;
+		break;
+	case SET_CLK:
+		*gpreg |= 0x02;
+		break;
+	case CLR_CLK:
+		*gpreg &= 0xfd;
+		break;
+
+	}
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (5);
+}
+
+/*
+ *  Send START condition to NVRAM to wake it up.
+ */
+static void __init S24C16_start(ncr_slot *np, u_char *gpreg)
+{
+	S24C16_set_bit(np, 1, gpreg, SET_BIT);
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+	S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+static void __init S24C16_stop(ncr_slot *np, u_char *gpreg)
+{
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	S24C16_set_bit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ *  Read or write a bit to the NVRAM,
+ *  read if GPIO0 input else write if GPIO0 output
+ */
+static void __init 
+S24C16_do_bit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+{
+	S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	if (read_bit)
+		*read_bit = INB (nc_gpreg);
+	S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+	S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ *  Output an ACK to the NVRAM after reading,
+ *  change GPIO0 to output and when done back to an input
+ */
+static void __init
+S24C16_write_ack(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+{
+	OUTB (nc_gpcntl, *gpcntl & 0xfe);
+	S24C16_do_bit(np, 0, write_bit, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ *  Input an ACK from NVRAM after writing,
+ *  change GPIO0 to input and when done back to an output
+ */
+static void __init 
+S24C16_read_ack(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+{
+	OUTB (nc_gpcntl, *gpcntl | 0x01);
+	S24C16_do_bit(np, read_bit, 1, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ *  WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ *  GPIO0 must already be set as an output
+ */
+static void __init 
+S24C16_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, 
+		  u_char *gpreg, u_char *gpcntl)
+{
+	int x;
+	
+	for (x = 0; x < 8; x++)
+		S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+		
+	S24C16_read_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ *  READ a byte from the NVRAM and then send an ACK to say we have got it,
+ *  GPIO0 must already be set as an input
+ */
+static void __init 
+S24C16_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, 
+	         u_char *gpreg, u_char *gpcntl)
+{
+	int x;
+	u_char read_bit;
+
+	*read_data = 0;
+	for (x = 0; x < 8; x++) {
+		S24C16_do_bit(np, &read_bit, 1, gpreg);
+		*read_data |= ((read_bit & 0x01) << (7 - x));
+	}
+
+	S24C16_write_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ *  Read 'len' bytes starting at 'offset'.
+ */
+static int __init 
+sym_read_S24C16_nvram (ncr_slot *np, int offset, u_char *data, int len)
+{
+	u_char	gpcntl, gpreg;
+	u_char	old_gpcntl, old_gpreg;
+	u_char	ack_data;
+	int	retv = 1;
+	int	x;
+
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+	gpcntl		= old_gpcntl & 0xfc;
+
+	/* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+	OUTB (nc_gpreg,  old_gpreg);
+	OUTB (nc_gpcntl, gpcntl);
+
+	/* this is to set NVRAM into a known state with GPIO0/1 both low */
+	gpreg = old_gpreg;
+	S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+	S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
+		
+	/* now set NVRAM inactive with GPIO0/1 both high */
+	S24C16_stop(np, &gpreg);
+	
+	/* activate NVRAM */
+	S24C16_start(np, &gpreg);
+
+	/* write device code and random address MSB */
+	S24C16_write_byte(np, &ack_data,
+		0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* write random address LSB */
+	S24C16_write_byte(np, &ack_data,
+		offset & 0xff, &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* regenerate START state to set up for reading */
+	S24C16_start(np, &gpreg);
+	
+	/* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+	S24C16_write_byte(np, &ack_data,
+		0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* now set up GPIO0 for inputting data */
+	gpcntl |= 0x01;
+	OUTB (nc_gpcntl, gpcntl);
+		
+	/* input all requested data - only part of total NVRAM */
+	for (x = 0; x < len; x++) 
+		S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
+
+	/* finally put NVRAM back in inactive mode */
+	gpcntl &= 0xfe;
+	OUTB (nc_gpcntl, gpcntl);
+	S24C16_stop(np, &gpreg);
+	retv = 0;
+out:
+	/* return GPIO0/1 to original states after having accessed NVRAM */
+	OUTB (nc_gpcntl, old_gpcntl);
+	OUTB (nc_gpreg,  old_gpreg);
+
+	return retv;
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+/*
+ *  Try reading Symbios NVRAM.
+ *  Return 0 if OK.
+ */
+static int __init sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+{
+	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+	u_char *data = (u_char *) nvram;
+	int len  = sizeof(*nvram);
+	u_short	csum;
+	int x;
+
+	/* probe the 24c16 and read the SYMBIOS 24c16 area */
+	if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
+		return 1;
+
+	/* check valid NVRAM signature, verify byte count and checksum */
+	if (nvram->type != 0 ||
+	    memcmp(nvram->trailer, Symbios_trailer, 6) ||
+	    nvram->byte_count != len - 12)
+		return 1;
+
+	/* verify checksum */
+	for (x = 6, csum = 0; x < len - 6; x++)
+		csum += data[x];
+	if (csum != nvram->checksum)
+		return 1;
+
+	return 0;
+}
+
+/*
+ *  93C46 EEPROM reading.
+ *
+ *  GPOI0 - data in
+ *  GPIO1 - data out
+ *  GPIO2 - clock
+ *  GPIO4 - chip select
+ *
+ *  Used by Tekram.
+ */
+
+/*
+ *  Pulse clock bit in GPIO0
+ */
+static void __init T93C46_Clk(ncr_slot *np, u_char *gpreg)
+{
+	OUTB (nc_gpreg, *gpreg | 0x04);
+	UDELAY (2);
+	OUTB (nc_gpreg, *gpreg);
+}
+
+/* 
+ *  Read bit from NVRAM
+ */
+static void __init T93C46_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+{
+	UDELAY (2);
+	T93C46_Clk(np, gpreg);
+	*read_bit = INB (nc_gpreg);
+}
+
+/*
+ *  Write bit to GPIO0
+ */
+static void __init T93C46_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+{
+	if (write_bit & 0x01)
+		*gpreg |= 0x02;
+	else
+		*gpreg &= 0xfd;
+		
+	*gpreg |= 0x10;
+		
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (2);
+
+	T93C46_Clk(np, gpreg);
+}
+
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+static void __init T93C46_Stop(ncr_slot *np, u_char *gpreg)
+{
+	*gpreg &= 0xef;
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (2);
+
+	T93C46_Clk(np, gpreg);
+}
+
+/*
+ *  Send read command and address to NVRAM
+ */
+static void __init 
+T93C46_Send_Command(ncr_slot *np, u_short write_data, 
+		    u_char *read_bit, u_char *gpreg)
+{
+	int x;
+
+	/* send 9 bits, start bit (1), command (2), address (6)  */
+	for (x = 0; x < 9; x++)
+		T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+	*read_bit = INB (nc_gpreg);
+}
+
+/*
+ *  READ 2 bytes from the NVRAM
+ */
+static void __init 
+T93C46_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+{
+	int x;
+	u_char read_bit;
+
+	*nvram_data = 0;
+	for (x = 0; x < 16; x++) {
+		T93C46_Read_Bit(np, &read_bit, gpreg);
+
+		if (read_bit & 0x01)
+			*nvram_data |=  (0x01 << (15 - x));
+		else
+			*nvram_data &= ~(0x01 << (15 - x));
+	}
+}
+
+/*
+ *  Read Tekram NvRAM data.
+ */
+static int __init 
+T93C46_Read_Data(ncr_slot *np, u_short *data,int len,u_char *gpreg)
+{
+	u_char	read_bit;
+	int	x;
+
+	for (x = 0; x < len; x++)  {
+
+		/* output read command and address */
+		T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+		if (read_bit & 0x01)
+			return 1; /* Bad */
+		T93C46_Read_Word(np, &data[x], gpreg);
+		T93C46_Stop(np, gpreg);
+	}
+
+	return 0;
+}
+
+/*
+ *  Try reading 93C46 Tekram NVRAM.
+ */
+static int __init 
+sym_read_T93C46_nvram (ncr_slot *np, Tekram_nvram *nvram)
+{
+	u_char gpcntl, gpreg;
+	u_char old_gpcntl, old_gpreg;
+	int retv = 1;
+
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+
+	/* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+	   1/2/4 out */
+	gpreg = old_gpreg & 0xe9;
+	OUTB (nc_gpreg, gpreg);
+	gpcntl = (old_gpcntl & 0xe9) | 0x09;
+	OUTB (nc_gpcntl, gpcntl);
+
+	/* input all of NVRAM, 64 words */
+	retv = T93C46_Read_Data(np, (u_short *) nvram,
+				sizeof(*nvram) / sizeof(short), &gpreg);
+	
+	/* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+	OUTB (nc_gpcntl, old_gpcntl);
+	OUTB (nc_gpreg,  old_gpreg);
+
+	return retv;
+}
+
+/*
+ *  Try reading Tekram NVRAM.
+ *  Return 0 if OK.
+ */
+static int __init 
+sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, Tekram_nvram *nvram)
+{
+	u_char *data = (u_char *) nvram;
+	int len = sizeof(*nvram);
+	u_short	csum;
+	int x;
+
+	switch (device_id) {
+	case PCI_DEVICE_ID_NCR_53C885:
+	case PCI_DEVICE_ID_NCR_53C895:
+	case PCI_DEVICE_ID_NCR_53C896:
+		x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+					  data, len);
+		break;
+	case PCI_DEVICE_ID_NCR_53C875:
+		x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+					  data, len);
+		if (!x)
+			break;
+	default:
+		x = sym_read_T93C46_nvram(np, nvram);
+		break;
+	}
+	if (x)
+		return 1;
+
+	/* verify checksum */
+	for (x = 0, csum = 0; x < len - 1; x += 2)
+		csum += data[x] + (data[x+1] << 8);
+	if (csum != 0x1234)
+		return 1;
+
+	return 0;
+}
+
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
+
+/*===================================================================
+**
+**    Detect and try to read SYMBIOS and TEKRAM NVRAM.
+**
+**    Data can be used to order booting of boards.
+**
+**    Data is saved in ncr_device structure if NVRAM found. This
+**    is then used to find drive boot order for ncr_attach().
+**
+**    NVRAM data is passed to Scsi_Host_Template later during 
+**    ncr_attach() for any device set up.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp)
+{
+	devp->nvram = nvp;
+	if (!nvp)
+		return;
+	/*
+	**    Get access to chip IO registers
+	*/
+#ifdef NCR_IOMAPPED
+	request_region(devp->slot.io_port, 128, NAME53C8XX);
+	devp->slot.base_io = devp->slot.io_port;
+#else
+	devp->slot.reg = (struct ncr_reg *) remap_pci_mem(devp->slot.base, 128);
+	if (!devp->slot.reg)
+		return;
+#endif
+
+	/*
+	**    Try to read SYMBIOS nvram.
+	**    Try to read TEKRAM nvram if Symbios nvram not found.
+	*/
+	if	(!sym_read_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
+		nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
+	else if	(!sym_read_Tekram_nvram(&devp->slot, devp->chip.device_id,
+					&nvp->data.Tekram))
+		nvp->type = SCSI_NCR_TEKRAM_NVRAM;
+	else {
+		nvp->type = 0;
+		devp->nvram = 0;
+	}
+
+	/*
+	** Release access to chip IO registers
+	*/
+#ifdef NCR_IOMAPPED
+	release_region(devp->slot.base_io, 128);
+#else
+	unmap_pci_mem((u_long) devp->slot.reg, 128ul);
+#endif
+
+}
+
+/*===================================================================
+**
+**	Display the content of NVRAM for debugging purpose.
+**
+**===================================================================
+*/
+#ifdef	SCSI_NCR_DEBUG_NVRAM
+static void __init ncr_display_Symbios_nvram(Symbios_nvram *nvram)
+{
+	int i;
+
+	/* display Symbios nvram host data */
+	printk(KERN_DEBUG NAME53C8XX ": HOST ID=%d%s%s%s%s%s\n",
+		nvram->host_id & 0x0f,
+		(nvram->flags  & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
+		(nvram->flags  & SYMBIOS_PARITY_ENABLE)	? " PARITY"	:"",
+		(nvram->flags  & SYMBIOS_VERBOSE_MSGS)	? " VERBOSE"	:"", 
+		(nvram->flags  & SYMBIOS_CHS_MAPPING)	? " CHS_ALT"	:"", 
+		(nvram->flags1 & SYMBIOS_SCAN_HI_LO)	? " HI_LO"	:"");
+
+	/* display Symbios nvram drive data */
+	for (i = 0 ; i < 15 ; i++) {
+		struct Symbios_target *tn = &nvram->target[i];
+		printk(KERN_DEBUG NAME53C8XX 
+		"-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+		i,
+		(tn->flags & SYMBIOS_DISCONNECT_ENABLE)	? " DISC"	: "",
+		(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)	? " SCAN_BOOT"	: "",
+		(tn->flags & SYMBIOS_SCAN_LUNS)		? " SCAN_LUNS"	: "",
+		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"	: "",
+		tn->bus_width,
+		tn->sync_period / 4,
+		tn->timeout);
+	}
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+static void __init ncr_display_Tekram_nvram(Tekram_nvram *nvram)
+{
+	int i, tags, boot_delay;
+	char *rem;
+
+	/* display Tekram nvram host data */
+	tags = 2 << nvram->max_tags_index;
+	boot_delay = 0;
+	if (nvram->boot_delay_index < 6)
+		boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+	switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+	default:
+	case 0:	rem = "";			break;
+	case 1: rem = " REMOVABLE=boot device";	break;
+	case 2: rem = " REMOVABLE=all";		break;
+	}
+
+	printk(KERN_DEBUG NAME53C8XX
+		": HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+		nvram->host_id & 0x0f,
+		(nvram->flags1 & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
+		(nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES":"",
+		(nvram->flags & TEKRAM_DRIVES_SUP_1GB)	? " >1GB"	:"",
+		(nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"	:"",
+		(nvram->flags & TEKRAM_ACTIVE_NEGATION)	? " ACT_NEG"	:"",
+		(nvram->flags & TEKRAM_IMMEDIATE_SEEK)	? " IMM_SEEK"	:"",
+		(nvram->flags & TEKRAM_SCAN_LUNS)	? " SCAN_LUNS"	:"",
+		(nvram->flags1 & TEKRAM_F2_F6_ENABLED)	? " F2_F6"	:"",
+		rem, boot_delay, tags);
+
+	/* display Tekram nvram drive data */
+	for (i = 0; i <= 15; i++) {
+		int sync, j;
+		struct Tekram_target *tn = &nvram->target[i];
+		j = tn->sync_index & 0xf;
+		sync = Tekram_sync[j];
+		printk(KERN_DEBUG NAME53C8XX "-%d:%s%s%s%s%s%s PERIOD=%d\n",
+		i,
+		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
+		(tn->flags & TEKRAM_SYNC_NEGO)		? " SYNC"	: "",
+		(tn->flags & TEKRAM_DISCONNECT_ENABLE)	? " DISC"	: "",
+		(tn->flags & TEKRAM_START_CMD)		? " START"	: "",
+		(tn->flags & TEKRAM_TAGGED_COMMANDS)	? " TCQ"	: "",
+		(tn->flags & TEKRAM_WIDE_NEGO)		? " WIDE"	: "",
+		sync);
+	}
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
+
+
+/*===================================================================
+**
+**	Utility routines that protperly return data through /proc FS.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
+struct info_str
+{
+	char *buffer;
+	int length;
+	int offset;
+	int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+	if (info->pos + len > info->length)
+		len = info->length - info->pos;
+
+	if (info->pos + len < info->offset) {
+		info->pos += len;
+		return;
+	}
+	if (info->pos < info->offset) {
+		data += (info->offset - info->pos);
+		len  -= (info->offset - info->pos);
+	}
+
+	if (len > 0) {
+		memcpy(info->buffer + info->pos, data, len);
+		info->pos += len;
+	}
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+	va_list args;
+	char buf[81];
+	int len;
+
+	va_start(args, fmt);
+	len = vsprintf(buf, fmt, args);
+	va_end(args);
+
+	copy_mem_info(info, buf, len);
+	return len;
+}
+
+#endif
+
+/*===================================================================
+**
+**	Driver setup from the boot command line
+**
+**===================================================================
+*/
+
+#ifdef MODULE
+#define	ARG_SEP	' '
+#else
+#define	ARG_SEP	','
+#endif
+
+#define OPT_TAGS		1
+#define OPT_MASTER_PARITY	2
+#define OPT_SCSI_PARITY		3
+#define OPT_DISCONNECTION	4
+#define OPT_SPECIAL_FEATURES	5
+#define OPT_ULTRA_SCSI		6
+#define OPT_FORCE_SYNC_NEGO	7
+#define OPT_REVERSE_PROBE	8
+#define OPT_DEFAULT_SYNC	9
+#define OPT_VERBOSE		10
+#define OPT_DEBUG		11
+#define OPT_BURST_MAX		12
+#define OPT_LED_PIN		13
+#define OPT_MAX_WIDE		14
+#define OPT_SETTLE_DELAY	15
+#define OPT_DIFF_SUPPORT	16
+#define OPT_IRQM		17
+#define OPT_PCI_FIX_UP		18
+#define OPT_BUS_CHECK		19
+#define OPT_OPTIMIZE		20
+#define OPT_RECOVERY		21
+#define OPT_SAFE_SETUP		22
+#define OPT_USE_NVRAM		23
+#define OPT_EXCLUDE		24
+#define OPT_HOST_ID		25
+
+#ifdef SCSI_NCR_IARB_SUPPORT
+#define OPT_IARB		26
+#endif
+
+static char setup_token[] __initdata = 
+	"tags:"   "mpar:"
+	"spar:"   "disc:"
+	"specf:"  "ultra:"
+	"fsn:"    "revprob:"
+	"sync:"   "verb:"
+	"debug:"  "burst:"
+	"led:"    "wide:"
+	"settle:" "diff:"
+	"irqm:"   "pcifix:"
+	"buschk:" "optim:"
+	"recovery:"
+	"safe:"   "nvram:"
+	"excl:"   "hostid:"
+#ifdef SCSI_NCR_IARB_SUPPORT
+	"iarb:"
+#endif
+	;	/* DONNOT REMOVE THIS ';' */
+
+#ifdef MODULE
+#define	ARG_SEP	' '
+#else
+#define	ARG_SEP	','
+#endif
+
+static int __init get_setup_token(char *p)
+{
+	char *cur = setup_token;
+	char *pc;
+	int i = 0;
+
+	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+		++pc;
+		++i;
+		if (!strncmp(p, cur, pc - cur))
+			return i;
+		cur = pc;
+	}
+	return 0;
+}
+
+
+static int __init sym53c8xx__setup(char *str)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+	char *cur = str;
+	char *pc, *pv;
+	int i, val, c;
+	int xi = 0;
+
+	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+		char *pe;
+
+		val = 0;
+		pv = pc;
+		c = *++pv;
+
+		if	(c == 'n')
+			val = 0;
+		else if	(c == 'y')
+			val = 1;
+		else
+			val = (int) simple_strtoul(pv, &pe, 0);
+
+		switch (get_setup_token(cur)) {
+		case OPT_TAGS:
+			driver_setup.default_tags = val;
+			if (pe && *pe == '/') {
+				i = 0;
+				while (*pe && *pe != ARG_SEP && 
+					i < sizeof(driver_setup.tag_ctrl)-1) {
+					driver_setup.tag_ctrl[i++] = *pe++;
+				}
+				driver_setup.tag_ctrl[i] = '\0';
+			}
+			break;
+		case OPT_MASTER_PARITY:
+			driver_setup.master_parity = val;
+			break;
+		case OPT_SCSI_PARITY:
+			driver_setup.scsi_parity = val;
+			break;
+		case OPT_DISCONNECTION:
+			driver_setup.disconnection = val;
+			break;
+		case OPT_SPECIAL_FEATURES:
+			driver_setup.special_features = val;
+			break;
+		case OPT_ULTRA_SCSI:
+			driver_setup.ultra_scsi	= val;
+			break;
+		case OPT_FORCE_SYNC_NEGO:
+			driver_setup.force_sync_nego = val;
+			break;
+		case OPT_REVERSE_PROBE:
+			driver_setup.reverse_probe = val;
+			break;
+		case OPT_DEFAULT_SYNC:
+			driver_setup.default_sync = val;
+			break;
+		case OPT_VERBOSE:
+			driver_setup.verbose = val;
+			break;
+		case OPT_DEBUG:
+			driver_setup.debug = val;
+			break;
+		case OPT_BURST_MAX:
+			driver_setup.burst_max = val;
+			break;
+		case OPT_LED_PIN:
+			driver_setup.led_pin = val;
+			break;
+		case OPT_MAX_WIDE:
+			driver_setup.max_wide = val? 1:0;
+			break;
+		case OPT_SETTLE_DELAY:
+			driver_setup.settle_delay = val;
+			break;
+		case OPT_DIFF_SUPPORT:
+			driver_setup.diff_support = val;
+			break;
+		case OPT_IRQM:
+			driver_setup.irqm = val;
+			break;
+		case OPT_PCI_FIX_UP:
+			driver_setup.pci_fix_up	= val;
+			break;
+		case OPT_BUS_CHECK:
+			driver_setup.bus_check = val;
+			break;
+		case OPT_OPTIMIZE:
+			driver_setup.optimize = val;
+			break;
+		case OPT_RECOVERY:
+			driver_setup.recovery = val;
+			break;
+		case OPT_USE_NVRAM:
+			driver_setup.use_nvram = val;
+			break;
+		case OPT_SAFE_SETUP:
+			memcpy(&driver_setup, &driver_safe_setup,
+				sizeof(driver_setup));
+			break;
+		case OPT_EXCLUDE:
+			if (xi < SCSI_NCR_MAX_EXCLUDES)
+				driver_setup.excludes[xi++] = val;
+			break;
+		case OPT_HOST_ID:
+			driver_setup.host_id = val;
+			break;
+#ifdef SCSI_NCR_IARB_SUPPORT
+		case OPT_IARB:
+			driver_setup.iarb = val;
+			break;
+#endif
+		default:
+			printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+			break;
+		}
+
+		if ((cur = strchr(cur, ARG_SEP)) != NULL)
+			++cur;
+	}
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+	return 0;
+}
+
+/*===================================================================
+**
+**	Get device queue depth from boot command line.
+**
+**===================================================================
+*/
+#define DEF_DEPTH	(driver_setup.default_tags)
+#define ALL_TARGETS	-2
+#define NO_TARGET	-1
+#define ALL_LUNS	-2
+#define NO_LUN		-1
+
+static int device_queue_depth(int unit, int target, int lun)
+{
+	int c, h, t, u, v;
+	char *p = driver_setup.tag_ctrl;
+	char *ep;
+
+	h = -1;
+	t = NO_TARGET;
+	u = NO_LUN;
+	while ((c = *p++) != 0) {
+		v = simple_strtoul(p, &ep, 0);
+		switch(c) {
+		case '/':
+			++h;
+			t = ALL_TARGETS;
+			u = ALL_LUNS;
+			break;
+		case 't':
+			if (t != target)
+				t = (target == v) ? v : NO_TARGET;
+			u = ALL_LUNS;
+			break;
+		case 'u':
+			if (u != lun)
+				u = (lun == v) ? v : NO_LUN;
+			break;
+		case 'q':
+			if (h == unit &&
+				(t == ALL_TARGETS || t == target) &&
+				(u == ALL_LUNS    || u == lun))
+				return v;
+			break;
+		case '-':
+			t = ALL_TARGETS;
+			u = ALL_LUNS;
+			break;
+		default:
+			break;
+		}
+		p = ep;
+	}
+	return DEF_DEPTH;
+}
+
+/*===================================================================
+**
+**	Print out information about driver configuration.
+**
+**===================================================================
+*/
+static void __init ncr_print_driver_setup(void)
+{
+#define YesNo(y)	y ? 'y' : 'n'
+	printk (NAME53C8XX ": setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
+		"burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
+		YesNo(driver_setup.disconnection),
+		driver_setup.special_features,
+		driver_setup.ultra_scsi,
+		driver_setup.default_tags,
+		driver_setup.default_sync,
+		driver_setup.burst_max,
+		YesNo(driver_setup.max_wide),
+		driver_setup.diff_support,
+		YesNo(driver_setup.reverse_probe),
+		driver_setup.bus_check);
+
+	printk (NAME53C8XX ": setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
+		"led:%c,settle:%d,irqm:0x%x,nvram:0x%x,pcifix:0x%x\n",
+		YesNo(driver_setup.master_parity),
+		YesNo(driver_setup.scsi_parity),
+		YesNo(driver_setup.force_sync_nego),
+		driver_setup.verbose,
+		driver_setup.debug,
+		YesNo(driver_setup.led_pin),
+		driver_setup.settle_delay,
+		driver_setup.irqm,
+		driver_setup.use_nvram,
+		driver_setup.pci_fix_up);
+#undef YesNo
+}
+
+/*===================================================================
+**
+**   SYM53C8XX devices description table.
+**
+**===================================================================
+*/
+
+static ncr_chip	ncr_chip_table[] __initdata	= SCSI_NCR_CHIP_TABLE;
+
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+/*===================================================================
+**
+**    Detect all NCR PQS/PDS boards and keep track of their bus nr.
+**
+**    The NCR PQS or PDS card is constructed as a DEC bridge
+**    behind which sit a proprietary NCR memory controller and
+**    four or two 53c875s as separate devices.  In its usual mode
+**    of operation, the 875s are slaved to the memory controller
+**    for all transfers.  We can tell if an 875 is part of a
+**    PQS/PDS or not since if it is, it will be on the same bus
+**    as the memory controller.  To operate with the Linux
+**    driver, the memory controller is disabled and the 875s
+**    freed to function independently.  The only wrinkle is that
+**    the preset SCSI ID (which may be zero) must be read in from
+**    a special configuration space register of the 875.
+**
+**===================================================================
+*/
+#define	SCSI_NCR_MAX_PQS_BUS	16
+static int pqs_bus[SCSI_NCR_MAX_PQS_BUS] __initdata = { 0 };
+
+static void __init ncr_detect_pqs_pds(void)
+{
+	short index;
+	pcidev_t dev = PCIDEV_NULL;
+
+	for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index++) {
+		u_char tmp;
+
+		dev = pci_find_device(0x101a, 0x0009, dev);
+		if (dev == PCIDEV_NULL) {
+			pqs_bus[index] = -1;
+			break;
+		}
+		printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", PciBusNumber(dev));
+		pci_read_config_byte(dev, 0x44, &tmp);
+		/* bit 1: allow individual 875 configuration */
+		tmp |= 0x2;
+		pci_write_config_byte(dev, 0x44, tmp);
+		pci_read_config_byte(dev, 0x45, &tmp);
+		/* bit 2: drive individual 875 interrupts to the bus */
+		tmp |= 0x4;
+		pci_write_config_byte(dev, 0x45, tmp);
+
+		pqs_bus[index] = PciBusNumber(dev);
+	}
+}
+#endif /* SCSI_NCR_PQS_PDS_SUPPORT */
+
+/*===================================================================
+**
+**   Read and check the PCI configuration for any detected NCR 
+**   boards and save data for attaching after all boards have 
+**   been detected.
+**
+**===================================================================
+*/
+static int __init
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
+{
+	u_short vendor_id, device_id, command;
+	u_char cache_line_size, latency_timer;
+	u_char suggested_cache_line_size = 0;
+	u_char pci_fix_up = driver_setup.pci_fix_up;
+	u_char revision;
+	u_int irq;
+	u_long base, base_2, io_port; 
+	int i;
+	ncr_chip *chip;
+
+	printk(KERN_INFO NAME53C8XX ": at PCI bus %d, device %d, function %d\n",
+		PciBusNumber(pdev),
+		(int) (PciDeviceFn(pdev) & 0xf8) >> 3,
+		(int) (PciDeviceFn(pdev) & 7));
+
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+	if (!pci_dma_supported(pdev, (dma_addr_t) (0xffffffffUL))) {
+		printk(KERN_WARNING NAME53C8XX
+		       "32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
+		return -1;
+	}
+#endif
+
+	/*
+	**    Read info from the PCI config space.
+	**    pci_read_config_xxx() functions are assumed to be used for 
+	**    successfully detected PCI devices.
+	*/
+	vendor_id = PciVendorId(pdev);
+	device_id = PciDeviceId(pdev);
+	irq	  = PciIrqLine(pdev);
+	i =	0;
+	i =	pci_get_base_address(pdev, i, &io_port);
+	i =	pci_get_base_address(pdev, i, &base);
+	(void)	pci_get_base_address(pdev, i, &base_2);
+
+	pci_read_config_word(pdev, PCI_COMMAND,		&command);
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION,	&revision);
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE,	&cache_line_size);
+	pci_read_config_byte(pdev, PCI_LATENCY_TIMER,	&latency_timer);
+
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+	/*
+	**    Match the BUS number for PQS/PDS devices.
+	**    Read the SCSI ID from a special register mapped
+	**    into the configuration space of the individual
+	**    875s.  This register is set up by the PQS bios
+	*/
+	for(i = 0; i < SCSI_NCR_MAX_PQS_BUS && pqs_bus[i] != -1; i++) {
+		u_char tmp;
+		if (pqs_bus[i] == PciBusNumber(pdev)) {
+			pci_read_config_byte(pdev, 0x84, &tmp);
+			device->pqs_pds = 1;
+			device->host_id = tmp;
+			break;
+		}
+	}
+#endif /* SCSI_NCR_PQS_PDS_SUPPORT */
+
+	/*
+	**	If user excludes this chip, donnot initialize it.
+	*/
+	for (i = 0 ; i < SCSI_NCR_MAX_EXCLUDES ; i++) {
+		if (driver_setup.excludes[i] ==
+				(io_port & PCI_BASE_ADDRESS_IO_MASK))
+			return -1;
+	}
+	/*
+	**    Check if the chip is supported
+	*/
+	chip = 0;
+	for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+		if (device_id != ncr_chip_table[i].device_id)
+			continue;
+		if (revision > ncr_chip_table[i].revision_id)
+			continue;
+		chip = &device->chip;
+		memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+		chip->revision_id = revision;
+		break;
+	}
+
+	/*
+	**	Ignore Symbios chips controlled by SISL RAID controller.
+	**	This controller sets value 0x52414944 at RAM end - 16.
+	*/
+#if defined(__i386__) && !defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED)
+	if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
+		unsigned int ram_size, ram_val;
+		u_long ram_ptr;
+
+		if (chip->features & FE_RAM8K)
+			ram_size = 8192;
+		else
+			ram_size = 4096;
+
+		ram_ptr = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
+					ram_size);
+		if (ram_ptr) {
+			ram_val = readl_raw(ram_ptr + ram_size - 16);
+			unmap_pci_mem(ram_ptr, ram_size);
+			if (ram_val == 0x52414944) {
+				printk(NAME53C8XX": not initializing, "
+				       "driven by SISL RAID controller.\n");
+				return -1;
+			}
+		}
+	}
+#endif /* i386 and PCI MEMORY accessible */
+
+	if (!chip) {
+		printk(NAME53C8XX ": not initializing, device not supported\n");
+		return -1;
+	}
+
+#ifdef __powerpc__
+	/*
+	**	Fix-up for power/pc.
+	**	Should not be performed by the driver.
+	*/
+	if ((command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+		    != (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
+		printk(NAME53C8XX ": setting%s%s...\n",
+		(command & PCI_COMMAND_IO)     ? "" : " PCI_COMMAND_IO",
+		(command & PCI_COMMAND_MEMORY) ? "" : " PCI_COMMAND_MEMORY");
+		command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+	}
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0)
+	if ( is_prep ) {
+		if (io_port >= 0x10000000) {
+			printk(NAME53C8XX ": reallocating io_port (Wacky IBM)");
+			io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_0, io_port);
+		}
+		if (base >= 0x10000000) {
+			printk(NAME53C8XX ": reallocating base (Wacky IBM)");
+			base = (base & 0x00FFFFFF) | 0x01000000;
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_1, base);
+		}
+		if (base_2 >= 0x10000000) {
+			printk(NAME53C8XX ": reallocating base2 (Wacky IBM)");
+			base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_2, base_2);
+		}
+	}
+#endif
+#endif	/* __powerpc__ */
+
+#ifdef __sparc__
+	/*
+	**    Fix-ups for sparc.
+	*/
+	if (!cache_line_size)
+		suggested_cache_line_size = 16;
+
+	driver_setup.pci_fix_up |= 0x7;
+#endif	/* __sparc__ */
+
+#if defined(__i386__) && !defined(MODULE)
+	if (!cache_line_size) {
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
+		extern char x86;
+		switch(x86) {
+#else
+		switch(boot_cpu_data.x86) {
+#endif
+		case 4:	suggested_cache_line_size = 4; break;
+		case 6:
+		case 5:	suggested_cache_line_size = 8; break;
+		}
+	}
+#endif	/* __i386__ */
+
+	/*
+	**    Check availability of IO space, memory space.
+	**    Enable master capability if not yet.
+	**
+	**    We shouldn't have to care about the IO region when 
+	**    we are using MMIO. But calling check_region() from 
+	**    both the ncr53c8xx and the sym53c8xx drivers prevents 
+	**    from attaching devices from the both drivers.
+	**    If you have a better idea, let me know.
+	*/
+/* #ifdef NCR_IOMAPPED */
+#if 1
+	if (!(command & PCI_COMMAND_IO)) { 
+		printk(NAME53C8XX ": I/O base address (0x%lx) disabled.\n",
+			(long) io_port);
+		io_port = 0;
+	}
+#endif
+	if (!(command & PCI_COMMAND_MEMORY)) {
+		printk(NAME53C8XX ": PCI_COMMAND_MEMORY not set.\n");
+		base	= 0;
+		base_2	= 0;
+	}
+	io_port &= PCI_BASE_ADDRESS_IO_MASK;
+	base	&= PCI_BASE_ADDRESS_MEM_MASK;
+	base_2	&= PCI_BASE_ADDRESS_MEM_MASK;
+
+/* #ifdef NCR_IOMAPPED */
+#if 1
+	if (io_port && check_region (io_port, 128)) {
+		printk(NAME53C8XX ": IO region 0x%lx[0..127] is in use\n",
+			(long) io_port);
+		io_port = 0;
+	}
+	if (!io_port)
+		return -1;
+#endif
+#ifndef NCR_IOMAPPED
+	if (!base) {
+		printk(NAME53C8XX ": MMIO base address disabled.\n");
+		return -1;
+	}
+#endif
+
+/* The ncr53c8xx driver never did set the PCI parity bit.	*/
+/* Since setting this bit is known to trigger spurious MDPE	*/
+/* errors on some 895 controllers when noise on power lines is	*/
+/* too high, I donnot want to change previous ncr53c8xx driver	*/
+/* behaviour on that point (the sym53c8xx driver set this bit).	*/
+#if 0
+	/*
+	**    Set MASTER capable and PARITY bit, if not yet.
+	*/
+	if ((command & (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY))
+		     != (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY)) {
+		printk(NAME53C8XX ": setting%s%s...(fix-up)\n",
+		(command & PCI_COMMAND_MASTER) ? "" : " PCI_COMMAND_MASTER",
+		(command & PCI_COMMAND_PARITY) ? "" : " PCI_COMMAND_PARITY");
+		command |= (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+	}
+#else
+	/*
+	**    Set MASTER capable if not yet.
+	*/
+	if ((command & PCI_COMMAND_MASTER) != PCI_COMMAND_MASTER) {
+		printk(NAME53C8XX ": setting PCI_COMMAND_MASTER...(fix-up)\n");
+		command |= PCI_COMMAND_MASTER;
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+	}
+#endif
+
+	/*
+	**    Fix some features according to driver setup.
+	*/
+	if (!(driver_setup.special_features & 1))
+		chip->features &= ~FE_SPECIAL_SET;
+	else {
+		if (driver_setup.special_features & 2)
+			chip->features &= ~FE_WRIE;
+		if (driver_setup.special_features & 4)
+			chip->features &= ~FE_NOPM;
+	}
+	if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+		chip->features |=  FE_ULTRA;
+		chip->features &= ~FE_ULTRA2;
+	}
+	if (driver_setup.ultra_scsi < 1)
+		chip->features &= ~FE_ULTRA;
+	if (!driver_setup.max_wide)
+		chip->features &= ~FE_WIDE;
+
+	/*
+	**	Some features are required to be enabled in order to 
+	**	work around some chip problems. :) ;)
+	**	(ITEM 12 of a DEL about the 896 I haven't yet).
+	**	We must ensure the chip will use WRITE AND INVALIDATE.
+	**	The revision number limit is for now arbitrary.
+	*/
+	if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision <= 0x10) {
+		chip->features	|= (FE_WRIE | FE_CLSE);
+		pci_fix_up	|=  3;	/* Force appropriate PCI fix-up */
+	}
+
+#ifdef	SCSI_NCR_PCI_FIX_UP_SUPPORT
+	/*
+	**    Try to fix up PCI config according to wished features.
+	*/
+	if ((pci_fix_up & 1) && (chip->features & FE_CLSE) && 
+	    !cache_line_size && suggested_cache_line_size) {
+		cache_line_size = suggested_cache_line_size;
+		pci_write_config_byte(pdev,
+				      PCI_CACHE_LINE_SIZE, cache_line_size);
+		printk(NAME53C8XX ": PCI_CACHE_LINE_SIZE set to %d (fix-up).\n",
+			cache_line_size);
+	}
+
+	if ((pci_fix_up & 2) && cache_line_size &&
+	    (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+		printk(NAME53C8XX": setting PCI_COMMAND_INVALIDATE (fix-up)\n");
+		command |= PCI_COMMAND_INVALIDATE;
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+	}
+
+	/*
+	**    Tune PCI LATENCY TIMER according to burst max length transfer.
+	**    (latency timer >= burst length + 6, we add 10 to be quite sure)
+	*/
+
+	if (chip->burst_max && (latency_timer == 0 || (pci_fix_up & 4))) {
+		u_char lt = (1 << chip->burst_max) + 6 + 10;
+		if (latency_timer < lt) {
+			printk(NAME53C8XX 
+			       ": changing PCI_LATENCY_TIMER from %d to %d.\n",
+			       (int) latency_timer, (int) lt);
+			latency_timer = lt;
+			pci_write_config_byte(pdev,
+					      PCI_LATENCY_TIMER, latency_timer);
+		}
+	}
+
+#endif	/* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ 	/*
+	**    Initialise ncr_device structure with items required by ncr_attach.
+	*/
+	device->pdev		= pdev;
+	device->slot.bus	= PciBusNumber(pdev);
+	device->slot.device_fn	= PciDeviceFn(pdev);
+	device->slot.base	= base;
+	device->slot.base_2	= base_2;
+	device->slot.io_port	= io_port;
+	device->slot.irq	= irq;
+	device->attach_done	= 0;
+
+	return 0;
+}
+
+/*===================================================================
+**
+**    Detect all 53c8xx hosts and then attach them.
+**
+**    If we are using NVRAM, once all hosts are detected, we need to 
+**    check any NVRAM for boot order in case detect and boot order 
+**    differ and attach them using the order in the NVRAM.
+**
+**    If no NVRAM is found or data appears invalid attach boards in 
+**    the the order they are detected.
+**
+**===================================================================
+*/
+static int __init 
+sym53c8xx__detect(Scsi_Host_Template *tpnt, u_short ncr_chip_ids[], int chips)
+{
+	pcidev_t pcidev;
+	int i, j, hosts, count;
+	int attach_count = 0;
+	ncr_device *devtbl, *devp;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	ncr_nvram  nvram0, nvram, *nvp;
+#endif
+
+	/*
+	**    PCI is required.
+	*/
+	if (!pci_present())
+		return 0;
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+	ncr_debug = driver_setup.debug;
+#endif
+	if (initverbose >= 2)
+		ncr_print_driver_setup();
+
+	/*
+	**	Allocate the device table since we donnot want to 
+	**	overflow the kernel stack.
+	**	1 x 4K PAGE is enough for more than 40 devices for i386.
+	*/
+	devtbl = m_calloc(PAGE_SIZE, "devtbl");
+	if (!devtbl)
+		return 0;
+
+	/*
+	**    Detect all NCR PQS/PDS memory controllers.
+	*/
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+	ncr_detect_pqs_pds();
+#endif
+
+	/* 
+	**    Detect all 53c8xx hosts.
+	**    Save the first Symbios NVRAM content if any 
+	**    for the boot order.
+	*/
+	hosts	= PAGE_SIZE		/ sizeof(*devtbl);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
+#endif
+	j = 0;
+	count = 0;
+	pcidev = PCIDEV_NULL;
+	while (1) {
+		char *msg = "";
+		if (count >= hosts)
+			break;
+		if (j >= chips)
+			break;
+		i = driver_setup.reverse_probe ? chips - 1 - j : j;
+		pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+					 pcidev);
+		if (pcidev == PCIDEV_NULL) {
+			++j;
+			continue;
+		}
+		/* Some HW as the HP LH4 may report twice PCI devices */
+		for (i = 0; i < count ; i++) {
+			if (devtbl[i].slot.bus	     == PciBusNumber(pcidev) && 
+			    devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
+				break;
+		}
+		if (i != count)	/* Ignore this device if we already have it */
+			continue;
+		devp = &devtbl[count];
+		devp->host_id = driver_setup.host_id;
+		devp->attach_done = 0;
+		if (sym53c8xx_pci_init(tpnt, pcidev, devp)) {
+			continue;
+		}
+		++count;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+		if (nvp) {
+			ncr_get_nvram(devp, nvp);
+			switch(nvp->type) {
+			case SCSI_NCR_SYMBIOS_NVRAM:
+				/*
+				 *   Switch to the other nvram buffer, so that 
+				 *   nvram0 will contain the first Symbios 
+				 *   format NVRAM content with boot order.
+				 */
+				nvp = &nvram;
+				msg = "with Symbios NVRAM";
+				break;
+			case SCSI_NCR_TEKRAM_NVRAM:
+				msg = "with Tekram NVRAM";
+				break;
+			}
+		}
+#endif
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+		if (devp->pqs_pds)
+			msg = "(NCR PQS/PDS)";
+#endif
+		printk(KERN_INFO NAME53C8XX ": 53c%s detected %s\n",
+		       devp->chip.name, msg);
+	}
+
+	/*
+	**    If we have found a SYMBIOS NVRAM, use first the NVRAM boot 
+	**    sequence as device boot order.
+	**    check devices in the boot record against devices detected. 
+	**    attach devices if we find a match. boot table records that 
+	**    do not match any detected devices will be ignored. 
+	**    devices that do not match any boot table will not be attached
+	**    here but will attempt to be attached during the device table 
+	**    rescan.
+	*/
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	if (!nvp || nvram0.type != SCSI_NCR_SYMBIOS_NVRAM)
+		goto next;
+	for (i = 0; i < 4; i++) {
+		Symbios_host *h = &nvram0.data.Symbios.host[i];
+		for (j = 0 ; j < count ; j++) {
+			devp = &devtbl[j];
+			if (h->device_fn != devp->slot.device_fn ||
+			    h->bus_nr	 != devp->slot.bus	 ||
+			    h->device_id != devp->chip.device_id)
+				continue;
+			if (devp->attach_done)
+				continue;
+			if (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) {
+				ncr_get_nvram(devp, nvp);
+				if (!ncr_attach (tpnt, attach_count, devp))
+					attach_count++;
+			}
+			else if (!(driver_setup.use_nvram & 0x80))
+				printk(KERN_INFO NAME53C8XX
+				       ": 53c%s state OFF thus not attached\n",
+				       devp->chip.name);
+			else
+				continue;
+
+			devp->attach_done = 1;
+			break;
+		}
+	}
+next:
+#endif
+
+	/* 
+	**    Rescan device list to make sure all boards attached.
+	**    Devices without boot records will not be attached yet
+	**    so try to attach them here.
+	*/
+	for (i= 0; i < count; i++) {
+		devp = &devtbl[i];
+		if (!devp->attach_done) {
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+			ncr_get_nvram(devp, nvp);
+#endif
+			if (!ncr_attach (tpnt, attach_count, devp))
+				attach_count++;
+		}
+	}
+
+	m_free(devtbl, PAGE_SIZE, "devtbl");
+
+	return attach_count;
+}
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.3.51/linux/drivers/sound/Config.in	Fri Mar 10 16:40:43 2000
+++ linux/drivers/sound/Config.in	Mon Mar 13 12:29:00 2000
@@ -81,11 +81,14 @@
 dep_tristate '  OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
 
 if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
+   bool '      Verbose initialisation' CONFIG_SOUND_TRACEINIT
+   bool '      Persistent DMA buffers' CONFIG_SOUND_DMAP
+
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       dep_tristate '    AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_AD1816 $CONFIG_SOUND
    fi
-
    dep_tristate '    Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS
+   dep_tristate '    Adlib Cards' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS
    dep_tristate '    ACI mixer (miroPCM12)' CONFIG_SOUND_ACI_MIXER $CONFIG_SOUND_OSS
    dep_tristate '    Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS
    dep_tristate '    Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.3.51/linux/drivers/sound/Makefile	Fri Mar 10 16:40:43 2000
+++ linux/drivers/sound/Makefile	Sat Mar 11 11:28:47 2000
@@ -44,26 +44,27 @@
 obj-$(CONFIG_SOUND_AEDSP16)	+= aedsp16.o
 obj-$(CONFIG_SOUND_PSS)		+= pss.o ad1848.o mpu401.o
 obj-$(CONFIG_SOUND_TRIX)	+= trix.o ad1848.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_OPL3SA1)     += opl3sa.o ad1848.o uart401.o
-obj-$(CONFIG_SOUND_SOFTOSS)     += softoss2.o
-obj-$(CONFIG_SOUND_SSCAPE)      += sscape.o ad1848.o mpu401.o
-obj-$(CONFIG_SOUND_MAD16)       += mad16.o ad1848.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_OPL3SA1)	+= opl3sa.o ad1848.o uart401.o
+obj-$(CONFIG_SOUND_SOFTOSS)	+= softoss2.o
+obj-$(CONFIG_SOUND_SSCAPE)	+= sscape.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_MAD16)	+= mad16.o ad1848.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_CS4232)	+= cs4232.o uart401.o
-obj-$(CONFIG_SOUND_OPL3SA2)     += opl3sa2.o ad1848.o uart401.o mpu401.o
-obj-$(CONFIG_SOUND_MSS)         += ad1848.o
-obj-$(CONFIG_SOUND_PAS)         += pas2.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_SB)          += sb.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_WAVEFRONT)   += wavefront.o
-obj-$(CONFIG_SOUND_MAUI)        += maui.o mpu401.o
-obj-$(CONFIG_SOUND_MPU401)      += mpu401.o
-obj-$(CONFIG_SOUND_UART6850)    += uart6850.o
+obj-$(CONFIG_SOUND_OPL3SA2)	+= opl3sa2.o ad1848.o uart401.o mpu401.o
+obj-$(CONFIG_SOUND_MSS)		+= ad1848.o
+obj-$(CONFIG_SOUND_PAS)		+= pas2.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_SB)		+= sb.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_WAVEFRONT)	+= wavefront.o
+obj-$(CONFIG_SOUND_MAUI)	+= maui.o mpu401.o
+obj-$(CONFIG_SOUND_MPU401)	+= mpu401.o
+obj-$(CONFIG_SOUND_UART6850)	+= uart6850.o
 obj-$(CONFIG_SOUND_GUS)		+= gus.o ad1848.o
-obj-$(CONFIG_SOUND_YM3812)      += adlib_card.o opl3.o
-obj-$(CONFIG_SOUND_VMIDI)       += v_midi.o
-obj-$(CONFIG_SOUND_VIDC)        += vidc_mod.o
-obj-$(CONFIG_SOUND_WAVEARTIST)  += waveartist.o
-obj-$(CONFIG_SOUND_SGALAXY)     += sgalaxy.o ad1848.o
-obj-$(CONFIG_SOUND_AD1816)      += ad1816.o
+obj-$(CONFIG_SOUND_ADLIB)	+= adlib_card.o opl3.o
+obj-$(CONFIG_SOUND_YM3812)	+= opl3.o
+obj-$(CONFIG_SOUND_VMIDI)	+= v_midi.o
+obj-$(CONFIG_SOUND_VIDC)	+= vidc_mod.o
+obj-$(CONFIG_SOUND_WAVEARTIST)	+= waveartist.o
+obj-$(CONFIG_SOUND_SGALAXY)	+= sgalaxy.o ad1848.o
+obj-$(CONFIG_SOUND_AD1816)	+= ad1816.o
 obj-$(CONFIG_SOUND_ACI_MIXER)	+= aci.o
 obj-$(CONFIG_SOUND_AWE32_SYNTH)	+= awe_wave.o
 
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.3.51/linux/drivers/sound/dev_table.c	Fri Mar 10 16:40:44 2000
+++ linux/drivers/sound/dev_table.c	Mon Mar 13 12:29:00 2000
@@ -17,8 +17,6 @@
 #include "sound_config.h"
 
 int softoss_dev = 0;
-int sound_started = 0;
-int sndtable_get_cardcount(void);
 
 int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
 			int driver_size, int flags, unsigned int format_mask,
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v2.3.51/linux/drivers/sound/dev_table.h	Fri Mar 10 16:40:44 2000
+++ linux/drivers/sound/dev_table.h	Mon Mar 13 12:29:00 2000
@@ -43,8 +43,6 @@
  *	NOTE! 	NOTE!	NOTE!	NOTE!
  */
 
-extern int sound_started;
-
 struct driver_info 
 {
 	char *driver_id;
@@ -350,11 +348,14 @@
 
 #ifdef _DEV_TABLE_C_   
 
-struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0;
-struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
-struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0;
-struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
-
+struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL};
+int num_audiodevs = 0;
+struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL};
+int num_mixers = 0;
+struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL};
+int num_synths = 0;
+struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL};
+int num_midis = 0;
 #ifndef EXCLUDE_TIMERS
 extern struct sound_timer_operations default_sound_timer;
 struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
@@ -370,18 +371,17 @@
 
 
 #else
-extern struct audio_operations * audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs;
-extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
-extern struct synth_operations * synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_synths;
-extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
-extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; extern int num_sound_timers;
+extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
+extern int num_audiodevs;
+extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
+extern int num_mixers;
+extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
+extern int num_synths;
+extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
+extern int num_midis;
+extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
+extern int num_sound_timers;
 #endif	/* _DEV_TABLE_C_ */
-void setup_cards(void);
-int sndtable_get_cardcount (void);
-void sound_chconf(int card_type, int ioaddr, int irq, int dma);
-int snd_find_driver(int type);
-void sound_unload_driver(int type);
-int sndtable_identify_card(char *name);
 
 extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
 int sndtable_probe (int unit, struct address_info *hw_config);
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.3.51/linux/drivers/sound/dmabuf.c	Thu Mar  2 14:36:23 2000
+++ linux/drivers/sound/dmabuf.c	Sun Mar 12 19:39:48 2000
@@ -66,6 +66,14 @@
 	if (dma_buffsize < 4096)
 		dma_buffsize = 4096;
 	dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
+	
+	/*
+	 *	Now check for the Cyrix problem.
+	 */
+	 
+	if(isa_dma_bridge_buggy==2)
+		dma_pagesize=32768;
+	 
 	dmap->raw_buf = NULL;
 	dmap->buffsize = dma_buffsize;
 	if (dmap->buffsize > dma_pagesize)
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/miroaci.h linux/drivers/sound/miroaci.h
--- v2.3.51/linux/drivers/sound/miroaci.h	Fri Mar 10 16:40:44 2000
+++ linux/drivers/sound/miroaci.h	Sun Mar 12 19:13:06 2000
@@ -1,4 +1,3 @@
-#include <linux/config.h>
 extern int aci_implied_cmd(unsigned char opcode);
 extern int aci_write_cmd(unsigned char opcode, unsigned char parameter);
 extern int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2);
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v2.3.51/linux/drivers/sound/mpu401.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/sound/mpu401.c	Mon Mar 13 12:34:01 2000
@@ -1726,25 +1726,24 @@
 {
 	/* Can be loaded either for module use or to provide functions
 	   to others */
-	cfg.irq = irq;
-	cfg.io_base = io;
-	
-	if (cfg.io_base != -1 && cfg.irq != -1) {
-		printk(KERN_WARNING "mpu401: need io and irq !");
-		return -ENODEV;
+	if (io != -1 && irq != -1) {
+	        cfg.irq = irq;
+		cfg.io_base = io;
+		if (probe_mpu401(&cfg) == 0)
+			return -ENODEV;
+		attach_mpu401(&cfg);
 	}
 	
-	if (probe_mpu401(&cfg) == 0)
-		return -ENODEV;
-	attach_mpu401(&cfg);
-
 	SOUND_LOCK;
 	return 0;
 }
 
 void cleanup_mpu401(void)
 {
-	unload_mpu401(&cfg);
+	if (io != -1 && irq != -1) {
+		/* Check for use by, for example, sscape driver */
+		unload_mpu401(&cfg);
+	}
 	SOUND_LOCK_END;
 }
 
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c
--- v2.3.51/linux/drivers/sound/sb_card.c	Fri Mar 10 16:40:44 2000
+++ linux/drivers/sound/sb_card.c	Mon Mar 13 12:29:00 2000
@@ -399,7 +399,6 @@
 	/*  @X@0001:mpu
 	 */
 
-#ifdef CONFIG_MIDI
 	if((mpu_dev = isapnp_find_dev(bus,
 				ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL)))
 	{
@@ -413,7 +412,6 @@
 	}
 	else
 		printk(KERN_ERR "sb: DT0197H panic: mpu not found\n");
-#endif
 
 
 	/*  @P@:Gameport
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.3.51/linux/drivers/sound/sound_core.c	Wed Feb 16 17:03:52 2000
+++ linux/drivers/sound/sound_core.c	Tue Mar 14 17:54:42 2000
@@ -217,6 +217,16 @@
 
 static struct sound_unit *chains[16];
 
+/**
+ *	register_sound_special
+ *	@fops: File operations for the driver
+ *	@unit: Unit number to allocate
+ *
+ *	Allocate a special sound device by minor number from the sound
+ *	subsystem. The allocated number is returned on succes. On failure
+ *	a negative error code is returned.
+ */
+ 
 int register_sound_special(struct file_operations *fops, int unit)
 {
 	char *name;
@@ -240,8 +250,8 @@
 	    case 5:
 		name = "unknown5";
 		break;
-	    case 6:
-		name = "sndstat";
+	    case 6:		/* Was once sndstat */
+		name = "unknown6";
 		break;
 	    case 7:
 		name = "unknown7";
@@ -272,23 +282,43 @@
 		break;
 	}
 	return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1,
-				 name, S_IRUGO | S_IWUGO);
+				 name, S_IRUSR | S_IWUSR);
 }
  
 EXPORT_SYMBOL(register_sound_special);
 
+/**
+ *	register_sound_mixer
+ *	@fops: File operations for the driver
+ *	@dev: Unit number to allocate
+ *
+ *	Allocate a mixer device. Unit is the number of the mixer requested.
+ *	Pass -1 to request the next free mixer unit. On success the allocated
+ *	number is returned, on failure a negative error code is returned.
+ */
+
 int register_sound_mixer(struct file_operations *fops, int dev)
 {
 	return sound_insert_unit(&chains[0], fops, dev, 0, 128,
-				 "mixer", S_IRUGO | S_IWUGO);
+				 "mixer", S_IRUSR | S_IWUSR);
 }
 
 EXPORT_SYMBOL(register_sound_mixer);
 
+/**
+ *	register_sound_midi
+ *	@fops: File operations for the driver
+ *	@dev: Unit number to allocate
+ *
+ *	Allocate a midi device. Unit is the number of the midi device requested.
+ *	Pass -1 to request the next free midi unit. On success the allocated
+ *	number is returned, on failure a negative error code is returned.
+ */
+
 int register_sound_midi(struct file_operations *fops, int dev)
 {
 	return sound_insert_unit(&chains[2], fops, dev, 2, 130,
-				 "midi", S_IRUGO | S_IWUGO);
+				 "midi", S_IRUSR | S_IWUSR);
 }
 
 EXPORT_SYMBOL(register_sound_midi);
@@ -298,22 +328,55 @@
  *	in open - see below.
  */
  
+/**
+ *	register_sound_dsp
+ *	@fops: File operations for the driver
+ *	@dev: Unit number to allocate
+ *
+ *	Allocate a DSP device. Unit is the number of the DSP requested.
+ *	Pass -1 to request the next free DSP unit. On success the allocated
+ *	number is returned, on failure a negative error code is returned.
+ *
+ *	This function allocates both the audio and dsp device entries together
+ *	and will always allocate them as a matching pair - eg dsp3/audio3
+ */
+
 int register_sound_dsp(struct file_operations *fops, int dev)
 {
 	return sound_insert_unit(&chains[3], fops, dev, 3, 131,
-				 "dsp", S_IWUGO | S_IRUSR | S_IRGRP);
+				 "dsp", S_IWUSR | S_IRUSR);
 }
 
 EXPORT_SYMBOL(register_sound_dsp);
 
+/**
+ *	register_sound_synth
+ *	@fops: File operations for the driver
+ *	@dev: Unit number to allocate
+ *
+ *	Allocate a synth device. Unit is the number of the synth device requested.
+ *	Pass -1 to request the next free synth unit. On success the allocated
+ *	number is returned, on failure a negative error code is returned.
+ */
+
+
 int register_sound_synth(struct file_operations *fops, int dev)
 {
 	return sound_insert_unit(&chains[9], fops, dev, 9, 137,
-				 "synth", S_IRUGO | S_IWUGO);
+				 "synth", S_IRUSR | S_IWUSR);
 }
 
 EXPORT_SYMBOL(register_sound_synth);
 
+/**
+ *	unregister_sound_special
+ *	@unit: Unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_special.
+ *	The unit passed is the return value from the register function.
+ */
+
+
 void unregister_sound_special(int unit)
 {
 	sound_remove_unit(&chains[unit&15], unit);
@@ -321,6 +384,14 @@
  
 EXPORT_SYMBOL(unregister_sound_special);
 
+/**
+ *	unregister_sound_mixer
+ *	@unit: Unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_mixer.
+ *	The unit passed is the return value from the register function.
+ */
+
 void unregister_sound_mixer(int unit)
 {
 	sound_remove_unit(&chains[0], unit);
@@ -328,6 +399,14 @@
 
 EXPORT_SYMBOL(unregister_sound_mixer);
 
+/**
+ *	unregister_sound_midi
+ *	@unit: Unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_midi.
+ *	The unit passed is the return value from the register function.
+ */
+
 void unregister_sound_midi(int unit)
 {
 	return sound_remove_unit(&chains[2], unit);
@@ -335,12 +414,31 @@
 
 EXPORT_SYMBOL(unregister_sound_midi);
 
+/**
+ *	unregister_sound_dsp
+ *	@unit: Unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_dsp.
+ *	The unit passed is the return value from the register function.
+ *
+ *	Both of the allocated units are released together automatically.
+ */
+
 void unregister_sound_dsp(int unit)
 {
 	return sound_remove_unit(&chains[3], unit);
 }
 
+
 EXPORT_SYMBOL(unregister_sound_dsp);
+
+/**
+ *	unregister_sound_synth
+ *	@unit: Unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_synth.
+ *	The unit passed is the return value from the register function.
+ */
 
 void unregister_sound_synth(int unit)
 {
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/sound_firmware.c linux/drivers/sound/sound_firmware.c
--- v2.3.51/linux/drivers/sound/sound_firmware.c	Mon Jan  4 11:37:30 1999
+++ linux/drivers/sound/sound_firmware.c	Tue Mar 14 17:54:42 2000
@@ -47,6 +47,24 @@
 	return (int) l;
 }
 
+/**
+ *	mod_firmware_load - load sound driver firmware
+ *	@fn: filename
+ *	@fp: return for the buffer.
+ *
+ *	Load the firmware for a sound module (up to 128K) into a buffer.
+ *	The buffer is returned in *fp. It is allocated with vmalloc so is
+ *	virtually linear and not DMAable. The caller should free it with
+ *	vfree when finished.
+ *
+ *	The length of the buffer is returned on a successful load, the
+ *	value zero on a failure.
+ *
+ *	Caution: This API is not recommended. Firmware should be loaded via
+ *	an ioctl call and a setup application. This function may disappear
+ *	in future.
+ */
+ 
 int mod_firmware_load(const char *fn, char **fp)
 {
 	int r;
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.3.51/linux/drivers/sound/soundcard.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/sound/soundcard.c	Mon Mar 13 22:19:55 2000
@@ -74,7 +74,12 @@
 int             sound_nblocks = 0;
 
 /* Persistent DMA buffers */
-int		sound_dmap_flag = 0;
+#ifdef CONFIG_SOUND_DMAP
+int             sound_dmap_flag = 1;
+#else
+int             sound_dmap_flag = 0;
+#endif
+
 static int      soundcard_configured = 0;
 static char     dma_alloc_map[MAX_DMA_CHANNELS] = {0};
 
@@ -92,8 +97,6 @@
 static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
 static int num_mixer_volumes = 0;
 
-int traceinit = 0;
-
 int *load_mixer_volumes(char *name, int *levels, int present)
 {
 	int             i, n;
@@ -637,11 +640,6 @@
 
 	soundcard_configured = 1;
 
-#if defined(CONFIG_LOWLEVEL_SOUND) && !defined(MODULE)
-        sound_preinit_lowlevel_drivers();
-	sound_init_lowlevel_drivers();
-#endif
-
 	audio_init_devices();
 
 	soundcard_register_devfs(1); /* register after we know # of devices */
@@ -663,38 +661,15 @@
 static int dmabuf = 0;
 static int dmabug = 0;
 
-MODULE_PARM(traceinit, "i");
 MODULE_PARM(dmabuf, "i");
 MODULE_PARM(dmabug, "i");
 
 int init_module(void)
 {
 	int             err;
-#if FIXED_FOR_2_4_0
-	int             ints[21];
-	int             i;
-#endif
 
-#ifdef HAS_BRIDGE_BUGGY_FUNC
 	if(dmabug)
 		isa_dma_bridge_buggy = dmabug;
-#else
-	if(dmabug)
-		printk(KERN_ERR "sound: rebuild with PCI_QUIRKS enabled to configure this.\n");
-#endif
-		
-#if FIXED_FOR_2_4_0
-	/*
-	 * "sound=" command line handling by Harald Milz.
-	 */
-	i = 0;
-	while (i < 20 && sound[i])
-		ints[i + 1] = sound[i++];
-	ints[0] = i;
-
-	if (i)
-		sound_setup("sound=", ints);
-#endif
 
 	err = create_special_devices();
 	if (err)
@@ -730,13 +705,6 @@
 
 	sound_stop_timer();
 
-#ifdef CONFIG_LOWLEVEL_SOUND
-	{
-		extern void     sound_unload_lowlevel_drivers(void);
-
-		sound_unload_lowlevel_drivers();
-	}
-#endif
 	sequencer_unload();
 
 	for (i = 0; i < MAX_DMA_CHANNELS; i++)
@@ -855,8 +823,9 @@
 
 void conf_printf(char *name, struct address_info *hw_config)
 {
-	if (!traceinit)
-		return;
+#ifndef CONFIG_SOUND_TRACEINIT
+	return;
+#else
 	printk("<%s> at 0x%03x", name, hw_config->io_base);
 
 	if (hw_config->irq)
@@ -869,13 +838,14 @@
 			printk(",%d", hw_config->dma2);
 	}
 	printk("\n");
+#endif
 }
 
 void conf_printf2(char *name, int base, int irq, int dma, int dma2)
 {
-	if (!traceinit)
-		return;
-
+#ifndef CONFIG_SOUND_TRACEINIT
+	return;
+#else
 	printk("<%s> at 0x%03x", name, base);
 
 	if (irq)
@@ -888,6 +858,7 @@
 			  printk(",%d", dma2);
 	}
 	printk("\n");
+#endif
 }
 
 /*
diff -u --recursive --new-file v2.3.51/linux/drivers/sound/waveartist.c linux/drivers/sound/waveartist.c
--- v2.3.51/linux/drivers/sound/waveartist.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/sound/waveartist.c	Sun Mar 12 19:39:39 2000
@@ -1771,6 +1771,18 @@
 
 static int __init init_waveartist(void)
 {
+	if (!io && machine_is_netwinder()) {
+		/*
+		 * The NetWinder WaveArtist is at a fixed address.
+		 * If the user does not supply an address, use the
+		 * well-known parameters.
+		 */
+		io   = 0x250;
+		irq  = 12;
+		dma  = 3;
+		dma2 = 7;
+	}
+
 	cfg.io_base = io;
 	cfg.irq = irq;
 	cfg.dma = dma;
diff -u --recursive --new-file v2.3.51/linux/drivers/telephony/ixj.c linux/drivers/telephony/ixj.c
--- v2.3.51/linux/drivers/telephony/ixj.c	Thu Feb 10 17:11:14 2000
+++ linux/drivers/telephony/ixj.c	Sun Mar 12 19:18:55 2000
@@ -508,7 +508,7 @@
 					j->flags.cringing = 0;
 					ixj_ring_off(board);
 				} else {
-					if (jiffies - j->ring_cadence_jif >= (.5 * hertz)) {
+					if (jiffies - j->ring_cadence_jif >= (hertz/2)) {
 						j->ring_cadence_t--;
 						if (j->ring_cadence_t == -1)
 							j->ring_cadence_t = 15;
@@ -3799,6 +3799,32 @@
 	case PHONE_CPT_STOP:
 		ixj_cpt_stop(board);
 		break;
+	case PHONE_QUERY_CODEC:
+	{
+		struct phone_codec_data pd;
+		int val;
+		int proto_size[] = {
+			-1, 
+			12, 10, 16, 9, 8, 48, 5,
+			40, 40, 80, 40, 40
+		};
+		if(copy_from_user(&pd, (void *)arg, sizeof(pd)))
+			return -EFAULT;
+		if(pd.type<1 || pd.type>12)
+			return -EPROTONOSUPPORT;
+		if(pd.type<G729)
+			val=proto_size[pd.type];
+		else switch(j->baseframe.low)
+		{
+			case 0xA0:val=2*proto_size[pd.type];break;
+			case 0x50:val=proto_size[pd.type];break;
+			default:val=proto_size[pd.type]*3;break;
+		}
+		pd.buf_min=pd.buf_max=pd.buf_opt=val;
+		if(copy_to_user((void *)arg, &pd, sizeof(pd)))
+			return -EFAULT;
+		return 0;
+	}
 	case IXJCTL_DSP_IDLE:
 		idle(board);
 		break;
@@ -3839,6 +3865,7 @@
 		ixj_daa_cr4(board, arg | 0x02);
 		break;
 	case IXJCTL_PSTN_LINETEST:
+	case PHONE_PSTN_LINETEST:
 		retval = ixj_linetest(board);
 		break;
 	case IXJCTL_CID:
diff -u --recursive --new-file v2.3.51/linux/drivers/telephony/phonedev.c linux/drivers/telephony/phonedev.c
--- v2.3.51/linux/drivers/telephony/phonedev.c	Thu Feb 10 17:11:14 2000
+++ linux/drivers/telephony/phonedev.c	Sun Mar 12 19:18:55 2000
@@ -10,7 +10,8 @@
  *
  * Author:      Alan Cox, <alan@redhat.com>
  *
- * Fixes:
+ * Fixes:       Mar 01 2000 Thomas Sparr, <thomas.l.sparr@telia.com>
+ *              phone_register_device now works with unit!=PHONE_UNIT_ANY
  */
 
 #include <linux/config.h>
@@ -84,7 +85,7 @@
 
 	if (unit != PHONE_UNIT_ANY) {
 		base = unit;
-		end = unit;
+		end = unit + 1;  /* enter the loop at least one time */
 	}
 	for (i = base; i < end; i++) {
 		if (phone_device[i] == NULL) {
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/Config.in linux/drivers/usb/Config.in
--- v2.3.51/linux/drivers/usb/Config.in	Fri Mar 10 16:40:44 2000
+++ linux/drivers/usb/Config.in	Tue Mar 14 18:09:51 2000
@@ -9,9 +9,6 @@
 
 comment 'USB Controllers'
    dep_tristate '  UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
-   if [ "$CONFIG_USB_UHCI" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
-      bool '    USB-UHCI High Bandwidth (EXPERIMENTAL)' CONFIG_USB_UHCI_HIGH_BANDWIDTH
-   fi
    if [ "$CONFIG_USB_UHCI" != "y" ]; then
       dep_tristate '  UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
       if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
@@ -51,6 +48,7 @@
    dep_tristate '  PLUSB Prolific USB-Network driver' CONFIG_USB_PLUSB $CONFIG_USB
    dep_tristate '  USB ADMtek Pegasus-based device support' CONFIG_USB_PEGASUS $CONFIG_USB
    dep_tristate '  USB Diamond Rio500 support' CONFIG_USB_RIO500 $CONFIG_USB
+   dep_tristate '  D-Link USB FM radio support' CONFIG_USB_DSBR $CONFIG_USB
    
 comment 'USB HID'
    dep_tristate '  USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/Makefile linux/drivers/usb/Makefile
--- v2.3.51/linux/drivers/usb/Makefile	Fri Mar 10 16:40:44 2000
+++ linux/drivers/usb/Makefile	Sun Mar 12 19:18:56 2000
@@ -4,9 +4,10 @@
 
 # Subdirs.
 
-SUB_DIRS	:= serial
+SUB_DIRS	:=
 MOD_SUB_DIRS	:= $(SUB_DIRS)
-ALL_SUB_DIRS	:= $(SUB_DIRS)
+MOD_IN_SUB_DIRS	:= $(SUB_DIRS)
+ALL_SUB_DIRS	:= $(SUB_DIRS) serial
 
 # The target object and module list name.
 
@@ -37,6 +38,18 @@
 obj-n	:=
 obj-	:=
 
+# Object files in subdirectories
+
+ifeq ($(CONFIG_USB_SERIAL),y)
+	SUB_DIRS += serial
+	obj-y += serial/serial.o
+else
+	ifeq ($(CONFIG_USB_SERIAL),m)
+		MOD_SUB_DIRS += serial
+	endif
+endif
+
+
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_USB)		+= usbcore.o
@@ -68,6 +81,7 @@
 obj-$(CONFIG_USB_OV511)		+= ov511.o
 obj-$(CONFIG_USB_PEGASUS)	+= pegasus.o
 obj-$(CONFIG_USB_RIO500)	+= rio500.o
+obj-$(CONFIG_USB_DSBR)		+= dsbr100.o
 
 # Extract lists of the multi-part drivers.
 # The 'int-*' lists are the intermediate files used to build the multi's.
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/dsbr100.c linux/drivers/usb/dsbr100.c
--- v2.3.51/linux/drivers/usb/dsbr100.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/dsbr100.c	Tue Mar 14 17:54:42 2000
@@ -0,0 +1,353 @@
+/* A driver for the D-Link DSB-R100 USB radio.  The R100 plugs
+ into both the USB and an analog audio input, so this thing
+ only deals with initialisation and frequency setting, the
+ audio data has to be handled by a sound driver.
+
+ Major issue: I can't find out where the device reports the signal
+ strength, and indeed the windows software appearantly just looks
+ at the stereo indicator as well.  So, scanning will only find
+ stereo stations.  Sad, but I can't help it.
+
+ Also, the windows program sends oodles of messages over to the
+ device, and I couldn't figure out their meaning.  My suspicion
+ is that they don't have any:-)
+
+ You might find some interesting stuff about this module at
+ http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+
+ Copyright (c) 2000 Markus Demleitner
+
+ 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
+
+ History:
+
+ Version 0.21:
+ 	Markus Demleitner <msdemlei@tucana.harvard.edu>:
+	Minor cleanup, warnings if something goes wrong, lame attempt
+	to adhere to Documentation/CodingStyle
+
+ Version 0.2: 
+ 	Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
+	Markus: Copyright clarification
+
+ Version 0.01: Markus: initial release
+
+*/
+
+
+#include <linux/kernel.h>
+
+#if CONFIG_MODVERSIONS==1
+#define MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+
+#define DSB100_VENDOR 0x04b4
+#define DSB100_PRODUCT 0x1002
+
+#define TB_LEN 16
+
+static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum);
+static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr);
+static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, 
+	void *arg);
+static int usb_dsbr100_open(struct video_device *dev, int flags);
+static void usb_dsbr100_close(struct video_device *dev);
+
+
+typedef struct
+{	struct urb readurb,writeurb;
+	struct usb_device *dev;
+	char transfer_buffer[TB_LEN];
+	int curfreq;
+	int stereo;
+	int ifnum;
+} usb_dsbr100;
+
+
+static struct video_device usb_dsbr100_radio=
+{
+	"D-Link DSB R-100 USB radio",
+	VID_TYPE_TUNER,
+	VID_HARDWARE_AZTECH,
+	usb_dsbr100_open,
+	usb_dsbr100_close,
+	NULL,	/* Can't read  (no capture ability) */
+	NULL,	/* Can't write */
+	NULL,	/* No poll */
+	usb_dsbr100_ioctl,
+	NULL,
+	NULL
+};
+
+static int users = 0;
+
+static struct usb_driver usb_dsbr100_driver = {
+	name:		"dsbr100",
+	probe:		usb_dsbr100_probe,
+	disconnect:	usb_dsbr100_disconnect,
+	driver_list: {NULL,NULL},
+	fops: NULL,
+	minor: 0
+};
+
+
+static int dsbr100_start(usb_dsbr100 *radio)
+{
+	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
+	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
+		return -1;
+	return (radio->transfer_buffer)[0];
+}
+
+
+static int dsbr100_stop(usb_dsbr100 *radio)
+{
+	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
+	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
+		return -1;
+	return (radio->transfer_buffer)[0];
+}
+
+
+static int dsbr100_setfreq(usb_dsbr100 *radio, int freq)
+{
+	freq = (freq*80)/16+856;
+	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x01, 0xC0, (freq&0xff00)>>8, freq&0xff, 
+		radio->transfer_buffer, 8, 300)<0
+	 || usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
+	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
+		radio->stereo = -1;
+		return -1;
+	}
+	radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
+	return (radio->transfer_buffer)[0];
+}
+
+static void dsbr100_getstat(usb_dsbr100 *radio)
+{
+	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
+		radio->stereo = -1;
+	else
+		radio->stereo = ! (radio->transfer_buffer[0]&0x01);
+}
+
+
+static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	usb_dsbr100 *radio;
+
+	if (dev->descriptor.idVendor!=DSB100_VENDOR ||
+			dev->descriptor.idProduct!=DSB100_PRODUCT)
+		return NULL;
+	if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL)))
+		return NULL;
+	usb_dsbr100_radio.priv = radio;
+	radio->dev = dev;
+	radio->ifnum = ifnum;
+	radio->curfreq = 1454;
+	return (void*)radio;
+}
+
+static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr)
+{
+	usb_dsbr100 *radio=ptr;
+
+	if (users)
+		return;
+	kfree(radio);
+	usb_dsbr100_radio.priv = NULL;
+}
+
+static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, 
+	void *arg)
+{
+	usb_dsbr100 *radio=dev->priv;
+
+	if (!radio)
+		return -EINVAL;
+
+	switch(cmd)
+	{
+		case VIDIOCGCAP: {
+			struct video_capability v;
+			v.type=VID_TYPE_TUNER;
+			v.channels=1;
+			v.audios=1;
+			/* No we don't do pictures */
+			v.maxwidth=0;
+			v.maxheight=0;
+			v.minwidth=0;
+			v.minheight=0;
+			strcpy(v.name, "D-Link R-100 USB Radio");
+			if(copy_to_user(arg,&v,sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGTUNER: {
+			struct video_tuner v;
+			dsbr100_getstat(radio);
+			if(copy_from_user(&v, arg,sizeof(v))!=0) 
+				return -EFAULT;
+			if(v.tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v.rangelow=(87*16000);
+			v.rangehigh=(108*16000);
+			/*v.flags=VIDEO_TUNER_LOW;*/
+			v.mode=VIDEO_MODE_AUTO;
+			v.signal=radio->stereo;
+			v.flags|=VIDEO_TUNER_STEREO_ON;
+			strcpy(v.name, "FM");
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSTUNER: {
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ: 
+			if (radio->curfreq==-1)
+				return -EINVAL;
+			if(copy_to_user(arg, &(radio->curfreq), 
+				sizeof(radio->curfreq)))
+				return -EFAULT;
+			return 0;
+
+		case VIDIOCSFREQ:
+			if(copy_from_user(&(radio->curfreq), arg,
+				sizeof(radio->curfreq)))
+				return -EFAULT;
+			if (dsbr100_setfreq(radio, radio->curfreq)==-1)
+				warn("set frequency failed");
+			return 0;
+
+		case VIDIOCGAUDIO: {
+			struct video_audio v;
+			memset(&v,0, sizeof(v));
+			v.flags|=VIDEO_AUDIO_MUTABLE;
+			v.mode=VIDEO_SOUND_STEREO;
+			v.volume=1;
+			v.step=1;
+			strcpy(v.name, "Radio");
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;			
+		}
+		case VIDIOCSAUDIO: {
+			struct video_audio v;
+			if(copy_from_user(&v, arg, sizeof(v))) 
+				return -EFAULT;	
+			if(v.audio) 
+				return -EINVAL;
+
+			if(v.flags&VIDEO_AUDIO_MUTE) {
+				if (dsbr100_stop(radio)==-1)
+					warn("radio did not respond properly");
+			}
+			else
+				if (dsbr100_start(radio)==-1)
+					warn("radio did not respond properly");
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+
+static int usb_dsbr100_open(struct video_device *dev, int flags)
+{
+	usb_dsbr100 *radio=dev->priv;
+
+	if (! radio) {
+		warn("radio not initialised");
+		return -EAGAIN;
+	}
+	if(users)
+	{
+		warn("radio in use");
+		return -EBUSY;
+	}
+	users++;
+	MOD_INC_USE_COUNT;
+	if (dsbr100_start(radio)<0)
+		warn("radio did not start up properly");
+	dsbr100_setfreq(radio,radio->curfreq);
+	return 0;
+}
+
+static void usb_dsbr100_close(struct video_device *dev)
+{
+	usb_dsbr100 *radio=dev->priv;
+
+	if (!radio)
+		return;
+	users--;
+	dsbr100_stop(radio);
+	MOD_DEC_USE_COUNT;
+}
+
+int __init dsbr100_init(void)
+{
+	usb_dsbr100_radio.priv = NULL;
+	usb_register(&usb_dsbr100_driver);
+	if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) {	
+		warn("couldn't register video device");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int __init init_module(void)
+{
+	return dsbr100_init();
+}
+
+void cleanup_module(void)
+{
+	usb_dsbr100 *radio=usb_dsbr100_radio.priv;
+
+	if (radio)
+		dsbr100_stop(radio);
+	video_unregister_device(&usb_dsbr100_radio);
+	usb_deregister(&usb_dsbr100_driver);
+}
+
+/*
+vi: ts=8
+Sigh.  Of course, I am one of the ts=2 heretics, but Linus' wish is
+my command.
+*/
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/inode.c linux/drivers/usb/inode.c
--- v2.3.51/linux/drivers/usb/inode.c	Fri Mar 10 16:40:44 2000
+++ linux/drivers/usb/inode.c	Sun Mar 12 19:13:06 2000
@@ -29,6 +29,7 @@
 /*****************************************************************************/
 
 #define __NO_VERSION__
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- v2.3.51/linux/drivers/usb/ov511.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/ov511.c	Mon Mar 13 13:55:09 2000
@@ -2,20 +2,17 @@
  * OmniVision OV511 Camera-to-USB Bridge Driver
  * Copyright (c) 1999/2000 Mark W. McClelland
  * Many improvements by Bret Wallach
- *
+ * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000
+ * Snapshot code by Kevin Moore
+ * 
  * Based on the Linux CPiA driver.
  * 
  * Released under GPL v.2 license.
  *
- * Important keywords in comments:
- *    CAMERA SPECIFIC - Camera specific code; may not work with other cameras.
- *    DEBUG - Debugging code.
- *    FIXME - Something that is broken or needs improvement.
- *
- * Version: 1.07
+ * Version: 1.09
  *
  * Please see the file: linux/Documentation/usb/ov511.txt 
- * and the website at:  http://people.delphi.com/mmcclelland/linux/ 
+ * and the website at:  http://alpha.dyndns.org/ov511 
  * for more info.
  */
 
@@ -39,15 +36,6 @@
 
 /* Handle mangled (versioned) external symbols */
 
-#include <linux/config.h>   /* retrieve the CONFIG_* macros */
-#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
-#	define MODVERSIONS  /* force it on */
-#endif
-
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
-
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/list.h>
@@ -58,6 +46,7 @@
 #include <linux/vmalloc.h>
 #include <linux/wrapper.h>
 #include <linux/module.h>
+#include <linux/init.h>
 #include <linux/spinlock.h>
 #include <linux/time.h>
 #include <linux/usb.h>
@@ -67,8 +56,6 @@
 
 #define OV511_I2C_RETRIES 3
 
-#define OV7610_AUTO_ADJUST 1
-
 /* Video Size 640 x 480 x 3 bytes for RGB */
 #define MAX_FRAME_SIZE (640 * 480 * 3)
 #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
@@ -77,6 +64,33 @@
 #define DEFAULT_WIDTH 640
 #define DEFAULT_HEIGHT 480
 
+// PARAMETER VARIABLES:
+static int autoadjust = 1;    /* CCD dynamically changes exposure, etc... */
+
+/* 0=no debug messages
+ * 1=init/detection/unload and other significant messages,
+ * 2=some warning messages
+ * 3=config/control function calls
+ * 4=most function calls and data parsing messages
+ * 5=highly repetitive mesgs
+ * NOTE: This should be changed to 0, 1, or 2 for production kernels
+ */
+static int debug = 3;
+
+/* Fix vertical misalignment of red and blue at 640x480 */
+static int fix_rgb_offset = 0;
+
+/* Snapshot mode enabled flag */
+static int snapshot = 0;
+
+MODULE_PARM(autoadjust, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(fix_rgb_offset, "i");
+MODULE_PARM(snapshot, "i");
+
+MODULE_AUTHOR("Mark McClelland (and others)");
+MODULE_DESCRIPTION("OV511 USB Camera Driver");
+
 char kernel_version[] = UTS_RELEASE;
 
 /*******************************/
@@ -206,10 +220,8 @@
 		USB_TYPE_CLASS | USB_RECIP_DEVICE,
 		0, (__u16)reg, &value, 1, HZ);	
 			
-#if 0
-	PDEBUG("reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
-#endif
-			
+	PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
+		
 	return rc;
 }
 
@@ -225,9 +237,7 @@
 		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
 		0, (__u16)reg, buffer, 1, HZ);
                                
-#if 0
-	PDEBUG("reg read: 0x%02X:0x%02X", reg, buffer[0]);
-#endif
+	PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
 	
 	if(rc < 0)
 		return rc;
@@ -239,9 +249,8 @@
 {
 	int rc, retries;
 
-#if 0
-	PDEBUG("i2c write: 0x%02X:0x%02X", reg, value);
-#endif
+	PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+
 	/* Three byte write cycle */
 	for(retries = OV511_I2C_RETRIES;;) {
 		/* Select camera register */
@@ -321,9 +330,8 @@
 	}
 
 	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
-#if 0
-	PDEBUG("i2c read: 0x%02X:0x%02X", reg, value);
-#endif
+
+	PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
 		
 	/* This is needed to make ov511_i2c_write() work */
 	rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
@@ -355,9 +363,8 @@
 	if (rc < 0) return rc;
  
  	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
- #if 0
- 	PDEBUG("i2c read: 0x%02X:0x%02X", reg, value);
- #endif
+
+ 	PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
  		
  	return (value);
  }
@@ -391,15 +398,14 @@
 	int rc;
 	for(i=reg1; i<=regn; i++) {
 	  rc = ov511_i2c_read(dev, i);
-#if 0
-	  PDEBUG("OV7610[0x%X] = 0x%X", i, rc);
-#endif
+
+	  PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
 	}
 }
 
 static void ov511_dump_i2c_regs( struct usb_device *dev)
 {
-	PDEBUG("I2C REGS");
+	PDEBUG(3, "I2C REGS");
 	ov511_dump_i2c_range(dev, 0x00, 0x38);
 }
 
@@ -409,27 +415,27 @@
 	int rc;
 	for(i=reg1; i<=regn; i++) {
 	  rc = ov511_reg_read(dev, i);
-	  PDEBUG("OV511[0x%X] = 0x%X", i, rc);
+	  PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
 	}
 }
 
 static void ov511_dump_regs( struct usb_device *dev)
 {
-	PDEBUG("CAMERA INTERFACE REGS");
+	PDEBUG(1, "CAMERA INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x10, 0x1f);
-	PDEBUG("DRAM INTERFACE REGS");
+	PDEBUG(1, "DRAM INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x20, 0x23);
-	PDEBUG("ISO FIFO REGS");
+	PDEBUG(1, "ISO FIFO REGS");
 	ov511_dump_reg_range(dev, 0x30, 0x31);
-	PDEBUG("PIO REGS");
+	PDEBUG(1, "PIO REGS");
 	ov511_dump_reg_range(dev, 0x38, 0x39);
 	ov511_dump_reg_range(dev, 0x3e, 0x3e);
-	PDEBUG("I2C REGS");
+	PDEBUG(1, "I2C REGS");
 	ov511_dump_reg_range(dev, 0x40, 0x49);
-	PDEBUG("SYSTEM CONTROL REGS");
+	PDEBUG(1, "SYSTEM CONTROL REGS");
 	ov511_dump_reg_range(dev, 0x50, 0x53);
 	ov511_dump_reg_range(dev, 0x5e, 0x5f);
-	PDEBUG("OmniCE REGS");
+	PDEBUG(1, "OmniCE REGS");
 	ov511_dump_reg_range(dev, 0x70, 0x79);
 	ov511_dump_reg_range(dev, 0x80, 0x9f);
 	ov511_dump_reg_range(dev, 0xa0, 0xbf);
@@ -441,7 +447,7 @@
 {
 	int rc;
 	
-	PDEBUG("Reset: type=0x%X", reset_type);
+	PDEBUG(3, "Reset: type=0x%X", reset_type);
 	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
 	if (rc < 0)
 		err("reset: command failed");
@@ -457,9 +463,7 @@
 {
 	int alt, multiplier, rc;
 		
-#if 0
-	PDEBUG("set packet size: %d", size);
-#endif
+	PDEBUG(3, "set packet size: %d", size);
 	
 	switch (size) {
 		case 992:
@@ -576,7 +580,7 @@
 
 	p->hue = 0x8000;
 	p->whiteness = 105 << 8;
-	p->depth = 24;
+	p->depth = 3;  /* Don't know if this is right */
 	p->palette = VIDEO_PALETTE_RGB24;
 
 	/* Restart the camera */
@@ -594,10 +598,8 @@
 	int rc = 0;
 	struct usb_device *dev = ov511->dev;
 
-#if 0
-	PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d, %d)",
+	PDEBUG(3, "ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)",
 	       width, height, mode, sub_flag);
-#endif
 
 //	ov511_set_packet_size(ov511, 0);
 	if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
@@ -606,13 +608,19 @@
 	}
 
 	if (mode == VIDEO_PALETTE_GREY) {
-		ov511_reg_write(dev, 0x16, 0);
-		ov511_i2c_write(dev, 0xe, 0x44);
+		ov511_reg_write(dev, 0x16, 0x00);
+		ov511_i2c_write(dev, 0x0e, 0x44);
 		ov511_i2c_write(dev, 0x13, 0x21);
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x00);
+		ov511_reg_write(dev, 0x1f, 0x01);
 	} else {
-		ov511_reg_write(dev, 0x16, 1);
-		ov511_i2c_write(dev, 0xe, 0x4);
-		ov511_i2c_write(dev, 0x13, 0x1);
+		ov511_reg_write(dev, 0x16, 0x01);
+		ov511_i2c_write(dev, 0x0e, 0x04);
+		ov511_i2c_write(dev, 0x13, 0x01);
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x01);
+		ov511_reg_write(dev, 0x1f, 0x03);
 	}
 
 	if (width == 640 && height == 480) {
@@ -626,13 +634,26 @@
 			ov511_reg_write(ov511->dev, 0x12, (ov511->subw>>3)-1);
 			ov511_reg_write(ov511->dev, 0x13, (ov511->subh>>3)-1);
 			ov511_i2c_write(dev, 0x11, 0x01);
+
+			/* Snapshot additions */
+			ov511_reg_write(ov511->dev, 0x1a, (ov511->subw>>3)-1);
+			ov511_reg_write(ov511->dev, 0x1b, (ov511->subh>>3)-1);
+			ov511_reg_write(ov511->dev, 0x1c, 0x00);
+			ov511_reg_write(ov511->dev, 0x1d, 0x00);
 		} else {
 			ov511_i2c_write(ov511->dev, 0x17, 0x38);
 			ov511_i2c_write(ov511->dev, 0x18, 0x3a + (640>>2));
 			ov511_i2c_write(ov511->dev, 0x19, 0x5);
-			ov511_i2c_write(ov511->dev, 0x1c, + (480>>1));
+			ov511_i2c_write(ov511->dev, 0x1a, 5 + (480>>1));
 			ov511_reg_write(dev, 0x12, 0x4f);
 			ov511_reg_write(dev, 0x13, 0x3d);
+
+			/* Snapshot additions */
+			ov511_reg_write(ov511->dev, 0x1a, 0x4f);
+			ov511_reg_write(ov511->dev, 0x1b, 0x3d);
+			ov511_reg_write(ov511->dev, 0x1c, 0x00);
+			ov511_reg_write(ov511->dev, 0x1d, 0x00);
+
 			if (mode == VIDEO_PALETTE_GREY) {
 			  ov511_i2c_write(dev, 0x11, 4); /* check */
 			} else {
@@ -642,6 +663,8 @@
 
 		ov511_reg_write(dev, 0x14, 0x00);
 		ov511_reg_write(dev, 0x15, 0x00);
+
+		/* FIXME?? Shouldn't below be true only for YUV420? */
 		ov511_reg_write(dev, 0x18, 0x03);
 
 		ov511_i2c_write(dev, 0x12, 0x24);
@@ -654,6 +677,12 @@
 		ov511_reg_write(dev, 0x15, 0x00);
 		ov511_reg_write(dev, 0x18, 0x03);
 
+		/* Snapshot additions */
+		ov511_reg_write(dev, 0x1a, 0x27);
+		ov511_reg_write(dev, 0x1b, 0x1f);
+                ov511_reg_write(dev, 0x1c, 0x00);
+                ov511_reg_write(dev, 0x1d, 0x00);
+
 		if (mode == VIDEO_PALETTE_GREY) {
 		  ov511_i2c_write(dev, 0x11, 1); /* check */
 		} else {
@@ -671,7 +700,7 @@
 //	ov511_set_packet_size(ov511, 993);
 
 	if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00) < 0) {
-		PDEBUG("reset: command failed");
+		err("reset: command failed");
 		return -EIO;
 	}
 
@@ -683,52 +712,74 @@
 
 Turn a YUV4:2:0 block into an RGB block
 
+Video4Linux seems to use the blue, green, red channel
+order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
+
+Color space conversion coefficients taken from the excellent
+http://www.inforamp.net/~poynton/ColorFAQ.html
+In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
+Y values are given for all 4 pixels, but the U (Pb)
+and V (Pr) are assumed constant over the 2x2 block.
+
+To avoid floating point arithmetic, the color conversion
+coefficients are scaled into 16.16 fixed-point integers.
+
 *************************************************************/
-#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
-static inline void ov511_move_420_block(int y00, int y01, int y10, int y11,
-					int u, int v, int w,
-					unsigned char * pOut)
-{
-	int r    = 68911 * v;
-	int g    = -16915 * u + -35101 * v;
-	int b    = 87097 * u;
-	y00 *= 49152;
-	y01 *= 49152;
-	y10 *= 49152;
-	y11 *= 49152;
-	*(pOut+w*3) = LIMIT(r + y10);
-	*pOut++     = LIMIT(r + y00);
-	*(pOut+w*3) = LIMIT(g + y10);
-	*pOut++     = LIMIT(g + y00);
-	*(pOut+w*3) = LIMIT(b + y10);
-	*pOut++     = LIMIT(b + y00);
-	*(pOut+w*3) = LIMIT(r + y11);
-	*pOut++     = LIMIT(r + y01);
-	*(pOut+w*3) = LIMIT(g + y11);
-	*pOut++     = LIMIT(g + y01);
-	*(pOut+w*3) = LIMIT(b + y11);
-	*pOut++     = LIMIT(b + y01);
+// LIMIT: convert a 16.16 fixed-point value to a byte, with clipping.
+#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
+static inline void ov511_move_420_block(
+	                int yTL, int yTR, int yBL, int yBR,
+					int u, int v, 
+					int rowPixels, unsigned char * rgb)
+{
+	const double brightness=1.0;//0->black; 1->full scale
+	const double saturation=1.0;//0->greyscale; 1->full color
+	const double fixScale=brightness*256*256;
+	const int rvScale=(int)(1.402*saturation*fixScale);
+	const int guScale=(int)(-0.344136*saturation*fixScale);
+	const int gvScale=(int)(-0.714136*saturation*fixScale);
+	const int buScale=(int)(1.772*saturation*fixScale);
+	const int yScale=(int)(fixScale);	
+
+	int r    =               rvScale * v;
+	int g    = guScale * u + gvScale * v;
+	int b    = buScale * u;
+	yTL *= yScale; yTR *= yScale;
+	yBL *= yScale; yBR *= yScale;
+
+	//Write out top two pixels
+	rgb[0]=LIMIT(b+yTL); rgb[1]=LIMIT(g+yTL); rgb[2]=LIMIT(r+yTL);
+	rgb[3]=LIMIT(b+yTR); rgb[4]=LIMIT(g+yTR); rgb[5]=LIMIT(r+yTR);
+	rgb+=3*rowPixels;//Skip down to next line to write out bottom two pixels
+	rgb[0]=LIMIT(b+yBL); rgb[1]=LIMIT(g+yBL); rgb[2]=LIMIT(r+yBL);
+	rgb[3]=LIMIT(b+yBR); rgb[4]=LIMIT(g+yBR); rgb[5]=LIMIT(r+yBR);
 }
 
+
 /***************************************************************
 
 For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments.  The
-first 64 bytes of each segment are V, the next 64 are U.  The V and
-U are arranged as follows:
+first 64 bytes of each segment are U, the next 64 are V.  The U and
+V are arranged as follows:
 
   0  1 ...  7
   8  9 ... 15
        ...   
  56 57 ... 63
 
-The next 256 bytes are Y data and represent 4 squares of 8x8 pixels as
-follows:
+U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+
+The next 256 bytes are full resolution Y data and represent 4 
+squares of 8x8 pixels as follows:
 
   0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
   8  9 ... 15    72  73 ...  79        200 201 ... 207
        ...              ...                    ...
  56 57 ... 63   120 121     127        248 249 ... 255
 
+Note that the U and V data in one segment represents a 16 x 16 pixel
+area, but the Y data represents a 32 x 8 pixel area.
+
 If OV511_DUMPPIX is defined, _parse_data just dumps the
 incoming segments, verbatim, in order, into the frame.
 When used with vidcat -f ppm -s 640x480 this puts the data
@@ -780,8 +831,8 @@
 	    int y01 = *(pOut+3);
 	    int y10 = *(pOut+iWidth*3);
 	    int y11 = *(pOut+iWidth*3+3);
-	    int u   = *(pIn+64) - 128;
-	    int v   = *pIn++ - 128;
+	    int v   = *(pIn+64) - 128;
+	    int u   = *pIn++ - 128;
 	    ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut);
 	    pOut += 6;
 	}
@@ -810,8 +861,8 @@
 		int y00 = *pIn++;
 		int y11 = *(pIn+8);
 		int y01 = *pIn++;
-		int u   = *pOut1 - 128;
-		int v   = *(pOut1+1) - 128;
+		int v   = *pOut1 - 128;
+		int u   = *(pOut1+1) - 128;
 		ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut1);
 		pOut1 += 6;
 	      }
@@ -868,6 +919,42 @@
     }
 }
 
+
+/**************************************************************
+ * fixFrameRGBoffset--
+ * My camera seems to return the red channel about 1 pixel
+ * low, and the blue channel about 1 pixel high. After YUV->RGB
+ * conversion, we can correct this easily. OSL 2/24/2000.
+ *************************************************************/
+static void fixFrameRGBoffset(struct ov511_frame *frame)
+{
+  int x,y;
+  int rowBytes=frame->width*3,w=frame->width;
+  unsigned char *rgb=frame->data;
+  const int shift=1;//Distance to shift pixels by, vertically
+
+  if (frame->width<400) 
+	   return;//Don't bother with little images
+
+  //Shift red channel up
+  for (y=shift;y<frame->height;y++)
+  {
+    int lp=(y-shift)*rowBytes;//Previous line offset
+    int lc=y*rowBytes;//Current line offset
+    for (x=0;x<w;x++)
+      rgb[lp+x*3+2]=rgb[lc+x*3+2];//Shift red up
+  }
+  //Shift blue channel down
+  for (y=frame->height-shift-1;y>=0;y--)
+  {
+    int ln=(y+shift)*rowBytes;//Next line offset
+    int lc=y*rowBytes;//Current line offset
+    for (x=0;x<w;x++)
+      rgb[ln+x*3+0]=rgb[lc+x*3+0];//Shift blue down
+  }
+}
+
+
 static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
 {
 	unsigned char *cdata;
@@ -887,7 +974,7 @@
 		if (!n || ov511->curframe == -1) continue;
 
 		if (st)
-			PDEBUG("data error: [%d] len=%d, status=%d", i, n, st);
+			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
 
 		frame = &ov511->frame[ov511->curframe];
 		
@@ -899,14 +986,15 @@
 		    struct timeval *ts;
 		    ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
 		    do_gettimeofday(ts);
-#if 0
-		    PDEBUG("Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
+
+		    PDEBUG(4, "Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
 			   ov511->curframe, (int)(cdata[992]),
 			   (int)(cdata[9]), (int)(cdata[10]));
-#endif
 
 		    if (frame->scanstate == STATE_LINES) {
 		        int iFrameNext;
+				if (fix_rgb_offset)
+					fixFrameRGBoffset(frame);
 				frame->grabstate = FRAME_DONE;
 		        if (waitqueue_active(&frame->wq)) {
 			  		frame->grabstate = FRAME_DONE;
@@ -919,10 +1007,10 @@
 				  ov511->curframe = iFrameNext;
 				  ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
 				} else {
-#if 0
-				  PDEBUG("Frame not ready? state = %d",
+
+				  PDEBUG(4, "Frame not ready? state = %d",
 					 ov511->frame[iFrameNext].grabstate);
-#endif
+
 				  ov511->curframe = -1;
 				}
 		    }
@@ -932,11 +1020,18 @@
 		else if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
 			  cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 &&
 			 (cdata[8] & 8)) {
-#if 0
-			PDEBUG("ov511: Found Frame Start!, framenum = %d",
+
+			PDEBUG(4, "ov511: Found Frame Start!, framenum = %d",
 			       ov511->curframe);
-#endif
-		    frame->scanstate = STATE_LINES;
+
+			/* Check to see if it's a snapshot frame */
+			/* FIXME?? Should the snapshot reset go here? Performance? */
+			if (cdata[8] & 0x02) {
+				frame->snapshot = 1;
+				PDEBUG(3, "ov511_move_data: snapshot detected");
+			}
+
+			frame->scanstate = STATE_LINES;
 			frame->segment = 0;
 		}
 
@@ -1014,11 +1109,11 @@
 		}
 	}
 
-#if 0
-	PDEBUG("pn: %d %d %d %d %d %d %d %d %d %d\n",
+
+	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d\n",
 	       aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
 	       aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
-#endif
+
 	return totlen;
 }
 
@@ -1032,7 +1127,7 @@
 		return;
 
 	if (!ov511->streaming) {
-		PDEBUG("hmmm... not streaming, but got interrupt\n");
+		PDEBUG(2, "hmmm... not streaming, but got interrupt");
 		return;
 	}
 	
@@ -1166,6 +1261,7 @@
 	frame->grabstate = FRAME_GRABBING;
 	frame->scanstate = STATE_SCANNING;
 	frame->scanlength = 0;		/* accumulated in ov511_parse_data() */
+	frame->snapshot = 0;
 
 	ov511->curframe = framenum;
 
@@ -1192,7 +1288,7 @@
 	int err = -EBUSY;
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
 
-	PDEBUG("ov511_open");
+	PDEBUG(4, "ov511_open");
 
 	down(&ov511->lock);
 	if (ov511->user)
@@ -1212,8 +1308,8 @@
 	ov511->frame[1].data = ov511->fbuf + MAX_DATA_SIZE;
 	ov511->sub_flag = 0;
 
-	PDEBUG("frame [0] @ %p", ov511->frame[0].data);
-	PDEBUG("frame [1] @ %p", ov511->frame[1].data);
+	PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data);
+	PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data);
 
 	ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
 	if (!ov511->sbuf[0].data)
@@ -1222,8 +1318,8 @@
 	if (!ov511->sbuf[1].data)
 		goto open_err_on1;
 		
-	PDEBUG("sbuf[0] @ %p", ov511->sbuf[0].data);
-	PDEBUG("sbuf[1] @ %p", ov511->sbuf[1].data);
+	PDEBUG(4, "sbuf[0] @ %p", ov511->sbuf[0].data);
+	PDEBUG(4, "sbuf[1] @ %p", ov511->sbuf[1].data);
 
 	err = ov511_init_isoc(ov511);
 	if (err)
@@ -1254,7 +1350,7 @@
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
 
-	PDEBUG("ov511_close");
+	PDEBUG(4, "ov511_close");
 	
 	down(&ov511->lock);	
 	ov511->user--;
@@ -1289,9 +1385,8 @@
 static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
-#if 0	
-	PDEBUG("IOCtl: 0x%X", cmd);
-#endif	
+
+	PDEBUG(4, "IOCtl: 0x%X", cmd);
 
 	if (!ov511->dev)
 		return -EIO;	
@@ -1464,11 +1559,9 @@
 			if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
 				return -EFAULT;
 
-#if 0
-			PDEBUG("MCAPTURE");
-			PDEBUG("frame: %d, size: %dx%d, format: %d",
+			PDEBUG(4, "MCAPTURE");
+			PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
 				vm.frame, vm.width, vm.height, vm.format);
-#endif
 
 			if (vm.format != VIDEO_PALETTE_RGB24 &&
 			    vm.format != VIDEO_PALETTE_GREY)
@@ -1516,10 +1609,9 @@
 			if (copy_from_user((void *)&frame, arg, sizeof(int)))
 				return -EFAULT;
 
-#if 0
-			PDEBUG("syncing to frame %d, grabstate = %d", frame,
+			PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
 			       ov511->frame[frame].grabstate);
-#endif
+
 			switch (ov511->frame[frame].grabstate) {
 				case FRAME_UNUSED:
 					return -EINVAL;
@@ -1552,7 +1644,17 @@
 			}
 
 			ov511->frame[frame].grabstate = FRAME_UNUSED;
-			
+
+			/* Reset the hardware snapshot button */
+			/* FIXME - Is this the best place for this? */
+			if ((ov511->snap_enabled) &&
+			    (ov511->frame[frame].snapshot)) {
+				ov511->frame[frame].snapshot = 0;
+				ov511_reg_write(ov511->dev, 0x52, 0x01);
+				ov511_reg_write(ov511->dev, 0x52, 0x03);
+				ov511_reg_write(ov511->dev, 0x52, 0x01);
+			}
+
 			return 0;
 		}
 		case VIDIOCGFBUF:
@@ -1594,7 +1696,7 @@
 	int frmx = -1;
 	volatile struct ov511_frame *frame;
 
-	PDEBUG("ov511_read: %ld bytes, noblock=%d", count, noblock);
+	PDEBUG(4, "ov511_read: %ld bytes, noblock=%d", count, noblock);
 
 	if (!dev || !buf)
 		return -EFAULT;
@@ -1644,18 +1746,37 @@
 		goto restart;
 	}
 
-	PDEBUG("ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
+
+	/* Repeat until we get a snapshot frame */
+	if (ov511->snap_enabled && !frame->snapshot) {
+		frame->bytes_read = 0;
+		if (ov511_new_frame(ov511, frmx))
+			err("ov511_read: ov511_new_frame error");
+		goto restart;
+	}
+
+	/* Clear the snapshot */
+	if (ov511->snap_enabled && frame->snapshot) {
+		frame->snapshot = 0;
+		ov511_reg_write(ov511->dev, 0x52, 0x01);
+		ov511_reg_write(ov511->dev, 0x52, 0x03);
+		ov511_reg_write(ov511->dev, 0x52, 0x01);
+	}
+
+	PDEBUG(4, "ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
 		frame->bytes_read, frame->scanlength);
 
 	/* copy bytes to user space; we allow for partials reads */
-	if ((count + frame->bytes_read) > frame->scanlength)
-		count = frame->scanlength - frame->bytes_read;
+//	if ((count + frame->bytes_read) > frame->scanlength)
+//		count = frame->scanlength - frame->bytes_read;
+	/* FIXME - count hardwired to be one frame... */
+	count = frame->width * frame->height * frame->depth;
 
 	if (copy_to_user(buf, frame->data + frame->bytes_read, count))
 		return -EFAULT;
 
 	frame->bytes_read += count;
-	PDEBUG("ov511_read: {copy} count used=%ld, new bytes_read=%ld",
+	PDEBUG(4, "ov511_read: {copy} count used=%ld, new bytes_read=%ld",
 		count, frame->bytes_read);
 
 	if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
@@ -1679,7 +1800,7 @@
 	if (!ov511->dev)
 		return -EIO;
 
-	PDEBUG("mmap: %ld (%lX) bytes", size, size);
+	PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
 
 	if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
 		return -EINVAL;
@@ -1702,24 +1823,22 @@
 }
 
 static struct video_device ov511_template = {
-	"OV511 USB Camera",
-	VID_TYPE_CAPTURE,
-	VID_HARDWARE_OV511,
-	ov511_open,
-	ov511_close,
-	ov511_read,
-	ov511_write,
-	NULL,
-	ov511_ioctl,
-	ov511_mmap,
-	ov511_init_done,
-	NULL,
-	0,
-	0
+	name:		"OV511 USB Camera",
+	type:		VID_TYPE_CAPTURE,
+	hardware:	VID_HARDWARE_OV511,
+	open:		ov511_open,
+	close:		ov511_close,
+	read:		ov511_read,
+	write:		ov511_write,
+	ioctl:		ov511_ioctl,
+	mmap:		ov511_mmap,
+	initialize:	ov511_init_done,
 };
 
 static int ov7610_configure(struct usb_device *dev)
 {
+	int tries;
+
 	if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
 	                        OV7610_I2C_WRITE_ID) < 0)
 		return -1;
@@ -1731,6 +1850,7 @@
 	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
 		return -1;
 	
+	/* Reset the 7610 and wait a bit for it to initialize */ 
 	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
 	schedule_timeout (1 + 150 * HZ / 1000);
 
@@ -1738,8 +1858,14 @@
 	if(ov511_i2c_read(dev, 0x00) < 0)
 		return -1;
  
-	if((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
-	   (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2)) {
+	tries = 5;
+	while((tries > 0) &&
+	      ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
+	       (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2))) {
+		--tries;
+	}
+	
+	if (tries == 0) {
 		err("Failed to read OV7610 ID. You might not have an OV7610,");
 		err("or it may be not responding. Report this to");
 		err("mmcclelland@delphi.com");
@@ -1786,12 +1912,11 @@
 	 {OV511_I2C_BUS, 0x16, 0x06},
 	 {OV511_I2C_BUS, 0x28, 0x24}, /* 24 */
 	 {OV511_I2C_BUS, 0x2b, 0xac},
-	 {OV511_I2C_BUS, 0x5, 0x00},
-	 {OV511_I2C_BUS, 0x6, 0x00},
-#if 0
-#endif
+	 {OV511_I2C_BUS, 0x05, 0x00},
+	 {OV511_I2C_BUS, 0x06, 0x00},
+
 	 {OV511_I2C_BUS, 0x12, 0x00},
-	 {OV511_I2C_BUS, 0x13, 0x00},
+//	 {OV511_I2C_BUS, 0x13, 0x00},
 	 {OV511_I2C_BUS, 0x38, 0x81},
 	 {OV511_I2C_BUS, 0x28, 0x24}, /* 0c */
 	 {OV511_I2C_BUS, 0x05, 0x00},
@@ -1813,7 +1938,7 @@
 	 {OV511_I2C_BUS, 0x33, 0x20},
 	 {OV511_I2C_BUS, 0x34, 0x48},
 	 {OV511_I2C_BUS, 0x12, 0x24},
-	 {OV511_I2C_BUS, 0x13, 0x01},
+//	 {OV511_I2C_BUS, 0x13, 0x01},
 	 {OV511_I2C_BUS, 0x11, 0x01},
 	 {OV511_I2C_BUS, 0x0c, 0x24},
 	 {OV511_I2C_BUS, 0x0d, 0x24},
@@ -1853,7 +1978,8 @@
 	}
 
 	ov511->compress = 0;
-	
+	ov511->snap_enabled = snapshot;	
+
 	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
 	 * (using read() instead). */
 	ov511->frame[0].width = DEFAULT_WIDTH;
@@ -1864,10 +1990,16 @@
 	ov511->frame[1].bytes_read = 0;
 
 	/* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */
-	if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) return rc;
+	if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) goto error;
 	if ((rc = ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT,
-				       VIDEO_PALETTE_RGB24, 0)) < 0) return rc;
+				       VIDEO_PALETTE_RGB24, 0)) < 0) goto error;
 
+	if (autoadjust) {
+		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) goto error;
+	}
+	else {
+		if (ov511_i2c_write(dev, 0x13, 0x00) < 0 ) goto error;
+	}
 
 	return 0;
 	
@@ -1887,7 +2019,7 @@
 	struct usb_ov511 *ov511;
 	int rc;
 
-	PDEBUG("probing for device...");
+	PDEBUG(1, "probing for device...");
 
 	/* We don't handle multi-config cameras */
 	if (dev->descriptor.bNumConfigurations != 1)
@@ -1933,19 +2065,25 @@
 	case 3:
 		printk("ov511: Camera is a D-Link DSB-C300\n");
 		break;
+	case 4:
+		printk("ov511: Camera is a generic OV511/OV7610\n");
+		break;
 	case 5:
 		printk("ov511: Camera is a Puretek PT-6007\n");
 		break;
 	case 21:
 		printk("ov511: Camera is a Creative Labs WebCam 3\n");
 		break;
+	case 36:
+		printk("ov511: Camera is a Koala-Cam\n");
+		break;
 	case 100:
 		printk("ov511: Camera is a Lifeview RoboCam\n");
 		break;
 	case 102:
 		printk("ov511: Camera is a AverMedia InterCam Elite\n");
 		break;
-	case 112:
+	case 112: /* The OmniVision OV7110 evaluation kit uses this too */
 		printk("ov511: Camera is a MediaForte MV300\n");
 		break;
 	default:
@@ -2025,31 +2163,21 @@
 	{ NULL, NULL }
 };
 
-int usb_ov511_init(void)
+static int __init usb_ov511_init(void)
 {
-	PDEBUG("usb_ov511_init()");
-	
-	EXPORT_NO_SYMBOLS;
-	
-	return usb_register(&ov511_driver);
-}
+	if (usb_register(&ov511_driver) < 0)
+		return -1;
 
-void usb_ov511_cleanup(void)
-{
-	usb_deregister(&ov511_driver);
-}
+	info("ov511 driver registered");
 
-#ifdef MODULE
-int init_module(void)
-{
-	return usb_ov511_init();
+	return 0;
 }
 
-void cleanup_module(void)
+static void __exit usb_ov511_exit(void)
 {
-	usb_ov511_cleanup();
-	
-	PDEBUG("Module unloaded");
+	usb_deregister(&ov511_driver);
+	info("ov511 driver deregistered");
 }
-#endif
 
+module_init(usb_ov511_init);
+module_exit(usb_ov511_exit);
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/ov511.h linux/drivers/usb/ov511.h
--- v2.3.51/linux/drivers/usb/ov511.h	Thu Feb 10 17:11:15 2000
+++ linux/drivers/usb/ov511.h	Sun Mar 12 19:18:54 2000
@@ -6,9 +6,10 @@
 #define OV511_DEBUG	/* Turn on debug messages */
 
 #ifdef OV511_DEBUG
-#  define PDEBUG(fmt, args...) printk("ov511: " fmt "\n" , ## args)
+#  define PDEBUG(level, fmt, args...) \
+if (debug >= level) printk("ov511: " fmt "\n" , ## args)
 #else
-#  define PDEBUG(fmt, args...) do {} while(0)
+#  define PDEBUG(level, fmt, args...) do {} while(0)
 #endif
 
 /* Camera interface register numbers */
@@ -227,6 +228,8 @@
 	long bytes_read;	/* amount of scanlength that has been read from *data */
 
 	wait_queue_head_t wq;	/* Processes waiting */
+
+	int snapshot;			/* True if frame was a snapshot */
 };
 
 #define OV511_NUMFRAMES	2
@@ -269,6 +272,8 @@
 	int scratchlen;
 
 	wait_queue_head_t wq;	/* Processes waiting */
+
+	int snap_enabled;   /* Snapshot mode enabled */
 };
 
 #endif
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
--- v2.3.51/linux/drivers/usb/pegasus.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/pegasus.c	Mon Mar 13 16:33:10 2000
@@ -1,578 +1,474 @@
 /*
-**
 **	Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
 **
-**	Copyleft (L) 1999 Petko Manolov - Petkan (petkan@spct.net)
-** 	
+**	Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net)
+**
 **	Distribute under GPL version 2 or later.
 */
 
-
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
 #include <linux/delay.h>
-#include <linux/usb.h>
-
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/usb.h>
 
-#if LINUX_VERSION_CODE<0x2032d || !defined(__KERNEL__) || !defined(__OPTIMIZE__)
-#error You can not compile this driver on this kernel with this C options!
-#endif
-
-
-#define	ADMTEK_VENDOR_ID	0x07a6
-#define	ADMTEK_HPNA_PEGASUS	0x0986
-
-#define	HPNA_MTU		1500
-#define MAX_MTU			1536
 
-#define	TX_TIMEOUT		(HZ*5)
-#define	SOMETHING		(jiffies + TX_TIMEOUT)
+static const char *version = __FILE__ ": v0.3.3 2000/03/13 Written by Petko Manolov (petkan@spct.net)\n";
 
 
-static const char version[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n";
+#define	ADMTEK_VENDOR_ID		0x07a6
+#define	ADMTEK_DEVICE_ID_PEGASUS	0x0986
 
+#define	PEGASUS_MTU		1500
+#define PEGASUS_MAX_MTU		1536
+#define	PEGASUS_TX_TIMEOUT	(HZ*5)
+#define	ALIGN(x)		x __attribute__((aligned(16)))
 
-typedef struct usb_hpna
-{
-	struct usb_device	*usb_dev;
-	struct net_device	*net_dev;
-	int			present;
-	int			active;
-	void			*irq_handler;
-	struct list_head	list;
+struct pegasus {
+	struct usb_device	*usb;
+	struct net_device	*net;
 	struct net_device_stats	stats;
-	spinlock_t		hpna_lock;
-	struct timer_list	timer;
-
-	unsigned int		rx_pipe;
-	unsigned char *		rx_buff;
-	urb_t 			rx_urb;
-
-	unsigned int		tx_pipe;
-	unsigned char *		tx_buff;
-	urb_t 			tx_urb;
-	struct sk_buff *	tx_skbuff;
-
-	__u8			intr_ival;
-	unsigned int		intr_pipe;
-	unsigned char 		intr_buff[8];
-	urb_t 			intr_urb;
-} usb_hpna_t;
-
-
-usb_hpna_t	usb_dev_hpna;
-static int	loopback = 0;
-int		multicast_filter_limit = 32;
-static LIST_HEAD(hpna_list);
+	spinlock_t		pegasus_lock;
+	struct urb		rx_urb, tx_urb, intr_urb;
+	unsigned char		ALIGN(rx_buff[PEGASUS_MAX_MTU]); 
+	unsigned char		ALIGN(tx_buff[PEGASUS_MAX_MTU]); 
+	unsigned char		ALIGN(intr_buff[8]);
+};
 
+static int loopback = 0;
+static int multicast_filter_limit = 32;
 
 MODULE_AUTHOR("Petko Manolov <petkan@spct.net>");
-MODULE_DESCRIPTION("ADMtek \"Pegasus\" USB Ethernet driver");
+MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
 MODULE_PARM(loopback, "i");
 
 
-
-/*** vendor specific commands ***/
-static __inline__ int	hpna_get_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
-{
-	return	usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0,
-				indx, data, size, HZ);
-}
-
-
-static __inline__ int	hpna_set_register( struct usb_device *dev, __u16 indx, __u8 value )
-{
-	__u8	data = value;
-	return	usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40,
-				data, indx, &data, 1, HZ);
-}
-
-
-static __inline__ int	hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
-{
-	return	usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0,
-				indx, data, size, HZ);
-}
-
-
-static int read_phy_word( struct usb_device *dev, __u8 index, __u16 *regdata )
-{
-	int	i;
-	__u8	data[4];
-
-	data[0] = 1;
-	data[1] = 0;
-	data[2] = 0;
-	data[3] = 0x40 + index;
-	hpna_set_registers( dev, 0x25, 4, data );
-	for ( i=0; i<100; i++ ) {
-		hpna_get_registers( dev, 0x25, 4, data );
-		if ( data[3] & 0x80 ) {
-			*regdata = *(__u16 *)(data+1);
-			return	0;
+#define pegasus_get_registers(dev, indx, size, data)\
+	usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
+#define pegasus_set_registers(dev, indx, size, data)\
+	usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ);
+#define pegasus_set_register(dev, indx, value)	\
+	{ __u8	data = value;			\
+	usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);}
+
+
+static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
+{
+	int i;
+	__u8 data[4] = { 1, 0, 0, 0x40 + index };
+
+	pegasus_set_registers(dev, 0x25, 4, data);
+	for (i = 0; i < 100; i++) {
+		pegasus_get_registers(dev, 0x26, 3, data);
+		if (data[2] & 0x80) {
+			*regdata = *(__u16 *)(data);
+			return 0;
 		}
 		udelay(100);
 	}
+
 	warn("read_phy_word() failed");
-	return	1;
+	return 1;
 }
 
-
-static int write_phy_word( struct usb_device *dev, __u8 index, __u16 regdata )
+static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata)
 {
-	int	i;
-	__u8	data[4];
+	int i;
+	__u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index };
 
-	data[0] = 1;
-	data[1] = regdata;
-	data[2] = regdata >> 8;
-	data[3] = 0x20 + index;
-	hpna_set_registers( dev, 0x25, 4, data );
-	for ( i=0; i<100; i++ ) {
-		hpna_get_registers( dev, 0x28, 1, data );
-		if ( data[0] & 0x80 ) {
-			return	0;
-		}
+	pegasus_set_registers(dev, 0x25, 4, data);
+	for (i = 0; i < 100; i++) {
+		pegasus_get_registers(dev, 0x28, 1, data);
+		if (data[0] & 0x80)
+			return 0;
 		udelay(100);
 	}
+
 	warn("write_phy_word() failed");
-	return	1;
+	return 1;
 }
 
-
-int read_srom_word( struct usb_device *dev, __u8 index, __u16 *retdata)
+static int pegasus_read_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata)
 {
-	int	i;
-	__u8	data[4];
+	int i;
+	__u8 data[4] = { index, 0, 0, 0x02 };
 
-	data[0] = index;
-	data[1] = data[2] = 0;
-	data[3] = 0x02;
-	hpna_set_registers(dev, 0x20, 4, data);
-	for ( i=0; i<100; i++ ) {
-		hpna_get_registers(dev, 0x23, 1, data);
-		if ( data[0] & 4 ) {
-			hpna_get_registers(dev, 0x21, 2, data);
+	pegasus_set_registers(dev, 0x20, 4, data);
+	for (i = 0; i < 100; i++) {
+		pegasus_get_registers(dev, 0x23, 1, data);
+		if (data[0] & 4) {
+			pegasus_get_registers(dev, 0x21, 2, data);
 			*retdata = *(__u16 *)data;
-			return	0;		
+			return 0;
 		}
 	}
+
 	warn("read_srom_word() failed");
-	return	1;
+	return 1;
 }
-/*** end ***/
 
-
-
-
-int get_node_id( struct usb_device *dev, __u8 *id )
+static int pegasus_get_node_id(struct usb_device *dev, __u8 *id)
 {
-	int	i;
-
-	for ( i=0; i<3; i++ ) {
-		if ( read_srom_word(dev, i, (__u16 *)&id[i*2] ) )
-			return	1;
-	}
-	return	0;
+	int i;
+	for (i = 0; i < 3; i++)
+		if (pegasus_read_srom_word(dev, i, (__u16 *)&id[i * 2]))
+			return 1;
+	return 0;
 }
 
-
-static int reset_mac( struct usb_device *dev )
+static int pegasus_reset_mac(struct usb_device *dev)
 {
-	__u8	data = 0x8;
-	int	i;
-	
-	hpna_set_register( dev, 1, 0x08 );
-	for ( i=0; i<100; i++ ) {
-		hpna_get_registers( dev, 1, 1, &data);
-		if ( !(data & 0x08) ) {
-			if ( loopback & 1 )
-				return	0;
-			else if ( loopback & 2 ) {
-				write_phy_word( dev, 0, 0x4000 );
-				/*return	0;*/
-			}
-			hpna_set_register( dev, 0x7e, 0x24 );
-			hpna_set_register( dev, 0x7e, 0x27 );
-			return	0;
+	__u8 data = 0x8;
+	int i;
+
+	pegasus_set_register(dev, 1, data);
+	for (i = 0; i < 100; i++) {
+		pegasus_get_registers(dev, 1, 1, &data);
+		if (~data & 0x08) {
+			if (loopback & 1) 
+				return 0;
+			if (loopback & 2) 
+				pegasus_write_phy_word(dev, 0, 0x4000);
+			pegasus_set_register(dev, 0x7e, 0x24);
+			pegasus_set_register(dev, 0x7e, 0x27);
+			return 0;
 		}
 	}
+
 	return 1;
 }
 
-
-int start_net( struct net_device *dev, struct usb_device *usb_dev )
+static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
 {
-	__u16	partmedia, temp;
-	__u8	node_id[6];
-	__u8	data[4];
-	
-	if ( get_node_id(usb_dev, node_id) )
-		return	1;
-	hpna_set_registers(usb_dev, 0x10, 6, node_id);
+	__u16 partmedia, temp;
+	__u8 node_id[6];
+	__u8 data[4];
+
+	if (pegasus_get_node_id(usb, node_id)) 
+		return 1;
+
+	pegasus_set_registers(usb, 0x10, 6, node_id);
 	memcpy(dev->dev_addr, node_id, 6);
-	if ( read_phy_word(usb_dev, 1, &temp) )
-		return	2;
-	if ( !(temp & 4) ) {
-		if ( loopback )
-			goto ok;
+	if (pegasus_read_phy_word(usb, 1, &temp)) 
+		return 2;
+
+	if ((~temp & 4) && !loopback) {
 		err("link NOT established - %x", temp);
-		return	3;
+		return 3;
 	}
-ok:
-	if ( read_phy_word(usb_dev, 5, &partmedia) )
-		return	4;
-	temp = partmedia;
-	partmedia &= 0x1f;
-	if ( partmedia != 1 ) {
-		err("party FAIL %x", temp);
-		return	5;
-	}
-	partmedia = temp;
-	if ( partmedia & 0x100 )
-		data[1] = 0x30;
-	else {
-		if ( partmedia & 0x80 )
-			data[1] = 0x10;
-		else 
-			data[1] = 0;
+
+	if (pegasus_read_phy_word(usb, 5, &partmedia))
+		return 4;
+
+	if ((partmedia & 0x1f) != 1) {
+		err("party FAIL %x", partmedia);
+		return 5;
 	}
-	
+
 	data[0] = 0xc9;
-	data[2] = (loopback & 1) ? 0x08 : 0x00; 
-	
-	hpna_set_registers(usb_dev, 0, 3, data);
-	
-	return	0;
-}
+	data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
+	data[2] = (loopback & 1) ? 0x08 : 0x00;
 
+	pegasus_set_registers(usb, 0, 3, data);
 
-static void hpna_read_irq( purb_t urb )
-{
-	struct net_device *net_dev = urb->context;
-	usb_hpna_t *hpna = net_dev->priv;
-	int	count = urb->actual_length, res;
-	int	rx_status = *(int *)(hpna->rx_buff + count - 4);
+	return 0;
+}
 
+static void pegasus_read_bulk(struct urb *urb)
+{
+	struct pegasus *pegasus = urb->context;
+	struct net_device *net = pegasus->net;
+	int count = urb->actual_length, res;
+	int rx_status = *(int *)(pegasus->rx_buff + count - 4);
+	struct sk_buff	*skb;
+	__u16 pkt_len;
 
-	if ( urb->status ) {
-		info( "%s: RX status %d\n", net_dev->name, urb->status );
+	if (urb->status) {
+		info("%s: RX status %d", net->name, urb->status);
 		goto goon;
 	}
 
-	if ( !count )
+	if (!count)
 		goto goon;
-/*	if ( rx_status & 0x00010000 )
+#if 0
+	if (rx_status & 0x00010000)
 		goto goon;
-*/	
-	if ( rx_status & 0x000e0000 ) {
-		dbg("%s: error receiving packet %x",
-			net_dev->name, rx_status & 0xe0000);
-		hpna->stats.rx_errors++;
-		if(rx_status & 0x060000) hpna->stats.rx_length_errors++;
-		if(rx_status & 0x080000) hpna->stats.rx_crc_errors++;
-		if(rx_status & 0x100000) hpna->stats.rx_frame_errors++;
-	} else {
-		struct sk_buff	*skb;
-		__u16 		pkt_len = (rx_status & 0xfff) - 8;
+#endif
+	if (rx_status & 0x000e0000) {
 
+		dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
+		pegasus->stats.rx_errors++;
+		if(rx_status & 0x060000) pegasus->stats.rx_length_errors++;
+		if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++;
+		if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++;
 
-		if((skb = dev_alloc_skb(pkt_len+2)) != NULL ) {
-			skb->dev = net_dev;
-			skb_reserve(skb, 2);
-			eth_copy_and_sum(skb, hpna->rx_buff, pkt_len, 0);
-			skb_put(skb, pkt_len);
-		} else
-			goto	goon;
-		skb->protocol = eth_type_trans(skb, net_dev);
-		netif_rx(skb);
-		hpna->stats.rx_packets++;
-		hpna->stats.rx_bytes += pkt_len;
+		goto goon;
 	}
+
+	pkt_len = (rx_status & 0xfff) - 8;
+
+	if(!(skb = dev_alloc_skb(pkt_len+2)))
+		goto goon;
+
+	skb->dev = net;
+	skb_reserve(skb, 2);
+	eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0);
+	skb_put(skb, pkt_len);
+
+	skb->protocol = eth_type_trans(skb, net);
+	netif_rx(skb);
+	pegasus->stats.rx_packets++;
+	pegasus->stats.rx_bytes += pkt_len;
+
 goon:
-	if ( (res = usb_submit_urb( &hpna->rx_urb )) )
-		warn("failed rx_urb %d", res);
+	if ((res = usb_submit_urb(&pegasus->rx_urb)))
+		warn("(prb)failed rx_urb %d", res);
 }
 
-
-static void hpna_irq( urb_t *urb)
+static void pegasus_irq(urb_t *urb)
 {
-	if( urb->status ) {
+	if(urb->status) {
 		__u8	*d = urb->transfer_buffer;
 		printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x",
-			d[0], d[1], d[2], d[3], d[4], d[5] );
+			d[0], d[1], d[2], d[3], d[4], d[5]);
 	}
 }
 
-
-static void hpna_write_irq( purb_t urb )
+static void pegasus_write_bulk(struct urb *urb)
 {
-	struct net_device *net_dev = urb->context;
-	usb_hpna_t *hpna = net_dev->priv;
+	struct pegasus *pegasus = urb->context;
 
+	spin_lock(&pegasus->pegasus_lock);
 
-	spin_lock( &hpna->hpna_lock );
-	
-	if ( urb->status ) 
-		info("%s: TX status %d\n", net_dev->name, urb->status);
-	netif_wake_queue( net_dev );
+	if (urb->status)
+		info("%s: TX status %d", pegasus->net->name, urb->status);
+	netif_wake_queue(pegasus->net);
 
-	spin_unlock( &hpna->hpna_lock );
+	spin_unlock(&pegasus->pegasus_lock);
 }
 
-
-static void tx_timeout( struct net_device *dev )
+static void pegasus_tx_timeout(struct net_device *net)
 {
-	usb_hpna_t	*hpna = dev->priv;
+	struct pegasus *pegasus = net->priv;
 
-	warn( "%s: Tx timed out. Reseting...", dev->name );
-	hpna->stats.tx_errors++;
-	dev->trans_start = jiffies;
-	netif_wake_queue( dev );
-}
+	warn("%s: Tx timed out. Reseting...", net->name);
+	pegasus->stats.tx_errors++;
+	net->trans_start = jiffies;
 
+	netif_wake_queue(net);
+}
 
-static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev )
+static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
 {
-	usb_hpna_t	*hpna = (usb_hpna_t *)net_dev->priv;
-	int		count = skb->len+2 % 64 ? skb->len+2 : skb->len+3;
-	int		res;
+	struct pegasus	*pegasus = net->priv;
+	int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
+	int res;
 
-	spin_lock( &hpna->hpna_lock );
+	spin_lock(&pegasus->pegasus_lock);
 
-	netif_stop_queue( net_dev );
-	((__u16 *)hpna->tx_buff)[0] = skb->len;
-	memcpy(hpna->tx_buff+2, skb->data, skb->len);
-	(&hpna->tx_urb)->transfer_buffer_length = count;
-	if ( (res = usb_submit_urb( &hpna->tx_urb )) ) {
+	netif_stop_queue(net);
+
+	((__u16 *)pegasus->tx_buff)[0] = skb->len;
+	memcpy(pegasus->tx_buff+2, skb->data, skb->len);
+	(&pegasus->tx_urb)->transfer_buffer_length = count;
+
+	if ((res = usb_submit_urb(&pegasus->tx_urb))) {
 		warn("failed tx_urb %d", res);
-		hpna->stats.tx_errors++;
-		netif_start_queue( net_dev );
+		pegasus->stats.tx_errors++;
+		netif_start_queue(net);
 	} else {
-		hpna->stats.tx_packets++;
-		hpna->stats.tx_bytes += skb->len;
-		net_dev->trans_start = jiffies;
+		pegasus->stats.tx_packets++;
+		pegasus->stats.tx_bytes += skb->len;
+		net->trans_start = jiffies;
 	}
-	dev_kfree_skb( skb );
-	spin_unlock( &hpna->hpna_lock );
-	return	0;
-}
 
+	dev_kfree_skb(skb);
 
-static struct net_device_stats *hpna_netdev_stats( struct net_device *dev )
-{
-	return	&((usb_hpna_t *)dev->priv)->stats;
+	spin_unlock(&pegasus->pegasus_lock);
+
+	return 0;
 }
 
-static int hpna_open( struct net_device *net_dev )
+static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
 {
-	usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
-	int	res;
+	return &((struct pegasus *)dev->priv)->stats;
+}
 
-	if ( hpna->active )
-		return	-EBUSY;
-	else
-		hpna->active = 1;
+static int pegasus_open(struct net_device *net)
+{
+	struct pegasus *pegasus = (struct pegasus *)net->priv;
+	int res;
 
-	if ( start_net(net_dev, hpna->usb_dev) ) {
-		err("can't start_net()");
-		return	-EIO;
+	if ((res = pegasus_start_net(net, pegasus->usb))) {
+		err("can't start_net() - %d", res);
+		return -EIO;
 	}
 
-	if ( (res = usb_submit_urb( &hpna->rx_urb )) )
-		warn("failed rx_urb %d", res);
+	if ((res = usb_submit_urb(&pegasus->rx_urb)))
+		warn("(open)failed rx_urb %d", res);
 
-/*	usb_submit_urb( &hpna->intr_urb );*/
-	netif_start_queue( net_dev );
+/*	usb_submit_urb(&pegasus->intr_urb);*/
+	netif_start_queue(net);
 
 	MOD_INC_USE_COUNT;
 
 	return 0;
 }
 
-
-static int hpna_close( struct net_device *net_dev )
+static int pegasus_close(struct net_device *net)
 {
-	usb_hpna_t	*hpna = net_dev->priv;
-
+	struct pegasus	*pegasus = net->priv;
 
-	netif_stop_queue( net_dev );
+	netif_stop_queue(net);
 
-	usb_unlink_urb( &hpna->rx_urb );
-	usb_unlink_urb( &hpna->tx_urb );
-/*	usb_unlink_urb( hpna->intr_urb );*/
-
-	hpna->active = 0;
+	usb_unlink_urb(&pegasus->rx_urb);
+	usb_unlink_urb(&pegasus->tx_urb);
+/*	usb_unlink_urb(&pegasus->intr_urb); */
 
 	MOD_DEC_USE_COUNT;
 
 	return 0;
 }
 
-
-static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd )
+static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
 {
-	__u16	*data = (__u16 *)&rq->ifr_data;
-	usb_hpna_t	*hpna = dev->priv;
+	__u16 *data = (__u16 *)&rq->ifr_data;
+	struct pegasus	*pegasus = net->priv;
 
-	switch( cmd ) {
-		case SIOCDEVPRIVATE: 
+	switch(cmd) {
+		case SIOCDEVPRIVATE:
 			data[0] = 1;
 		case SIOCDEVPRIVATE+1:
-			read_phy_word(hpna->usb_dev, data[1] & 0x1f, &data[3]);
-			return	0;
+			pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]);
+			return 0;
 		case SIOCDEVPRIVATE+2:
-			if ( !capable(CAP_NET_ADMIN) )
-				return	-EPERM;
-			write_phy_word(hpna->usb_dev, data[1] & 0x1f, data[2]);
-			return	0;
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]);
+			return 0;
 		default:
-			return	-EOPNOTSUPP;
+			return -EOPNOTSUPP;
 	}
 }
 
-
-static void set_rx_mode( struct net_device *net_dev )
+static void pegasus_set_rx_mode(struct net_device *net)
 {
-	usb_hpna_t	*hpna=net_dev->priv;
+	struct pegasus *pegasus = net->priv;
 
-	netif_stop_queue( net_dev );
-	
-	if ( net_dev->flags & IFF_PROMISC ) {
-		info("%s: Promiscuous mode enabled", net_dev->name);
-		hpna_set_register( hpna->usb_dev, 2, 0x04 );
-	} else if ((net_dev->mc_count > multicast_filter_limit) ||
-			(net_dev->flags & IFF_ALLMULTI)) {
-		hpna_set_register(hpna->usb_dev, 0, 0xfa);
-		hpna_set_register(hpna->usb_dev, 2, 0);
+	netif_stop_queue(net);
+
+	if (net->flags & IFF_PROMISC) {
+		info("%s: Promiscuous mode enabled", net->name);
+		pegasus_set_register(pegasus->usb, 2, 0x04);
+	} else if ((net->mc_count > multicast_filter_limit) ||
+			(net->flags & IFF_ALLMULTI)) {
+		pegasus_set_register(pegasus->usb, 0, 0xfa);
+		pegasus_set_register(pegasus->usb, 2, 0);
 	} else {
-		dbg("%s: set Rx mode", net_dev->name);
+		dbg("%s: set Rx mode", net->name);
 	}
 
-	netif_wake_queue( net_dev );
+	netif_wake_queue(net);
 }
 
-
-static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum )
+static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
 {
-	struct net_device 		*net_dev;
-	usb_hpna_t			*hpna = &usb_dev_hpna;
-
+	struct net_device *net;
+	struct pegasus *pegasus;
 
-
-	if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
-	     dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) {
-		return	NULL;
+	if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
+		dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) {
+		return NULL;
 	}
 
-	printk("USB HPNA Pegasus found\n");
-
-	if ( usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+	if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
 		err("usb_set_configuration() failed");
 		return NULL;
 	}
 
-	hpna->usb_dev = dev;
-
-	hpna->rx_pipe = usb_rcvbulkpipe(hpna->usb_dev, 1);
-	hpna->tx_pipe = usb_sndbulkpipe(hpna->usb_dev, 2);
-	hpna->intr_pipe = usb_rcvintpipe(hpna->usb_dev, 0);
+	if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
+		err("out of memory allocating device structure");
+		return NULL;
+	}
+	memset(pegasus, 0, sizeof(struct pegasus));
 
-	if ( reset_mac(dev) ) {
+	if (pegasus_reset_mac(dev)) {
 		err("can't reset MAC");
+		kfree(pegasus);
+		return NULL;
 	}
-
-	hpna->present = 1;
-
-	if(!(hpna->rx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
-		err("not enough mem for out buff");
-		return	NULL;
-	}
-	if(!(hpna->tx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
-		kfree_s(hpna->rx_buff, MAX_MTU);
-		err("not enough mem for out buff");
-		return	NULL;
-	}
-
-	net_dev = init_etherdev( 0, 0 );
-	hpna->net_dev = net_dev;
-	net_dev->priv = hpna;
-	net_dev->open = hpna_open;
-	net_dev->stop = hpna_close;
-	net_dev->watchdog_timeo = TX_TIMEOUT;
-	net_dev->tx_timeout = tx_timeout;
-	net_dev->do_ioctl = hpna_ioctl; 
-	net_dev->hard_start_xmit = hpna_start_xmit;
-	net_dev->set_multicast_list = set_rx_mode;
-	net_dev->get_stats = hpna_netdev_stats; 
-	net_dev->mtu = HPNA_MTU;
-	hpna->hpna_lock = SPIN_LOCK_UNLOCKED;
-	
-	FILL_BULK_URB( &hpna->rx_urb, hpna->usb_dev, hpna->rx_pipe, 
-			hpna->rx_buff, MAX_MTU, hpna_read_irq, net_dev );
-	FILL_BULK_URB( &hpna->tx_urb, hpna->usb_dev, hpna->tx_pipe, 
-			hpna->tx_buff, MAX_MTU, hpna_write_irq, net_dev );
-	FILL_INT_URB( &hpna->intr_urb, hpna->usb_dev, hpna->intr_pipe,
-			hpna->intr_buff, 8, hpna_irq, net_dev, 250 );
-	
-/*	list_add( &hpna->list, &hpna_list );*/
 	
-	return	net_dev; 
+	net = init_etherdev(0, 0);
+	net->priv = pegasus;
+	net->open = pegasus_open;
+	net->stop = pegasus_close;
+	net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
+	net->tx_timeout = pegasus_tx_timeout;
+	net->do_ioctl = pegasus_ioctl;
+	net->hard_start_xmit = pegasus_start_xmit;
+	net->set_multicast_list = pegasus_set_rx_mode;
+	net->get_stats = pegasus_netdev_stats;
+	net->mtu = PEGASUS_MTU;
+
+	pegasus->usb = dev;
+	pegasus->net = net;
+	pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED;
+
+	FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
+			pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, 
+			pegasus);
+	FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
+			pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk,
+			pegasus);
+	FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 0),
+			pegasus->intr_buff, 8, pegasus_irq, pegasus, 250);
+
+
+	printk(KERN_INFO "%s: ADMtek AN986 Pegasus usb device\n", net->name);
+
+	return pegasus;
 }
 
-
-static void usb_hpna_disconnect( struct usb_device *dev, void *ptr )
+static void pegasus_disconnect(struct usb_device *dev, void *ptr)
 {
-	struct net_device	*net_dev = ptr;
-	struct usb_hpna		*hpna = net_dev->priv;
+	struct pegasus *pegasus = ptr;
 
+	if (!pegasus) {
+		warn("unregistering non-existant device");
+		return;
+	}
 
-	if ( net_dev->flags & IFF_UP )
-		dev_close(net_dev);
-	
-	unregister_netdev( net_dev );
+	if (pegasus->net->flags & IFF_UP)
+		dev_close(pegasus->net);
 
-	if ( !hpna ) /* should never happen */
-		return;
-		
-	usb_unlink_urb( &hpna->rx_urb );
-	usb_unlink_urb( &hpna->tx_urb );
-/*	usb_unlink_urb( &hpna->intr_urb );*/
-	kfree_s(hpna->rx_buff, MAX_MTU);
-	kfree_s(hpna->tx_buff, MAX_MTU);
+	unregister_netdev(pegasus->net);
 
-	hpna->usb_dev = NULL;
-	hpna->present = 0;
+	usb_unlink_urb(&pegasus->rx_urb);
+	usb_unlink_urb(&pegasus->tx_urb);
+/*	usb_unlink_urb(&pegasus->intr_urb);*/
 
-	printk("USB HPNA disconnected\n");
+	kfree(pegasus);
 }
 
-
-static struct usb_driver usb_hpna_driver = {
-	"ADMtek \"Pegasus\" USB Ethernet",
-	usb_hpna_probe,
-	usb_hpna_disconnect,
-	{NULL, NULL}
+static struct usb_driver pegasus_driver = {
+	name:		"pegasus",
+	probe:		pegasus_probe,
+	disconnect:	pegasus_disconnect,
 };
 
-
-
-static int  __init start_hpna( void )
+int __init pegasus_init(void)
 {
 	printk( version );
-	return	usb_register( &usb_hpna_driver );
+	return usb_register(&pegasus_driver);
 }
 
-
-static void __exit stop_hpna( void )
+void __exit pegasus_exit(void)
 {
-	usb_deregister( &usb_hpna_driver );
+	usb_deregister(&pegasus_driver);
 }
 
-
-module_init( start_hpna );
-module_exit( stop_hpna );
+module_init(pegasus_init);
+module_exit(pegasus_exit);
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/serial/Makefile linux/drivers/usb/serial/Makefile
--- v2.3.51/linux/drivers/usb/serial/Makefile	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/serial/Makefile	Sun Mar 12 19:18:54 2000
@@ -6,14 +6,15 @@
 
 SUB_DIRS	:=
 MOD_SUB_DIRS	:= $(SUB_DIRS)
+MOD_IN_SUB_DIRS	:= $(SUB_DIRS)
 ALL_SUB_DIRS	:= $(SUB_DIRS)
 
 # The target object and module list name.
 
-O_TARGET	:= usbdrv.o
-M_OBJS		:=
-O_OBJS		:=
-MOD_LIST_NAME	:= USB_MODULES
+O_TARGET	:= serial.o
+M_OBJS		:= usb-serial.o
+O_OBJS		:= usb-serial.o
+#MOD_LIST_NAME	:= USB_MODULES
 
 # Objects that export symbols.
 
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- v2.3.51/linux/drivers/usb/uhci.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/uhci.c	Tue Mar 14 17:48:14 2000
@@ -236,7 +236,7 @@
 static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
 {
 	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td, *prevtd = NULL;
+	struct uhci_td *td, *prevtd;
 
 	if (!urbp)
 		return;
@@ -617,6 +617,8 @@
 	return -EINPROGRESS;
 }
 
+static int usb_control_retrigger_status(urb_t *urb);
+
 static int uhci_result_control(urb_t *urb)
 {
 	struct urb_priv *urbp = urb->hcpriv;
@@ -630,6 +632,9 @@
 	if (!td)
 		return -EINVAL;
 
+	if (urbp->short_control_packet)
+		goto status_phase;
+
 	/* The first TD is the SETUP phase, check the status, but skip */
 	/*  the count */
 	status = uhci_status_bits(td->status);
@@ -653,10 +658,9 @@
 
 		/* If SPD is set then we received a short packet */
 		/*  There will be no status phase at the end */
-		/* FIXME: Re-setup the queue to run the STATUS phase? */
 		if ((td->status & TD_CTRL_SPD) &&
 		    (uhci_actual_length(td->status) < uhci_expected_length(td->info)))
-			return 0;
+			return usb_control_retrigger_status(urb);
 
 		if (status)
 			goto td_error;
@@ -664,12 +668,13 @@
 		td = td->list.next;
 	}
 
+status_phase:
 	/* Control status phase */
 	status = uhci_status_bits(td->status);
 
 	/* APC BackUPS Pro kludge */
-	/* It tries to send all of the descriptor instead of */
-	/*  the amount we requested */
+	/* It tries to send all of the descriptor instead of the amount */
+	/*  we requested */
 	if (td->status & TD_CTRL_IOC &&
 	    status & TD_CTRL_ACTIVE &&
 	    status & TD_CTRL_NAK)
@@ -698,6 +703,47 @@
 	    			uhci_packetout(td->info));
 
 	return uhci_map_status(status, uhci_packetout(td->info));
+}
+
+static int usb_control_retrigger_status(urb_t *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci *uhci = urb->dev->bus->hcpriv;
+	struct uhci_td *td, *nexttd;
+
+	urbp->short_control_packet = 1;
+
+	/* Delete all of the TD's except for the status TD at the end */
+	td = urbp->list.begin;
+	while (td && td->list.next) {
+		nexttd = td->list.next;
+
+		uhci_remove_td_from_urb(urb, td);
+
+		uhci_remove_td(uhci, td);
+
+		uhci_free_td(td);
+		
+		td = nexttd;
+	}
+
+	/* Create a new QH to avoid pointer overwriting problems */
+	uhci_remove_qh(uhci, urbp->qh);
+
+	urbp->qh = uhci_alloc_qh(urb->dev);
+	if (!urbp->qh)
+		return -ENOMEM;
+
+	/* One TD, who cares about Breadth first? */
+	uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->pipe & TD_CTRL_LS)
+		uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+	else
+		uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+
+	return -EINPROGRESS;
 }
 
 /*
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/uhci.h linux/drivers/usb/uhci.h
--- v2.3.51/linux/drivers/usb/uhci.h	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/uhci.h	Tue Mar 14 17:48:14 2000
@@ -338,7 +338,11 @@
 struct urb_priv {
 	struct uhci_qh *qh;		/* QH for this URB */
 
-	int fsbr;
+	int fsbr;			/* Did this URB turn on FSBR? */
+
+	char short_control_packet;	/* If we get a short packet during */
+					/*  a control transfer, retrigger */
+					/*  the status phase */
 
 	unsigned long inserttime;	/* In jiffies */
 
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c
--- v2.3.51/linux/drivers/usb/usb-core.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/usb-core.c	Sun Mar 12 19:18:56 2000
@@ -31,7 +31,6 @@
 int usb_audio_init(void);
 int usb_cpia_init(void);
 int usb_ibmcam_init(void);
-int usb_ov511_init(void);
 int dabusb_init(void);
 int plusb_init(void);
 
@@ -78,11 +77,11 @@
 #ifdef CONFIG_USB_IBMCAM
 	usb_ibmcam_init();
 #endif
-#ifdef CONFIG_USB_OV511
-	usb_ov511_init();
-#endif
 #ifdef CONFIG_USB_DABUSB
 	dabusb_init();
+#endif
+#ifdef CONFIG_USB_DSBR
+	dsbr100_init();
 #endif
 #ifdef CONFIG_USB_PLUSB
 	plusb_init();
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/usb-storage.c linux/drivers/usb/usb-storage.c
--- v2.3.51/linux/drivers/usb/usb-storage.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/usb-storage.c	Tue Mar 14 17:54:42 2000
@@ -6,9 +6,9 @@
  * Further reference:
  *	This driver is based on the 'USB Mass Storage Class' document. This
  *	describes in detail the protocol used to communicate with such
- *      devices.  Clearly, the designers had SCSI commands in mind when they
- *      created this document.  The commands are all similar to commands
- *      in the SCSI-II specification.
+ *      devices.  Clearly, the designers had SCSI and ATAPI commands in mind 
+ *      when they created this document.  The commands are all very similar 
+ *      to commands in the SCSI-II and ATAPI specifications.
  *
  *	It is important to note that in a number of cases this class exhibits
  *	class-specific exemptions from the USB specification. Notably the
@@ -65,8 +65,6 @@
 
 static int my_host_number;
 
-int usb_stor_debug = 1;
-
 struct us_data;
 
 typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
@@ -74,7 +72,7 @@
 typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
 
 struct us_data {
-	struct us_data	*next;		         /* next device */
+	struct us_data	        *next;	         /* next device */
 	struct usb_device	*pusb_dev;       /* this usb_device */
 	unsigned int		flags;		 /* from filter initially */
 	__u8			ifnum;		 /* interface number */
@@ -93,15 +91,17 @@
 	int			host_number;	 /* to find us */
 	int			host_no;	 /* allocated by scsi */
 	Scsi_Cmnd		*srb;		 /* current srb */
+	Scsi_Cmnd		*queue_srb;	 /* the single queue slot */
 	int			action;		 /* what to do */
-	wait_queue_head_t	waitq;		 /* thread waits */
-	wait_queue_head_t	ip_waitq;	 /* for CBI interrupts */
+	struct semaphore	ip_waitq;	 /* for CBI interrupts */
 	__u16			ip_data;	 /* interrupt data */
 	int			ip_wanted;	 /* needed */
 	int			pid;		 /* control thread */
 	struct semaphore	*notify;	 /* wait for thread to begin */
 	void			*irq_handle;	 /* for USB int requests */
 	unsigned int		irqpipe;	 /* pipe for release_irq */
+	struct semaphore        sleeper;         /* to sleep on */
+	struct semaphore        queue_exclusion; /* to protect data structs */
 };
 
 /*
@@ -129,117 +129,100 @@
  * Data transfer routines
  ***********************************************************************/
 
-/* Transfer one buffer (breaking into packets if necessary)
- * Note that this function is necessary because if the device NAKs, we
- * need to know that information directly
+/* FIXME: the names of these functions are poorly choosen. */
+
+/*
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory.  Good performance is achived by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
  *
- * FIXME: is the above true?  Or will the URB status show ETIMEDOUT after
- * retrying several times allready?  Perhaps this is the way we should
- * be going anyway?
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit.  Thus we don't have to worry about it for individual
+ * packets.
  */
-static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+static int us_bulk_transfer(struct us_data *us, int pipe, 
+			    char *buf, int length)
 {
-	int max_size;
-	int this_xfer;
 	int result;
 	int partial;
-	int maxtry;
-
-	/* determine the maximum packet size for these transfers */
-	max_size = usb_maxpacket(us->pusb_dev, 
-				 pipe, usb_pipeout(pipe)) * 16;
-
-	/* while we have data left to transfer */
-	while (length) {
-
-		/* calculate how long this will be -- maximum or a remainder */
-		this_xfer = length > max_size ? max_size : length;
-		length -= this_xfer;
-
-		/* FIXME: this number is totally outrageous.  We need to pick
-		 * a better (smaller) number).
-		 */
-
-		/* setup the retry counter */
-		maxtry = 100;
-
-		/* set up the transfer loop */
-		do {
-			/* transfer the data */
-			US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n", 
-				  (unsigned int)buf, this_xfer, 101 - maxtry);
-			result = usb_bulk_msg(us->pusb_dev, pipe, buf,
-					      this_xfer, &partial, HZ*5);
-			US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
-				  result, partial, this_xfer);
-
-			/* if we stall, we need to clear it before we go on */
-			if (result == -EPIPE) {
-				US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-				usb_clear_halt(us->pusb_dev, pipe);
-			}
-
-			/* update to show what data was transferred */
-			this_xfer -= partial;
-			buf += partial;
 
-			/* NAK - we retry a few times */
-			if (result == -ETIMEDOUT) {
-
-				US_DEBUGP("us_one_transfer: device NAKed\n");
-
-				/* if our try counter reaches 0, bail out */
-				if (!maxtry--)
-					return -ETIMEDOUT;
+	/* transfer the data */
+	US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length);
+	result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, HZ*5);
+	US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
+		  result, partial, length);
+	
+	/* if we stall, we need to clear it before we go on */
+	if (result == -EPIPE) {
+		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+		usb_clear_halt(us->pusb_dev, pipe);
+	}
 
-				/* just continue the while loop */
-				continue;
-			}
-      
-			/* other errors (besides NAK) -- we just bail out*/
-			if (result != 0) {
-				US_DEBUGP("us_one_transfer: device returned error %d\n", result);
-				return result;
-			}
+	/* did we send all the data? */
+	if (partial == length) {
+		return US_BULK_TRANSFER_GOOD;
+	}
 
-			/* continue until this transfer is done */
-		} while ( this_xfer );
+	/* uh oh... we have an error code, so something went wrong. */
+	if (result) {
+		/* NAK - that means we've retried a few times allready */
+		if (result == -ETIMEDOUT) {
+			US_DEBUGP("us_bulk_transfer: device NAKed\n");
+		}
+		return US_BULK_TRANSFER_FAILED;
 	}
 
-	/* if we get here, we're done and successful */
-	return 0;
+	/* no error code, so we must have transferred some data, 
+	 * just not all of it */
+	return US_BULK_TRANSFER_SHORT;
 }
 
-static unsigned int us_transfer_length(Scsi_Cmnd *srb);
-
-/* transfer one SCSI command, using scatter-gather if requested */
-/* FIXME: what do the return codes here mean? */
-static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_bulk_transfer to achive it's goals -- this
+ * function simply determines if we're going to use scatter-gather or not,
+ * and acts appropriately.  For now, it also re-interprets the error codes.
+ */
+static void us_transfer(Scsi_Cmnd *srb, int dir_in)
 {
-	struct us_data *us = (struct us_data *)srb->host_scribble;
+	struct us_data *us;
 	int i;
 	int result = -1;
-	unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
-		usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+	unsigned int pipe;
+	struct scatterlist *sg;
 
-	/* FIXME: stop transferring data at us_transfer_length(), not 
-	 * bufflen */
+	/* calculate the appropriate pipe information */
+	us = (struct us_data*) srb->host_scribble;
+	if (dir_in)
+		pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+	else
+		pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+	/* are we scatter-gathering? */
 	if (srb->use_sg) {
-		struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
 
+		/* loop over all the scatter gather structures and 
+		 * make the appropriate requests for each, until done
+		 */
+		sg = (struct scatterlist *) srb->request_buffer;
 		for (i = 0; i < srb->use_sg; i++) {
-			result = us_one_transfer(us, pipe, sg[i].address, sg[i].length);
+			result = us_bulk_transfer(us, pipe, sg[i].address, 
+						  sg[i].length);
 			if (result)
 				break;
 		}
 	}
 	else
-		result = us_one_transfer(us, pipe, srb->request_buffer, 
-					 us_transfer_length(srb));
+		/* no scatter-gather, just make the request */
+		result = us_bulk_transfer(us, pipe, srb->request_buffer, 
+					  srb->request_bufflen);
 
-	if (result < 0)
-		US_DEBUGP("us_transfer returning error %d\n", result);
-	return result;
+	/* return the result in the data structure itself */
+	srb->result = result;
 }
 
 /* calculate the length of the data transfer (not the command) for any
@@ -265,6 +248,9 @@
 	case MODE_SENSE:
 		return srb->cmnd[4];
 
+	case READ_CAPACITY:
+		return 8;
+
 	case LOG_SENSE:
 	case MODE_SENSE_10:
 		return (srb->cmnd[7] << 8) + srb->cmnd[8];
@@ -274,8 +260,9 @@
 	}
 
 	if (srb->use_sg) {
-		struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+		struct scatterlist *sg;
 
+		sg = (struct scatterlist *) srb->request_buffer;
 		for (i = 0; i < srb->use_sg; i++) {
 			total += sg[i].length;
 		}
@@ -289,12 +276,148 @@
  * Protocol routines
  ***********************************************************************/
 
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us);
-static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us);
+static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int old_cmnd = 0;
+	int result;
+  
+	/* Fix some commands -- this is a form of mode translation
+	 * ATAPI devices only accept 12 byte long commands 
+	 *
+	 * NOTE: This only works because a Scsi_Cmnd struct field contains
+	 * a unsigned char cmnd[12], so we know we have storage available
+	 */
+
+	/* set command length to 12 bytes */
+	srb->cmd_len = 12;
+
+	/* determine the correct (or minimum) data length for these commands */
+	switch (us->srb->cmnd[0]) {
+
+		/* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+	case MODE_SENSE:
+	case MODE_SELECT:
+		/* save the command so we can tell what it was */
+		old_cmnd = srb->cmnd[0];
+
+		srb->cmnd[11] = 0;
+		srb->cmnd[10] = 0;
+		srb->cmnd[9] = 0;
+		srb->cmnd[8] = srb->cmnd[4];
+		srb->cmnd[7] = 0;
+		srb->cmnd[6] = 0;
+		srb->cmnd[5] = 0;
+		srb->cmnd[4] = 0;
+		srb->cmnd[3] = 0;
+		srb->cmnd[2] = srb->cmnd[2];
+		srb->cmnd[1] = srb->cmnd[1];
+		srb->cmnd[0] = srb->cmnd[0] | 0x40;
+		break;
+
+		/* change READ_6/WRITE_6 to READ_10/WRITE_10, which 
+		 * are ATAPI commands */
+	case WRITE_6:
+	case READ_6:
+		srb->cmnd[11] = 0;
+		srb->cmnd[10] = 0;
+		srb->cmnd[9] = 0;
+		srb->cmnd[8] = srb->cmnd[4];
+		srb->cmnd[7] = 0;
+		srb->cmnd[6] = 0;
+		srb->cmnd[5] = srb->cmnd[3];
+		srb->cmnd[4] = srb->cmnd[2];
+		srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+		srb->cmnd[2] = 0;
+		srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+		srb->cmnd[0] = srb->cmnd[0] | 0x20;
+		break;
+	} /* end switch on cmnd[0] */
+  
+	/* send the command to the transport layer */
+	result = us->transport(srb, us);
+
+	/* If we got a short transfer, but it was for a command that
+	 * can have short transfers, we're actually okay
+	 */
+	if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+	    ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+	     (us->srb->cmnd[0] == INQUIRY) ||
+	     (us->srb->cmnd[0] == MODE_SENSE) ||
+	     (us->srb->cmnd[0] == LOG_SENSE) ||
+	     (us->srb->cmnd[0] == MODE_SENSE_10))) {
+		us->srb->result = DID_OK;
+	}
+
+	/*
+	 * If we have an error, we're going to do a 
+	 * REQUEST_SENSE automatically
+	 */
+	if (result != USB_STOR_TRANSPORT_GOOD) {
+		int temp_result;
+		void* old_request_buffer;
+		int old_sg;
+
+		US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
+
+		us->srb->cmnd[0] = REQUEST_SENSE;
+		us->srb->cmnd[1] = 0;
+		us->srb->cmnd[2] = 0;
+		us->srb->cmnd[3] = 0;
+		us->srb->cmnd[4] = 18;
+		us->srb->cmnd[5] = 0;
+    
+		/* set the buffer length for transfer */
+		old_request_buffer = us->srb->request_buffer;
+	        old_sg = us->srb->use_sg;
+		us->srb->request_bufflen = 18;
+		us->srb->request_buffer = us->srb->sense_buffer;
+
+		/* FIXME: what if this command fails? */
+		temp_result = us->transport(us->srb, us);
+		US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+		US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+			  us->srb->sense_buffer[2] & 0xf,
+			  us->srb->sense_buffer[12], 
+			  us->srb->sense_buffer[13]);
+
+		/* set the result so the higher layers expect this data */
+		us->srb->result = CHECK_CONDITION;
+
+		/* we're done here */
+		us->srb->request_buffer = old_request_buffer;
+		us->srb->use_sg = old_sg;
+		return;
+	}
+  
+	/* Fix the MODE_SENSE data if we translated the command
+	 */
+	if (old_cmnd == MODE_SENSE) {
+		unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+		/* FIXME: we need to compress the entire data structure here
+		 */
+		dta[0] = dta[1];	/* data len */
+		dta[1] = dta[2];	/* med type */
+		dta[2] = dta[3];	/* dev-spec prm */
+		dta[3] = dta[7];	/* block desc len */
+		printk (KERN_DEBUG USB_STORAGE
+			"new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+			dta[0], dta[1], dta[2], dta[3]);
+	}
+
+	/* Fix-up the return data from an INQUIRY command to show 
+	 * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+	 */
+	if (us->srb->cmnd[0] == INQUIRY) {
+		((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+	}
+}
+
 
 static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
 {
 	int old_cmnd = 0;
+	int result;
   
 	/* fix some commands -- this is a form of mode translation
 	 * UFI devices only accept 12 byte long commands 
@@ -372,23 +495,31 @@
 	} /* end switch on cmnd[0] */
   
 	/* send the command to the transport layer */
-	us->srb->result = us->transport(srb, us);
+	result = us->transport(srb, us);
 
-	/* if we have an error, we're going to do a 
-	 * REQUEST_SENSE automatically */
+	/* If we got a short transfer, but it was for a command that
+	 * can have short transfers, we're actually okay
+	 */
+	if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+	    ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+	     (us->srb->cmnd[0] == INQUIRY) ||
+	     (us->srb->cmnd[0] == MODE_SENSE) ||
+	     (us->srb->cmnd[0] == LOG_SENSE) ||
+	     (us->srb->cmnd[0] == MODE_SENSE_10))) {
+		us->srb->result = DID_OK;
+	}
 
-	/* FIXME: we should only do this for device 
-	 * errors, not system errors */
-	if (us->srb->result) {
+	/*
+	 * If we have an error, we're going to do a 
+	 * REQUEST_SENSE automatically
+	 */
+	if (result != USB_STOR_TRANSPORT_GOOD) {
 		int temp_result;
-		int count;
 		void* old_request_buffer;
+		int old_sg;
 
 		US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
 
-		/* set the result so the higher layers expect this data */
-		us->srb->result = CHECK_CONDITION;
-
 		us->srb->cmnd[0] = REQUEST_SENSE;
 		us->srb->cmnd[1] = 0;
 		us->srb->cmnd[2] = 0;
@@ -398,49 +529,34 @@
     
 		/* set the buffer length for transfer */
 		old_request_buffer = us->srb->request_buffer;
+	        old_sg = us->srb->use_sg;
 		us->srb->request_bufflen = 18;
-		us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+		us->srb->request_buffer = us->srb->sense_buffer;
 
 		/* FIXME: what if this command fails? */
 		temp_result = us->transport(us->srb, us);
 		US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
-		/* copy the data from the request buffer to the sense buffer */
-		for(count = 0; count < 18; count++)
-			us->srb->sense_buffer[count] = 
-				((unsigned char *)(us->srb->request_buffer))[count];
-
 		US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
 			  us->srb->sense_buffer[2] & 0xf,
-			  us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+			  us->srb->sense_buffer[12], 
+			  us->srb->sense_buffer[13]);
+
+		/* set the result so the higher layers expect this data */
+		us->srb->result = CHECK_CONDITION;
 
 		/* we're done here */
-		kfree(us->srb->request_buffer);
 		us->srb->request_buffer = old_request_buffer;
+		us->srb->use_sg = old_sg;
 		return;
 	}
   
-	/* FIXME: if we need to send more data, or recieve data, we should
-	 * do it here.  Then, we can do status handling here also.
-	 *
-	 * This includes MODE_SENSE from above
+	/* Fix the MODE_SENSE data here if we had to translate the command
 	 */
 	if (old_cmnd == MODE_SENSE) {
 		unsigned char *dta = (unsigned char *)us->srb->request_buffer;
 
-		/* calculate the new length */
-		int length = (dta[0] << 8) + dta[1] + 2;
-
-		/* copy the available data length into the structure */
-		us->srb->cmnd[7] = length >> 8;
-		us->srb->cmnd[8] = length & 0xFF;
-
-		/* send the command to the transport layer */
-		us->srb->result = us->transport(srb, us);
-
-		/* FIXME: this assumes that the 2nd attempt is always
-		 * successful convert MODE_SENSE_10 return data format 
-		 * to MODE_SENSE_6 format */
+		/* FIXME: we need to compress the entire data structure here
+		 */
 		dta[0] = dta[1];	/* data len */
 		dta[1] = dta[2];	/* med type */
 		dta[2] = dta[3];	/* dev-spec prm */
@@ -450,126 +566,18 @@
 			dta[0], dta[1], dta[2], dta[3]);
 	}
 
-	/* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/
-	 * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry 
+	/* Fix-up the return data from an INQUIRY command to show 
+	 * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
 	 */
-
-	/* FIXME: here is where we need to fix-up the return data from 
-	 * an INQUIRY command to show ANSI SCSI rev 2
-	 */
-
-	/* FIXME: The rest of this is bogus.  usb_control_msg() will only
-	 * return an error if we've really honked things up.  If it just
-	 * needs a START_STOP, then we'll get some data back via 
-	 * REQUEST_SENSE --  either way, this belongs at a higher level
-	 */
-
-#if 0
-	/* For UFI, if this is the first time we've sent this TEST_UNIT_READY 
-	 * command, we can try again
-	 */
-	if (!done_start && (us->subclass == US_SC_UFI)
-	    && (cmd[0] == TEST_UNIT_READY) && (result < 0)) {
-    
-		/* as per spec try a start command, wait and retry */
-		wait_ms(100);
-    
-		done_start++;
-		memset(cmd, 0, sizeof(cmd));
-		cmd[0] = START_STOP;
-		cmd[4] = 1;		/* start */
-    
-		result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-					 US_CBI_ADSC, 
-					 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-					 0, us->ifnum,
-					 cmd, 12, HZ*5);
-		US_DEBUGP("Next usb_control_msg returns %d\n", result);
-    
-				/* allow another retry */
-		retry++;
-		continue;
+	if (us->srb->cmnd[0] == INQUIRY) {
+		((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
 	}
-#endif
 }
 
 static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
 {
-	unsigned int savelen = us->srb->request_bufflen;
-	unsigned int saveallocation = 0;
-
-#if 0
-	/* force attention on first command */
-	if (!us->attention_done) {
-		if (us->srb->cmnd[0] == REQUEST_SENSE) {
-			US_DEBUGP("forcing unit attention\n");
-			us->attention_done = 1;
-
-			if (us->srb->result == USB_STOR_TRANSPORT_GOOD) {
-				unsigned char *p = (unsigned char *)us->srb->request_buffer;
-	
-				if ((p[2] & 0x0f) != UNIT_ATTENTION) {
-					p[2] = UNIT_ATTENTION;
-					p[12] = 0x29;	/* power on, reset or bus-reset */
-					p[13] = 0;
-				} /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */
-			} /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */
-		}
-	} /* if (!us->attention_done) */
-#endif
+	unsigned int result = 0;
 
-	/* If the command has a variable-length payload, then we do them
-	 * in two steps -- first we do the minimum, then we recalculate
-	 * then length, and re-issue the command 
-	 *
-	 * we use savelen to remember how much buffer we really have
-	 * we use savealloction to remember how much was really requested
-	 */
-
-	/* FIXME: remove savelen based on mods to us_transfer_length() */
-	switch (us->srb->cmnd[0]) {
-	case REQUEST_SENSE:
-		if (us->srb->request_bufflen > 18)
-			us->srb->request_bufflen = 18;
-		else
-			break;
-		saveallocation = us->srb->cmnd[4];
-		us->srb->cmnd[4] = 18;
-		break;
-    
-	case INQUIRY:
-		if (us->srb->request_bufflen > 36)
-			us->srb->request_bufflen = 36;
-		else
-			break;
-		saveallocation = us->srb->cmnd[4];
-		us->srb->cmnd[4] = 36;
-		break;
-    
-	case MODE_SENSE:
-		if (us->srb->request_bufflen > 4)
-			us->srb->request_bufflen = 4;
-		else
-			break;
-		saveallocation = us->srb->cmnd[4];
-		us->srb->cmnd[4] = 4;
-		break;
-    
-	case LOG_SENSE:
-	case MODE_SENSE_10:
-		if (us->srb->request_bufflen > 8)
-			us->srb->request_bufflen = 8;
-		else
-			break;
-		saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
-		us->srb->cmnd[7] = 0;
-		us->srb->cmnd[8] = 8;
-		break;
-    
-	default:
-		break;
-	} /* end switch on cmnd[0] */
-  
 	/* This code supports devices which do not support {READ|WRITE}_6
 	 * Apparently, neither Windows or MacOS will use these commands,
 	 * so some devices do not support them
@@ -631,25 +639,33 @@
 			US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
 			US_DEBUG(us_show_command(us->srb));
 		}
-	} /* end if (us->flags & US_FL_MODE_XLATE) */
+	} /* if (us->flags & US_FL_MODE_XLATE) */
   
 	/* send the command to the transport layer */
-	us->srb->result = us->transport(us->srb, us);
+	result = us->transport(us->srb, us);
+
+	/* If we got a short transfer, but it was for a command that
+	 * can have short transfers, we're actually okay
+	 */
+	if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+	    ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+	     (us->srb->cmnd[0] == INQUIRY) ||
+	     (us->srb->cmnd[0] == MODE_SENSE) ||
+	     (us->srb->cmnd[0] == LOG_SENSE) ||
+	     (us->srb->cmnd[0] == MODE_SENSE_10))) {
+		us->srb->result = DID_OK;
+	}
 
 	/* if we have an error, we're going to do a REQUEST_SENSE 
 	 * automatically */
-	/* FIXME: we should only do this for device errors, not 
-	 * system errors */
-	if (us->srb->result) {
+	if (result != USB_STOR_TRANSPORT_GOOD) {
 		int temp_result;
-		int count;
+		int old_sg;
 		void* old_request_buffer;
 
 		US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
 
-		/* set the result so the higher layers expect this data */
-		us->srb->result = CHECK_CONDITION;
-
+		/* set up the REQUEST_SENSE command and parameters */
 		us->srb->cmnd[0] = REQUEST_SENSE;
 		us->srb->cmnd[1] = 0;
 		us->srb->cmnd[2] = 0;
@@ -659,115 +675,32 @@
     
 		/* set the buffer length for transfer */
 		old_request_buffer = us->srb->request_buffer;
+	        old_sg = us->srb->use_sg;
 		us->srb->request_bufflen = 18;
-		us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+		us->srb->request_buffer = us->srb->sense_buffer;
 
 		/* FIXME: what if this command fails? */
 		temp_result = us->transport(us->srb, us);
 		US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
-		/* copy the data from the request buffer to the sense buffer */
-		for(count = 0; count < 18; count++)
-			us->srb->sense_buffer[count] = 
-				((unsigned char *)(us->srb->request_buffer))[count];
-
 		US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
 			  us->srb->sense_buffer[2] & 0xf,
-			  us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+			  us->srb->sense_buffer[12], 
+			  us->srb->sense_buffer[13]);
+
+		/* set the result so the higher layers expect this data */
+		us->srb->result = CHECK_CONDITION;
 
 		/* we're done here */
-		kfree(us->srb->request_buffer);
+		us->srb->use_sg = old_sg;
 		us->srb->request_buffer = old_request_buffer;
 		return;
 	}
 
-	if (savelen != us->srb->request_bufflen) {
-		unsigned char *p = (unsigned char *)us->srb->request_buffer;
-		unsigned int length = 0;
-    
-		/* set correct length and retry */
-		switch (us->srb->cmnd[0]) {
-
-			/* FIXME: we should try to get all the sense data */
-		case REQUEST_SENSE:
-			/* simply return 18 bytes */
-			p[7] = 10;
-			length = us->srb->request_bufflen;
-			break;
-      
-		case INQUIRY:
-			length = p[4] + 5 > savelen ? savelen : p[4] + 5;
-			us->srb->cmnd[4] = length;
-			break;
-      
-		case MODE_SENSE:
-			US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]);
-			length = p[0] + 1 > savelen ? savelen : p[0] + 1;
-			us->srb->cmnd[4] = length;
-			break;
-      
-		case LOG_SENSE:
-			length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
-			us->srb->cmnd[7] = length >> 8;
-			us->srb->cmnd[8] = length;
-			break;
-      
-		case MODE_SENSE_10:
-			US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n",
-				  (p[0] << 8) + p[1]);
-			length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6;
-			us->srb->cmnd[7] = length >> 8;
-			us->srb->cmnd[8] = length;
-			break;
-		} /* end switch on cmnd[0] */
-    
-		US_DEBUGP("Old/New length = %d/%d\n",
-			  savelen, length);
-    
-		/* issue the new command */
-		/* FIXME: this assumes that the second attempt is 
-		 * always successful */
-		if (us->srb->request_bufflen != length) {
-			US_DEBUGP("redoing cmd with len=%d\n", length);
-			us->srb->request_bufflen = length;
-			us->srb->result = us->transport(us->srb, us);
-		}
-    
-		/* reset back to original values */
-		us->srb->request_bufflen = savelen;
-
-		/* fix data as necessary */
-		switch (us->srb->cmnd[0]) {
-		case INQUIRY:
-			if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) { 
-				US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
-				((unsigned char*)us->srb->request_buffer)[2] |= 2;
-			}
-			/* FALL THROUGH */
-		case REQUEST_SENSE:
-		case MODE_SENSE:
-			if (us->srb->use_sg == 0 && length > 0) {
-				int i;
-				printk(KERN_DEBUG "Data is");
-				for (i = 0; i < 32 && i < length; ++i)
-					printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]);
-				if (i < length)
-					printk(" ...");
-				printk("\n");
-			}
-
-			/* FIXME: is this really necessary? */
-			us->srb->cmnd[4] = saveallocation;
-			break;
-      
-		case LOG_SENSE:
-		case MODE_SENSE_10:
-			/* FIXME: is this really necessary? */
-			us->srb->cmnd[7] = saveallocation >> 8;
-			us->srb->cmnd[8] = saveallocation;
-			break;
-		} /* end switch on cmnd[0] */
-	} /* if good command */
+	/* fix the results of an INQUIRY */
+	if (us->srb->cmnd[0] == INQUIRY) {
+		US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+		((unsigned char*)us->srb->request_buffer)[2] |= 2;
+	}
 }
 
 /***********************************************************************
@@ -789,7 +722,7 @@
 	/* was this a wanted interrupt? */
 	if (us->ip_wanted) {
 		us->ip_wanted = 0;
-		wake_up(&us->ip_waitq);
+		up(&(us->ip_waitq));
 	} else {
 		US_DEBUGP("ERROR: Unwanted interrupt received!\n");
 	}
@@ -801,9 +734,7 @@
 	return 0;
 }
 
-/* FIXME: this reset function doesn't really reset the port, and it
- * should. Actually it should probably do what it's doing here, and
- * reset the port physically
+/* This issues a CB[I] Reset to the device in question
  */
 static int CB_reset(struct us_data *us)
 {
@@ -816,41 +747,39 @@
 	cmd[0] = SEND_DIAGNOSTIC;
 	cmd[1] = 4;
 	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-				 US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				 US_CBI_ADSC, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
 				 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
 
 	/* long wait for reset */
 	schedule_timeout(HZ*6);
 
 	US_DEBUGP("CB_reset: clearing endpoint halt\n");
-	usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-	usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
 
 	US_DEBUGP("CB_reset done\n");
 	return 0;
 }
 
-static int pop_CB_status(Scsi_Cmnd *srb);
-
-/* FIXME: we also need a CBI_command which sets up the completion
- * interrupt, and waits for it
+/*
+ * Control/Bulk/Interrupt transport
  */
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
 	int result;
 
 	US_DEBUGP("CBI gets a command:\n");
 	US_DEBUG(us_show_command(srb));
 
-	/* FIXME: we aren't setting the ip_wanted indicator early enough, which
-	 * causes some commands to never complete.  This hangs the driver.
-	 */
-
+	/* COMMAND STAGE */
 	/* let's send the command via the control pipe */
-	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-				 US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				 0, us->ifnum,
-				 srb->cmnd, srb->cmd_len, HZ*5);
+	result = usb_control_msg(us->pusb_dev, 
+				 usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
+				 us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
 
 	/* check the return code for the command */
 	if (result < 0) {
@@ -858,131 +787,160 @@
 
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
-			US_DEBUGP("-- Stall on control pipe detected. Clearing\n");
-      
+			US_DEBUGP("-- Stall on control pipe. Clearing\n");
 			US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
 				  usb_clear_halt(us->pusb_dev, 
-						 usb_sndctrlpipe(us->pusb_dev, 0)));
+						 usb_sndctrlpipe(us->pusb_dev,
+								 0)));
 			return USB_STOR_TRANSPORT_ERROR;
 		}
 
-		/* FIXME: we need to handle NAKs here */
+				/* FIXME: we need to handle NAKs here */
 		return USB_STOR_TRANSPORT_ERROR;
 	}
 
+	/* Set up for status notification */
+	us->ip_wanted = 1;
+
+	/* DATA STAGE */
 	/* transfer the data payload for this command, if one exists*/
 	if (us_transfer_length(srb)) {
-		result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
-		US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result);
+		us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+		US_DEBUGP("CBI data stage result is 0x%x\n", result);
+	}
 
-		/* FIXME: what do the return codes from us_transfer mean? */
-		if ((result < 0) && 
-		    (result != USB_ST_DATAUNDERRUN) && 
-		    (result != USB_ST_STALL)) {
-			return DID_ERROR << 16;
-		}
-	} /* if (us_transfer_length(srb)) */
+	/* STATUS STAGE */
 
-	/* get status and return it */
-	return pop_CB_status(srb);
+	/* go to sleep until we get this interrup */
+	/* FIXME: this should be changed to use a timeout */
+	down(&(us->ip_waitq));
+	
+	/* FIXME: currently this code is unreachable, but the idea is
+	 * necessary.  See above comment.
+	 */
+	if (us->ip_wanted) {
+		US_DEBUGP("Did not get interrupt on CBI\n");
+		us->ip_wanted = 0;
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	
+	US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
+	
+	/* UFI gives us ASC and ASCQ, like a request sense */
+	/* FIXME: is this right?  Do REQUEST_SENSE and INQUIRY need special
+	 * case handling?
+	 */
+	if (us->subclass == US_SC_UFI) {
+		if (srb->cmnd[0] == REQUEST_SENSE ||
+		    srb->cmnd[0] == INQUIRY)
+			return USB_STOR_TRANSPORT_GOOD;
+		else
+			if (us->ip_data)
+				return USB_STOR_TRANSPORT_FAILED;
+			else
+				return USB_STOR_TRANSPORT_GOOD;
+	}
+	
+	/* otherwise, we interpret the data normally */
+	switch (us->ip_data) {
+	case 0x0001: 
+		return USB_STOR_TRANSPORT_GOOD;
+	case 0x0002: 
+		return USB_STOR_TRANSPORT_FAILED;
+	default: 
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
+	US_DEBUGP("CBI_transport() reached end of function\n");
+	return USB_STOR_TRANSPORT_ERROR;
 }
 
 /*
- * Control/Bulk status handler
+ * Control/Bulk transport
  */
-
-static int pop_CB_status(Scsi_Cmnd *srb)
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
-	struct us_data *us = (struct us_data *)srb->host_scribble;
-	int result = 0;
+	int result;
 	__u8 status[2];
-	int retry = 5;
-
-	US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol);
-	switch (us->protocol) {
-	case US_PR_CB:
-		/* get from control */
 
-		while (retry--) {
-			result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0),
-						 USB_REQ_GET_STATUS, USB_DIR_IN |
-						 USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-						 0, us->ifnum, status, sizeof(status), HZ*5);
-			if (result != USB_ST_TIMEOUT)
-				break;
-		}
-		if (result) {
-			US_DEBUGP("Bad AP status request %d\n", result);
-			return DID_ABORT << 16;
-		}
-		US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]);
-		if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
-		    ( (status[0] & ~3) || status[1]))
-			return (DID_OK << 16) | 2;
-		else
-			return USB_STOR_TRANSPORT_GOOD;
-		break;
+	US_DEBUGP("CBC gets a command:\n");
+	US_DEBUG(us_show_command(srb));
 
-		/* FIXME: this should be in a separate function */
-	case US_PR_CBI:
-		/* get from interrupt pipe */
+	/* COMMAND STAGE */
+	/* let's send the command via the control pipe */
+	result = usb_control_msg(us->pusb_dev, 
+				 usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
+				 us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
 
-		/* add interrupt transfer, marked for removal */
-		us->ip_wanted = 1;
+	/* check the return code for the command */
+	if (result < 0) {
+		US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
 
-		/* go to sleep until we get this interrup */
-		/* FIXME: this should be changed to use a timeout */
-		sleep_on(&us->ip_waitq);
-    
-		if (us->ip_wanted) {
-			US_DEBUGP("Did not get interrupt on CBI\n");
-			us->ip_wanted = 0;
+		/* a stall is a fatal condition from the device */
+		if (result == -EPIPE) {
+			US_DEBUGP("-- Stall on control pipe. Clearing\n");
+			US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
+				  usb_clear_halt(us->pusb_dev, 
+						 usb_sndctrlpipe(us->pusb_dev,
+								 0)));
 			return USB_STOR_TRANSPORT_ERROR;
 		}
-    
-		US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
 
-		/* UFI gives us ASC and ASCQ, like a request sense */
-		/* FIXME: is this right?  do REQUEST_SENSE and INQUIRY need special
-		 * case handling?
-		 */
-		if (us->subclass == US_SC_UFI) {
-			if (srb->cmnd[0] == REQUEST_SENSE ||
-			    srb->cmnd[0] == INQUIRY)
-				return USB_STOR_TRANSPORT_GOOD;
-			else
-				if (us->ip_data)
-					return USB_STOR_TRANSPORT_FAILED;
-				else
-					return USB_STOR_TRANSPORT_GOOD;
-		}
+				/* FIXME: we need to handle NAKs here */
+		return USB_STOR_TRANSPORT_ERROR;
+	}
 
-		/* otherwise, we interpret the data normally */
-		switch (us->ip_data) {
-		case 0x0001: 
-			return USB_STOR_TRANSPORT_GOOD;
-		case 0x0002: 
-			return USB_STOR_TRANSPORT_FAILED;
-		default: 
-			return USB_STOR_TRANSPORT_ERROR;
-		}
+	/* DATA STAGE */
+	/* transfer the data payload for this command, if one exists*/
+	if (us_transfer_length(srb)) {
+		us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+		US_DEBUGP("CBC data stage result is 0x%x\n", result);
 	}
-	US_DEBUGP("pop_CB_status, reached end of function\n");
+	
+	
+	/* STATUS STAGE */
+	/* FIXME: this is wrong */
+	result = usb_control_msg(us->pusb_dev, 
+				 usb_rcvctrlpipe(us->pusb_dev,0),
+				 USB_REQ_GET_STATUS, USB_DIR_IN |
+				 USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+				 0, us->ifnum, status, sizeof(status), HZ*5);
+
+	if (result < 0) {
+		US_DEBUGP("CBC Status stage returns %d\n", result);
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
+	US_DEBUGP("Got CB status 0x%x 0x%x\n", status[0], status[1]);
+	if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
+	    ( (status[0] & ~3) || status[1]))
+		return USB_STOR_TRANSPORT_FAILED;
+	else
+		return USB_STOR_TRANSPORT_GOOD;
+	
+	US_DEBUGP("CB_transport() reached end of function\n");
 	return USB_STOR_TRANSPORT_ERROR;
 }
 
+/* FIXME: Does this work? */
 static int Bulk_reset(struct us_data *us)
 {
 	int result;
 
-	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-				 US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				 US_BULK_RESET_HARD, us->ifnum,
-				 NULL, 0, HZ*5);
-	if (result)
+	result = usb_control_msg(us->pusb_dev, 
+				 usb_sndctrlpipe(us->pusb_dev,0), 
+				 US_BULK_RESET, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				 US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5);
+
+	if (result < 0)
 		US_DEBUGP("Bulk hard reset failed %d\n", result);
-	usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-	usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	usb_clear_halt(us->pusb_dev, 
+		       usb_sndbulkpipe(us->pusb_dev, us->ep_out));
 
 	/* long wait for reset */
 	schedule_timeout(HZ*6);
@@ -991,8 +949,7 @@
 }
 
 /*
- * The bulk only protocol handler.
- *	Uses the in and out endpoints to transfer commands and data
+ * Bulk only transport
  */
 static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
@@ -1001,7 +958,7 @@
 	int result;
 	int pipe;
 	int partial;
-
+	
 	/* set up the command wrapper */
 	bcb.Signature = US_BULK_CB_SIGN;
 	bcb.DataTransferLength = us_transfer_length(srb);
@@ -1009,14 +966,14 @@
 	bcb.Tag = srb->serial_number;
 	bcb.Lun = 0;
 	bcb.Length = srb->cmd_len;
-
+	
 	/* construct the pipe handle */
 	pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
+	
 	/* copy the command payload */
 	memset(bcb.CDB, 0, sizeof(bcb.CDB));
 	memcpy(bcb.CDB, srb->cmnd, bcb.Length);
-
+	
 	/* send it to out endpoint */
 	US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n",
 		  bcb.Signature, bcb.Tag, bcb.DataTransferLength,
@@ -1024,94 +981,83 @@
 	result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
 			      US_BULK_CB_WRAP_LEN, &partial, HZ*5);
 	US_DEBUGP("Bulk command transfer result=%d\n", result);
-
+	
 	/* if we stall, we need to clear it before we go on */
 	if (result == -EPIPE) {
 		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
 		usb_clear_halt(us->pusb_dev, pipe);
 	}
-  
+	
 	/* if the command transfered well, then we go to the data stage */
-	/* FIXME: Regardless of the status of the data stage, we go on to the
-	 * status stage.  Note that this implies that if a command is
-	 * partially successful, we rely on the device reporting an error
-	 * the CSW. The spec says that the device may just decide to short us.
-	 */
 	if (result == 0) {
 		/* send/receive data payload, if there is any */
 		if (bcb.DataTransferLength) {
-			result = us_transfer(srb, bcb.Flags);
-			US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-#if 0
-			if ((result < 0) && (result != USB_ST_DATAUNDERRUN) 
-			    && (result != USB_ST_STALL)) {
-				US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-				return DID_ABORT << 16;
-			}
-#endif
+			us_transfer(srb, bcb.Flags);
+			US_DEBUGP("Bulk data transfer result 0x%x\n", 
+				  srb->result);
 		}
 	}
-
+	
 	/* See flow chart on pg 15 of the Bulk Only Transport spec for
 	 * an explanation of how this code works.
 	 */
-
+	
 	/* construct the pipe handle */
 	pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
-
+	
 	/* get CSW for device status */
 	result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
 			      US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+	
 	/* did the attempt to read the CSW fail? */
 	if (result == -EPIPE) {
 		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
 		usb_clear_halt(us->pusb_dev, pipe);
-
+		
 		/* get the status again */
 		result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
 				      US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-   
+		
 		/* if it fails again, we need a reset and return an error*/
 		if (result == -EPIPE) {
 			Bulk_reset(us);
-			return (DID_ABORT << 16);
+			return USB_STOR_TRANSPORT_ERROR;
 		}
 	}
-
+	
 	/* if we still have a failure at this point, we're in trouble */
 	if (result) {
-		US_DEBUGP("Bulk status result = 0x%x\n", result);
-		return DID_ABORT << 16;
+		US_DEBUGP("Bulk status result = %d\n", result);
+		return USB_STOR_TRANSPORT_ERROR;
 	}
-
+	
 	/* check bulk status */
 	US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
 		  bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status);
 	if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
 	    bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
 		US_DEBUGP("Bulk logical error\n");
-		return DID_ABORT << 16;
+		return USB_STOR_TRANSPORT_ERROR;
 	}
-
+	
 	/* based on the status code, we report good or bad */
 	switch (bcs.Status) {
 	case US_BULK_STAT_OK:
-		/* if there is residue, we really didn't finish the command */
-		if (bcs.Residue)
-			return DID_ERROR << 16;
-		else
-			return DID_OK << 16;
+		/* command good -- note that we could be short on data */
+		return USB_STOR_TRANSPORT_GOOD;
 
 	case US_BULK_STAT_FAIL:
-		return DID_ERROR << 16;
-
+		/* command failed */
+		return USB_STOR_TRANSPORT_FAILED;
+		
 	case US_BULK_STAT_PHASE:
+		/* phase error */
 		Bulk_reset(us);
-		return DID_ERROR << 16;
+		return USB_STOR_TRANSPORT_ERROR;
 	}
-
-	return DID_OK << 16;	    /* check sense required */
+	
+	/* we should never get here, but if we do, we're in trouble */
+	return USB_STOR_TRANSPORT_ERROR;
 }
 
 /***********************************************************************
@@ -1163,14 +1109,20 @@
 		usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
 		us->irq_handle = NULL;
 	}
-	if (us->pusb_dev)
-		usb_deregister(&storage_driver);
+
+	/* FIXME: release the interface claim here? */
+	//	if (us->pusb_dev)
+	//		usb_deregister(&storage_driver);
 
 	/* FIXME - leaves hanging host template copy */
 	/* (because scsi layer uses it after removal !!!) */
-	while (prev->next != us)
-		prev = prev->next;
-	prev->next = us->next;
+	if (us_list == us)
+		us_list = us->next;
+	else {
+		while (prev->next != us)
+			prev = prev->next;
+		prev->next = us->next;
+	}
 	return 0;
 }
 
@@ -1188,17 +1140,19 @@
 	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
 
 	US_DEBUGP("Command wakeup\n");
-	if (us->srb) {
-		/* busy */
-	}
 	srb->host_scribble = (unsigned char *)us;
-	us->srb = srb;
+
+	/* get exclusive access to the structures we want */
+	down(&(us->queue_exclusion));
+
+	/* enqueue the command */
+	us->queue_srb = srb;
 	srb->scsi_done = done;
 	us->action = US_ACT_COMMAND;
 
 	/* wake up the process task */
-
-	wake_up_interruptible(&us->waitq);
+	up(&(us->queue_exclusion));
+	up(&(us->sleeper));
 
 	return 0;
 }
@@ -1209,6 +1163,7 @@
 	return 0;
 }
 
+/* FIXME: this doesn't do anything right now */
 static int us_bus_reset( Scsi_Cmnd *srb )
 {
 	//  struct us_data *us = (struct us_data *)srb->host->hostdata[0];
@@ -1340,11 +1295,11 @@
 	NULL,			    /* select_queue_depths */
 	1,			    /* can_queue */
 	-1,			    /* this_id */
-	SG_ALL,		    /* sg_tablesize */
+	SG_ALL, 		    /* sg_tablesize */
 	1,			    /* cmd_per_lun */
 	0,			    /* present */
-	FALSE,		    /* unchecked_isa_dma */
-	FALSE,		    /* use_clustering */
+	FALSE,		            /* unchecked_isa_dma */
+	TRUE,  		            /* use_clustering */
 	TRUE,			    /* use_new_eh_code */
 	TRUE			    /* emulated */
 };
@@ -1391,10 +1346,18 @@
 		siginfo_t info;
 		int unsigned long signr;
 
-		interruptible_sleep_on(&us->waitq);
+		US_DEBUGP("*** thread sleeping.\n");
+		down(&(us->sleeper));
+		down(&(us->queue_exclusion));
+		US_DEBUGP("*** thread awakened.\n");
 
+				/* take the command off the queue */
 		action = us->action;
 		us->action = 0;
+		us->srb = us-> queue_srb;
+     	
+		/* release the queue lock as fast as possible */
+		up(&(us->queue_exclusion));
 
 		/* FIXME: we need to examine placment of break; and 
 		 * scsi_done() calls */
@@ -1460,29 +1423,20 @@
 			break;
 
 		} /* end switch on action */
-    
+
+		/* FIXME: we ignore TERM and KILL... is this right? */
 		if (signal_pending(current)) {
 			/* sending SIGUSR1 makes us print out some info */
 			spin_lock_irq(&current->sigmask_lock);
 			signr = dequeue_signal(&current->blocked, &info);
 			spin_unlock_irq(&current->sigmask_lock);
-
-			if (signr == SIGUSR2) {
-				usb_stor_debug = !usb_stor_debug;
-				printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug);
-			} else {
-				break;	    /* exit the loop on any other signal */
-			}
-		}
-	}
+		} /* if (singal_pending(current)) */
+	} /* for (;;) */
   
 	//  MOD_DEC_USE_COUNT;
 
 	printk("usb_stor_control_thread exiting\n");
 
-	/* FIXME: this is a hack to allow for debugging */
-	// scsi_unregister_module(MODULE_SCSI_HA, us->htmplt);
-
 	return 0;
 }	
 
@@ -1498,7 +1452,6 @@
 	unsigned int flags = 0;
 	GUID(guid);		     /* Global Unique Identifier */
 	struct us_data *prev;
-	Scsi_Host_Template *htmplt;
 	int protocol = 0;
 	int subclass = 0;
 	struct usb_interface_descriptor *altsetting = 
@@ -1565,14 +1518,18 @@
 			return NULL;
 		}
 		memset(ss, 0, sizeof(struct us_data));
+
+		/* Initialize the mutexes only when the struct is new */
+		init_MUTEX_LOCKED(&(ss->sleeper));
+		init_MUTEX(&(ss->queue_exclusion));
 	}
 
-	/* Initialize the us_data structure with some useful info */
+	/* establish the connection to the new device */
 	interface = altsetting;
 	ss->flags = flags;
 	ss->ifnum = ifnum;
-	ss->pusb_dev = dev;
 	ss->attention_done = 0;
+	ss->pusb_dev = dev;
 
 	/* If the device has subclass and protocol, then use that.  Otherwise, 
 	 * take data from the specific interface.
@@ -1596,7 +1553,7 @@
 
 	case US_PR_CBI:
 		US_DEBUGPX("Control/Bulk/Interrupt\n");
-		ss->transport = CB_transport;
+		ss->transport = CBI_transport;
 		ss->transport_reset = CB_reset;
 		break;
 
@@ -1620,7 +1577,7 @@
 	 */
 	for (i = 0; i < interface->bNumEndpoints; i++) {
 		/* is it an BULK endpoint? */
-		if ((interface->endpoint[i].bmAttributes &  USB_ENDPOINT_XFERTYPE_MASK)
+		if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
 		    == USB_ENDPOINT_XFER_BULK) {
 			if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
 				ss->ep_in = interface->endpoint[i].bEndpointAddress &
@@ -1646,7 +1603,6 @@
 	    (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
 		US_DEBUGP("Problems with device\n");
 		if (ss->host) {
-			scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
 			kfree(ss->htmplt->name);
 			kfree(ss->htmplt);
 		}
@@ -1667,11 +1623,13 @@
 		US_DEBUGP("Protocol: ");
 		switch (ss->subclass) {
 		case US_SC_RBC:
-			US_DEBUGPX("Reduced Block Commands\n");
+			US_DEBUGPX("Reduced Block Commands (RBC)\n");
+			ss->proto_handler = transparent_scsi_command;
 			break;
 
 		case US_SC_8020:
-			US_DEBUGPX("8020\n");
+			US_DEBUGPX("8020i\n");
+			ss->proto_handler = ATAPI_command;
 			break;
 
 		case US_SC_QIC:
@@ -1679,7 +1637,8 @@
 			break;
 
 		case US_SC_8070:
-			US_DEBUGPX("8070\n");
+			US_DEBUGPX("8070i\n");
+			ss->proto_handler = ATAPI_command;
 			break;
 
 		case US_SC_SCSI:
@@ -1697,22 +1656,9 @@
 			break;
 		}
 
-		/* We only handle certain protocols.  Currently, these are
-		 *the only ones that devices use.
-		 */
-		if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) {
-			US_DEBUGP("Sorry, we do not support that protocol yet.\n");
-			US_DEBUGP("If you have a device which uses one of the unsupported\n");
-			US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n");
-     
-			kfree(ss);
-			return NULL;
-		}
-
 		/* Allocate memory for the SCSI Host Template */
-		if ((htmplt = (Scsi_Host_Template *)
-		     kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
-
+		if ((ss->htmplt = (Scsi_Host_Template *)
+		     kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) {
 			printk(KERN_WARNING USB_STORAGE "Out of memory\n");
 
 			kfree(ss);
@@ -1720,7 +1666,7 @@
 		}
 
 		/* Initialize the host template based on the default one */
-		memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+		memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template));
 
 		/* Grab the next host number */
 		ss->host_number = my_host_number++;
@@ -1729,32 +1675,34 @@
 		 * can pass the ss pointer to the host controler thread
 		 * in us_detect
 		 */
-		(struct us_data *)htmplt->proc_dir = ss; 
+		(struct us_data *)ss->htmplt->proc_dir = ss; 
 
 		/* shuttle E-USB */	
 		if (dev->descriptor.idVendor == 0x04e6 &&
 		    dev->descriptor.idProduct == 0x0001) {
 			__u8 qstat[2];
 			int result;
-	    
-			result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0),
+			
+			result = usb_control_msg(ss->pusb_dev, 
+						 usb_rcvctrlpipe(dev,0),
 						 1, 0xC0,
 						 0, ss->ifnum,
 						 qstat, 2, HZ*5);
 			US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
-			init_waitqueue_head(&ss->ip_waitq);
+			init_MUTEX_LOCKED(&(ss->ip_waitq));
 			ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
-			result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
-						 255, (void *)ss, &ss->irq_handle);
-			if (result)
+			result = usb_request_irq(ss->pusb_dev, ss->irqpipe, 
+						 CBI_irq, 255, (void *)ss, 
+						 &ss->irq_handle);
+			if (result < 0)
 				return NULL;
-
-			interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6);
-		} else if (ss->protocol == US_PR_CBI)
-		{
+			/* FIXME: what is this?? */
+			down(&(ss->ip_waitq));
+		} else if (ss->protocol == US_PR_CBI) {
 			int result; 
-
-			init_waitqueue_head(&ss->ip_waitq);
+			
+			/* set up so we'll wait for notification */
+			init_MUTEX_LOCKED(&(ss->ip_waitq));
 
 			/* set up the IRQ pipe and handler */
 			/* FIXME: This needs to get the period from the device */
@@ -1768,18 +1716,16 @@
 		}
     
 
-		/* start up our thread */
+				/* start up our thread */
 		{
 			DECLARE_MUTEX_LOCKED(sem);
 
-			init_waitqueue_head(&ss->waitq);
-
 			ss->notify = &sem;
 			ss->pid = kernel_thread(usb_stor_control_thread, ss,
 						CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 			if (ss->pid < 0) {
 				printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
-				kfree(htmplt);
+				kfree(ss->htmplt);
 
 				kfree(ss);
 				return NULL;
@@ -1790,17 +1736,16 @@
 		}
 
 		/* now register - our detect function will be called */
-		scsi_register_module(MODULE_SCSI_HA, htmplt);
+		ss->htmplt->module = &__this_module;
+		scsi_register_module(MODULE_SCSI_HA, ss->htmplt);
 
 		/* put us in the list */
-		prev = (struct us_data *)&us_list;
-		while (prev->next)
-			prev = prev->next;
-		prev->next = ss;
+		ss->next = us_list;
+                us_list = ss;
 	}
 
-	printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n");
-	printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum);
+	printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n");
+	printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum);
 
 	return ss;
 }
@@ -1814,7 +1759,6 @@
 		return;
 
 	ss->pusb_dev = NULL;
-	//  MOD_DEC_USE_COUNT;
 }
 
 
@@ -1824,8 +1768,6 @@
 
 int __init usb_stor_init(void)
 {
-	//  MOD_INC_USE_COUNT;
-
 	if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
 		printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ;
 		printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n", 
@@ -1844,6 +1786,14 @@
 
 void __exit usb_stor_exit(void)
 {
+	static struct us_data *ptr;
+
+	// FIXME: this needs to be put back to free _all_ the hosts
+	//	for (ptr = us_list; ptr != NULL; ptr = ptr->next)
+	//		scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt);
+	printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt));
+	scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt);
+
 	usb_deregister(&storage_driver) ;
 }
 
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/usb-storage.h linux/drivers/usb/usb-storage.h
--- v2.3.51/linux/drivers/usb/usb-storage.h	Sun Feb 20 21:12:39 2000
+++ linux/drivers/usb/usb-storage.h	Tue Mar 14 17:54:42 2000
@@ -9,13 +9,11 @@
 
 #define USB_STORAGE "usb-storage: "
 
-extern int usb_stor_debug;
-
 #ifdef CONFIG_USB_STORAGE_DEBUG
 void us_show_command(Scsi_Cmnd *srb);
-#define US_DEBUGP(x...) { if(usb_stor_debug) printk( KERN_DEBUG USB_STORAGE ## x ); }
-#define US_DEBUGPX(x...) { if(usb_stor_debug) printk( ## x ); }
-#define US_DEBUG(x)  { if(usb_stor_debug) x; }
+#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x )
+#define US_DEBUGPX(x...) printk( ## x )
+#define US_DEBUG(x) x 
 #else
 #define US_DEBUGP(x...)
 #define US_DEBUGPX(x...)
@@ -83,15 +81,22 @@
 #define US_BULK_RESET_HARD	0
 
 /*
+ * us_bulk_transfer() return codes
+ */
+#define US_BULK_TRANSFER_GOOD   0
+#define US_BULK_TRANSFER_SHORT  1
+#define US_BULK_TRANSFER_FAILED 2
+
+/*
  * Transport return codes
  */
 
-#define USB_STOR_TRANSPORT_GOOD    0    /* Transport good, command good    */
-#define USB_STOR_TRANSPORT_FAILED  1    /* Transport good, command failed  */
-#define USB_STOR_TRANSPORT_ERROR   2    /* Transport bad (i.e. device dead */
+#define USB_STOR_TRANSPORT_GOOD    0   /* Transport good, command good     */
+#define USB_STOR_TRANSPORT_FAILED  1   /* Transport good, command failed   */
+#define USB_STOR_TRANSPORT_ERROR   2   /* Transport bad (i.e. device dead) */
 
 /*
- * CBI style
+ * CBI accept device specific command
  */
 
 #define US_CBI_ADSC		0
@@ -128,3 +133,4 @@
 #define US_FL_FIXED_COMMAND   0x00000002 /* expand commands to fixed size */
 #define US_FL_MODE_XLATE      0x00000004 /* translate _6 to _10 comands for
 					    Win/MacOS compatibility */
+
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/usb-uhci-debug.h linux/drivers/usb/usb-uhci-debug.h
--- v2.3.51/linux/drivers/usb/usb-uhci-debug.h	Tue Feb  1 01:35:44 2000
+++ linux/drivers/usb/usb-uhci-debug.h	Tue Mar 14 18:09:51 2000
@@ -1,41 +1,32 @@
 #ifdef DEBUG
-
 static void uhci_show_qh (puhci_desc_t qh)
 {
 	if (qh->type != QH_TYPE) {
 		dbg("qh has not QH_TYPE");
 		return;
 	}
-	dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh));
+	dbg("QH @ %p/%08lX:", qh, virt_to_bus (qh));
 
 	if (qh->hw.qh.head & UHCI_PTR_TERM)
-		dbg("Head Terminate");
-	else {
-		if (qh->hw.qh.head & UHCI_PTR_QH)
-			dbg("Head points to QH");
-		else
-			dbg("Head points to TD");
+		dbg("    Head Terminate");
+	else 
+		dbg("    Head: %s @ %08X",
+		    (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
+		    qh->hw.qh.head & ~UHCI_PTR_BITS);
 
-		dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS);
-	}
 	if (qh->hw.qh.element & UHCI_PTR_TERM)
-		dbg("Element Terminate");
-	else {
-
-		if (qh->hw.qh.element & UHCI_PTR_QH)
-			dbg("Element points to QH");
-		else
-			dbg("Element points to TD");
-		dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS);
-	}
+		dbg("    Element Terminate");
+	else 
+		dbg("    Element: %s @ %08X",
+		    (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
+		    qh->hw.qh.element & ~UHCI_PTR_BITS);
 }
 #endif
 
 static void uhci_show_td (puhci_desc_t td)
 {
 	char *spid;
-	warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td));
-
+	
 	switch (td->hw.td.info & 0xff) {
 	case USB_PID_SETUP:
 		spid = "SETUP";
@@ -51,16 +42,16 @@
 		break;
 	}
 
-	warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)",
+	warn("  TD @ %p/%08lX, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
+	     td, virt_to_bus (td),
 	     td->hw.td.info >> 21,
 	     ((td->hw.td.info >> 19) & 1),
 	     (td->hw.td.info >> 15) & 15,
 	     (td->hw.td.info >> 8) & 127,
-	     (td->hw.td.info & 0xff),
 	     spid,
 	     td->hw.td.buffer);
 
-	warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
+	warn("    Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
 	     td->hw.td.status & 0x7ff,
 	     ((td->hw.td.status >> 27) & 3),
 	     (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
@@ -74,50 +65,41 @@
 	     (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
 	     (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
 		);
-#if 1
+
 	if (td->hw.td.link & UHCI_PTR_TERM)
-		warn("Link Terminate");
-	else {
-		if (td->hw.td.link & UHCI_PTR_QH)
-			warn("%s, link points to QH @ %08x",
-			     (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
-			     td->hw.td.link & ~UHCI_PTR_BITS);
-		else
-			warn("%s, link points to TD @ %08x",
-			     (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
-			     td->hw.td.link & ~UHCI_PTR_BITS);
-	}
-#endif
+		warn("   TD Link Terminate");
+	else 
+		warn("    Link points to %s @ %08x, %s",
+		     (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
+		     td->hw.td.link & ~UHCI_PTR_BITS,
+		     (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
 }
 #ifdef DEBUG
 static void uhci_show_td_queue (puhci_desc_t td)
 {
-	dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
+	//dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
 	while (1) {
 		uhci_show_td (td);
 		if (td->hw.td.link & UHCI_PTR_TERM)
 			break;
-		//if(!(td->hw.td.link&UHCI_PTR_DEPTH))
-		//      break;
 		if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
 			td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
 		else {
 			dbg("td points to itself!");
 			break;
 		}
-//              schedule();
 	}
 }
 
 static void uhci_show_queue (puhci_desc_t qh)
 {
+	uhci_desc_t *start_qh=qh;
+
 	dbg("uhci_show_queue %p:", qh);
 	while (1) {
 		uhci_show_qh (qh);
 
-		if (qh->hw.qh.element & UHCI_PTR_QH)
-			dbg("Warning: qh->element points to qh!");
-		else if (!(qh->hw.qh.element & UHCI_PTR_TERM))
+		if (!(qh->hw.qh.element & UHCI_PTR_TERM))
 			uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
 
 		if (qh->hw.qh.head & UHCI_PTR_TERM)
@@ -129,7 +111,12 @@
 			dbg("qh points to itself!");
 			break;
 		}
-	}
+		
+		if (qh==start_qh) { // avoid loop
+			dbg("Loop detect");
+			break;
+		}
+	}		
 }
 
 static void uhci_show_sc (int port, unsigned short status)
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
--- v2.3.51/linux/drivers/usb/usb-uhci.c	Fri Mar 10 16:40:45 2000
+++ linux/drivers/usb/usb-uhci.c	Tue Mar 14 18:09:51 2000
@@ -12,7 +12,7 @@
  * (C) Copyright 1999 Johannes Erdfelt
  * (C) Copyright 1999 Randy Dunlap
  *
- * $Id: usb-uhci.c,v 1.197 2000/02/15 17:44:22 acher Exp $
+ * $Id: usb-uhci.c,v 1.222 2000/03/13 21:18:02 fliegl Exp $
  */
 
 #include <linux/config.h>
@@ -28,9 +28,9 @@
 #include <linux/unistd.h>
 #include <linux/interrupt.h>	/* for in_interrupt() */
 #include <linux/init.h>
-/* This enables debug printks */
-#define DEBUG
-#include <linux/usb.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
+#include <linux/pm.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -40,51 +40,56 @@
 /* This enables more detailed sanity checks in submit_iso */
 //#define ISO_SANITY_CHECK
 
+/* This enables debug printks */
+#define DEBUG
+
 /* This enables all symbols to be exported, to ease debugging oopses */
 //#define DEBUG_SYMBOLS
 
 /* This enables an extra UHCI slab for memory debugging */
 #define DEBUG_SLAB
 
+#include <linux/usb.h>
 #include "usb-uhci.h"
 #include "usb-uhci-debug.h"
 
 #undef DEBUG
 #undef dbg
 #define dbg(format, arg...) do {} while (0)
-
-#include <linux/pm.h>
-
+#define DEBUG_SYMBOLS
 #ifdef DEBUG_SYMBOLS
 	#define _static
 	#ifndef EXPORT_SYMTAB
-	#define EXPORT_SYMTAB
+		#define EXPORT_SYMTAB
 	#endif
 #else
 	#define _static static
 #endif
 
+#define queue_dbg dbg //err
+#define async_dbg dbg //err
+
 #ifdef DEBUG_SLAB
 	static kmem_cache_t *uhci_desc_kmem;
 	static kmem_cache_t *urb_priv_kmem;
 #endif
 
+#define SLAB_FLAG     (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG  (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL)
+
+#define CONFIG_USB_UHCI_HIGH_BANDWIDTH 
 #define USE_CTRL_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
 #define USE_BULK_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
 
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-#define USE_RECLAMATION_LOOP
-#else
-//#define USE_RECLAMATION_LOOP
-#endif
-
-// stop bandwidth reclamation after (roughly) 50ms (depends also on
-// hub polling interval)
+// stop bandwidth reclamation after (roughly) 50ms
 #define IDLE_TIMEOUT  (HZ/20)
 
 _static int rh_submit_urb (urb_t *urb);
 _static int rh_unlink_urb (urb_t *urb);
 _static int delete_qh (uhci_t *s, uhci_desc_t *qh);
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode);
+_static int process_interrupt (uhci_t *s, urb_t *urb);
+_static int process_iso (uhci_t *s, urb_t *urb, int force);
 
 static uhci_t *devs = NULL;
 
@@ -105,58 +110,47 @@
 		qh = list_entry (q, uhci_desc_t, horizontal);
 		if ((qh->last_used!=now) || force)
 			delete_qh(s,qh);
+
 		q=qh->horizontal.prev;
 	}
 }
 /*-------------------------------------------------------------------*/
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
 _static void enable_desc_loop(uhci_t *s, urb_t *urb)
 {
 	int flags;
-	
-	dbg("enable_desc_loop: enter");
-	
+
 	spin_lock_irqsave (&s->qh_lock, flags);
-	s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
+	s->chain_end->hw.qh.head&=~UHCI_PTR_TERM; 
+	mb();
 	s->loop_usage++;
 	((urb_priv_t*)urb->hcpriv)->use_loop=1;
 	spin_unlock_irqrestore (&s->qh_lock, flags);
-	
-	dbg("enable_desc_loop: finished");
 }
 /*-------------------------------------------------------------------*/
 _static void disable_desc_loop(uhci_t *s, urb_t *urb)
 {
 	int flags;
-	
-	dbg("disable_desc_loop: enter\n");
-	
+
 	spin_lock_irqsave (&s->qh_lock, flags);
 
 	if (((urb_priv_t*)urb->hcpriv)->use_loop) {
 		s->loop_usage--;
 
-		if (!s->loop_usage)
-			s->chain_end->hw.qh.head=UHCI_PTR_TERM;
-
+		if (!s->loop_usage) {
+			s->chain_end->hw.qh.head|=UHCI_PTR_TERM;
+			mb();
+		}
 		((urb_priv_t*)urb->hcpriv)->use_loop=0;
 	}
 	spin_unlock_irqrestore (&s->qh_lock, flags);
-	
-	dbg("disable_desc_loop: finished");
-
 }
 #endif
 /*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, urb_t *urb)
+_static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
 {
-	unsigned long flags=0;
 	struct list_head *p=&urb->urb_list;
-	
-
-	spin_lock_irqsave (&s->urb_list_lock, flags);
-
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
 	{
 		int type;
 		type=usb_pipetype (urb->pipe);
@@ -166,15 +160,21 @@
 	}
 #endif
 	((urb_priv_t*)urb->hcpriv)->started=jiffies;
-	list_add_tail (p, &s->urb_list);
-	
-	spin_unlock_irqrestore (&s->urb_list_lock, flags);
+	list_add (p, &s->urb_list);
 }
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, urb_t *urb)
+{
+	unsigned long flags=0;
 
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+	queue_urb_unlocked(s,urb);
+	spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
 /*-------------------------------------------------------------------*/
 _static void dequeue_urb (uhci_t *s, urb_t *urb)
 {
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
 	int type;
 
 	type=usb_pipetype (urb->pipe);
@@ -189,9 +189,9 @@
 _static int alloc_td (uhci_desc_t ** new, int flags)
 {
 #ifdef DEBUG_SLAB
-	*new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+	*new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
 #else
-	*new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+	*new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
 #endif
 	if (!*new)
 		return -ENOMEM;
@@ -205,6 +205,19 @@
 	return 0;
 }
 /*-------------------------------------------------------------------*/
+// append a qh to td.link physically, the SW linkage is not affected
+_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int  flags)
+{
+	unsigned long xxx;
+	
+	spin_lock_irqsave (&s->td_lock, xxx);
+
+	td->hw.td.link = virt_to_bus (qh) | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH;
+       
+	mb();
+	spin_unlock_irqrestore (&s->td_lock, xxx);
+}
+/*-------------------------------------------------------------------*/
 /* insert td at last position in td-list of qh (vertical) */
 _static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
 {
@@ -271,10 +284,9 @@
 		if (prev->type == TD_TYPE)
 			prev->hw.td.link = element->hw.td.link;
 		else
-			prev->hw.qh.element = element->hw.td.link;	
+			prev->hw.qh.element = element->hw.td.link;
 	}
 
-	element->hw.td.link=UHCI_PTR_TERM;
 	mb ();
 
 	if (dir == 0)
@@ -302,9 +314,9 @@
 _static int alloc_qh (uhci_desc_t ** new)
 {
 #ifdef DEBUG_SLAB
-	*new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+	*new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
 #else
-	*new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+	*new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
 #endif	
 	if (!*new)
 		return -ENOMEM;
@@ -350,9 +362,10 @@
 	mb ();
 	
 	spin_unlock_irqrestore (&s->qh_lock, flags);
-	
+
 	return 0;
 }
+
 /*-------------------------------------------------------------------*/
 _static int unlink_qh (uhci_t *s, uhci_desc_t *element)
 {
@@ -406,6 +419,14 @@
 	
 	delete_desc (td);
 }
+
+/*-------------------------------------------------------------------*/
+_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
+{
+	td->hw.td.status = status;
+	td->hw.td.info = info;
+	td->hw.td.buffer = buffer;
+}
 /*-------------------------------------------------------------------*/
 // Removes ALL qhs in chain (paranoia!)
 _static void cleanup_skel (uhci_t *s)
@@ -441,19 +462,20 @@
 		qh = s->control_chain;
 		while ((p = qh->horizontal.next) != &qh->horizontal) {
 			qh1 = list_entry (p, uhci_desc_t, horizontal);
-			dbg("delete_qh @ %p",qh1);
 			delete_qh (s, qh1);
 		}
-			dbg("delete_qh last @ %p",qh);
+
 		delete_qh (s, qh);
 	}
 	else {
+		if (s->ls_control_chain)
+			delete_desc (s->ls_control_chain);
 		if (s->control_chain)
-			kfree (s->control_chain);
+			 delete_desc(s->control_chain);
 		if (s->bulk_chain)
-			kfree (s->bulk_chain);
+			delete_desc (s->bulk_chain);
 		if (s->chain_end)
-			kfree (s->chain_end);
+			delete_desc (s->chain_end);
 	}
 	dbg("cleanup_skel finished");	
 }
@@ -480,6 +502,7 @@
 	if (!s->iso_td)
 		goto init_skel_cleanup;
 
+	s->ls_control_chain = NULL;
 	s->control_chain = NULL;
 	s->bulk_chain = NULL;
 	s->chain_end = NULL;
@@ -499,26 +522,46 @@
 	
 	if (ret)
 		goto init_skel_cleanup;
-	
+				
 	s->chain_end = qh;
 
+	ret = alloc_td (&td, 0);
+
+	if (ret)
+		goto init_skel_cleanup;
+	
+	fill_td (td, TD_CTRL_IOC, 0, 0); // generate 1ms interrupt
+	insert_td (s, qh, td, 0);
+
 	dbg("allocating qh: bulk_chain");
 	ret = alloc_qh (&qh);
-	
 	if (ret)
 		goto init_skel_cleanup;
 	
 	insert_qh (s, s->chain_end, qh, 0);
 	s->bulk_chain = qh;
+
 	dbg("allocating qh: control_chain");
 	ret = alloc_qh (&qh);
-	
 	if (ret)
 		goto init_skel_cleanup;
 	
 	insert_qh (s, s->bulk_chain, qh, 0);
 	s->control_chain = qh;
 
+#ifdef	CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	// disabled reclamation loop
+	s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM;
+#endif
+
+	dbg("allocating qh: ls_control_chain");
+	ret = alloc_qh (&qh);
+	if (ret)
+		goto init_skel_cleanup;
+	
+	insert_qh (s, s->control_chain, qh, 0);
+	s->ls_control_chain = qh;
+
 	for (n = 0; n < 8; n++)
 		s->int_chain[n] = 0;
 
@@ -532,7 +575,7 @@
 			goto init_skel_cleanup;
 		s->int_chain[n] = td;
 		if (n == 0) {
-			s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain) | UHCI_PTR_QH;
+			s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH;
 		}
 		else {
 			s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]);
@@ -547,20 +590,16 @@
 		dbg("framelist[%i]=%x",n,s->framelist[n]);
 		if ((n&127)==127) 
 			((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]);
-		else {
-			for (o = 1, m = 2; m <= 128; o++, m += m) {
-				// n&(m-1) = n%m
-				if ((n & (m - 1)) == ((m - 1) / 2)) {
+		else 
+			for (o = 1, m = 2; m <= 128; o++, m += m)
+				if ((n & (m - 1)) == ((m - 1) / 2))
 					((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
-				}
-			}
-		}
 	}
 
 	mb();
 	//uhci_show_queue(s->control_chain);   
 	dbg("init_skel exit");
-	return 0;		// OK
+	return 0;
 
       init_skel_cleanup:
 	cleanup_skel (s);
@@ -568,14 +607,6 @@
 }
 
 /*-------------------------------------------------------------------*/
-_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
-{
-	td->hw.td.status = status;
-	td->hw.td.info = info;
-	td->hw.td.buffer = buffer;
-}
-
-/*-------------------------------------------------------------------*/
 //                         LOW LEVEL STUFF
 //          assembles QHs und TDs for control, bulk and iso
 /*-------------------------------------------------------------------*/
@@ -586,10 +617,15 @@
 	urb_priv_t *urb_priv = urb->hcpriv;
 	unsigned long destination, status;
 	int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
-	unsigned long len, bytesrequested;
+	unsigned long len;
 	char *data;
 	int depth_first=USE_CTRL_DEPTH_FIRST;  // UHCI descriptor chasing method
 
+	if (!maxsze) {
+		err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe);
+		return -EINVAL;
+	}
+
 	dbg("uhci_submit_control start");
 	alloc_qh (&qh);		// alloc qh for this request
 
@@ -615,26 +651,21 @@
 
 	insert_td (s, qh, td, 0);	// queue 'setup stage'-td in qh
 #if 0
-	dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
-		urb->setup_packet[0], urb->setup_packet[1], urb->setup_packet[2], urb->setup_packet[3],
-		urb->setup_packet[4], urb->setup_packet[5], urb->setup_packet[6], urb->setup_packet[7]);
+	{
+		char *sp=urb->setup_packet;
+		dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
+		    sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
+	}
 	//uhci_show_td(td);
 #endif
 
-	/*  Build the DATA TD's */
 	len = urb->transfer_buffer_length;
-	bytesrequested = len;
 	data = urb->transfer_buffer;
 
 	/* If direction is "send", change the frame from SETUP (0x2D)
 	   to OUT (0xE1). Else change it from SETUP to IN (0x69). */
 
-	destination &= ~UHCI_PID;
-	
-	if (usb_pipeout (urb->pipe))
-		destination |= USB_PID_OUT;
-	else
-		destination |= USB_PID_IN;	
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
 
 	while (len > 0) {
 		int pktsze = len;
@@ -664,7 +695,7 @@
 
 	destination &= ~UHCI_PID;
 
-	if (usb_pipeout (urb->pipe) || (bytesrequested == 0))
+	if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
 		destination |= USB_PID_IN;
 	else
 		destination |= USB_PID_OUT;
@@ -690,32 +721,34 @@
 	urb->status = -EINPROGRESS;
 	queue_urb (s, urb);	// queue before inserting in desc chain
 
-	qh->hw.qh.element&=~UHCI_PTR_TERM;
+	qh->hw.qh.element &= ~UHCI_PTR_TERM;
 
 	//uhci_show_queue(qh);
 	/* Start it up... put low speed first */
 	if (urb->pipe & TD_CTRL_LS)
-		insert_qh (s, s->control_chain, qh, 1);	// insert after control chain
+		insert_qh (s, s->control_chain, qh, 0);
 	else
-		insert_qh (s, s->bulk_chain, qh, 0);	// insert before bulk chain
-	//uhci_show_queue(qh);
+		insert_qh (s, s->bulk_chain, qh, 0);
 
 	dbg("uhci_submit_control end");
 	return 0;
 }
 /*-------------------------------------------------------------------*/
-_static int uhci_submit_bulk_urb (urb_t *urb)
+// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
+// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
+
+_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
 {
 	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
 	urb_priv_t *urb_priv = urb->hcpriv;
-	uhci_desc_t *qh, *td;
+	uhci_desc_t *qh, *td, *nqh, *bqh;
 	unsigned long destination, status;
 	char *data;
 	unsigned int pipe = urb->pipe;
 	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
 	int info, len;
 	int depth_first=USE_BULK_DEPTH_FIRST;  // UHCI descriptor chasing method
-
+	urb_priv_t *upriv, *bpriv;
 
 	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
 		return -EPIPE;
@@ -727,12 +760,55 @@
 	
 	if (!maxsze)
 		return -EMSGSIZE;
-	/* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */
+	
+	queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
+		  urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
 
-	alloc_qh (&qh);		// get qh for this request
+	upriv=(urb_priv_t*)urb->hcpriv;
 
-	if (!qh)
-		return -ENOMEM;
+	if (!bulk_urb) {
+		alloc_qh (&qh);		// get qh for this request
+		
+		if (!qh)
+			return -ENOMEM;
+
+		if (urb->transfer_flags & USB_QUEUE_BULK) {
+			alloc_qh(&nqh); // placeholder for clean unlink
+			if (!nqh) {
+				delete_desc (qh);
+				return -ENOMEM;
+			}
+			upriv->next_qh = nqh;
+			queue_dbg("new next qh %p",nqh);
+		}
+	}
+	else { 
+		bpriv = (urb_priv_t*)bulk_urb->hcpriv;
+		qh = bpriv->bottom_qh;  // re-use bottom qh and next qh
+		nqh = bpriv->next_qh;
+		upriv->next_qh=nqh;
+		bpriv->next_queued_urb=urb;
+		upriv->prev_queued_urb=bulk_urb;
+	}
+
+	queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh);
+
+	if (urb->transfer_flags & USB_QUEUE_BULK) {
+		alloc_qh (&bqh); // "bottom" QH,
+		
+		if (!bqh) {
+			if (!bulk_urb) { 
+				delete_desc(qh);
+				delete_desc(nqh);
+			}
+			return -ENOMEM;
+		}
+		bqh->hw.qh.element = UHCI_PTR_TERM;
+		bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH;
+		upriv->bottom_qh = bqh;
+		queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh);
+	}
+	
 
 	/* The "pipe" thing contains the destination in bits 8--18. */
 	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
@@ -744,7 +820,6 @@
 	/* Build the TDs for the bulk request */
 	len = urb->transfer_buffer_length;
 	data = urb->transfer_buffer;
-	dbg("uhci_submit_bulk_urb: pipe %x, len %d", pipe, len);
 	
 	do {					// TBD: Really allow zero-length packets?
 		int pktsze = len;
@@ -770,54 +845,149 @@
 
 		if (!len)
 			td->hw.td.status |= TD_CTRL_IOC;	// last one generates INT
-		//dbg("insert td %p, len %i",td,pktsze);
 
 		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
-		
-		/* Alternate Data0/1 (start with Data0) */
 		usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
 	} while (len > 0);
 
 	list_add (&qh->desc_list, &urb_priv->desc_list);
 
+	if (urb->transfer_flags & USB_QUEUE_BULK) {
+		qh->hw.qh.element&=~UHCI_PTR_TERM;
+		append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
+	}
+
 	urb->status = -EINPROGRESS;
-	queue_urb (s, urb);
+	queue_urb_unlocked (s, urb);
 	
-	qh->hw.qh.element&=~UHCI_PTR_TERM;
+	qh->hw.qh.element  &= ~UHCI_PTR_TERM;
 
-	insert_qh (s, s->chain_end, qh, 0);	// insert before end marker
+	if (!bulk_urb) { 
+		if (urb->transfer_flags & USB_QUEUE_BULK) {
+			spin_lock (&s->td_lock);			// both QHs in one go
+			insert_qh (s, s->chain_end, qh, 0);	// Main QH
+			insert_qh (s, s->chain_end, nqh, 0);	// Helper QH
+			spin_unlock (&s->td_lock);
+		}
+		else
+			insert_qh (s, s->chain_end, qh, 0);
+	}
+	
 	//uhci_show_queue(s->bulk_chain);
-
-	dbg("uhci_submit_bulk_urb: exit");
+	//dbg("uhci_submit_bulk_urb: exit\n");
 	return 0;
 }
-
 /*-------------------------------------------------------------------*/
-// unlinks an urb by dequeuing its qh, waits some frames and forgets it
-// Problem: unlinking in interrupt requires waiting for one frame (udelay)
-// to allow the whole structures to be safely removed
-_static int uhci_unlink_urb (urb_t *urb)
+_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv)
 {
-	uhci_t *s;
-	uhci_desc_t *qh;
+	struct list_head *p;
 	uhci_desc_t *td;
-	urb_priv_t *urb_priv;
-	unsigned long flags=0;
 
+	for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
+				td = list_entry (p, uhci_desc_t, desc_list);
+				unlink_td (s, td, 1);
+	}
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
+{
 	struct list_head *p;
+	uhci_desc_t *td;
 
-	if (!urb || !urb->dev)		// you never know...
-		return -EINVAL;
+	while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
+				td = list_entry (p, uhci_desc_t, desc_list);
+				list_del (p);
+				delete_desc (td);
+	}
+}
+/*-------------------------------------------------------------------*/
+// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink
+// looks a bit complicated because of all the bulk queueing goodies
+
+_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
+{
+	uhci_desc_t *bqh, *nqh, *prevqh;
+	int now;
+	urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
 
-	s = (uhci_t*) urb->dev->bus->hcpriv;	// get pointer to uhci struct
+	now=UHCI_GET_CURRENT_FRAME(s);
 
-	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
-		return rh_unlink_urb (urb);
+	dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode);
+	bqh=priv->bottom_qh;	
 
-	if (!urb->hcpriv)		// you never know...
-		return -EINVAL;
+	if (!priv->next_queued_urb)  { // no more appended bulk queues
 
-	//dbg("unlink_urb called %p",urb);
+		if (mode != 2) 
+			unlink_qh (s, qh);
+	
+		if (priv->prev_queued_urb) {
+			urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+
+			ppriv->bottom_qh = priv->bottom_qh;
+			ppriv->next_queued_urb = NULL;
+		}
+		else if (bqh) {  // queue dead
+			nqh=priv->next_qh;
+			
+			if (mode != 2)
+				unlink_qh(s, nqh);
+
+			if (mode) {
+				nqh->last_used = bqh->last_used = now;
+				list_add_tail (&nqh->horizontal, &s->free_desc);
+				list_add_tail (&bqh->horizontal, &s->free_desc);
+			}			
+		}
+	}
+	else { // there are queued urbs following
+		urb_t *nurb;
+		unsigned long flags;
+		
+       		nurb=priv->next_queued_urb;
+		spin_lock_irqsave (&s->qh_lock, flags);
+		
+		if (!priv->prev_queued_urb) { // top
+			if (mode !=2) {
+				prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
+				prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+				queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh);	
+			
+				list_del (&qh->horizontal);
+				list_add (&bqh->horizontal, &prevqh->horizontal);
+			}	
+		}
+		else {		//intermediate
+			urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+			uhci_desc_t * bnqh;
+			
+			bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list);
+			ppriv->bottom_qh=bnqh;
+			ppriv->next_queued_urb=nurb;
+			
+			if (mode!=2) {
+				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+				prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+				queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh);	
+			}
+		}
+		mb();
+		spin_unlock_irqrestore (&s->qh_lock, flags);
+		((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;	
+	}
+
+	if (mode) {
+		qh->last_used = now;
+		list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+	}
+}
+/*-------------------------------------------------------------------*/
+// unlinks an urb by dequeuing its qh, waits some frames and forgets it
+_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
+{
+	uhci_desc_t *qh;
+	urb_priv_t *urb_priv;
+	unsigned long flags=0;
 
 	spin_lock_irqsave (&s->urb_list_lock, flags);
 
@@ -833,32 +1003,22 @@
 		switch (usb_pipetype (urb->pipe)) {
 		case PIPE_ISOCHRONOUS:
 		case PIPE_INTERRUPT:
-			for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
-				td = list_entry (p, uhci_desc_t, desc_list);
-				unlink_td (s, td, 1);
-			}
-			// wait at least 1 Frame
-			uhci_wait_ms(1);	
-			while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
-				td = list_entry (p, uhci_desc_t, desc_list);
-				list_del (p);
-				delete_desc (td);
-			}
+			uhci_clean_iso_step1(s, urb_priv);
+			uhci_wait_ms(1);
+			uhci_clean_iso_step2(s, urb_priv);
 			break;
 
 		case PIPE_BULK:
 		case PIPE_CONTROL:
 			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
-
-			unlink_qh (s, qh);	// remove this qh from qh-list
-			qh->last_used=UHCI_GET_CURRENT_FRAME(s);
-			list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
-			// wait at least 1 Frame
+			spin_lock_irqsave (&s->urb_list_lock, flags);
+			uhci_clean_transfer(s, urb, qh, 1);
+			spin_unlock_irqrestore (&s->urb_list_lock, flags);
 			uhci_wait_ms(1);
 		}
 		
 #ifdef DEBUG_SLAB
-		kmem_cache_free(urb_priv_kmem, urb->hcpriv);
+		kmem_cache_free (urb_priv_kmem, urb->hcpriv);
 #else
 		kfree (urb->hcpriv);
 #endif
@@ -874,6 +1034,147 @@
 	return 0;
 }
 /*-------------------------------------------------------------------*/
+// async unlink_urb completion/cleanup work
+// has to be protected by urb_list_lock!
+// features: if set in transfer_flags, the resulting status of the killed
+// transaction is not overwritten
+
+_static void uhci_cleanup_unlink(uhci_t *s, int force)
+{
+	struct list_head *q;
+	urb_t *urb;
+	struct usb_device *dev;
+	int pipe,now;
+	urb_priv_t *urb_priv;
+
+	q=s->urb_unlinked.next;
+	now=UHCI_GET_CURRENT_FRAME(s);
+
+	while (q != &s->urb_unlinked) {
+
+		urb = list_entry (q, urb_t, urb_list);
+
+		urb_priv = (urb_priv_t*)urb->hcpriv;
+		q = urb->urb_list.next;
+		
+		if (force ||
+ 		    ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) {
+			async_dbg("async cleanup %p",urb);
+			switch (usb_pipetype (urb->pipe)) { // process descriptors
+			case PIPE_CONTROL:
+				process_transfer (s, urb, 2);
+				break;
+			case PIPE_BULK:
+				if (!s->avoid_bulk.counter)
+					process_transfer (s, urb, 2); // don't unlink (already done)
+				else
+					continue;
+				break;
+			case PIPE_ISOCHRONOUS:
+				process_iso (s, urb, 1); // force, don't unlink
+				break;
+			case PIPE_INTERRUPT:
+				process_interrupt (s, urb);
+				break;
+			}
+
+			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+		  		urb->status = -ECONNRESET; // mark as asynchronously killed
+
+			pipe = urb->pipe;		// completion may destroy all...
+			dev = urb->dev;
+			urb_priv = urb->hcpriv;
+
+			if (urb->complete) {
+				spin_unlock(&s->urb_list_lock);
+				urb->complete ((struct urb *) urb);
+				spin_lock(&s->urb_list_lock);
+			}
+
+			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+				urb->status = -ENOENT;  // now the urb is really dead
+	
+			usb_dec_dev_use (dev);
+#ifdef DEBUG_SLAB
+			kmem_cache_free (urb_priv_kmem, urb_priv);
+#else
+			kfree (urb_priv);
+#endif
+			switch (usb_pipetype (pipe)) {
+			case PIPE_ISOCHRONOUS:
+			case PIPE_INTERRUPT:
+				uhci_clean_iso_step2(s, urb_priv);
+				break;
+			}
+			list_del (&urb->urb_list);
+		}
+	}
+}
+
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
+{
+	uhci_desc_t *qh;
+	urb_priv_t *urb_priv;
+	
+	async_dbg("unlink_urb_async called %p",urb);
+
+	if (urb->status == -EINPROGRESS) {
+		((urb_priv_t*)urb->hcpriv)->started = ~0;
+		dequeue_urb (s, urb);
+		list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
+
+		s->unlink_urb_done = 1;
+		
+		urb->status = -ECONNABORTED;	// mark urb as "waiting to be killed"	
+		urb_priv = (urb_priv_t*)urb->hcpriv;
+
+		switch (usb_pipetype (urb->pipe)) {
+		case PIPE_ISOCHRONOUS:
+		case PIPE_INTERRUPT:
+			uhci_clean_iso_step1 (s, urb_priv);
+			break;
+
+		case PIPE_BULK:
+		case PIPE_CONTROL:
+			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+			uhci_clean_transfer (s, urb, qh, 0);
+			break;
+		}
+		((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+	}		
+
+	return -EINPROGRESS;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb (urb_t *urb)
+{
+	uhci_t *s;
+	unsigned long flags=0;
+	dbg("uhci_unlink_urb called for %p",urb);
+	if (!urb || !urb->dev)		// you never know...
+		return -EINVAL;
+	
+	s = (uhci_t*) urb->dev->bus->hcpriv;
+
+	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+		return rh_unlink_urb (urb);
+
+	if (!urb->hcpriv)
+		return -EINVAL;
+
+	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+		int ret;
+
+		spin_lock_irqsave (&s->urb_list_lock, flags);
+		ret = uhci_unlink_urb_async(s, urb);
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+		return ret;
+	}
+	else
+		return uhci_unlink_urb_sync(s, urb);
+}
+/*-------------------------------------------------------------------*/
 // In case of ASAP iso transfer, search the URB-list for already queued URBs
 // for this EP and calculate the earliest start frame for the new
 // URB (easy seamless URB continuation!)
@@ -886,9 +1187,9 @@
 	unsigned long flags;
 	
 	spin_lock_irqsave (&s->urb_list_lock, flags);
-	p=s->urb_list.next;
+	p=s->urb_list.prev;
 
-	for (; p != &s->urb_list; p = p->next) {
+	for (; p != &s->urb_list; p = p->prev) {
 		u = list_entry (p, urb_t, urb_list);
 		// look for pending URBs with identical pipe handle
 		// works only because iso doesn't toggle the data bit!
@@ -906,8 +1207,7 @@
 	
 	spin_unlock_irqrestore(&s->urb_list_lock, flags);
 	
-	return ret;	// no previous urb found
-
+	return ret;
 }
 /*-------------------------------------------------------------------*/
 // adjust start_frame according to scheduling constraints (ASAP etc)
@@ -940,35 +1240,7 @@
 				info("iso_find_start: gap in seamless isochronous scheduling");
 				dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
 					now, urb->start_frame, urb->number_of_packets, urb->pipe);
-// The following code is only for debugging purposes...
-#if 0
-				{
-					uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
-					struct list_head *p;
-					urb_t *u;
-					int a = -1, b = -1;
-					unsigned long flags;
-
-					spin_lock_irqsave (&s->urb_list_lock, flags);
-					p=s->urb_list.next;
-
-					for (; p != &s->urb_list; p = p->next) {
-						u = list_entry (p, urb_t, urb_list);
-						if (urb->dev != u->dev)
-							continue;
-						dbg("urb: pipe 0x%08x status %d start_frame %u number_of_packets %u",
-							u->pipe, u->status, u->start_frame, u->number_of_packets);
-						if (!usb_pipeisoc (u->pipe))
-							continue;
-						if (a == -1)
-							a = u->start_frame;
-						b = (u->start_frame + u->number_of_packets - 1) & 1023;
-					}
-					spin_unlock_irqrestore(&s->urb_list_lock, flags);
-				}
-#endif
 				urb->start_frame = (now + 5) & 1023;	// 5ms setup should be enough //FIXME!
-				//return -EAGAIN; //FIXME
 			}
 		}
 	}
@@ -996,7 +1268,7 @@
 /*-------------------------------------------------------------------*/
 // submits USB interrupt (ie. polling ;-) 
 // ASAP-flag set implicitely
-// if period==0, the the transfer is only done once (usb_scsi need this...)
+// if period==0, the the transfer is only done once
 
 _static int uhci_submit_int_urb (urb_t *urb)
 {
@@ -1005,12 +1277,9 @@
 	int nint, n, ret;
 	uhci_desc_t *td;
 	int status, destination;
-	int now;
 	int info;
 	unsigned int pipe = urb->pipe;
 
-	//dbg("SUBMIT INT");
-
 	if (urb->interval < 0 || urb->interval >= 256)
 		return -EINVAL;
 
@@ -1029,8 +1298,7 @@
 
 	dbg("Rounded interval to %i, chain  %i", urb->interval, nint);
 
-	now = UHCI_GET_CURRENT_FRAME (s) & 1023;
-	urb->start_frame = now;	// remember start frame, just in case...
+	urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023;	// remember start frame, just in case...
 
 	urb->number_of_packets = 1;
 
@@ -1062,13 +1330,6 @@
 
 	usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
 
-#if 0
-	td = tdm[urb->number_of_packets];
-	fill_td (td, TD_CTRL_IOC, 0, 0);
-	insert_td_horizontal (s, s->iso_td[(urb->start_frame + (urb->number_of_packets) * urb->interval + 1) & 1023], td);
-	list_add_tail (&td->desc_list, &urb_priv->desc_list);
-#endif
-
 	return 0;
 }
 /*-------------------------------------------------------------------*/
@@ -1086,11 +1347,11 @@
 	__save_flags(flags);
 	__cli();		      // Disable IRQs to schedule all ISO-TDs in time
 	ret = iso_find_start (urb);	// adjusts urb->start_frame for later use
-
+	
 	if (ret)
 		goto err;
 
-	tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+	tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);
 
 	if (!tdm) {
 		ret = -ENOMEM;
@@ -1105,14 +1366,16 @@
 			tdm[n] = 0;
 			continue;
 		}
-		#ifdef ISO_SANITY_CHECK
+		
 		if(urb->iso_frame_desc[n].length > maxsze) {
+#ifdef ISO_SANITY_CHECK
 			err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
 			tdm[n] = 0;
 			ret=-EINVAL;
 			goto inval;
+#endif
 		}
-		#endif
+		
 		ret = alloc_td (&td, UHCI_PTR_DEPTH);
 	inval:
 		if (ret) {
@@ -1120,7 +1383,7 @@
 
 			for (i = 0; i < n; n++)
 				if (tdm[i])
-					kfree (tdm[i]);
+					 delete_desc(tdm[i]);
 			kfree (tdm);
 			goto err;
 		}
@@ -1165,15 +1428,16 @@
 
 }
 /*-------------------------------------------------------------------*/
-_static int search_dev_ep (uhci_t *s, urb_t *urb)
+// returns: 0 (no transfer queued), urb* (this urb already queued)
+ 
+_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
 {
-	unsigned long flags;
 	struct list_head *p;
 	urb_t *tmp;
 	unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
 
 	dbg("search_dev_ep:");
-	spin_lock_irqsave (&s->urb_list_lock, flags);
+
 	p=s->urb_list.next;
 
 	for (; p != &s->urb_list; p = p->next) {
@@ -1181,13 +1445,12 @@
 		dbg("urb: %p", tmp);
 		// we can accept this urb if it is not queued at this time 
 		// or if non-iso transfer requests should be scheduled for the same device and pipe
-		if ((!usb_pipeisoc(urb->pipe) && tmp->dev == urb->dev && !((tmp->pipe ^ urb->pipe) & mask)) ||
+		if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
 		    (urb == tmp)) {
-			spin_unlock_irqrestore (&s->urb_list_lock, flags);
-			return 1;	// found another urb already queued for processing
+			return tmp;	// found another urb already queued for processing
 		}
 	}
-	spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
 	return 0;
 }
 /*-------------------------------------------------------------------*/
@@ -1196,60 +1459,93 @@
 	uhci_t *s;
 	urb_priv_t *urb_priv;
 	int ret = 0;
-
+	unsigned long flags;
+	urb_t *bulk_urb=NULL;
+		
 	if (!urb->dev || !urb->dev->bus)
 		return -ENODEV;
 
 	s = (uhci_t*) urb->dev->bus->hcpriv;
 	//dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
-
+	
+	if (!s->running)
+		return -ENODEV;
+		
 	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
 		return rh_submit_urb (urb);	/* virtual root hub */
 
 	usb_inc_dev_use (urb->dev);
 
-	if (search_dev_ep (s, urb)) {
-		usb_dec_dev_use (urb->dev);
-		return -ENXIO;	// urb already queued
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+
+	bulk_urb = search_dev_ep (s, urb);
+
+	if (bulk_urb) {
 
+		queue_dbg("found bulk urb %p\n",bulk_urb);
+
+		if ((usb_pipetype (urb->pipe) != PIPE_BULK) ||
+		    ((usb_pipetype (urb->pipe) == PIPE_BULK) &&
+		     (!(urb->transfer_flags & USB_QUEUE_BULK) || !(bulk_urb->transfer_flags & USB_QUEUE_BULK)))) {
+			spin_unlock_irqrestore (&s->urb_list_lock, flags);
+			usb_dec_dev_use (urb->dev);
+			err("ENXIO1 %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,bulk_urb);
+			return -ENXIO;	// urb already queued
+		}
 	}
 
 #ifdef DEBUG_SLAB
-	urb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+	urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
 #else
-	urb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+	urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG);
 #endif
 	if (!urb_priv) {
 		usb_dec_dev_use (urb->dev);
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
 		return -ENOMEM;
 	}
 
 	urb->hcpriv = urb_priv;
 	INIT_LIST_HEAD (&urb_priv->desc_list);
-	urb_priv->short_control_packet=0;
+	urb_priv->short_control_packet = 0;
 	dbg("submit_urb: scheduling %p", urb);
-
-	switch (usb_pipetype (urb->pipe)) {
-	case PIPE_ISOCHRONOUS:
-		ret = uhci_submit_iso_urb (urb);
-		break;
-	case PIPE_INTERRUPT:
-		ret = uhci_submit_int_urb (urb);
-		break;
-	case PIPE_CONTROL:
-		//dump_urb (urb);
-		ret = uhci_submit_control_urb (urb);
-		break;
-	case PIPE_BULK:
-		ret = uhci_submit_bulk_urb (urb);
-		break;
-	default:
-		ret = -EINVAL;
+	urb_priv->next_queued_urb = NULL;
+	urb_priv->prev_queued_urb = NULL;
+	urb_priv->bottom_qh = NULL;
+	urb_priv->next_qh = NULL;
+	
+	if (usb_pipetype (urb->pipe) == PIPE_BULK) {
+	
+		if (bulk_urb) {
+			while (((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb)  // find last queued bulk
+				bulk_urb=((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb;
+			
+			((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb=urb;
+		}
+		atomic_inc (&s->avoid_bulk);
+		ret = uhci_submit_bulk_urb (urb, bulk_urb);
+		atomic_dec (&s->avoid_bulk);
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+	}
+	else {
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+		switch (usb_pipetype (urb->pipe)) {
+		case PIPE_ISOCHRONOUS:
+			ret = uhci_submit_iso_urb (urb);
+			break;
+		case PIPE_INTERRUPT:
+			ret = uhci_submit_int_urb (urb);
+			break;
+		case PIPE_CONTROL:
+			ret = uhci_submit_control_urb (urb);
+			break;
+		default:
+			ret = -EINVAL;
+		}
 	}
 
 	dbg("submit_urb: scheduled with ret: %d", ret);
 	
-	
 	if (ret != 0) {
 		usb_dec_dev_use (urb->dev);
 #ifdef DEBUG_SLAB
@@ -1262,41 +1558,43 @@
 
 	return 0;
 }
-#ifdef USE_RECLAMATION_LOOP
-// Removes bandwidth reclamation if URB idles too long
-void check_idling_urbs(uhci_t *s)
+
+// Checks for URB timeout and removes bandwidth reclamation 
+// if URB idles too long
+_static void uhci_check_timeouts(uhci_t *s)
 {
 	struct list_head *p,*p2;
 	urb_t *urb;
 	int type;	
 
-	//dbg("check_idling_urbs: enter i:%d",in_interrupt());
-	
-	spin_lock (&s->urb_list_lock);
 	p = s->urb_list.prev;	
 
 	while (p != &s->urb_list) {
+		urb_priv_t *hcpriv;
+
 		p2 = p;
 		p = p->prev;
-		urb=list_entry (p2, urb_t, urb_list);
-		type=usb_pipetype (urb->pipe);
+		urb = list_entry (p2, urb_t, urb_list);
+		type = usb_pipetype (urb->pipe);
 
-#if 0
-		err("URB timers: %li now: %li %i\n",
-			((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT, jiffies,
-			type);
-#endif			 
-		if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
-		    (((urb_priv_t*)urb->hcpriv)->use_loop) &&
-		    ((((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT) < jiffies))
-			disable_desc_loop(s,urb);
+		hcpriv = (urb_priv_t*)urb->hcpriv;
+				
+		if ( urb->timeout && 
+			((hcpriv->started + urb->timeout) < jiffies)) {
+			urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+			async_dbg("uhci_check_timeout: timeout for %p",urb);
+			uhci_unlink_urb_async(s, urb);
+		}
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+		else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
+		     (hcpriv->use_loop) &&
+		     ((hcpriv->started + IDLE_TIMEOUT) < jiffies))
+			disable_desc_loop(s, urb);
+#endif
 
 	}
-	spin_unlock (&s->urb_list_lock);
-	
-	//dbg("check_idling_urbs: finished");
 }
-#endif
+
 /*-------------------------------------------------------------------
  Virtual Root Hub
  -------------------------------------------------------------------*/
@@ -1396,7 +1694,6 @@
 		dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
 		     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
 		urb->complete (urb);
-
 	}
 	return 0;
 }
@@ -1411,10 +1708,6 @@
 	urb_t *urb = (urb_t*) ptr;
 	uhci_t *uhci = urb->dev->bus->hcpriv;
 
-#ifdef USE_RECLAMATION_LOOP
-	check_idling_urbs(uhci);
-#endif
-
 	if (uhci->rh.send) {
 		len = rh_send_irq (urb);
 		if (len > 0) {
@@ -1427,7 +1720,9 @@
 }
 
 /*-------------------------------------------------------------------------*/
-/* Root Hub INTs are polled by this timer */
+/* Root Hub INTs are polled by this timer, polling interval 20ms */
+/* This time is also used for URB-timeout checking */
+
 _static int rh_init_int_timer (urb_t *urb)
 {
 	uhci_t *uhci = urb->dev->bus->hcpriv;
@@ -1436,7 +1731,7 @@
 	init_timer (&uhci->rh.rh_int_timer);
 	uhci->rh.rh_int_timer.function = rh_int_timer_do;
 	uhci->rh.rh_int_timer.data = (unsigned long) urb;
-	uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+	uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000;
 	add_timer (&uhci->rh.rh_int_timer);
 
 	return 0;
@@ -1640,7 +1935,6 @@
 		stat = -EPIPE;
 	}
 
-
 	dbg("Root-Hub stat port1: %x port2: %x",
 	     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
 
@@ -1716,13 +2010,19 @@
 	p = s->urb_list.prev;	
 	while (p != &s->urb_list) {
 		p2 = p;
-		p = p->prev;
+		p = p->prev ;
 		urb = list_entry (p2, urb_t, urb_list);
-		dbg("urb: %p", urb);
+		dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
+		
+		//urb->transfer_flags |=USB_ASYNC_UNLINK; 
+			
 		if (remove_all || (usb_dev == urb->dev)) {
+			spin_unlock_irqrestore (&s->urb_list_lock, flags);
 			warn("forced removing of queued URB %p due to disconnect",urb);
 			uhci_unlink_urb(urb);
-			urb->dev = NULL; // avoid further processing of this URB
+			urb->dev = NULL; // avoid further processing of this UR
+			spin_lock_irqsave (&s->urb_list_lock, flags);
+			p = s->urb_list.prev;	
 		}
 	}
 	spin_unlock_irqrestore (&s->urb_list_lock, flags);
@@ -1732,13 +2032,11 @@
 {
 	uhci_t *s;
 	
-	dbg("uhci_free_dev");	
 
 	if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)
 		return -EINVAL;
 	
-	s=(uhci_t*) usb_dev->bus->hcpriv;
-	
+	s=(uhci_t*) usb_dev->bus->hcpriv;	
 	uhci_unlink_urbs(s, usb_dev, 0);
 
 	return 0;
@@ -1769,12 +2067,10 @@
  * have announced. This leads to a queue abort due to the short packet,
  * the status stage is not executed. If this happens, the status stage
  * is manually re-executed.
- * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer
- * when the transfered length fits exactly in maxsze-packets. A bit
- * more intelligence is needed to detect this and finish without error.
+ * mode: 0: QHs already unlinked
  */
 
-_static int process_transfer (uhci_t *s, urb_t *urb)
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
 {
 	int ret = 0;
 	urb_priv_t *urb_priv = urb->hcpriv;
@@ -1784,25 +2080,21 @@
 	uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
 	uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
 	int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));	// save initial data_toggle
-
-
-	// extracted and remapped info from TD
-	int maxlength;
+	int maxlength; 	// extracted and remapped info from TD
 	int actual_length;
 	int status = 0;
 
-	dbg("process_transfer: urb contains bulk/control request");
-
+	//dbg("process_transfer: urb contains bulk/control request");
 
 	/* if the status phase has been retriggered and the
 	   queue is empty or the last status-TD is inactive, the retriggered
 	   status stage is completed
 	 */
-#if 1
+
 	if (urb_priv->short_control_packet && 
 		((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) 
 		goto transfer_finished;
-#endif
+
 	urb->actual_length=0;
 
 	for (; p != &qh->vertical; p = p->next) {
@@ -1810,22 +2102,20 @@
 
 		if (desc->hw.td.status & TD_CTRL_ACTIVE)	// do not process active TDs
 			return ret;
-
-		// extract transfer parameters from TD
-		actual_length = (desc->hw.td.status + 1) & 0x7ff;
+	
+		actual_length = (desc->hw.td.status + 1) & 0x7ff;		// extract transfer parameters from TD
 		maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;
 		status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));
 
-		// see if EP is stalled
-		if (status == -EPIPE) {
+		if (status == -EPIPE) { 		// see if EP is stalled
 			// set up stalled condition
 			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
 		}
 
-		// if any error occured stop processing of further TDs
-		if (status != 0) {
+		if (status != 0) {		// if any error occured stop processing of further TDs
 			// only set ret if status returned an error
-			uhci_show_td (desc);
+			if (status != -EPIPE) 
+				uhci_show_td (desc);
 			ret = status;
 			urb->error_count++;
 			break;
@@ -1833,11 +2123,6 @@
 		else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)
 			urb->actual_length += actual_length;
 
-#if 0
-		//	if (i++==0)
-		       	uhci_show_td (desc);	// show first TD of each transfer
-#endif
-
 		// got less data than requested
 		if ( (actual_length < maxlength)) {
 			if (urb->transfer_flags & USB_DISABLE_SPD) {
@@ -1852,8 +2137,8 @@
 			
 					qh->hw.qh.element = virt_to_bus (last_desc);  // re-trigger status stage
 					dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
-					uhci_show_td (desc);
-					uhci_show_td (last_desc);
+					//uhci_show_td (desc);
+					//uhci_show_td (last_desc);
 					urb_priv->short_control_packet=1;
 					return 0;
 				}
@@ -1864,34 +2149,24 @@
 		}
 
 		data_toggle = uhci_toggle (desc->hw.td.info);
-		//dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);      
+		queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);      
 
 	}
+
 	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);
-	transfer_finished:
 
-	unlink_qh (s, qh);
-	//delete_qh (s, qh);
-	qh->last_used=UHCI_GET_CURRENT_FRAME(s);
-	list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+ transfer_finished:
+	
+	uhci_clean_transfer(s, urb, qh, (mode==0?2:1));
 
 	urb->status = status;
 
-#ifdef USE_RECLAMATION_LOOP	
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH	
 	disable_desc_loop(s,urb);
 #endif	
 
-	dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d",
+	queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
 		urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
-	//dbg("process_transfer: exit");
-#if 0
-	if (urb->actual_length){
-		char *uu;
-		uu=urb->transfer_buffer;
-		dbg("%x %x %x %x %x %x %x %x",
-			*uu,*(uu+1),*(uu+2),*(uu+3),*(uu+4),*(uu+5),*(uu+6),*(uu+7));
-	}
-#endif
 	return ret;
 }
 
@@ -1934,15 +2209,13 @@
 
 		// if any error occured: ignore this td, and continue
 		if (status != 0) {
-			uhci_show_td (desc);
+			//uhci_show_td (desc);
 			urb->error_count++;
 			goto recycle;
 		}
 		else
 			urb->actual_length = actual_length;
 
-		// FIXME: SPD?
-
 	recycle:
 		if (urb->complete) {
 			//dbg("process_interrupt: calling completion, status %i",status);
@@ -1962,6 +2235,7 @@
 
 			desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
 			if (status==0) {
+				((urb_priv_t*)urb->hcpriv)->started=jiffies;
 				desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
 				      usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);
 				usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
@@ -1981,8 +2255,8 @@
 	return ret;
 }
 
-
-_static int process_iso (uhci_t *s, urb_t *urb)
+// mode: 1: force processing, don't unlink tds (already unlinked)
+_static int process_iso (uhci_t *s, urb_t *urb, int mode)
 {
 	int i;
 	int ret = 0;
@@ -1991,16 +2265,18 @@
 	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
 
 	dbg("urb contains iso request");
-	if (desc->hw.td.status & TD_CTRL_ACTIVE)
+	if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode)
 		return -EXDEV;	// last TD not finished
 
 	urb->error_count = 0;
 	urb->actual_length = 0;
 	urb->status = 0;
+	dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
+	    urb->number_of_packets,mode,desc->hw.td.status);
 
 	for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) {
 		desc = list_entry (p, uhci_desc_t, desc_list);
-
+		
 		//uhci_show_td(desc);
 		if (desc->hw.td.status & TD_CTRL_ACTIVE) {
 			// means we have completed the last TD, but not the TDs before
@@ -2013,7 +2289,8 @@
 			goto err;
 		}
 
-		unlink_td (s, desc, 1);
+		if (!mode)
+			unlink_td (s, desc, 1);
 
 		if (urb->number_of_packets <= i) {
 			dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
@@ -2038,13 +2315,14 @@
 			urb->error_count++;
 			urb->status = urb->iso_frame_desc[i].status;
 		}
-		dbg("process_iso: len:%d status:%x",
-		     urb->iso_frame_desc[i].length, urb->iso_frame_desc[i].status);
+		dbg("process_iso: %i: len:%d %08x status:%x",
+		     i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status);
 
 		delete_desc (desc);
 		list_del (p);
 	}
-	dbg("process_iso: exit %i (%d)", i, ret);
+	
+	dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
 	return ret;
 }
 
@@ -2056,15 +2334,20 @@
 
 
 	urb=list_entry (p, urb_t, urb_list);
-	dbg("found queued urb: %p", urb);
+	//dbg("process_urb: found queued urb: %p", urb);
 
 	switch (usb_pipetype (urb->pipe)) {
 	case PIPE_CONTROL:
+		ret = process_transfer (s, urb, 1);
+		break;
 	case PIPE_BULK:
-		ret = process_transfer (s, urb);
+		if (!s->avoid_bulk.counter)
+			ret = process_transfer (s, urb, 1);
+		else
+			return 0;
 		break;
 	case PIPE_ISOCHRONOUS:
-		ret = process_iso (s, urb);
+		ret = process_iso (s, urb, 0);
 		break;
 	case PIPE_INTERRUPT:
 		ret = process_interrupt (s, urb);
@@ -2158,23 +2441,18 @@
 	if (status != 1) {
 		warn("interrupt, status %x, frame# %i", status, 
 		     UHCI_GET_CURRENT_FRAME(s));
-		//uhci_show_queue(s->control_chain);
+
 		// remove host controller halted state
 		if ((status&0x20) && (s->running)) {
-			// more to be done - check TDs for invalid entries
-			// but TDs are only invalid if somewhere else is a (memory ?) problem
 			outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
 		}
 		//uhci_show_status (s);
 	}
-	//beep(1000);           
 	/*
-	 * the following is very subtle and was blatantly wrong before
 	 * traverse the list in *reverse* direction, because new entries
 	 * may be added at the end.
 	 * also, because process_urb may unlink the current urb,
 	 * we need to advance the list before
-	 * - Thomas Sailer
 	 */
 
 	spin_lock (&s->urb_list_lock);
@@ -2186,18 +2464,23 @@
 		p2 = p;
 		p = p->prev;
 		process_urb (s, p2);
-		if(s->unlink_urb_done)
-		{
+		if (s->unlink_urb_done) {
 			s->unlink_urb_done=0;
 			goto restart;
 		}
 	}
-	spin_unlock (&s->urb_list_lock);
-	clean_descs(s,0);	
+	if ((s->frame_counter & 63) == 0)
+		uhci_check_timeouts(s);
 
+	clean_descs(s,0);
+	uhci_cleanup_unlink(s, 0);
+	
+	spin_unlock (&s->urb_list_lock);
+	
+	s->frame_counter++;
 	outw (status, io_addr + USBSTS);
 
-	dbg("done");
+	//dbg("uhci_interrupt: done");
 }
 
 _static void reset_hc (uhci_t *s)
@@ -2249,15 +2532,19 @@
 {
 	struct usb_device *root_hub = s->bus->root_hub;
 
+	s->running = 0;		    // Don't allow submit_urb
+
 	if (root_hub)
 		usb_disconnect (&root_hub);
 
-	uhci_unlink_urbs(s, 0, 1);  // Forced unlink of remaining URBs
+	reset_hc (s);
+	wait_ms (1);
 
+	uhci_unlink_urbs (s, 0, 1);  // Forced unlink of remaining URBs
+	uhci_cleanup_unlink (s, 1);  // force cleanup of async killed URBs
+	
 	usb_deregister_bus (s->bus);
 
-	s->running = 0;
-	reset_hc (s);
 	release_region (s->io_addr, s->io_size);
 	free_irq (s->irq, s);
 	usb_free_bus (s->bus);
@@ -2285,6 +2572,7 @@
 	return 0;
 }
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
 _static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
 {
 	uhci_t *s = (uhci_t*) dev->data;
@@ -2301,6 +2589,7 @@
 	}
 	return 0;
 }
+#endif
 
 _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
 {
@@ -2315,14 +2604,17 @@
 	memset (s, 0, sizeof (uhci_t));
 	INIT_LIST_HEAD (&s->free_desc);
 	INIT_LIST_HEAD (&s->urb_list);
+	INIT_LIST_HEAD (&s->urb_unlinked);
 	spin_lock_init (&s->urb_list_lock);
 	spin_lock_init (&s->qh_lock);
 	spin_lock_init (&s->td_lock);
+	atomic_set(&s->avoid_bulk, 0);
 	s->irq = -1;
 	s->io_addr = io_addr;
 	s->io_size = io_size;
 	s->next = devs;	//chain new uhci device into global list	
-
+	s->frame_counter = 0;
+	
 	bus = usb_alloc_bus (&uhci_device_operations);
 	if (!bus) {
 		kfree (s);
@@ -2389,11 +2681,11 @@
 
 	//chain new uhci device into global list
 	devs = s;
-
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
 	pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(dev), handle_pm_event);
 	if (pmdev)
 		pmdev->data = s;
-
+#endif
 	return 0;
 }
 
@@ -2407,7 +2699,7 @@
 		unsigned int io_addr = dev->resource[i].start;
 		unsigned int io_size =
 		dev->resource[i].end - dev->resource[i].start + 1;
-		if (!(dev->resource[i].flags & IORESOURCE_IO))
+		if (!(dev->resource[i].flags & 1))
 			continue;
 #else
 		unsigned int io_addr = dev->base_address[i];
@@ -2422,6 +2714,10 @@
 			break;
 		/* disable legacy emulation */
 		pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+		if(dev->vendor==0x8086) {
+			info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER);
+			pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER);
+		}
 		return alloc_uhci(dev, dev->irq, io_addr, io_size);
 	}
 	return -1;
@@ -2452,6 +2748,9 @@
 #endif	
 	info(VERSTR);
 
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	info("High bandwidth mode enabled");	
+#endif
 	for (;;) {
 		dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev);
 		if (!dev)
@@ -2506,7 +2805,9 @@
 
 void cleanup_module (void)
 {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
 	pm_unregister_all (handle_pm_event);
+#endif
 	uhci_cleanup ();
 }
 
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/usb-uhci.h linux/drivers/usb/usb-uhci.h
--- v2.3.51/linux/drivers/usb/usb-uhci.h	Wed Feb 16 17:03:52 2000
+++ linux/drivers/usb/usb-uhci.h	Tue Mar 14 18:09:51 2000
@@ -2,10 +2,11 @@
 #define __LINUX_UHCI_H
 
 /*
-   $Id: usb-uhci.h,v 1.41 2000/02/13 21:37:38 acher Exp $
+   $Id: usb-uhci.h,v 1.50 2000/03/13 21:18:04 fliegl Exp $
  */
 #define MODNAME "usb-uhci"
-#define VERSTR "version v1.184 time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.50 $ time " __TIME__ " " __DATE__
+#define UHCI_LATENCY_TIMER 0
 
 static __inline__ void uhci_wait_ms(unsigned int ms)
 {
@@ -154,9 +155,13 @@
 
 typedef struct {
 	struct list_head desc_list;	// list pointer to all corresponding TDs/QHs associated with this request
-	int short_control_packet;
 	unsigned long started;
-	int use_loop;
+	urb_t *next_queued_urb;         // next queued urb for this EP
+	urb_t *prev_queued_urb;
+	uhci_desc_t *bottom_qh;
+	uhci_desc_t *next_qh;       	// next helper QH
+	char use_loop;
+	char short_control_packet;
 } urb_priv_t, *purb_priv_t;
 
 struct virt_root_hub {
@@ -186,12 +191,14 @@
 	spinlock_t urb_list_lock;	// lock to keep consistency 
 
 	int unlink_urb_done;
+	atomic_t avoid_bulk;
 	
 	struct usb_bus *bus;	// our bus
 
 	__u32 *framelist;
 	uhci_desc_t **iso_td;
 	uhci_desc_t *int_chain[8];
+	uhci_desc_t *ls_control_chain;
 	uhci_desc_t *control_chain;
 	uhci_desc_t *bulk_chain;
 	uhci_desc_t *chain_end;
@@ -200,6 +207,9 @@
 	spinlock_t td_lock;
 	struct virt_root_hub rh;	//private data of the virtual root hub
 	int loop_usage;            // URBs using bandwidth reclamation
+
+	struct list_head urb_unlinked;	// list of all unlinked  urbs
+	int frame_counter;
 } uhci_t, *puhci_t;
 
 
diff -u --recursive --new-file v2.3.51/linux/drivers/video/aty128.h linux/drivers/video/aty128.h
--- v2.3.51/linux/drivers/video/aty128.h	Tue Jan 11 22:31:43 2000
+++ linux/drivers/video/aty128.h	Tue Mar 14 18:05:12 2000
@@ -264,6 +264,8 @@
 /* DAC_CNTL bit constants */                       
 #define DAC_8BIT_EN				0x00000100
 #define DAC_MASK				0xFF000000
+#define DAC_BLANKING				0x00000004
+#define DAC_RANGE_CNTL				0x00000003
 
 /* GEN_RESET_CNTL bit constants */
 #define SOFT_RESET_GUI				0x00000001
diff -u --recursive --new-file v2.3.51/linux/drivers/video/aty128fb.c linux/drivers/video/aty128fb.c
--- v2.3.51/linux/drivers/video/aty128fb.c	Fri Mar 10 16:40:46 2000
+++ linux/drivers/video/aty128fb.c	Tue Mar 14 18:05:12 2000
@@ -48,13 +48,13 @@
 #include <linux/ioport.h>
 #include <asm/io.h>
 
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
+#include <video/macmodes.h>
 #ifdef CONFIG_NVRAM
 #include <linux/nvram.h>
 #endif
-#include <video/macmodes.h>
 #endif
 
 #ifdef CONFIG_FB_COMPAT_XPMAC
@@ -69,7 +69,7 @@
 
 #ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
-#endif /* CONFIG_MTRR */
+#endif
 
 #include "aty128.h"
 
@@ -158,13 +158,18 @@
     u8 LoopLatency;
     u8 DspOn;
     u8 Rloop;
+    const char *name;
 };
 
 /* various memory configurations */
-const struct aty128_meminfo sdr_128   = { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16 };
-const struct aty128_meminfo sdr_64    = { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17 };
-const struct aty128_meminfo sdr_sgram = { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16 };
-const struct aty128_meminfo ddr_sgram = { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16 };
+const struct aty128_meminfo sdr_128   =
+    { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16, "128-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_64    =
+    { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17, "64-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_sgram =
+    { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16, "64-bit SDR SGRAM (2:1)" };
+const struct aty128_meminfo ddr_sgram =
+    { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" };
 
 static int currcon = 0;
 
@@ -176,20 +181,23 @@
 #ifndef MODULE
 static const char *mode_option __initdata = NULL;
 #endif
+#ifndef CONFIG_PPC
+static void *bios_seg = NULL;
+#endif
 
 #ifdef CONFIG_PPC
 #ifdef CONFIG_NVRAM_NOT_DEFINED
+static int default_vmode __initdata = VMODE_640_480_60;
+static int default_cmode __initdata = CMODE_8;
+#else
 static int default_vmode __initdata = VMODE_NVRAM;
 static int default_cmode __initdata = CMODE_NVRAM;
-#else
-static int default_vmode __initdata = VMODE_CHOOSE;
-static int default_cmode __initdata = CMODE_8;
 #endif
 #endif
 
 #ifdef CONFIG_MTRR
 static int mtrr = 1;
-#endif /* CONFIG_MTRR */
+#endif
 
 /* PLL constants */
 struct aty128_constants {
@@ -236,17 +244,13 @@
 struct fb_info_aty128 {
     struct fb_info fb_info;
     struct fb_info_aty128 *next;
-    struct aty128_constants constants;
-    unsigned long regbase_phys;         /* mmio                */
-    unsigned long frame_buffer_phys;    /* framebuffer memory  */
+    struct aty128_constants constants;  /* PLL and others      */
+    unsigned long regbase_phys;         /* physical mmio       */
+    void *regbase;                      /* remapped mmio       */
+    unsigned long frame_buffer_phys;    /* physical fb memory  */
     unsigned long frame_buffer;         /* remaped framebuffer */
-    void *regbase;
     const struct aty128_meminfo *mem;   /* onboard mem info    */
     u32 vram_size;                      /* onboard video ram   */
-#ifndef CONFIG_PPC
-    void *bios_seg;                     /* video BIOS segment  */
-#endif
-    unsigned short card_revision;	/* video card revision */
     struct aty128fb_par default_par, current_par;
     struct display disp;
     struct display_switch dispsw;       /* for cursor and font */
@@ -268,7 +272,7 @@
 #endif
 #ifdef CONFIG_MTRR
     struct { int vram; int vram_valid; } mtrr;
-#endif /* CONFIG_MTRR */
+#endif
 };
 
 static struct fb_info_aty128 *board_list = NULL;
@@ -321,12 +325,18 @@
 static int aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 				u_int transp, struct fb_info *info);
 static void do_install_cmap(int con, struct fb_info *info);
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+                             const struct aty128fb_par *par,
+                             const struct fb_info_aty128 *info);
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+                             struct aty128fb_par *par,
+                             const struct fb_info_aty128 *info);
 static int aty128_pci_register(struct pci_dev *pdev,
                                const struct aty128_chip_info *aci);
 static struct fb_info_aty128 *aty128_board_list_add(struct fb_info_aty128
 				*board_list, struct fb_info_aty128 *new_node);
-#ifndef CONFIG_PPC
 static int aty128find_ROM(struct fb_info_aty128 *info);
+#ifndef CONFIG_PPC
 static void aty128_get_pllinfo(struct fb_info_aty128 *info);
 #endif
 static void aty128_timings(struct fb_info_aty128 *info);
@@ -513,7 +523,7 @@
 
 
 /* write to the scratch register to test r/w functionality */
-static u32
+static int __init
 register_test(const struct fb_info_aty128 *info)
 {
     u32 val, flag = 0;
@@ -720,6 +730,9 @@
     aty_st_le32(CRTC_PITCH, crtc->pitch);
     aty_st_le32(CRTC_OFFSET, crtc->offset);
     aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
+
+    /* Disable ATOMIC updating.  Is this the right place? */
+    aty_st_le32(PPLL_CNTL, aty_ld_le32(PPLL_CNTL) & ~(0x00030000));
 }
 
 
@@ -818,7 +831,7 @@
     
     c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
 
-    crtc->gen_cntl = 0x03000000L | c_sync | (depth << 8);
+    crtc->gen_cntl = 0x3000000L | c_sync | (depth << 8);
 
     crtc->h_total = h_total | (h_disp << 16);
     crtc->v_total = v_total | (v_disp << 16);
@@ -896,7 +909,7 @@
 	break;
     default:
         printk(KERN_ERR "Invalid pixel width\n");
-        return -1;
+        return -EINVAL;
     }
 
     return 0;
@@ -913,31 +926,31 @@
     u32 pix_width;
 
     /* fun with masking */
-    h_total = crtc->h_total & 0x1ff;
-    h_disp = (crtc->h_total>>16) & 0xff;
+    h_total     = crtc->h_total & 0x1ff;
+    h_disp      = (crtc->h_total>>16) & 0xff;
     h_sync_strt = (crtc->h_sync_strt_wid>>3) & 0x1ff;
-    h_sync_dly = crtc->h_sync_strt_wid & 0x7;
-    h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x3f;
-    h_sync_pol = (crtc->h_sync_strt_wid>>23) & 0x1;
-    v_total = crtc->v_total & 0x7ff;
-    v_disp = (crtc->v_total>>16) & 0x7ff;
+    h_sync_dly  = crtc->h_sync_strt_wid & 0x7;
+    h_sync_wid  = (crtc->h_sync_strt_wid>>16) & 0x3f;
+    h_sync_pol  = (crtc->h_sync_strt_wid>>23) & 0x1;
+    v_total     = crtc->v_total & 0x7ff;
+    v_disp      = (crtc->v_total>>16) & 0x7ff;
     v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
-    v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f;
-    v_sync_pol = (crtc->v_sync_strt_wid>>23) & 0x1;
-    c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
-    pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
-
-    xres = (h_disp+1)*8;
-    yres = v_disp+1;
-    left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
+    v_sync_wid  = (crtc->v_sync_strt_wid>>16) & 0x1f;
+    v_sync_pol  = (crtc->v_sync_strt_wid>>23) & 0x1;
+    c_sync      = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+    pix_width   = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+
+    xres  = (h_disp+1) << 3;
+    yres  = v_disp+1;
+    left  = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
     right = (h_sync_strt-h_disp)*8+h_sync_dly;
     hslen = h_sync_wid*8;
     upper = v_total-v_sync_strt-v_sync_wid;
     lower = v_sync_strt-v_disp;
     vslen = v_sync_wid;
-    sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
-           (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
-           (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+    sync  = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+            (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+            (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
 
     aty128_bpp_to_var(pix_width, var);
 
@@ -1007,7 +1020,7 @@
 			const struct fb_info_aty128 *info)
 {
     const struct aty128_constants c = info->constants;
-    unsigned char post_dividers [] = {1,2,4,8,3,6,12};
+    unsigned char post_dividers[] = {1,2,4,8,3,6,12};
     u32 output_freq;
     u32 vclk;        /* in .01 MHz */
     int i;
@@ -1110,6 +1123,7 @@
     n <<= (11 - p);
     x = round_div(n, d);
     roff = x * (fifo_depth - 4);
+
     if ((ron + m->Rloop) >= roff) {
 	printk(KERN_ERR "aty128fb: Mode out of range!\n");
 	return -EINVAL;
@@ -1134,7 +1148,14 @@
 			struct fb_info_aty128 *info)
 { 
     u32 config;
-    
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* enable this when macmodes gets updated */
+    struct vc_mode disp_info;
+#endif
+    struct fb_var_screeninfo var;
+    int cmode, vmode;
+#endif
+
     info->current_par = *par;
     
     if (info->blitter_may_be_busy)
@@ -1174,6 +1195,46 @@
 
     if (par->accel_flags & FB_ACCELF_TEXT)
         aty128_init_engine(par, info);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* use this when macmodes gets updated */
+    if (!console_fb_info || console_fb_info == &info->fb_info) {
+	disp_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+	disp_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+	disp_info.depth = par->crtc.bpp;
+	disp_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+        aty128_encode_var(&var, par, info);
+	if (mac_var_to_vmode(&var, &vmode, &cmode))
+	    disp_info.mode = 0;
+	else
+	    disp_info.mode = vmode;
+	strcpy(disp_info.name, aty128fb_name);
+	disp_info.fb_address = info->frame_buffer_phys;
+	disp_info.cmap_adr_address = 0;
+	disp_info.cmap_data_address = 0;
+	disp_info.disp_reg_address = info->regbase_phys;
+        register_compat_xpmac(disp_info);
+    }
+#else
+    if (!console_fb_info || console_fb_info == &info->fb_info) {
+	display_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+	display_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+	display_info.depth = par->crtc.bpp;
+	display_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+        aty128_encode_var(&var, par, info);
+	if (mac_var_to_vmode(&var, &vmode, &cmode))
+	    display_info.mode = 0;
+	else
+	    display_info.mode = vmode;
+	strcpy(display_info.name, aty128fb_name);
+	display_info.fb_address = info->frame_buffer_phys;
+	display_info.cmap_adr_address = 0;
+	display_info.cmap_data_address = 0;
+	display_info.disp_reg_address = info->regbase_phys;
+        register_compat_xpmac(display_info);
+    }
+#endif
+#endif /* CONFIG_FB_COMPAT_XPMAC */
 }
 
 
@@ -1293,16 +1354,23 @@
         var->xres_virtual = var->xres;
     if (var->yres > var->yres_virtual)
         var->yres_virtual = var->yres;
-    if (var->bits_per_pixel <= 8)
-        var->bits_per_pixel = 8;
-    else if (var->bits_per_pixel <= 16)
-        var->bits_per_pixel = 16;
-    else if (var->bits_per_pixel <= 24)
-        var->bits_per_pixel = 24;
-    else if (var->bits_per_pixel <= 32)
-        var->bits_per_pixel = 32;
-    else
-        return -EINVAL;
+
+    switch (var->bits_per_pixel) {
+        case 0 ... 8:
+            var->bits_per_pixel = 8;
+            break;
+        case 9 ... 16:
+            var->bits_per_pixel = 16;
+            break;
+        case 17 ... 24:
+            var->bits_per_pixel = 24;
+            break;
+        case 25 ... 32:
+            var->bits_per_pixel = 32;
+            break;
+        default:
+            return -EINVAL;
+    }
 
     if ((err = aty128_decode_var(var, &par, info)))
 	return err;
@@ -1357,26 +1425,6 @@
 	do_install_cmap(con, &info->fb_info);
     } 
 
-#ifdef CONFIG_FB_COMPAT_XPMAC
-    if (!console_fb_info || console_fb_info == &info->fb_info) {
-	int vmode, cmode;
-
-	display_info.width = var->xres;
-	display_info.height = var->yres;
-	display_info.depth = var->bits_per_pixel;
-	display_info.pitch = (var->xres_virtual)*(var->bits_per_pixel)/8;
-	if (mac_var_to_vmode(var, &vmode, &cmode))
-	    display_info.mode = 0;
-	else
-	    display_info.mode = vmode;
-	strcpy(info->fb_info.modename, aty128fb_name);
-	display_info.fb_address = info->frame_buffer_phys;
-	display_info.cmap_adr_address = 0;
-	display_info.cmap_data_address = 0;
-	display_info.disp_reg_address = info->regbase_phys;
-    }
-#endif
-
     return 0;
 }
 
@@ -1435,14 +1483,14 @@
     fix->smem_len = (u32)info->vram_size;
     fix->mmio_len = 0x1fff;
 
-    fix->type = FB_TYPE_PACKED_PIXELS;
-    fix->type_aux = 0;
+    fix->type        = FB_TYPE_PACKED_PIXELS;
+    fix->type_aux    = 0;
     fix->line_length = par->crtc.vxres*par->crtc.bpp/8;
-    fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
-                                     : FB_VISUAL_DIRECTCOLOR;
+    fix->visual      = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
+                                          : FB_VISUAL_DIRECTCOLOR;
     fix->ywrapstep = 0;
-    fix->xpanstep = 8;
-    fix->ypanstep = 1;
+    fix->xpanstep  = 8;
+    fix->ypanstep  = 1;
 
     fix->accel = FB_ACCEL_ATI_RAGE128;
 
@@ -1540,6 +1588,7 @@
 	disp = &fb_display[con];
     else
 	disp = info->disp;
+
     if (!disp->cmap.len) {      /* no colormap allocated? */
         int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
 	if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
@@ -1614,7 +1663,7 @@
             mtrr = 0;
         }
 #endif /* CONFIG_MTRR */
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
         /* vmode and cmode depreciated */
 	else if (!strncmp(this_opt, "vmode:", 6)) {
             unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
@@ -1660,27 +1709,22 @@
     const struct aty128_chip_info *aci = &aty128_pci_probe_list[0];
     char *video_card = "Rage128";
 
-    if (!register_test(info)) {
-	printk(KERN_ERR "aty128fb: Can't write to video registers\n");
-	return 0;
-    }
-
     if (!info->vram_size)	/* may have already been probed */
 	info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
 
+    /* Get the chip revision */
     chip_rev = (aty_ld_le32(CONFIG_CNTL) >> 16) & 0x1F;
 
     /* put a name with the face */
     while (aci->name && info->pdev->device != aci->device) { aci++; }
     video_card = (char *)aci->name;
 
-    printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] [card rev %x] ",
-                      video_card, chip_rev, info->card_revision);
+    printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
 
     if (info->vram_size % (1024 * 1024) == 0)
-	printk("%dM\n", info->vram_size / (1024*1024));
+	printk("%dM %s\n", info->vram_size / (1024*1024), info->mem->name);
     else
-	printk("%dk\n", info->vram_size / 1024);
+	printk("%dk %s\n", info->vram_size / 1024, info->mem->name);
 
     /* fill in info */
     strcpy(info->fb_info.modename, aty128fb_name);
@@ -1697,32 +1741,34 @@
     var = default_var;
 #else
     memset(&var, 0, sizeof(var));
+#ifdef CONFIG_FB_COMPAT_XPMAC       /* CONFIG_PPC implied */
+    if (_machine == _MACH_Pmac) {
+        if (mode_option) {
+            if (!mac_find_mode(&var, &info->fb_info, mode_option, 8))
+                var = default_var;
+        } else {
 #ifdef CONFIG_NVRAM
-    if (default_vmode == VMODE_NVRAM) {
-        default_vmode = nvram_read_byte(NV_VMODE);
-        if (default_vmode <= 0 || default_vmode > VMODE_MAX)
-            default_vmode = VMODE_CHOOSE;
-    }
-#endif
-#ifdef CONFIG_PPC
-    if (default_vmode == VMODE_CHOOSE) {
-        var = default_var;
-#endif /* CONFIG_PPC */
+            if (default_vmode == VMODE_NVRAM)
+                default_vmode = nvram_read_byte(NV_VMODE);
 
-	if (!fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
-			  &defaultmode, initdepth))
-	    var = default_var;
-
-#ifdef CONFIG_PPC
-#ifdef CONFIG_NVRAM
-    if (default_cmode == CMODE_NVRAM)
-        default_cmode = nvram_read_byte(NV_CMODE);
+            if (default_cmode == CMODE_NVRAM)
+                default_cmode = nvram_read_byte(NV_CMODE);
 #endif
-    } else if (_machine == _MACH_Pmac)
- 	if (mac_vmode_to_var(default_vmode, default_cmode, &var))
-	    var = default_var;
+            if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+                default_vmode = VMODE_640_480_60;
 
-#endif
+            if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
+                default_cmode = CMODE_8;
+
+            if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+                var = default_var;
+        }
+    }
+#else
+    if (fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
+                      &defaultmode, initdepth) == 0)
+        var = default_var;
+#endif /* CONFIG_FB_COMPAT_XPMAC */
 #endif /* MODULE */
 
     if (noaccel)
@@ -1743,8 +1789,8 @@
         info->palette[j].blue = default_blu[k];
     }
 
-    dac = aty_ld_le32(DAC_CNTL) & 15;	/* preserve lower three bits */
-    dac |= DAC_8BIT_EN;			/* set 8 bit dac             */
+    dac = aty_ld_le32(DAC_CNTL);
+    dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL | DAC_BLANKING);
     dac |= DAC_MASK;			/* set DAC mask              */
     aty_st_le32(DAC_CNTL, dac);
 
@@ -1845,40 +1891,51 @@
     }
     memset(info, 0, sizeof(struct fb_info_aty128));
 
+    /* Copy PCI device info into info->pdev */
     info->pdev = pdev;
 
+    /* Virtualize mmio region */
     info->regbase_phys = reg_addr;
     info->regbase = ioremap(reg_addr, 0x1FFF);
 
     if (!info->regbase)
         goto err_out;
 
-    pci_read_config_word(pdev, 0x08, &tmp);
-    info->card_revision = tmp;
-
     info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
 
+    pci_read_config_word(pdev, PCI_COMMAND, &tmp);
+    if (!(tmp & PCI_COMMAND_MEMORY)) {
+        tmp |= PCI_COMMAND_MEMORY;
+        pci_write_config_word(pdev, PCI_COMMAND, tmp);
+    }
+
+    /* Virtualize the framebuffer */
     info->frame_buffer_phys = fb_addr;
     info->frame_buffer = (unsigned long)ioremap(fb_addr, info->vram_size);
 
     if (!info->frame_buffer)
         goto err_out;
 
-    pci_read_config_word(pdev, PCI_COMMAND, &tmp);
-    if (!(tmp & PCI_COMMAND_MEMORY)) {
-        tmp |= PCI_COMMAND_MEMORY;
-        pci_write_config_word(pdev, PCI_COMMAND, tmp);
+    /* If we can't test scratch registers, something is seriously wrong */
+    if (!register_test(info)) {
+        printk(KERN_ERR "aty128fb: Can't write to video register!\n");
+        goto err_out;
     }
 
-#if defined(CONFIG_PPC)
-    aty128_timings(info);
-#else
     if (!aty128find_ROM(info)) {
-        printk(KERN_INFO "Rage128 BIOS not located.  Guessing...\n");
+        printk(KERN_INFO "aty128fb: Rage128 BIOS not located. Guessing...\n");
         aty128_timings(info);
     }
-    else
+#ifndef CONFIG_PPC
+ else
         aty128_get_pllinfo(info);
+
+        /* free up to-be unused resources. bios_seg is mapped by
+         * aty128find_ROM() and used by aty128_get_pllinfo()
+         *
+         * TODO: make more elegant. doesn't need to be global */
+        if (bios_seg)
+            iounmap(bios_seg);
 #endif
 #ifdef CONFIG_MTRR
     if (mtrr) {
@@ -1910,27 +1967,26 @@
 #endif /* CONFIG_PCI */
 
 
-#ifndef CONFIG_PPC
+/* PPC cannot read video ROM, so we fail by default */
 static int __init
 aty128find_ROM(struct fb_info_aty128 *info)
 {
-    u32 segstart;
+    int  flag = 0;
+#ifndef CONFIG_PPC
+    u32  segstart;
     char *rom_base;
-    char *rom_base1;
     char *rom;
-    int stage;
-    int i;
+    int  stage;
+    int  i;
     char aty_rom_sig[] = "761295520";   /* ATI ROM Signature      */
     char R128_sig[] = "R128";           /* Rage128 ROM identifier */
-    int flag = 0;
 
     for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
         stage = 1;
 
         rom_base = (char *) ioremap(segstart, 0x1000);
-        rom_base1 = (char *) (rom_base+1);
 
-        if ((*rom_base == 0x55) && (((*rom_base1) & 0xff) == 0xaa))
+        if ((*rom_base == 0x55) && (((*(rom_base + 1)) & 0xff) == 0xaa))
             stage = 2;
 
         if (stage != 2) {
@@ -1967,16 +2023,18 @@
             continue;
         }
 
+        bios_seg = rom_base;
         printk(KERN_INFO "aty128fb: Rage128 BIOS located at segment %4.4X\n",
-                         (u32)rom_base);
-        info->bios_seg = rom_base;
+                         (unsigned int)rom_base);
         flag = 1;
         break;
     }
+#endif /* !CONFIG_PPC */
     return (flag);
 }
 
 
+#ifndef CONFIG_PPC
 static void __init
 aty128_get_pllinfo(struct fb_info_aty128 *info)
 {   
@@ -1985,16 +2043,16 @@
     u16 bios_header_offset, pll_info_offset;
     PLL_BLOCK pll;
 
-    bios_header = info->bios_seg + 0x48L;
-    header_ptr = bios_header;
+    bios_header = bios_seg + 0x48L;
+    header_ptr  = bios_header;
 
     bios_header_offset = readw(header_ptr);
-    bios_header = info->bios_seg + bios_header_offset;
+    bios_header = bios_seg + bios_header_offset;
     bios_header += 0x30;
 
     header_ptr = bios_header;
     pll_info_offset = readw(header_ptr);
-    header_ptr = info->bios_seg + pll_info_offset;
+    header_ptr = bios_seg + pll_info_offset;
 
     memcpy_fromio(&pll, header_ptr, 50);
 
@@ -2032,10 +2090,6 @@
         info->mem = &sdr_sgram;
     }       
 
-    /* free up to-be unused resources */
-    if (info->bios_seg)
-        iounmap(info->bios_seg);
-
     return;
 }           
 #endif /* ! CONFIG_PPC */
@@ -2056,7 +2110,7 @@
         info->constants.ppll_max = 25000;    /* 23000 on some cards? */
 
 #if 1
-    /* XXX TODO. Calculuate properly. Fix OF's pll ideas. */
+    /* XXX TODO. Calculuate properly. */
     if (!info->constants.ref_divider)
         info->constants.ref_divider = 0x3b;
     aty_st_pll(PPLL_REF_DIV, info->constants.ref_divider);
@@ -2473,10 +2527,9 @@
 };
 #endif
 
-#if defined(MODULE)
-MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>, Anthony Tong "
-              "<atong@uiuc.edu>");
-MODULE_DESCRIPTION("FBDev driver for ATI Rage128 cards");
+#ifdef MODULE
+MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>");
+MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
 
 int __init
 init_module(void)
diff -u --recursive --new-file v2.3.51/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
--- v2.3.51/linux/drivers/video/atyfb.c	Fri Mar 10 16:40:46 2000
+++ linux/drivers/video/atyfb.c	Sun Mar 12 19:11:17 2000
@@ -1,4 +1,4 @@
-/*  $Id: atyfb.c,v 1.140 2000/02/25 05:46:27 davem Exp $
+/*  $Id: atyfb.c,v 1.141 2000/03/12 03:53:16 davem Exp $
  *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
  *
  *	Copyright (C) 1997-1998  Geert Uytterhoeven
diff -u --recursive --new-file v2.3.51/linux/drivers/video/cyber2000fb.c linux/drivers/video/cyber2000fb.c
--- v2.3.51/linux/drivers/video/cyber2000fb.c	Thu Mar  2 14:36:23 2000
+++ linux/drivers/video/cyber2000fb.c	Sun Mar 12 19:39:39 2000
@@ -32,6 +32,7 @@
 #include <video/fbcon-cfb24.h>
 
 #define MMIO_SIZE	0x000c0000
+/*#define CFB16_IS_CFB15*/
 
 static char			*CyberRegs;
 
@@ -275,18 +276,8 @@
 #endif
 
 #ifdef FBCON_HAS_CFB16
-	case 15:
-		if (regno < 32) {
-			cyber2000_outb(regno << 3, 0x3c8);
-			cyber2000_outb(red, 0x3c9);
-			cyber2000_outb(green, 0x3c9);
-			cyber2000_outb(blue, 0x3c9);
-		}
-		if (regno < 16)
-			current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
-		break;
-
 	case 16:
+#ifndef CFB16_IS_CFB15
 		if (regno < 64) {
 			/* write green */
 			cyber2000_outb(regno << 2, 0x3c8);
@@ -308,6 +299,19 @@
 		break;
 #endif
 
+	case 15:
+		if (regno < 32) {
+			cyber2000_outb(regno << 3, 0x3c8);
+			cyber2000_outb(red, 0x3c9);
+			cyber2000_outb(green, 0x3c9);
+			cyber2000_outb(blue, 0x3c9);
+		}
+		if (regno < 16)
+			current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
+		break;
+
+#endif
+
 #ifdef FBCON_HAS_CFB24
 	case 24:
 		cyber2000_outb(regno, 0x3c8);
@@ -345,6 +349,7 @@
 	 * Other
 	 */
 	unsigned int	visual;
+	unsigned char	palette_ctrl;
 };
 
 static const char crtc_idx[] = {
@@ -418,7 +423,7 @@
 	cyber2000_outb(0x56, 0x3ce);
 	i = cyber2000_inb(0x3cf);
 	cyber2000_outb(i | 4, 0x3cf);
-	cyber2000_outb(0x04, 0x3c6);
+	cyber2000_outb(hw->palette_ctrl, 0x3c6);
 	cyber2000_outb(i,    0x3cf);
 
 	cyber2000_outb(0x20, 0x3c0);
@@ -776,37 +781,76 @@
 	int err;
 
 	hw->width = var->xres_virtual;
+
+	var->red.msb_right	= 0;
+	var->green.msb_right	= 0;
+	var->blue.msb_right	= 0;
+
 	switch (var->bits_per_pixel) {
 #ifdef FBCON_HAS_CFB8
 	case 8:	/* PSEUDOCOLOUR, 256 */
-		hw->visual    = FB_VISUAL_PSEUDOCOLOR;
-		hw->pixformat = PIXFORMAT_8BPP;
-		hw->visualid  = VISUALID_256;
-		hw->pitch     = hw->width >> 3;
+		var->bits_per_pixel	= 8;
+		var->red.offset		= 0;
+		var->red.length		= 8;
+		var->green.offset	= 0;
+		var->green.length	= 8;
+		var->blue.offset	= 0;
+		var->blue.length	= 8;
+		hw->visual		= FB_VISUAL_PSEUDOCOLOR;
+		hw->pixformat		= PIXFORMAT_8BPP;
+		hw->visualid		= VISUALID_256;
+		hw->pitch		= hw->width >> 3;
+		hw->palette_ctrl	= 0x04;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB16
+	case 16:/* DIRECTCOLOUR, 64k */
+#ifndef CFB16_IS_CFB15
+		var->bits_per_pixel	= 16;
+		var->red.offset		= 11;
+		var->red.length		= 5;
+		var->green.offset	= 5;
+		var->green.length	= 6;
+		var->blue.offset	= 0;
+		var->blue.length	= 5;
+		hw->visual		= FB_VISUAL_DIRECTCOLOR;
+		hw->pixformat		= PIXFORMAT_16BPP;
+		hw->visualid		= VISUALID_64K;
+		hw->pitch		= hw->width >> 2;
+		hw->palette_ctrl	= 0x14;
+		break;
+#endif
 	case 15:/* DIRECTCOLOUR, 32k */
-		hw->visual    = FB_VISUAL_DIRECTCOLOR;
-		hw->pixformat = PIXFORMAT_16BPP;
-		hw->visualid  = VISUALID_32K;
-		hw->pitch     = hw->width >> 2;
+		var->bits_per_pixel	= 15;
+		var->red.offset		= 10;
+		var->red.length		= 5;
+		var->green.offset	= 5;
+		var->green.length	= 5;
+		var->blue.offset	= 0;
+		var->blue.length	= 5;
+		hw->visual		= FB_VISUAL_DIRECTCOLOR;
+		hw->pixformat		= PIXFORMAT_16BPP;
+		hw->visualid		= VISUALID_32K;
+		hw->pitch		= hw->width >> 2;
+		hw->palette_ctrl	= 0x14;
 		break;
 
-	case 16:/* DIRECTCOLOUR, 64k */
-		hw->visual    = FB_VISUAL_DIRECTCOLOR;
-		hw->pixformat = PIXFORMAT_16BPP;
-		hw->visualid  = VISUALID_64K;
-		hw->pitch     = hw->width >> 2;
-		break;
 #endif
 #ifdef FBCON_HAS_CFB24
 	case 24:/* TRUECOLOUR, 16m */
-		hw->visual    = FB_VISUAL_TRUECOLOR;
-		hw->pixformat = PIXFORMAT_24BPP;
-		hw->visualid  = VISUALID_16M;
-		hw->width    *= 3;
-		hw->pitch     = hw->width >> 3;
+		var->bits_per_pixel	= 24;
+		var->red.offset		= 16;
+		var->red.length		= 8;
+		var->green.offset	= 8;
+		var->green.length	= 8;
+		var->blue.offset	= 0;
+		var->blue.length	= 8;
+		hw->visual		= FB_VISUAL_TRUECOLOR;
+		hw->pixformat		= PIXFORMAT_24BPP;
+		hw->visualid		= VISUALID_16M;
+		hw->width		*= 3;
+		hw->pitch		= hw->width >> 3;
+		hw->palette_ctrl	= 0x14;
 		break;
 #endif
 	default:
diff -u --recursive --new-file v2.3.51/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- v2.3.51/linux/drivers/video/fbcon.c	Fri Mar 10 16:40:46 2000
+++ linux/drivers/video/fbcon.c	Mon Mar 13 10:21:50 2000
@@ -111,10 +111,6 @@
 #define LOGO_W			80
 #define LOGO_LINE	(LOGO_W/8)
 
-static int first_fb_vc = 0;
-static int last_fb_vc = MAX_NR_CONSOLES-1;
-static int fbcon_is_default = 1;
-
 struct display fb_display[MAX_NR_CONSOLES];
 char con2fb_map[MAX_NR_CONSOLES];
 static int logo_lines;
@@ -132,8 +128,6 @@
 #define FNTSUM(fd)	(((int *)(fd))[-4])
 #define FONT_EXTRA_WORDS 4
 
-static char fontname[40] __initdata = { 0 };     /* default font name */
-
 #define CM_SOFTBACK	(8)
 
 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * conp->vc_size_row)
@@ -203,8 +197,7 @@
  *  Internal routines
  */
 
-static int __init fbcon_setup(char *options);
-static void fbcon_set_disp(int con, int init, int logo);
+static void fbcon_setup(int con, int init, int logo);
 static __inline__ int real_y(struct display *p, int ypos);
 static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
 static __inline__ void updatescrollmode(struct display *p);
@@ -300,8 +293,6 @@
        newfb = registered_fb[newidx];
        if (newfb->fbops->fb_open(newfb,0))
            return;
-       newfb->count++;
-       oldfb->count--;		
        oldfb->fbops->fb_release(oldfb,0);
        conp = fb_display[unit].conp;
        fontdata = fb_display[unit].fontdata;
@@ -332,57 +323,6 @@
    }
 }
 
-static int __init fbcon_setup(char *options)
-{
-    int i, j;
-
-    if (!options || !*options)
-            return 0;
-
-    if (!strncmp(options, "font:", 5))
-        strcpy(fontname, options+5);
-
-    if (!strncmp(options, "scrollback:", 11)) {
-            options += 11;
-            if (*options) {
-                fbcon_softback_size = simple_strtoul(options, &options, 0);
-                if (*options == 'k' || *options == 'K') {
-                        fbcon_softback_size *= 1024;
-                        options++;
-                }
-                if (*options != ',')
-                        return 0;
-                options++;
-            } else
-                return 0;
-    }
-
-    if (!strncmp(options, "map:", 4)) {
-            options += 4;
-            if (*options)
-                    for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
-                            if (!options[j])
-                                    j = 0;
-                            con2fb_map[i] = (options[j++]-'0') % FB_MAX;
-                    }
-            return 0;
-    }
-
-    if (!strncmp(options, "vc:", 3)) {
-            options += 3;
-            if (*options)
-                first_fb_vc = simple_strtoul(options, &options, 10) - 1;
-            if (first_fb_vc < 0)
-                first_fb_vc = 0;
-            if (*options++ == '-')
-                last_fb_vc = simple_strtoul(options, &options, 10) - 1;
-            fbcon_is_default = 0;
-    }
-    return 0;
-}
-
-__setup("fbcon=", fbcon_setup);
-
 /*
  *  Low Level Operations
  */
@@ -479,23 +419,15 @@
     return display_desc;
 }
 
+
 static void fbcon_init(struct vc_data *conp, int init)
 {
-    int j, unit = conp->vc_num;
+    int unit = conp->vc_num;
     struct fb_info *info;
-	
+
     /* on which frame buffer will we open this console? */
     info = registered_fb[(int)con2fb_map[unit]];
 
-    /*
-     *  We assume initial frame buffer devices can be opened this
-     *  many times
-     */
-    for (j = 0; j < (last_fb_vc - first_fb_vc + 1); j++) {
-        info->fbops->fb_open(info,0);
-	info->count++;	
-    } 	
-
     info->changevar = &fbcon_changevar;
     fb_display[unit] = *(info->disp);	/* copy from default */
     DPRINTK("mode:   %s\n",info->modename);
@@ -511,8 +443,8 @@
     fb_display[unit].cmap.green = 0;
     fb_display[unit].cmap.blue = 0;
     fb_display[unit].cmap.transp = 0;
-    fbcon_set_disp(unit, init, !init);
-    /* Must be done after fbcon_set_disp to prevent excess updates */
+    fbcon_setup(unit, init, !init);
+    /* Must be done after fbcon_setup to prevent excess updates */
     conp->vc_display_fg = &info->display_fg;
     if (!info->display_fg)
         info->display_fg = conp;
@@ -526,7 +458,6 @@
 
     fbcon_free_font(p);
     p->dispsw = &fbcon_dummy;
-    p->fb_info->count = 0;	
     p->conp = 0;
 }
 
@@ -534,7 +465,7 @@
 static int fbcon_changevar(int con)
 {
     if (fb_display[con].conp)
-	    fbcon_set_disp(con, 0, 0);
+	    fbcon_setup(con, 0, 0);
     return 0;
 }
 
@@ -573,7 +504,7 @@
 
 #define fontwidthvalid(p,w) ((p)->dispsw->fontwidthmask & FONTWIDTH(w))
 
-static void fbcon_set_disp(int con, int init, int logo)
+static void fbcon_setup(int con, int init, int logo)
 {
     struct display *p = &fb_display[con];
     struct vc_data *conp = p->conp;
@@ -637,8 +568,9 @@
     }
 
     if (!p->fontdata) {
-        if (!fontname[0] || !(font = fbcon_find_font(fontname)))
-	    font = fbcon_get_default_font(p->var.xres, p->var.yres);
+        if (!p->fb_info->fontname[0] ||
+	    !(font = fbcon_find_font(p->fb_info->fontname)))
+	        font = fbcon_get_default_font(p->var.xres, p->var.yres);
         p->_fontwidth = font->width;
         p->_fontheight = font->height;
         p->fontdata = font->data;
@@ -654,7 +586,7 @@
 #endif
 	{
 	    /* ++Geert: changed from panic() to `correct and continue' */
-	    printk(KERN_ERR "fbcon_set_disp: No support for fontwidth %d\n", fontwidth(p));
+	    printk(KERN_ERR "fbcon_setup: No support for fontwidth %d\n", fontwidth(p));
 	    p->dispsw = &fbcon_dummy;
 	}
     }
@@ -737,7 +669,7 @@
     }
 
     if (p->dispsw == &fbcon_dummy)
-	printk(KERN_WARNING "fbcon_set_disp: type %d (aux %d, depth %d) not "
+	printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not "
 	       "supported\n", p->type, p->type_aux, p->var.bits_per_pixel);
     p->dispsw->setup(p);
 
@@ -2462,13 +2394,6 @@
     con_getxy:		fbcon_getxy,
 };
 
-void __init fbconsole_init(void)
-{
-	if (!num_registered_fb || (first_fb_vc > last_fb_vc))
-                return;
-
-	take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
-}
 
 /*
  *  Dummy Low Level Operations
diff -u --recursive --new-file v2.3.51/linux/drivers/video/fbmem.c linux/drivers/video/fbmem.c
--- v2.3.51/linux/drivers/video/fbmem.c	Fri Mar 10 16:40:46 2000
+++ linux/drivers/video/fbmem.c	Tue Mar 14 18:05:12 2000
@@ -239,8 +239,14 @@
 static initcall_t pref_init_funcs[FB_MAX];
 static int num_pref_init_funcs __initdata = 0;
 
+
 struct fb_info *registered_fb[FB_MAX];
 int num_registered_fb = 0;
+extern int fbcon_softback_size; 
+
+static int first_fb_vc = 0;
+static int last_fb_vc = MAX_NR_CONSOLES-1;
+static int fbcon_is_default = 1;
 
 static int fbmem_read_proc(char *buf, char **start, off_t offset,
 			   int len, int *eof, void *private)
@@ -418,10 +424,13 @@
 	int fbidx = GET_FB_IDX(file->f_dentry->d_inode->i_rdev);
 	struct fb_info *info = registered_fb[fbidx];
 	struct fb_ops *fb = info->fbops;
+	unsigned long off;
+#if !defined(__sparc__) || defined(__sparc_v9__)
 	struct fb_fix_screeninfo fix;
 	struct fb_var_screeninfo var;
-	unsigned long start, off;
+	unsigned long start;
 	u32 len;
+#endif
 
 	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
 		return -EINVAL;
@@ -520,7 +529,7 @@
 #endif /* !sparc32 */
 }
 
-#if 1 /* to go away in 2.4.0 */
+#if 1 /* to go away in 2.5.0 */
 int GET_FB_IDX(kdev_t rdev)
 {
     int fbidx = MINOR(rdev);
@@ -550,8 +559,6 @@
 #endif /* CONFIG_KMOD */
 	if (!(info = registered_fb[fbidx]))
 		return -ENODEV;
-	if (info->flags & FBINFO_FLAG_OPEN) return -EBUSY; 
-	info->flags |= FBINFO_FLAG_OPEN;		
 	return info->fbops->fb_open(info,1);
 }
 
@@ -562,7 +569,6 @@
 	struct fb_info *info = registered_fb[fbidx];
 
 	info->fbops->fb_release(info,1);
-        info->flags &= ~FBINFO_FLAG_OPEN;
 	return 0;
 }
 
@@ -580,8 +586,10 @@
 int
 register_framebuffer(struct fb_info *fb_info)
 {
+	int i, j;
 	char name_buf[8];
- 	int i;
+	static int fb_ever_opened[FB_MAX];
+	static int first = 1;
 
 	if (num_registered_fb == FB_MAX)
 		return -ENXIO;
@@ -590,9 +598,22 @@
 		if (!registered_fb[i])
 			break;
 	fb_info->node = MKDEV(FB_MAJOR, i);
-	fb_info->flags &= ~FBINFO_FLAG_OPEN;
-	fb_info->count = 0;
 	registered_fb[i] = fb_info;
+	if (!fb_ever_opened[i]) {
+		/*
+		 *  We assume initial frame buffer devices can be opened this
+		 *  many times
+		 */
+		for (j = 0; j < MAX_NR_CONSOLES; j++)
+			if (con2fb_map[j] == i)
+				fb_info->fbops->fb_open(fb_info,0);
+		fb_ever_opened[i] = 1;
+	}
+
+	if (first) {
+		first = 0;
+		take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
+	}
 	sprintf (name_buf, "%d", i);
 	fb_info->devfs_handle =
 	    devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE,
@@ -605,12 +626,12 @@
 int
 unregister_framebuffer(struct fb_info *fb_info)
 {
-	int i;
+	int i, j;
 
 	i = GET_FB_IDX(fb_info->node);
-	
-	if (fb_info->count || (fb_info->flags & FBINFO_FLAG_OPEN))
-		return -EBUSY;
+	for (j = 0; j < MAX_NR_CONSOLES; j++)
+		if (con2fb_map[j] == i)
+			return -EBUSY;
 	if (!registered_fb[i])
 		return -EINVAL;
 	devfs_unregister (fb_info->devfs_handle);
@@ -654,6 +675,43 @@
 
     if (!options || !*options)
 	    return 0;
+	    
+    if (!strncmp(options, "scrollback:", 11)) {
+	    options += 11;
+	    if (*options) {
+		fbcon_softback_size = simple_strtoul(options, &options, 0);
+		if (*options == 'k' || *options == 'K') {
+			fbcon_softback_size *= 1024;
+			options++;
+		}
+		if (*options != ',')
+			return 0;
+		options++;
+	    } else
+	        return 0;
+    }
+
+    if (!strncmp(options, "map:", 4)) {
+	    options += 4;
+	    if (*options)
+		    for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
+			    if (!options[j])
+				    j = 0;
+			    con2fb_map[i] = (options[j++]-'0') % FB_MAX;
+		    }
+	    return 0;
+    }
+    
+    if (!strncmp(options, "vc:", 3)) {
+	    options += 3;
+	    if (*options)
+		first_fb_vc = simple_strtoul(options, &options, 10) - 1;
+	    if (first_fb_vc < 0)
+		first_fb_vc = 0;
+	    if (*options++ == '-')
+		last_fb_vc = simple_strtoul(options, &options, 10) - 1;
+	    fbcon_is_default = 0;
+    }
 
     if (num_pref_init_funcs == FB_MAX)
 	    return 0;
@@ -695,6 +753,6 @@
 EXPORT_SYMBOL(unregister_framebuffer);
 EXPORT_SYMBOL(registered_fb);
 EXPORT_SYMBOL(num_registered_fb);
-#if 1 /* to go away in 2.4.0 */
+#if 1 /* to go away in 2.5.0 */
 EXPORT_SYMBOL(GET_FB_IDX);
 #endif
diff -u --recursive --new-file v2.3.51/linux/drivers/video/hgafb.c linux/drivers/video/hgafb.c
--- v2.3.51/linux/drivers/video/hgafb.c	Tue Mar  7 14:32:26 2000
+++ linux/drivers/video/hgafb.c	Mon Mar 13 13:55:14 2000
@@ -7,6 +7,8 @@
  *
  * History:
  *
+ * - Revision 0.1.5 (13 Mar 2000): spinlocks instead of saveflags();cli();etc
+ *                                 minor fixes
  * - Revision 0.1.4 (24 Jan 2000): fixed a bug in hga_card_detect() for 
  *                                  HGA-only systems
  * - Revision 0.1.3 (22 Jan 2000): modified for the new fb_info structure
@@ -27,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
+#include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/tty.h>
@@ -53,7 +56,7 @@
 #define DPRINTK(args...)
 #endif
 
-#if 1
+#if 0
 #define CHKINFO(ret) if (info != &fb_info) { printk(KERN_DEBUG __FILE__": This should never happen, line:%d \n", __LINE__); return ret; }
 #else
 #define CHKINFO(ret)
@@ -97,6 +100,10 @@
 #define HGA_GFX_MODE_EN		0x01
 #define HGA_GFX_PAGE_EN		0x02
 
+/* Global locks */
+
+spinlock_t hga_reg_lock = SPIN_LOCK_UNLOCKED;
+
 /* Framebuffer driver structures */
 
 static struct fb_var_screeninfo hga_default_var = {
@@ -158,55 +165,46 @@
 
 static void write_hga_b(unsigned int val, unsigned char reg)
 {
-	unsigned long flags;
-
-	save_flags(flags); cli();
-
 	outb_p(reg, HGA_INDEX_PORT); 
 	outb_p(val, HGA_VALUE_PORT);
-
-	restore_flags(flags);
 }
 
 static void write_hga_w(unsigned int val, unsigned char reg)
 {
-	unsigned long flags;
-
-	save_flags(flags); cli();
-
 	outb_p(reg,   HGA_INDEX_PORT); outb_p(val >> 8,   HGA_VALUE_PORT);
 	outb_p(reg+1, HGA_INDEX_PORT); outb_p(val & 0xff, HGA_VALUE_PORT);
-
-	restore_flags(flags);
 }
 
 static int test_hga_b(unsigned char val, unsigned char reg)
 {
-	unsigned long flags;
-
-	save_flags(flags); cli();
-
 	outb_p(reg, HGA_INDEX_PORT); 
 	outb  (val, HGA_VALUE_PORT);
-
 	udelay(20); val = (inb_p(HGA_VALUE_PORT) == val);
-
-	restore_flags(flags);
-
 	return val;
 }
 
 static void hga_clear_screen(void)
 {
+	unsigned char fillchar = 0xbf; /* magic */
+	unsigned long flags;
+
+	spin_lock_irqsave(&hga_reg_lock, flags);
 	if (hga_mode == HGA_TXT)
-		memset((char *)hga_vram_base, ' ', hga_vram_len);
+		fillchar = ' ';
 	else if (hga_mode == HGA_GFX)
-		memset((char *)hga_vram_base, 0, hga_vram_len);
+		fillchar = 0x00;
+	spin_unlock_irqrestore(&hga_reg_lock, flags);
+	if (fillchar != 0xbf)
+		memset((char *)hga_vram_base, fillchar, hga_vram_len);
 }
 
 
+#ifdef MODULE
 static void hga_txt_mode(void)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&hga_reg_lock, flags);
 	outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_BLINK_EN, HGA_MODE_PORT);
 	outb_p(0x00, HGA_GFX_PORT);
 	outb_p(0x00, HGA_STATUS_PORT);
@@ -230,10 +228,15 @@
 	write_hga_w(0x0000, 0x0e);	/* cursor location */
 
 	hga_mode = HGA_TXT;
+	spin_unlock_irqrestore(&hga_reg_lock, flags);
 }
+#endif /* MODULE */
 
 static void hga_gfx_mode(void)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&hga_reg_lock, flags);
 	outb_p(0x00, HGA_STATUS_PORT);
 	outb_p(HGA_GFX_MODE_EN, HGA_GFX_PORT);
 	outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
@@ -257,6 +260,7 @@
 	write_hga_w(0x0000, 0x0e);	/* cursor location */
 
 	hga_mode = HGA_GFX;
+	spin_unlock_irqrestore(&hga_reg_lock, flags);
 }
 
 #ifdef MODULE
@@ -274,12 +278,29 @@
 static void hga_pan(unsigned int xoffset, unsigned int yoffset)
 {
 	unsigned int base;
+	unsigned long flags;
+	
 	base = (yoffset / 8) * 90 + xoffset;
+	spin_lock_irqsave(&hga_reg_lock, flags);
 	write_hga_w(base, 0x0c);	/* start address */
+	spin_unlock_irqrestore(&hga_reg_lock, flags);
 	DPRINTK("hga_pan: base:%d\n", base);
 }
 
-static int hga_card_detect(void)
+static void hga_blank(int blank_mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hga_reg_lock, flags);
+	if (blank_mode) {
+		outb_p(0x00, HGA_MODE_PORT);	/* disable video */
+	} else {
+		outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
+	}
+	spin_unlock_irqrestore(&hga_reg_lock, flags);
+}
+
+static int __init hga_card_detect(void)
 {
 	int count=0;
 	u16 *p, p_save;
@@ -288,11 +309,6 @@
 	hga_vram_base = VGA_MAP_MEM(0xb0000);
 	hga_vram_len  = 0x08000;
 
-	if (!request_mem_region(hga_vram_base, hga_vram_len, "hgafb")) {
-		printk(KERN_ERR "hgafb: cannot reserve video memory at 0x%lX\n",
-				hga_vram_base);
-		return 0;
-	}
 	if (request_region(0x3b0, 12, "hgafb"))
 		release_io_ports = 1;
 	if (request_region(0x3bf, 1, "hgafb"))
@@ -598,11 +614,7 @@
 	CHKINFO( );
 	DPRINTK("hga_blank: blank_mode:%d, info:%x, fb_info:%x\n", blank_mode, (unsigned)info, (unsigned)&fb_info);
 
-	if (blank_mode) {
-		outb_p(0x00, HGA_MODE_PORT);	/* disable video */
-	} else {
-		outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
-	}
+	hga_blank(blank_mode);
 }
 
 
@@ -642,7 +654,12 @@
 	disp.line_length = hga_fix.line_length;
 	disp.can_soft_blank = 1;
 	disp.inverse = 0;
+#ifdef FBCON_HAS_HGA
 	disp.dispsw = &fbcon_hga;
+#else
+#warning HGAFB will not work as a console!
+	disp.dispsw = &fbcon_dummy;
+#endif
 	disp.dispsw_data = NULL;
 
 	disp.scrollmode = SCROLL_YREDRAW;
@@ -723,7 +740,6 @@
 	unregister_framebuffer(info);
 	if (release_io_ports) release_region(0x3b0, 12);
 	if (release_io_port) release_region(0x3bf, 1);
-	release_mem_region(hga_vram_base, hga_vram_len);
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.3.51/linux/fs/Config.in linux/fs/Config.in
--- v2.3.51/linux/fs/Config.in	Sat Feb 26 22:31:51 2000
+++ linux/fs/Config.in	Sun Mar 12 19:39:39 2000
@@ -9,7 +9,10 @@
 tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
 
 
-dep_tristate 'ADFS file system support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
+dep_tristate 'ADFS file system support' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_ADFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   bool '  ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW
+fi
 
 tristate 'Amiga FFS file system support' CONFIG_AFFS_FS
 
diff -u --recursive --new-file v2.3.51/linux/fs/adfs/dir.c linux/fs/adfs/dir.c
--- v2.3.51/linux/fs/adfs/dir.c	Tue Mar  7 14:32:26 2000
+++ linux/fs/adfs/dir.c	Mon Mar 13 13:55:09 2000
@@ -1,10 +1,11 @@
 /*
  * linux/fs/adfs/dir.c
  *
- * Copyright (C) 1999 Russell King
+ * Copyright (C) 1999-2000 Russell King
  *
  * Common directory handling for ADFS
  */
+#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -33,13 +34,16 @@
 	struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
 	struct object_info obj;
 	struct adfs_dir dir;
-	int ret;
+	int ret = 0;
+
+	if (filp->f_pos >> 32)
+		goto out;
 
 	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
 	if (ret)
 		goto out;
 
-	switch (filp->f_pos) {
+	switch ((unsigned long)filp->f_pos) {
 	case 0:
 		if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
 			goto free_out;
@@ -79,13 +83,14 @@
 int
 adfs_dir_update(struct super_block *sb, struct object_info *obj)
 {
+	int ret = -EINVAL;
+#ifdef CONFIG_ADFS_FS_RW
 	struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
 	struct adfs_dir dir;
-	int ret = -EINVAL;
 
 	printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
 		 obj->file_id, obj->parent_id);
-#if 0
+
 	if (!ops->update) {
 		ret = -EINVAL;
 		goto out;
diff -u --recursive --new-file v2.3.51/linux/fs/affs/super.c linux/fs/affs/super.c
--- v2.3.51/linux/fs/affs/super.c	Fri Mar 10 16:40:46 2000
+++ linux/fs/affs/super.c	Mon Mar 13 12:35:39 2000
@@ -672,24 +672,17 @@
 
 static DECLARE_FSTYPE_DEV(affs_fs_type, "affs", affs_read_super);
 
-int __init init_affs_fs(void)
+static int __init init_affs_fs(void)
 {
 	return register_filesystem(&affs_fs_type);
 }
 
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int
-init_module(void)
-{
-	return register_filesystem(&affs_fs_type);
-}
-
-void
-cleanup_module(void)
+static void __exit exit_affs_fs(void)
 {
 	unregister_filesystem(&affs_fs_type);
 }
 
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_affs_fs)
+module_exit(exit_affs_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/bfs/inode.c linux/fs/bfs/inode.c
--- v2.3.51/linux/fs/bfs/inode.c	Fri Mar 10 16:40:46 2000
+++ linux/fs/bfs/inode.c	Mon Mar 13 12:35:39 2000
@@ -322,16 +322,15 @@
 
 static DECLARE_FSTYPE_DEV( bfs_fs_type, "bfs", bfs_read_super);
 
-#ifdef MODULE
-#define init_bfs_fs init_module
-
-void cleanup_module(void)
+static int __init init_bfs_fs(void)
 {
-	unregister_filesystem(&bfs_fs_type);
+	return register_filesystem(&bfs_fs_type);
 }
-#endif
 
-int __init init_bfs_fs(void)
+static void __exit exit_bfs_fs(void)
 {
-	return register_filesystem(&bfs_fs_type);
+	unregister_filesystem(&bfs_fs_type);
 }
+
+module_init(init_bfs_fs)
+module_exit(exit_bfs_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.3.51/linux/fs/binfmt_aout.c	Tue Nov 23 22:42:21 1999
+++ linux/fs/binfmt_aout.c	Mon Mar 13 12:35:39 2000
@@ -78,8 +78,7 @@
  * dumping of the process results in another error..
  */
 
-static inline int
-do_aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
+static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
 {
 	mm_segment_t fs;
 	int has_dumped = 0;
@@ -183,17 +182,6 @@
 	return has_dumped;
 }
 
-static int
-aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_aout_core_dump(signr, regs, file);
-	MOD_DEC_USE_COUNT;
-	return retval;
-}
-
 /*
  * create_aout_tables() parses the env- and arg-strings in new user
  * memory and creates the pointer tables from them, and puts their
@@ -261,7 +249,7 @@
  * libraries.  There is no binary dependent code anywhere else.
  */
 
-static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
 	struct exec ex;
 	struct file * file;
@@ -398,14 +386,11 @@
 		}
 	}
 beyond_if:
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_DEC_USE_COUNT(current->exec_domain->module);
+	put_exec_domain(current->exec_domain);
 	if (current->binfmt && current->binfmt->module)
 		__MOD_DEC_USE_COUNT(current->binfmt->module);
 	current->exec_domain = lookup_exec_domain(current->personality);
 	current->binfmt = &aout_format;
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_INC_USE_COUNT(current->exec_domain->module);
 	if (current->binfmt && current->binfmt->module)
 		__MOD_INC_USE_COUNT(current->binfmt->module);
 
@@ -429,20 +414,7 @@
 	return 0;
 }
 
-
-static int
-load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_load_aout_binary(bprm, regs);
-	MOD_DEC_USE_COUNT;
-	return retval;
-}
-
-static inline int
-do_load_aout_library(int fd)
+static int load_aout_library(int fd)
 {
         struct file * file;
 	struct inode * inode;
@@ -529,18 +501,6 @@
 	return retval;
 }
 
-static int
-load_aout_library(int fd)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_load_aout_library(fd);
-	MOD_DEC_USE_COUNT;
-	return retval;
-}
-
-
 static int __init init_aout_binfmt(void)
 {
 	return register_binfmt(&aout_format);
@@ -555,4 +515,3 @@
 
 module_init(init_aout_binfmt);
 module_exit(exit_aout_binfmt);
-
diff -u --recursive --new-file v2.3.51/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.3.51/linux/fs/binfmt_elf.c	Tue Mar  7 14:32:26 2000
+++ linux/fs/binfmt_elf.c	Mon Mar 13 22:18:11 2000
@@ -35,8 +35,6 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 
-#include <linux/config.h>
-
 #define DLINFO_ITEMS 13
 
 #include <linux/elf.h>
@@ -383,8 +381,7 @@
 #define INTERPRETER_ELF 2
 
 
-static inline int
-do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
 	struct file * file;
 	struct dentry *interpreter_dentry = NULL; /* to shut gcc up */
@@ -695,14 +692,11 @@
 	if (interpreter_type != INTERPRETER_AOUT)
 		sys_close(elf_exec_fileno);
 
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_DEC_USE_COUNT(current->exec_domain->module);
+	put_exec_domain(current->exec_domain);
 	if (current->binfmt && current->binfmt->module)
 		__MOD_DEC_USE_COUNT(current->binfmt->module);
 	current->exec_domain = lookup_exec_domain(current->personality);
 	current->binfmt = &elf_format;
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_INC_USE_COUNT(current->exec_domain->module);
 	if (current->binfmt && current->binfmt->module)
 		__MOD_INC_USE_COUNT(current->binfmt->module);
 
@@ -792,22 +786,10 @@
 	goto out;
 }
 
-static int
-load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_load_elf_binary(bprm, regs);
-	MOD_DEC_USE_COUNT;
-	return retval;
-}
-
 /* This is really simpleminded and specialized - we are loading an
    a.out library that is given an ELF header. */
 
-static inline int
-do_load_elf_library(int fd)
+static int load_elf_library(int fd)
 {
 	struct file * file;
 	struct dentry * dentry;
@@ -899,16 +881,6 @@
 	return error;
 }
 
-static int load_elf_library(int fd)
-{
-	int retval;
-
-	MOD_INC_USE_COUNT;
-	retval = do_load_elf_library(fd);
-	MOD_DEC_USE_COUNT;
-	return retval;
-}
-
 /*
  * Note that some platforms still use traditional core dumps and not
  * the ELF core dump.  Each platform can select it as appropriate.
@@ -1061,10 +1033,6 @@
 	elf_fpregset_t fpu;		/* NT_PRFPREG */
 	struct elf_prpsinfo psinfo;	/* NT_PRPSINFO */
 
-#ifndef CONFIG_BINFMT_ELF
-	MOD_INC_USE_COUNT;
-#endif
-
 	/* Count what's needed to dump, up to the limit of coredump size */
 	segs = 0;
 	size = 0;
@@ -1294,9 +1262,6 @@
 
  end_coredump:
 	set_fs(fs);
-#ifndef CONFIG_BINFMT_ELF
-	MOD_DEC_USE_COUNT;
-#endif
 	return has_dumped;
 }
 #endif		/* USE_ELF_CORE_DUMP */
diff -u --recursive --new-file v2.3.51/linux/fs/binfmt_em86.c linux/fs/binfmt_em86.c
--- v2.3.51/linux/fs/binfmt_em86.c	Tue Mar  7 14:32:26 2000
+++ linux/fs/binfmt_em86.c	Mon Mar 13 12:35:39 2000
@@ -20,7 +20,7 @@
 #define EM86_INTERP	"/usr/bin/em86"
 #define EM86_I_NAME	"em86"
 
-static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
+static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
 {
 	char *interp, *i_name, *i_arg;
 	struct dentry * dentry;
@@ -93,15 +93,6 @@
 		return retval;
 
 	return search_binary_handler(bprm, regs);
-}
-
-static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
-{
-	int retval;
-	MOD_INC_USE_COUNT;
-	retval = do_load_em86(bprm,regs);
-	MOD_DEC_USE_COUNT;
-	return retval;
 }
 
 struct linux_binfmt em86_format = {
diff -u --recursive --new-file v2.3.51/linux/fs/binfmt_misc.c linux/fs/binfmt_misc.c
--- v2.3.51/linux/fs/binfmt_misc.c	Tue Dec  7 09:32:47 1999
+++ linux/fs/binfmt_misc.c	Mon Mar 13 12:35:39 2000
@@ -185,7 +185,6 @@
 	char *iname_addr = iname;
 	int retval;
 
-	MOD_INC_USE_COUNT;
 	retval = -ENOEXEC;
 	if (!enabled)
 		goto _ret;
@@ -224,7 +223,6 @@
 	if (retval >= 0)
 		retval = search_binary_handler(bprm, regs);
 _ret:
-	MOD_DEC_USE_COUNT;
 	return retval;
 }
 
diff -u --recursive --new-file v2.3.51/linux/fs/binfmt_script.c linux/fs/binfmt_script.c
--- v2.3.51/linux/fs/binfmt_script.c	Thu Mar  2 14:36:23 2000
+++ linux/fs/binfmt_script.c	Mon Mar 13 12:35:39 2000
@@ -13,7 +13,7 @@
 #include <linux/init.h>
 #include <linux/smp_lock.h>
 
-static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs)
+static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
 {
 	char *cp, *i_name, *i_arg;
 	struct dentry * dentry;
@@ -92,15 +92,6 @@
 	if (retval < 0)
 		return retval;
 	return search_binary_handler(bprm,regs);
-}
-
-static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
-{
-	int retval;
-	MOD_INC_USE_COUNT;
-	retval = do_load_script(bprm,regs);
-	MOD_DEC_USE_COUNT;
-	return retval;
 }
 
 struct linux_binfmt script_format = {
diff -u --recursive --new-file v2.3.51/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.3.51/linux/fs/coda/psdev.c	Wed Feb 16 17:03:52 2000
+++ linux/fs/coda/psdev.c	Mon Mar 13 12:35:39 2000
@@ -260,7 +260,6 @@
 	}
 	
 	vcp->vc_inuse++;
-	MOD_INC_USE_COUNT;
 
 	if ( file->f_flags == O_RDWR ) {
 		vcp->vc_pid = current->pid;
@@ -290,7 +289,6 @@
 	}
 
 	vcp->vc_inuse--;
-	MOD_DEC_USE_COUNT;
 	CDEBUG(D_PSDEV, "inuse: %d, vc_pid %d, caller %d\n",
 	       vcp->vc_inuse, vcp->vc_pid, current->pid);
 
diff -u --recursive --new-file v2.3.51/linux/fs/efs/super.c linux/fs/efs/super.c
--- v2.3.51/linux/fs/efs/super.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/efs/super.c	Mon Mar 13 12:35:39 2000
@@ -17,26 +17,22 @@
 
 static struct super_operations efs_superblock_operations = {
 	read_inode:	efs_read_inode,
-	put_super:	efs_put_super,
 	statfs:		efs_statfs,
 };
 
-int __init init_efs_fs(void) {
-	return register_filesystem(&efs_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void) {
+static int __init init_efs_fs(void) {
 	printk("EFS: "EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n");
-	return init_efs_fs();
+	return register_filesystem(&efs_fs_type);
 }
 
-void cleanup_module(void) {
+static void __exit exit_efs_fs(void) {
 	unregister_filesystem(&efs_fs_type);
 }
-#endif
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_efs_fs)
+module_exit(exit_efs_fs)
 
 static efs_block_t efs_validate_vh(struct volume_header *vh) {
 	int		i;
@@ -207,9 +203,6 @@
 out_no_fs_ul:
 out_no_fs:
 	return(NULL);
-}
-
-void efs_put_super(struct super_block *s) {
 }
 
 int efs_statfs(struct super_block *s, struct statfs *buf) {
diff -u --recursive --new-file v2.3.51/linux/fs/exec.c linux/fs/exec.c
--- v2.3.51/linux/fs/exec.c	Tue Mar  7 14:32:26 2000
+++ linux/fs/exec.c	Mon Mar 13 12:35:39 2000
@@ -33,6 +33,9 @@
 #include <linux/init.h>
 #include <linux/pagemap.h>
 #include <linux/highmem.h>
+#include <linux/spinlock.h>
+#define __NO_VERSION__
+#include <linux/module.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
@@ -43,6 +46,7 @@
 #endif
 
 static struct linux_binfmt *formats = (struct linux_binfmt *) NULL;
+static spinlock_t binfmt_lock = SPIN_LOCK_UNLOCKED;
 
 int register_binfmt(struct linux_binfmt * fmt)
 {
@@ -52,13 +56,17 @@
 		return -EINVAL;
 	if (fmt->next)
 		return -EBUSY;
+	spin_lock(&binfmt_lock);
 	while (*tmp) {
-		if (fmt == *tmp)
+		if (fmt == *tmp) {
+			spin_unlock(&binfmt_lock);
 			return -EBUSY;
+		}
 		tmp = &(*tmp)->next;
 	}
 	fmt->next = formats;
 	formats = fmt;
+	spin_unlock(&binfmt_lock);
 	return 0;	
 }
 
@@ -66,16 +74,25 @@
 {
 	struct linux_binfmt ** tmp = &formats;
 
+	spin_lock(&binfmt_lock);
 	while (*tmp) {
 		if (fmt == *tmp) {
 			*tmp = fmt->next;
+			spin_unlock(&binfmt_lock);
 			return 0;
 		}
 		tmp = &(*tmp)->next;
 	}
+	spin_unlock(&binfmt_lock);
 	return -EINVAL;
 }
 
+static inline void put_binfmt(struct linux_binfmt * fmt)
+{
+	if (fmt->module)
+		__MOD_DEC_USE_COUNT(fmt->module);
+}
+
 /* N.B. Error returns must be < 0 */
 int open_dentry(struct dentry * dentry, int mode)
 {
@@ -146,15 +163,22 @@
 	file = fget(fd);
 	retval = -ENOEXEC;
 	if (file && file->f_dentry && file->f_op && file->f_op->read) {
+		spin_lock(&binfmt_lock);
 		for (fmt = formats ; fmt ; fmt = fmt->next) {
 			int (*fn)(int) = fmt->load_shlib;
 			if (!fn)
 				continue;
+			if (!try_inc_mod_count(fmt->module))
+				continue;
+			spin_unlock(&binfmt_lock);
 			/* N.B. Should use file instead of fd */
 			retval = fn(fd);
+			spin_lock(&binfmt_lock);
+			put_binfmt(fmt);
 			if (retval != -ENOEXEC)
 				break;
 		}
+		spin_unlock(&binfmt_lock);
 	}
 	fput(file);
 	sys_close(fd);
@@ -767,12 +791,17 @@
 	}
 #endif
 	for (try=0; try<2; try++) {
+		spin_lock(&binfmt_lock);
 		for (fmt = formats ; fmt ; fmt = fmt->next) {
 			int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
 			if (!fn)
 				continue;
+			if (!try_inc_mod_count(fmt->module))
+				continue;
+			spin_unlock(&binfmt_lock);
 			retval = fn(bprm, regs);
 			if (retval >= 0) {
+				put_binfmt(fmt);
 				if (bprm->dentry) {
 					lock_kernel();
 					dput(bprm->dentry);
@@ -782,11 +811,16 @@
 				current->did_exec = 1;
 				return retval;
 			}
+			spin_lock(&binfmt_lock);
+			put_binfmt(fmt);
 			if (retval != -ENOEXEC)
 				break;
-			if (!bprm->dentry) /* We don't have the dentry anymore */
+			if (!bprm->dentry) {
+				spin_unlock(&binfmt_lock);
 				return retval;
+			}
 		}
+		spin_unlock(&binfmt_lock);
 		if (retval != -ENOEXEC) {
 			break;
 #ifdef CONFIG_KMOD
diff -u --recursive --new-file v2.3.51/linux/fs/ext2/dir.c linux/fs/ext2/dir.c
--- v2.3.51/linux/fs/ext2/dir.c	Sat Feb 26 22:31:52 2000
+++ linux/fs/ext2/dir.c	Mon Mar 13 10:23:36 2000
@@ -151,14 +151,14 @@
 				 * not the directory has been modified
 				 * during the copy operation.
 				 */
-				unsigned long version = inode->i_version;
+				unsigned long version = filp->f_version;
 
 				error = filldir(dirent, de->name,
 						de->name_len,
 						filp->f_pos, le32_to_cpu(de->inode));
 				if (error)
 					break;
-				if (version != inode->i_version)
+				if (version != filp->f_version)
 					goto revalidate;
 				stored ++;
 			}
diff -u --recursive --new-file v2.3.51/linux/fs/fat/fatfs_syms.c linux/fs/fat/fatfs_syms.c
--- v2.3.51/linux/fs/fat/fatfs_syms.c	Tue Mar  7 14:32:26 2000
+++ linux/fs/fat/fatfs_syms.c	Mon Mar 13 12:35:39 2000
@@ -7,6 +7,7 @@
 #define ASC_LINUX_VERSION(V, P, S)	(((V) * 65536) + ((P) * 256) + (S))
 #include <linux/version.h>
 #include <linux/module.h>
+#include <linux/init.h>
 
 #include <linux/mm.h>
 #include <linux/msdos_fs.h>
@@ -54,8 +55,10 @@
 EXPORT_SYMBOL(fat_dir_empty);
 EXPORT_SYMBOL(fat_truncate);
 
-int init_fat_fs(void)
+static int __init init_fat_fs(void)
 {
 	fat_hash_init();
 	return 0;
 }
+
+module_init(init_fat_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.3.51/linux/fs/fat/inode.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/fat/inode.c	Mon Mar 13 12:35:39 2000
@@ -920,19 +920,3 @@
 	    ~MSDOS_SB(sb)->options.fs_umask;
 	return 0;
 }
-
-
-#ifdef MODULE
-int init_module(void)
-{
-	return init_fat_fs();
-}
-
-
-void cleanup_module(void)
-{
-	/* Nothing to be done, really! */
-	return;
-}
-#endif
-
diff -u --recursive --new-file v2.3.51/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.3.51/linux/fs/filesystems.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/filesystems.c	Mon Mar 13 12:43:48 2000
@@ -9,30 +9,10 @@
 #include <linux/config.h>
 #include <linux/fs.h>
 
-#include <linux/minix_fs.h>
-#include <linux/ext2_fs.h>
-#include <linux/msdos_fs.h>
-#include <linux/umsdos_fs.h>
-#include <linux/proc_fs.h>
 #include <linux/devfs_fs_kernel.h>
 #include <linux/nfs_fs.h>
-#include <linux/iso_fs.h>
-#include <linux/sysv_fs.h>
-#include <linux/hpfs_fs.h>
-#include <linux/smb_fs.h>
-#include <linux/ncp_fs.h>
-#include <linux/affs_fs.h>
-#include <linux/ufs_fs.h>
-#include <linux/efs_fs.h>
-#include <linux/romfs_fs.h>
 #include <linux/auto_fs.h>
-#include <linux/qnx4_fs.h>
-#include <linux/udf_fs.h>
-#include <linux/ntfs_fs.h>
-#include <linux/hfs_fs.h>
 #include <linux/devpts_fs.h>
-#include <linux/bfs_fs.h>
-#include <linux/openprom_fs.h>
 #include <linux/major.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -42,7 +22,6 @@
 #include <linux/lockd/bind.h>
 #include <linux/lockd/xdr.h>
 #include <linux/init.h>
-#include <linux/nls.h>
 
 #ifdef CONFIG_CODA_FS
 extern int init_coda(void);
@@ -52,40 +31,8 @@
 extern int init_devpts_fs(void);
 #endif
 
-#ifdef CONFIG_SUN_OPENPROMFS
-extern int init_openprom_fs(void);
-#endif
-
 void __init filesystem_setup(void)
 {
-#ifdef CONFIG_MINIX_FS
-	init_minix_fs();
-#endif
-
-#ifdef CONFIG_ROMFS_FS
-	init_romfs_fs();
-#endif
-
-#ifdef CONFIG_UMSDOS_FS
-	init_umsdos_fs();
-#endif
-
-#ifdef CONFIG_FAT_FS
-	init_fat_fs();
-#endif
-
-#ifdef CONFIG_MSDOS_FS
-	init_msdos_fs();
-#endif
-
-#ifdef CONFIG_VFAT_FS
-	init_vfat_fs();
-#endif
-
-#ifdef CONFIG_PROC_FS
-	init_proc_fs();
-#endif
-
 	init_devfs_fs();  /*  Header file may make this empty  */
 
 #ifdef CONFIG_NFS_FS
@@ -96,68 +43,8 @@
 	init_coda();
 #endif
 
-#ifdef CONFIG_SMB_FS
-	init_smb_fs();
-#endif
-
-#ifdef CONFIG_NCP_FS
-	init_ncp_fs();
-#endif
-
-#ifdef CONFIG_ISO9660_FS
-	init_iso9660_fs();
-#endif
-
-#ifdef CONFIG_SYSV_FS
-	init_sysv_fs();
-#endif
-
-#ifdef CONFIG_HPFS_FS
-	init_hpfs_fs();
-#endif
-
-#ifdef CONFIG_NTFS_FS
-	init_ntfs_fs();
-#endif
-
-#ifdef CONFIG_HFS_FS
-	init_hfs_fs();
-#endif
-
-#ifdef CONFIG_AFFS_FS
-	init_affs_fs();
-#endif
-
-#ifdef CONFIG_UFS_FS
-	init_ufs_fs();
-#endif
-
-#ifdef CONFIG_EFS_FS
-	init_efs_fs();
-#endif
-
 #ifdef CONFIG_DEVPTS_FS
 	init_devpts_fs();
-#endif
-
-#ifdef CONFIG_QNX4FS_FS
-	init_qnx4_fs();
-#endif
-
-#ifdef CONFIG_UDF_FS
-	init_udf_fs();
-#endif
-
-#ifdef CONFIG_BFS_FS
-	init_bfs_fs();
-#endif
-
-#ifdef CONFIG_SUN_OPENPROMFS
-	init_openprom_fs();
-#endif
-   
-#ifdef CONFIG_NLS
-	init_nls();
 #endif
 }
 
diff -u --recursive --new-file v2.3.51/linux/fs/hfs/super.c linux/fs/hfs/super.c
--- v2.3.51/linux/fs/hfs/super.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/hfs/super.c	Mon Mar 13 12:35:39 2000
@@ -472,38 +472,19 @@
 	return NULL;	
 }
 
-int __init init_hfs_fs(void)
+static int __init init_hfs_fs(void)
 {
         hfs_cat_init();
 	return register_filesystem(&hfs_fs);
 }
 
-#ifdef MODULE
-int init_module(void) {
-	int error;
-
-#if defined(DEBUG_SIZES) || defined(DEBUG_ALL)
-	hfs_warn("HFS inode: %d bytes available\n",
-		 sizeof(struct ext2_inode_info)-sizeof(struct hfs_inode_info));
-	hfs_warn("HFS super_block: %d bytes available\n",
-		 sizeof(struct ext2_sb_info)-sizeof(struct hfs_sb_info));
-	if ((sizeof(struct hfs_inode_info)>sizeof(struct ext2_inode_info)) ||
-	    (sizeof(struct hfs_sb_info)>sizeof(struct ext2_sb_info))) {
-		return -ENOMEM; /* well sort of */
-	}
-#endif
-	error = init_hfs_fs();
-	if (!error) {
-		/* register_symtab(NULL); */
-	}
-	return error;
-}
-
-void cleanup_module(void) {
+static void __exit exit_hfs_fs(void) {
 	hfs_cat_free();
 	unregister_filesystem(&hfs_fs);
 }
-#endif
+
+module_init(init_hfs_fs)
+module_exit(exit_hfs_fs)
 
 #if defined(DEBUG_ALL) || defined(DEBUG_MEM)
 long int hfs_alloc = 0;
diff -u --recursive --new-file v2.3.51/linux/fs/hpfs/buffer.c linux/fs/hpfs/buffer.c
--- v2.3.51/linux/fs/hpfs/buffer.c	Tue Jun  1 23:25:47 1999
+++ linux/fs/hpfs/buffer.c	Mon Mar 13 12:27:51 2000
@@ -125,7 +125,8 @@
 	kdev_t dev = s->s_dev;
 	struct buffer_head *bh;
 
-	if (!ahead || secno + ahead >= s->s_hpfs_fs_size)
+					/* vvvv - workaround for the breada bug */
+	if (!ahead || secno + ahead + (read_ahead[MAJOR(dev)] >> 9) >= s->s_hpfs_fs_size)
 		*bhp = bh = bread(dev, secno, 512);
 	else *bhp = bh = breada(dev, secno, 512, 0, (ahead + 1) << 9);
 	if (bh != NULL)
@@ -144,6 +145,7 @@
 	/*return hpfs_map_sector(s, secno, bhp, 0);*/
 
 	if ((*bhp = bh = getblk(s->s_dev, secno, 512)) != NULL) {
+		if (!buffer_uptodate(bh)) wait_on_buffer(bh);
 		mark_buffer_uptodate(bh, 1);
 		return bh->b_data;
 	} else {
@@ -172,8 +174,9 @@
 		goto bail;
 	}
 
-	if (!ahead || secno + 4 + ahead > s->s_hpfs_fs_size)
-		qbh->bh[0] = bh = breada(dev, secno, 512, 0, 2048);
+					/* vvvv - workaround for the breada bug */
+	if (!ahead || secno + 4 + ahead + (read_ahead[MAJOR(dev)] >> 9) > s->s_hpfs_fs_size)
+		qbh->bh[0] = bh = bread(dev, secno, 512);
 	else qbh->bh[0] = bh = breada(dev, secno, 512, 0, (ahead + 4) << 9);
 	if (!bh)
 		goto bail0;
diff -u --recursive --new-file v2.3.51/linux/fs/hpfs/hpfs_fn.h linux/fs/hpfs/hpfs_fn.h
--- v2.3.51/linux/fs/hpfs/hpfs_fn.h	Fri Mar 10 16:40:47 2000
+++ linux/fs/hpfs/hpfs_fn.h	Mon Mar 13 12:35:39 2000
@@ -312,6 +312,5 @@
 void hpfs_put_super(struct super_block *);
 unsigned hpfs_count_one_bitmap(struct super_block *, secno);
 int hpfs_statfs(struct super_block *, struct statfs *);
-struct super_block *hpfs_read_super(struct super_block *, void *, int);
 
 extern struct address_space_operations hpfs_aops;
diff -u --recursive --new-file v2.3.51/linux/fs/hpfs/inode.c linux/fs/hpfs/inode.c
--- v2.3.51/linux/fs/hpfs/inode.c	Sat Feb 26 22:31:53 2000
+++ linux/fs/hpfs/inode.c	Mon Mar 13 12:27:51 2000
@@ -314,4 +314,5 @@
 void hpfs_delete_inode(struct inode *inode)
 {
 	hpfs_remove_fnode(inode->i_sb, inode->i_ino);
+	clear_inode(inode);
 }
diff -u --recursive --new-file v2.3.51/linux/fs/hpfs/super.c linux/fs/hpfs/super.c
--- v2.3.51/linux/fs/hpfs/super.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/hpfs/super.c	Mon Mar 13 12:35:39 2000
@@ -9,6 +9,7 @@
 #include <linux/string.h>
 #include "hpfs_fn.h"
 #include <linux/module.h>
+#include <linux/init.h>
 
 /* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
 
@@ -557,26 +558,17 @@
 
 DECLARE_FSTYPE_DEV(hpfs_fs_type, "hpfs", hpfs_read_super);
 
-int init_hpfs_fs(void)
+static int __init init_hpfs_fs(void)
 {
 	return register_filesystem(&hpfs_fs_type);
 }
 
-#ifdef MODULE
-
-/*int register_symtab_from(struct symbol_table *, long *);*/
-
-int init_module(void)
-{
-	/*int status;
-	if (!(status = init_hpfs_fs())) register_symtab(NULL);
-	return status;*/
-	return init_hpfs_fs();
-}
-
-void cleanup_module(void)
+static void __exit exit_hpfs_fs(void)
 {
 	unregister_filesystem(&hpfs_fs_type);
 }
 
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_hpfs_fs)
+module_exit(exit_hpfs_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.3.51/linux/fs/isofs/inode.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/isofs/inode.c	Mon Mar 13 12:35:39 2000
@@ -994,16 +994,20 @@
 	bmap: _isofs_bmap
 };
 
-static void test_and_set_uid(uid_t *p, uid_t value)
+static inline void test_and_set_uid(uid_t *p, uid_t value)
 {
 	if(value) {
 		*p = value;
-#if 0
-		printk("Resetting to %d\n", value);
-#endif
 	}
 }
 
+static inline void test_and_set_gid(gid_t *p, gid_t value)
+{
+        if(value) {
+                *p = value;
+        }
+}
+
 static int isofs_read_level3_size(struct inode * inode)
 {
 	unsigned long ino = inode->i_ino;
@@ -1207,6 +1211,7 @@
 	  parse_rock_ridge_inode(raw_inode, inode);
 	  /* hmm..if we want uid or gid set, override the rock ridge setting */
 	 test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid);
+         test_and_set_gid(&inode->i_gid, inode->i_sb->u.isofs_sb.s_gid);
 	}
 
 #ifdef DEBUG
@@ -1431,22 +1436,17 @@
 
 static DECLARE_FSTYPE_DEV(iso9660_fs_type, "iso9660", isofs_read_super);
 
-int __init init_iso9660_fs(void)
+static int __init init_iso9660_fs(void)
 {
         return register_filesystem(&iso9660_fs_type);
 }
 
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
+static void __exit exit_iso9660_fs(void)
 {
-	return init_iso9660_fs();
+        unregister_filesystem(&iso9660_fs_type);
 }
 
-void cleanup_module(void)
-{
-	unregister_filesystem(&iso9660_fs_type);
-}
+EXPORT_NO_SYMBOLS;
 
-#endif
+module_init(init_iso9660_fs)
+module_exit(exit_iso9660_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/minix/inode.c linux/fs/minix/inode.c
--- v2.3.51/linux/fs/minix/inode.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/minix/inode.c	Mon Mar 13 12:35:39 2000
@@ -1267,22 +1267,17 @@
 
 static DECLARE_FSTYPE_DEV(minix_fs_type,"minix",minix_read_super);
 
-int __init init_minix_fs(void)
+static int __init init_minix_fs(void)
 {
         return register_filesystem(&minix_fs_type);
 }
 
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
+static void __exit exit_minix_fs(void)
 {
-	return init_minix_fs();
+        unregister_filesystem(&minix_fs_type);
 }
 
-void cleanup_module(void)
-{
-	unregister_filesystem(&minix_fs_type);
-}
+EXPORT_NO_SYMBOLS;
 
-#endif
+module_init(init_minix_fs)
+module_exit(exit_minix_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/msdos/msdosfs_syms.c linux/fs/msdos/msdosfs_syms.c
--- v2.3.51/linux/fs/msdos/msdosfs_syms.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/msdos/msdosfs_syms.c	Mon Mar 13 12:35:39 2000
@@ -26,10 +26,17 @@
 EXPORT_SYMBOL(msdos_read_super);
 EXPORT_SYMBOL(msdos_put_super);
 
+static DECLARE_FSTYPE_DEV(msdos_fs_type, "msdos", msdos_read_super);
 
-DECLARE_FSTYPE_DEV(msdos_fs_type, "msdos", msdos_read_super);
-
-int __init init_msdos_fs(void)
+static int __init init_msdos_fs(void)
 {
 	return register_filesystem(&msdos_fs_type);
 }
+
+static void __exit exit_msdos_fs(void)
+{
+	unregister_filesystem(&msdos_fs_type);
+}
+
+module_init(init_msdos_fs)
+module_exit(exit_msdos_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.3.51/linux/fs/msdos/namei.c	Fri Mar 10 16:40:47 2000
+++ linux/fs/msdos/namei.c	Mon Mar 13 12:35:39 2000
@@ -599,28 +599,7 @@
 
 	MSDOS_SB(sb)->options.isvfat = 0;
 	res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
-	if (res == NULL)
-		goto out_fail;
-	sb->s_root->d_op = &msdos_dentry_operations;
+	if (res)
+		sb->s_root->d_op = &msdos_dentry_operations;
 	return res;
-
-out_fail:
-	return NULL;
 }
-
-
-
-#ifdef MODULE
-int init_module(void)
-{
-	return init_msdos_fs();
-}
-
-
-void cleanup_module(void)
-{
-	unregister_filesystem(&msdos_fs_type);
-}
-
-#endif
-
diff -u --recursive --new-file v2.3.51/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.3.51/linux/fs/ncpfs/inode.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/ncpfs/inode.c	Tue Mar 14 09:45:09 2000
@@ -698,15 +698,7 @@
 
 static DECLARE_FSTYPE(ncp_fs_type, "ncpfs", ncp_read_super, 0);
 
-int __init init_ncp_fs(void)
-{
-	return register_filesystem(&ncp_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
+static int __init init_ncp_fs(void)
 {
 	DPRINTK("ncpfs: init_module called\n");
 
@@ -714,10 +706,10 @@
 	ncp_malloced = 0;
 	ncp_current_malloced = 0;
 #endif
-	return init_ncp_fs();
+	return register_filesystem(&ncp_fs_type);
 }
 
-void cleanup_module(void)
+static void __exit exit_ncp_fs(void)
 {
 	DPRINTK("ncpfs: cleanup_module called\n");
 	unregister_filesystem(&ncp_fs_type);
@@ -727,4 +719,7 @@
 #endif
 }
 
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_ncp_fs)
+module_exit(exit_ncp_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/nfs/read.c linux/fs/nfs/read.c
--- v2.3.51/linux/fs/nfs/read.c	Tue Dec  7 09:32:48 1999
+++ linux/fs/nfs/read.c	Fri Mar 10 21:24:44 2000
@@ -161,7 +161,7 @@
 	}
 	kunmap(page);
 	UnlockPage(page);
-	free_page(address);
+	__free_page(page);
 
 	rpc_release_task(task);
 	kfree(req);
diff -u --recursive --new-file v2.3.51/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.3.51/linux/fs/nfsd/nfsfh.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/nfsd/nfsfh.c	Tue Mar 14 18:05:12 2000
@@ -8,6 +8,7 @@
  * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
  */
 
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
 #include <linux/fs.h>
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_base.c linux/fs/nls/nls_base.c
--- v2.3.51/linux/fs/nls/nls_base.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_base.c	Mon Mar 13 10:23:36 2000
@@ -17,9 +17,11 @@
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
+#include <linux/spinlock.h>
 #include <asm/byteorder.h>
 
 static struct nls_table *tables = (struct nls_table *) NULL;
+static spinlock_t nls_lock = SPIN_LOCK_UNLOCKED;
 
 /*
  * Sample implementation from Unicode home page.
@@ -165,14 +167,18 @@
 		return -EINVAL;
 	if (nls->next)
 		return -EBUSY;
+
+	spin_lock(&nls_lock);
 	while (*tmp) {
 		if (nls == *tmp) {
+			spin_unlock(&nls_lock);
 			return -EBUSY;
 		}
 		tmp = &(*tmp)->next;
 	}
 	nls->next = tables;
 	tables = nls;
+	spin_unlock(&nls_lock);
 	return 0;	
 }
 
@@ -180,25 +186,30 @@
 {
 	struct nls_table ** tmp = &tables;
 
+	spin_lock(&nls_lock);
 	while (*tmp) {
 		if (nls == *tmp) {
 			*tmp = nls->next;
+			spin_unlock(&nls_lock);
 			return 0;
 		}
 		tmp = &(*tmp)->next;
 	}
+	spin_unlock(&nls_lock);
 	return -EINVAL;
 }
 
-struct nls_table *find_nls(char *charset)
+static struct nls_table *find_nls(char *charset)
 {
-	struct nls_table *nls = tables;
-	while (nls) {
+	struct nls_table *nls;
+	spin_lock(&nls_lock);
+	for (nls = tables; nls; nls = nls->next)
 		if (! strcmp(nls->charset, charset))
-			return nls;
-		nls = nls->next;
-	}
-	return NULL;
+			break;
+	if (nls && !try_inc_mod_count(nls->owner))
+		nls = NULL;
+	spin_unlock(&nls_lock);
+	return nls;
 }
 
 struct nls_table *load_nls(char *charset)
@@ -210,16 +221,13 @@
 #endif
 
 	nls = find_nls(charset);
-	if (nls) {
-		nls->inc_use_count();
+	if (nls)
 		return nls;
-	}
 
-#ifndef CONFIG_KMOD
-	return NULL;
-#else
+#ifdef CONFIG_KMOD
 	if (strlen(charset) > sizeof(buf) - sizeof("nls_")) {
-		printk("Unable to load NLS charset %s: name too long\n", charset);
+		printk("Unable to load NLS charset %s: name too long\n",
+			charset);
 		return NULL;
 	}
 		
@@ -230,16 +238,14 @@
 		return NULL;
 	}
 	nls = find_nls(charset);
-	if (nls) {
-		nls->inc_use_count();
-	}
-	return nls;
 #endif
+	return nls;
 }
 
 void unload_nls(struct nls_table *nls)
 {
-	nls->dec_use_count();
+	if (nls->owner)
+		__MOD_DEC_USE_COUNT(nls->owner);
 }
 
 struct nls_unicode charset2uni[256] = {
@@ -437,28 +443,15 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-void inc_use_count(void)
-{
-}
-
-void dec_use_count(void)
-{
-}
-
 static struct nls_table default_table = {
 	"default",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	NULL,
 };
 
-
-
 /* Returns a simple default translation table */
 struct nls_table *load_nls_default(void)
 {
@@ -468,111 +461,9 @@
 EXPORT_SYMBOL(register_nls);
 EXPORT_SYMBOL(unregister_nls);
 EXPORT_SYMBOL(unload_nls);
-EXPORT_SYMBOL(find_nls);
 EXPORT_SYMBOL(load_nls);
 EXPORT_SYMBOL(load_nls_default);
 EXPORT_SYMBOL(utf8_mbtowc);
 EXPORT_SYMBOL(utf8_mbstowcs);
 EXPORT_SYMBOL(utf8_wctomb);
 EXPORT_SYMBOL(utf8_wcstombs);
-
-int init_nls(void)
-{
-#ifdef CONFIG_NLS_ISO8859_1
-	init_nls_iso8859_1();
-#endif
-#ifdef CONFIG_NLS_ISO8859_2
-	init_nls_iso8859_2();
-#endif
-#ifdef CONFIG_NLS_ISO8859_3
-	init_nls_iso8859_3();
-#endif
-#ifdef CONFIG_NLS_ISO8859_4
-	init_nls_iso8859_4();
-#endif
-#ifdef CONFIG_NLS_ISO8859_5
-	init_nls_iso8859_5();
-#endif
-#ifdef CONFIG_NLS_ISO8859_6
-	init_nls_iso8859_6();
-#endif
-#ifdef CONFIG_NLS_ISO8859_7
-	init_nls_iso8859_7();
-#endif
-#ifdef CONFIG_NLS_ISO8859_8
-	init_nls_iso8859_8();
-#endif
-#ifdef CONFIG_NLS_ISO8859_9
-	init_nls_iso8859_9();
-#endif
-#ifdef CONFIG_NLS_ISO8859_14
-        init_nls_iso8859_14();
-#endif
-#ifdef CONFIG_NLS_ISO8859_15
-	init_nls_iso8859_15();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_437
-	init_nls_cp437();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_737
-	init_nls_cp737();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_775
-	init_nls_cp775();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_850
-	init_nls_cp850();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_852
-	init_nls_cp852();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_855
-	init_nls_cp855();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_857
-	init_nls_cp857();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_860
-	init_nls_cp860();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_861
-	init_nls_cp861();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_862
-	init_nls_cp862();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_863
-	init_nls_cp863();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_864
-	init_nls_cp864();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_865
-	init_nls_cp865();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_866
-	init_nls_cp866();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_869
-	init_nls_cp869();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_874
-	init_nls_cp874();
-#endif
-#ifdef CONFIG_NLS_KOI8_R
-	init_nls_koi8_r();
-#endif
-	return 0;
-}
-
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls();
-}
-
-
-void cleanup_module(void)
-{
-}
-#endif /* ifdef MODULE */
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp437.c linux/fs/nls/nls_cp437.c
--- v2.3.51/linux/fs/nls/nls_cp437.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp437.c	Mon Mar 13 10:23:36 2000
@@ -427,46 +427,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp437",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp437(void)
+static int __init init_nls_cp437(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp437();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp437(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp437)
+module_exit(exit_nls_cp437)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp737.c linux/fs/nls/nls_cp737.c
--- v2.3.51/linux/fs/nls/nls_cp737.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp737.c	Mon Mar 13 10:23:36 2000
@@ -355,46 +355,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp737",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp737(void)
+static int __init init_nls_cp737(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp737();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp737(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp737)
+module_exit(exit_nls_cp737)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp775.c linux/fs/nls/nls_cp775.c
--- v2.3.51/linux/fs/nls/nls_cp775.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp775.c	Mon Mar 13 10:23:36 2000
@@ -355,46 +355,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp775",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp775(void)
+static int __init init_nls_cp775(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp775();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp775(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp775)
+module_exit(exit_nls_cp775)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp850.c linux/fs/nls/nls_cp850.c
--- v2.3.51/linux/fs/nls/nls_cp850.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp850.c	Mon Mar 13 10:23:36 2000
@@ -319,46 +319,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp850",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp850(void)
+static int __init init_nls_cp850(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp850();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp850(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp850)
+module_exit(exit_nls_cp850)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp852.c linux/fs/nls/nls_cp852.c
--- v2.3.51/linux/fs/nls/nls_cp852.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp852.c	Mon Mar 13 10:23:36 2000
@@ -319,46 +319,27 @@
 	0xf8, 0xf9, 0xfa, 0xeb, 0xfc, 0xfc, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp852",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp852(void)
+static int __init init_nls_cp852(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp852();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp852(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp852)
+module_exit(exit_nls_cp852)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp855.c linux/fs/nls/nls_cp855.c
--- v2.3.51/linux/fs/nls/nls_cp855.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp855.c	Mon Mar 13 10:23:36 2000
@@ -319,46 +319,27 @@
 	0xf8, 0xfa, 0xfa, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp855",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp855(void)
+static int __init init_nls_cp855(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp855();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp855(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp855)
+module_exit(exit_nls_cp855)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp857.c linux/fs/nls/nls_cp857.c
--- v2.3.51/linux/fs/nls/nls_cp857.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp857.c	Mon Mar 13 10:23:36 2000
@@ -283,46 +283,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp857",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp857(void)
+static int __init init_nls_cp857(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp857();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp857(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp857)
+module_exit(exit_nls_cp857)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp860.c linux/fs/nls/nls_cp860.c
--- v2.3.51/linux/fs/nls/nls_cp860.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp860.c	Mon Mar 13 10:23:36 2000
@@ -391,46 +391,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp860",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp860(void)
+static int __init init_nls_cp860(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp860();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp860(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp860)
+module_exit(exit_nls_cp860)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp861.c linux/fs/nls/nls_cp861.c
--- v2.3.51/linux/fs/nls/nls_cp861.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp861.c	Mon Mar 13 10:23:36 2000
@@ -427,46 +427,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp861",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp861(void)
+static int __init init_nls_cp861(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp861();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp861(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp861)
+module_exit(exit_nls_cp861)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp862.c linux/fs/nls/nls_cp862.c
--- v2.3.51/linux/fs/nls/nls_cp862.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp862.c	Mon Mar 13 10:23:36 2000
@@ -463,46 +463,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp862",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp862(void)
+static int __init init_nls_cp862(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp862();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp862(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp862)
+module_exit(exit_nls_cp862)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp863.c linux/fs/nls/nls_cp863.c
--- v2.3.51/linux/fs/nls/nls_cp863.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp863.c	Mon Mar 13 10:23:36 2000
@@ -427,46 +427,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp863",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp863(void)
+static int __init init_nls_cp863(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp863();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp863(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp863)
+module_exit(exit_nls_cp863)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp864.c linux/fs/nls/nls_cp864.c
--- v2.3.51/linux/fs/nls/nls_cp864.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp864.c	Mon Mar 13 10:23:36 2000
@@ -418,46 +418,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x00, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp864",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp864(void)
+static int __init init_nls_cp864(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp864();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp864(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp864)
+module_exit(exit_nls_cp864)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp865.c linux/fs/nls/nls_cp865.c
--- v2.3.51/linux/fs/nls/nls_cp865.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp865.c	Mon Mar 13 10:23:36 2000
@@ -427,46 +427,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp865",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp865(void)
+static int __init init_nls_cp865(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp865();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp865(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp865)
+module_exit(exit_nls_cp865)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp866.c linux/fs/nls/nls_cp866.c
--- v2.3.51/linux/fs/nls/nls_cp866.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp866.c	Mon Mar 13 10:23:36 2000
@@ -355,46 +355,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp866",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp866(void)
+static int __init init_nls_cp866(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp866();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp866(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp866)
+module_exit(exit_nls_cp866)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp869.c linux/fs/nls/nls_cp869.c
--- v2.3.51/linux/fs/nls/nls_cp869.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp869.c	Mon Mar 13 10:23:36 2000
@@ -319,46 +319,27 @@
 	0xf8, 0xf9, 0xd5, 0x96, 0xfc, 0x98, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp869",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp869(void)
+static int __init init_nls_cp869(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp869();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp869(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp869)
+module_exit(exit_nls_cp869)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_cp874.c linux/fs/nls/nls_cp874.c
--- v2.3.51/linux/fs/nls/nls_cp874.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_cp874.c	Mon Mar 13 10:23:36 2000
@@ -283,46 +283,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"cp874",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_cp874(void)
+static int __init init_nls_cp874(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_cp874();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp874(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_cp874)
+module_exit(exit_nls_cp874)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-1.c linux/fs/nls/nls_iso8859-1.c
--- v2.3.51/linux/fs/nls/nls_iso8859-1.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_iso8859-1.c	Mon Mar 13 10:23:36 2000
@@ -207,46 +207,27 @@
 	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-1",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_1(void)
+static int __init init_nls_iso8859_1(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_1();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_1(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_1)
+module_exit(exit_nls_iso8859_1)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-14.c linux/fs/nls/nls_iso8859-14.c
--- v2.3.51/linux/fs/nls/nls_iso8859-14.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_iso8859-14.c	Mon Mar 13 10:23:36 2000
@@ -291,46 +291,27 @@
 	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xaf, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-14",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_14(void)
+static int __init init_nls_iso8859_14(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
+static void __exit exit_nls_iso8859_14(void)
 {
-	return init_nls_iso8859_14();
-}
-
-
-void cleanup_module(void)
-{	
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_14)
+module_exit(exit_nls_iso8859_14)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-15.c linux/fs/nls/nls_iso8859-15.c
--- v2.3.51/linux/fs/nls/nls_iso8859-15.c	Wed Dec  8 14:11:27 1999
+++ linux/fs/nls/nls_iso8859-15.c	Mon Mar 13 10:23:36 2000
@@ -284,46 +284,27 @@
 	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xbe, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-15",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_15(void)
+static int __init init_nls_iso8859_15(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_15();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_15(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_15)
+module_exit(exit_nls_iso8859_15)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-2.c linux/fs/nls/nls_iso8859-2.c
--- v2.3.51/linux/fs/nls/nls_iso8859-2.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_iso8859-2.c	Mon Mar 13 10:23:36 2000
@@ -279,46 +279,27 @@
 	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-2",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_2(void)
+static int __init init_nls_iso8859_2(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_2();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_2(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_2)
+module_exit(exit_nls_iso8859_2)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-3.c linux/fs/nls/nls_iso8859-3.c
--- v2.3.51/linux/fs/nls/nls_iso8859-3.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_iso8859-3.c	Mon Mar 13 10:23:36 2000
@@ -279,46 +279,27 @@
 	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-3",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_3(void)
+static int __init init_nls_iso8859_3(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_3();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_3(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_3)
+module_exit(exit_nls_iso8859_3)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-4.c linux/fs/nls/nls_iso8859-4.c
--- v2.3.51/linux/fs/nls/nls_iso8859-4.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_iso8859-4.c	Mon Mar 13 10:23:36 2000
@@ -279,46 +279,27 @@
 	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-4",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_4(void)
+static int __init init_nls_iso8859_4(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_4();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_4(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_4)
+module_exit(exit_nls_iso8859_4)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-5.c linux/fs/nls/nls_iso8859-5.c
--- v2.3.51/linux/fs/nls/nls_iso8859-5.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_iso8859-5.c	Mon Mar 13 10:23:36 2000
@@ -283,46 +283,27 @@
 	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xfd, 0xae, 0xaf, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-5",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_5(void)
+static int __init init_nls_iso8859_5(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_5();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_5(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_5)
+module_exit(exit_nls_iso8859_5)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-6.c linux/fs/nls/nls_iso8859-6.c
--- v2.3.51/linux/fs/nls/nls_iso8859-6.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_iso8859-6.c	Mon Mar 13 10:23:36 2000
@@ -243,46 +243,27 @@
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-6",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_6(void)
+static int __init init_nls_iso8859_6(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_6();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_6(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_6)
+module_exit(exit_nls_iso8859_6)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-7.c linux/fs/nls/nls_iso8859-7.c
--- v2.3.51/linux/fs/nls/nls_iso8859-7.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_iso8859-7.c	Mon Mar 13 10:23:36 2000
@@ -319,46 +319,27 @@
 	0xd8, 0xd9, 0xda, 0xdb, 0xbc, 0xbe, 0xbf, 0x00, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-7",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_7(void)
+static int __init init_nls_iso8859_7(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_7();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_7(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_7)
+module_exit(exit_nls_iso8859_7)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-8.c linux/fs/nls/nls_iso8859-8.c
--- v2.3.51/linux/fs/nls/nls_iso8859-8.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_iso8859-8.c	Mon Mar 13 10:23:36 2000
@@ -283,46 +283,27 @@
 	0xf8, 0xf9, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-8",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_8(void)
+static int __init init_nls_iso8859_8(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_8();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_8(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_8)
+module_exit(exit_nls_iso8859_8)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_iso8859-9.c linux/fs/nls/nls_iso8859-9.c
--- v2.3.51/linux/fs/nls/nls_iso8859-9.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_iso8859-9.c	Mon Mar 13 10:23:36 2000
@@ -243,46 +243,27 @@
 	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0x49, 0xde, 0x00, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"iso8859-9",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_iso8859_9(void)
+static int __init init_nls_iso8859_9(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_iso8859_9();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_9(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_iso8859_9)
+module_exit(exit_nls_iso8859_9)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/nls/nls_koi8-r.c linux/fs/nls/nls_koi8-r.c
--- v2.3.51/linux/fs/nls/nls_koi8-r.c	Wed Dec  8 14:11:28 1999
+++ linux/fs/nls/nls_koi8-r.c	Mon Mar 13 10:23:36 2000
@@ -355,46 +355,27 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
 
-
-static void inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 static struct nls_table table = {
 	"koi8-r",
 	page_uni2charset,
 	charset2uni,
 	charset2lower,
 	charset2upper,
-	inc_use_count,
-	dec_use_count,
-	NULL
+	THIS_MODULE,
 };
 
-int init_nls_koi8_r(void)
+static int __init init_nls_koi8_r(void)
 {
 	return register_nls(&table);
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_nls_koi8_r();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_koi8_r(void)
 {
 	unregister_nls(&table);
-	return;
 }
-#endif
+
+module_init(init_nls_koi8_r)
+module_exit(exit_nls_koi8_r)
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.3.51/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.3.51/linux/fs/ntfs/fs.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/ntfs/fs.c	Mon Mar 13 12:35:39 2000
@@ -921,13 +921,7 @@
  */
 static DECLARE_FSTYPE_DEV(ntfs_fs_type, "ntfs", ntfs_read_super);
 
-/* When this code is not compiled as a module, this is the main entry point,
- * called by do_sys_setup() in fs/filesystems.c
- *
- * NOTE : __init is a macro used to remove this function from memory
- * once initialization is done
- */
-int __init init_ntfs_fs(void)
+static int __init init_ntfs_fs(void)
 {
 	/* Comment this if you trust klogd. There are reasons not to trust it
 	 */
@@ -942,44 +936,21 @@
 	return register_filesystem(&ntfs_fs_type);
 }
 
-#ifdef MODULE
-/* A module is a piece of code which can be inserted in and removed
- * from the running kernel whenever you want using lsmod, or on demand using
- * kmod
- */
+static __exit void exit_ntfs_fs(void)
+{
+	SYSCTL(0);
+	ntfs_debug(DEBUG_OTHER, "unregistering %s\n",ntfs_fs_type.name);
+	unregister_filesystem(&ntfs_fs_type);
+}
 
-/* No function of this module is needed by another module */
 EXPORT_NO_SYMBOLS;
-/* Only used for documentation purposes at the moment,
- * see include/linux/module.h
- */
 MODULE_AUTHOR("Martin von Löwis");
 MODULE_DESCRIPTION("NTFS driver");
-/* no MODULE_SUPPORTED_DEVICE() */
-/* Load-time parameter */
 MODULE_PARM(ntdebug, "i");
 MODULE_PARM_DESC(ntdebug, "Debug level");
 
-/* When this code is compiled as a module, if you use mount -t ntfs when no
- * ntfs filesystem is registered (see /proc/filesystems), get_fs_type() in
- * fs/super.c asks kmod to load the module named ntfs in memory.
- *
- * Therefore, this function is the main entry point in this case
- */
-int init_module(void)
-{
-	return init_ntfs_fs();
-}
-
-/* Called by kmod just before the kernel removes the module from memory */
-void cleanup_module(void)
-{
-	SYSCTL(0);
-	ntfs_debug(DEBUG_OTHER, "unregistering %s\n",ntfs_fs_type.name);
-	unregister_filesystem(&ntfs_fs_type);
-}
-#endif
-
+module_init(init_ntfs_fs)
+module_exit(exit_ntfs_fs)
 /*
  * Local variables:
  *  c-file-style: "linux"
diff -u --recursive --new-file v2.3.51/linux/fs/openpromfs/inode.c linux/fs/openpromfs/inode.c
--- v2.3.51/linux/fs/openpromfs/inode.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/openpromfs/inode.c	Mon Mar 13 22:18:11 2000
@@ -1,4 +1,4 @@
-/* $Id: inode.c,v 1.7 2000/03/10 04:45:50 davem Exp $
+/* $Id: inode.c,v 1.9 2000/03/13 21:59:43 davem Exp $
  * openpromfs.c: /proc/openprom handling routines
  *
  * Copyright (C) 1996-1999 Jakub Jelinek  (jakub@redhat.com)
@@ -1026,7 +1026,7 @@
 
 static DECLARE_FSTYPE(openprom_fs_type, "openpromfs", openprom_read_super, 0);
 
-int init_openprom_fs(void)
+static int __init init_openprom_fs(void)
 {
 	nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0);
 	if (!nodes) {
@@ -1041,25 +1041,7 @@
 	return register_filesystem(&openprom_fs_type);
 }
 
-#ifdef MODULE
-
-EXPORT_NO_SYMBOLS;
-
-int init_module (void)
-{
-	return init_openprom_fs();
-}
-
-#else
-
-void __init openpromfs_init (void)
-{
-	init_openprom_fs();
-}
-#endif
-
-#ifdef MODULE
-void cleanup_module (void)
+static void __exit exit_openprom_fs(void)
 {
 	int i;
 	unregister_filesystem(&openprom_fs_type);
@@ -1069,4 +1051,8 @@
 			kfree (alias_names [i]);
 	nodes = NULL;
 }
-#endif
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_openprom_fs)
+module_exit(exit_openprom_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/partitions/acorn.c linux/fs/partitions/acorn.c
--- v2.3.51/linux/fs/partitions/acorn.c	Wed Feb 16 17:03:52 2000
+++ linux/fs/partitions/acorn.c	Sun Mar 12 19:39:39 2000
@@ -17,8 +17,6 @@
 #include "check.h"
 #include "acorn.h"
 
-extern void add_gd_partition(struct gendisk *hd, int minor, int start, int size);
-
 static void
 adfspart_setgeometry(kdev_t dev, unsigned int secspertrack, unsigned int heads,
 		     unsigned long totalblocks)
@@ -41,21 +39,22 @@
 	unsigned long nr_sects;
 };
 
-static struct disc_record *adfs_partition(struct gendisk *hd, char *name, char *data,
+static struct adfs_discrecord *adfs_partition(struct gendisk *hd, char *name, char *data,
 					   unsigned long first_sector, unsigned int minor)
 {
-	struct disc_record *dr;
+	struct adfs_discrecord *dr;
 	unsigned int nr_sects;
 
 	if (adfs_checkbblk(data))
 		return NULL;
 
-	dr = (struct disc_record *)(data + 0x1c0);
+	dr = (struct adfs_discrecord *)(data + 0x1c0);
 
 	if (dr->disc_size == 0 && dr->disc_size_high == 0)
 		return NULL;
 
-	nr_sects = (dr->disc_size_high << 23) | (dr->disc_size >> 9);
+	nr_sects = (le32_to_cpu(dr->disc_size_high) << 23) |
+		   (le32_to_cpu(dr->disc_size) >> 9);
 
 	if (name)
 		printk(" [%s]", name);
@@ -93,8 +92,8 @@
 			if (rr->part[part].one &&
 			    memcmp(rr->part[part].name, "All\0", 4)) {
 				add_gd_partition(hd, minor++,
-						rr->part[part].start,
-						rr->part[part].length);
+						le32_to_cpu(rr->part[part].start),
+						le32_to_cpu(rr->part[part].length));
 				printk("(%s)", rr->part[part].name);
 			}
 		}
@@ -131,11 +130,13 @@
 
 	linuxp = (struct linux_part *)bh->b_data;
 	printk(" <");
-	while (linuxp->magic == LINUX_NATIVE_MAGIC || linuxp->magic == LINUX_SWAP_MAGIC) {
+	while (linuxp->magic == cpu_to_le32(LINUX_NATIVE_MAGIC) ||
+	       linuxp->magic == cpu_to_le32(LINUX_SWAP_MAGIC)) {
 		if (!(minor & mask))
 			break;
-		add_gd_partition(hd, minor++, first_sect + linuxp->start_sect,
-			linuxp->nr_sects);
+		add_gd_partition(hd, minor++, first_sect +
+				 le32_to_cpu(linuxp->start_sect),
+				 le32_to_cpu(linuxp->nr_sects));
 		linuxp ++;
 	}
 	printk(" >");
@@ -174,7 +175,7 @@
 	 * Hence it is totally untested.
 	 */
 	do {
-		struct disc_record *dr;
+		struct adfs_discrecord *dr;
 		unsigned int nr_sects;
 
 		if (!(minor & mask))
@@ -189,7 +190,8 @@
 		name = NULL;
 
 		nr_sects = (bh->b_data[0x1fd] + (bh->b_data[0x1fe] << 8)) *
-			 (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) * dr->secspertrack;
+			   (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) *
+			   dr->secspertrack;
 
 		if (!nr_sects)
 			break;
@@ -244,7 +246,7 @@
 {
 	unsigned long start_sect, nr_sects, sectscyl, heads;
 	struct buffer_head *bh;
-	struct disc_record *dr;
+	struct adfs_discrecord *dr;
 
 	if(get_ptable_blocksize(dev)!=1024)
 		return 0;
@@ -300,9 +302,6 @@
 	unsigned int offset = block & 1 ? 512 : 0;
 	int result = 0;
 
-	if(get_ptable_blocksize(dev)!=1024)
-		return 0;
-
 	bh = bread(dev, block >> 1, 1024);
 
 	if (bh != NULL) {
@@ -350,7 +349,8 @@
 	for (i = 0, sum = 0x50617274; i < 508; i++)
 		sum += bh->b_data[i];
 
-	if (sum != *(unsigned long *)(&bh->b_data[508])) {
+	sum -= le32_to_cpu(*(unsigned long *)(&bh->b_data[508]));
+	if (sum) {
 	    	brelse(bh);
 		return 0; /* not ICS partition table */
 	}
@@ -358,19 +358,32 @@
 	printk(" [ICS]");
 
 	for (p = (struct ics_part *)bh->b_data; p->size; p++) {
+		unsigned long start;
+		long size;
+
 		if ((minor & mask) == 0)
 			break;
 
-		if (p->size < 0 && adfspart_check_ICSLinux(dev, p->start)) {
+		start = le32_to_cpu(p->start);
+		size  = le32_to_cpu(p->size);
+
+		if (size < 0) {
+			size = -size;
+
 			/*
 			 * We use the first sector to identify what type
 			 * this partition is...
 			 */
-			if (-p->size > 1)
-				add_gd_partition(hd, minor, first_sector + p->start + 1, -p->size - 1);
-		} else
-			add_gd_partition(hd, minor, first_sector + p->start, p->size);
-		minor++;
+			if (size > 1 && adfspart_check_ICSLinux(dev, start)) {
+				start += 1;
+				size -= 1;
+			}
+		}
+
+		if (size) {
+			add_gd_partition(hd, minor, first_sector + start, size);
+			minor++;
+		}
 	}
 
 	brelse(bh);
@@ -422,8 +435,15 @@
 	printk(" [POWERTEC]");
 
 	for (i = 0, p = (struct ptec_partition *)bh->b_data; i < 12; i++, p++) {
-		if (p->size)
-			add_gd_partition(hd, minor, first_sector + p->start, p->size);
+		unsigned long start;
+		unsigned long size;
+
+		start = le32_to_cpu(p->start);
+		size  = le32_to_cpu(p->size);
+
+		if (size)
+			add_gd_partition(hd, minor, first_sector + start,
+					 size);
 		minor++;
 	}
 
diff -u --recursive --new-file v2.3.51/linux/fs/partitions/acorn.h linux/fs/partitions/acorn.h
--- v2.3.51/linux/fs/partitions/acorn.h	Thu Aug 12 12:26:06 1999
+++ linux/fs/partitions/acorn.h	Tue Mar 14 18:36:51 2000
@@ -6,42 +6,6 @@
 #include <linux/adfs_fs.h>
 
 /*
- * Offset in bytes of the boot block on the disk.
- */
-#define BOOT_SECTOR_ADDRESS 0xc00
-
-/*
- * Disc record size
- */
-#define RECSIZE 60
-
-/*
- * Disc record
- */
-struct disc_record {
-	unsigned char  log2secsize;
-	unsigned char  secspertrack;
-	unsigned char  heads;
-	unsigned char  density;
-	unsigned char  idlen;
-	unsigned char  log2bpmb;
-	unsigned char  skew;
-	unsigned char  bootoption;
-	unsigned char  lowsector;
-	unsigned char  nzones;
-	unsigned short zone_spare;
-	unsigned long  root;
-	unsigned long  disc_size;
-	unsigned short disc_id;
-	unsigned char  disc_name[10];
-	unsigned long  disc_type;
-	unsigned long  disc_size_high;
-	unsigned char  log2sharesize:4;
-	unsigned char  unused:4;
-	unsigned char  big_flag:1;
-};
-
-/*
  * Partition types. (Oh for reusability)
  */
 #define PARTITION_RISCIX_MFM	1
diff -u --recursive --new-file v2.3.51/linux/fs/partitions/atari.c linux/fs/partitions/atari.c
--- v2.3.51/linux/fs/partitions/atari.c	Wed Feb 16 17:03:52 2000
+++ linux/fs/partitions/atari.c	Sun Mar 12 19:39:39 2000
@@ -34,9 +34,9 @@
      be32_to_cpu((pi)->st) + be32_to_cpu((pi)->siz) <= (hdsiz))
 
 int atari_partition (struct gendisk *hd, kdev_t dev,
-		     unsigned long first_sector, int first_part_minor)
+		     unsigned long first_sector, int minor)
 {
-  int minor = first_part_minor, m_lim = first_part_minor + hd->max_p;
+  int m_lim = minor + hd->max_p;
   struct buffer_head *bh;
   struct rootsector *rs;
   struct partition_info *pi;
diff -u --recursive --new-file v2.3.51/linux/fs/partitions/sgi.c linux/fs/partitions/sgi.c
--- v2.3.51/linux/fs/partitions/sgi.c	Wed Feb 16 17:03:52 2000
+++ linux/fs/partitions/sgi.c	Sun Mar 12 19:39:39 2000
@@ -17,9 +17,9 @@
 #include "check.h"
 #include "sgi.h"
 
-int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int first_part_minor)
+int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int current_minor)
 {
-	int i, csum, magic, current_minor = first_part_minor;
+	int i, csum, magic;
 	unsigned int *ui, start, blocks, cs;
 	struct buffer_head *bh;
 	struct sgi_disklabel {
diff -u --recursive --new-file v2.3.51/linux/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c
--- v2.3.51/linux/fs/proc/procfs_syms.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/proc/procfs_syms.c	Mon Mar 13 12:35:39 2000
@@ -22,7 +22,15 @@
 
 static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, 0);
 
-int __init init_proc_fs(void)
+static int __init init_proc_fs(void)
 {
-	return register_filesystem(&proc_fs_type) == 0;
+	return register_filesystem(&proc_fs_type);
 }
+
+static void __exit exit_proc_fs(void)
+{
+	unregister_filesystem(&proc_fs_type);
+}
+
+module_init(init_proc_fs)
+module_exit(exit_proc_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/qnx4/inode.c linux/fs/qnx4/inode.c
--- v2.3.51/linux/fs/qnx4/inode.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/qnx4/inode.c	Mon Mar 13 12:35:39 2000
@@ -491,23 +491,18 @@
 
 static DECLARE_FSTYPE_DEV(qnx4_fs_type, "qnx4", qnx4_read_super);
 
-int __init init_qnx4_fs(void)
+static int __init init_qnx4_fs(void)
 {
 	printk("QNX4 filesystem 0.2.2 registered.\n");
 	return register_filesystem(&qnx4_fs_type);
 }
 
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
-	return init_qnx4_fs();
-}
-
-void cleanup_module(void)
+static void __exit exit_qnx4_fs(void)
 {
 	unregister_filesystem(&qnx4_fs_type);
 }
 
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_qnx4_fs)
+module_exit(exit_qnx4_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/romfs/inode.c linux/fs/romfs/inode.c
--- v2.3.51/linux/fs/romfs/inode.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/romfs/inode.c	Mon Mar 13 12:35:39 2000
@@ -532,26 +532,19 @@
 
 static DECLARE_FSTYPE_DEV(romfs_fs_type, "romfs", romfs_read_super);
 
-int __init init_romfs_fs(void)
+static int __init init_romfs_fs(void)
 {
 	return register_filesystem(&romfs_fs_type);
 }
 
-#ifdef MODULE
+static void __exit exit_romfs_fs(void)
+{
+	unregister_filesystem(&romfs_fs_type);
+}
 
 /* Yes, works even as a module... :) */
 
 EXPORT_NO_SYMBOLS;
 
-int
-init_module(void)
-{
-	return init_romfs_fs();
-}
-
-void
-cleanup_module(void)
-{
-	unregister_filesystem(&romfs_fs_type);
-}
-#endif
+module_init(init_romfs_fs)
+module_exit(exit_romfs_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v2.3.51/linux/fs/smbfs/inode.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/smbfs/inode.c	Mon Mar 13 12:35:39 2000
@@ -536,16 +536,7 @@
 
 static DECLARE_FSTYPE( smb_fs_type, "smbfs", smb_read_super, 0);
 
-int __init init_smb_fs(void)
-{
-	return register_filesystem(&smb_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int
-init_module(void)
+static int __init init_smb_fs(void)
 {
 	pr_debug("smbfs: init_module called\n");
 
@@ -558,8 +549,7 @@
 	return init_smb_fs();
 }
 
-void
-cleanup_module(void)
+static void __exit exit_smb_fs(void)
 {
 	pr_debug("smbfs: cleanup_module called\n");
 	unregister_filesystem(&smb_fs_type);
@@ -570,4 +560,7 @@
 #endif
 }
 
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_smb_fs)
+module_exit(exit_smb_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/super.c linux/fs/super.c
--- v2.3.51/linux/fs/super.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/super.c	Mon Mar 13 10:23:36 2000
@@ -966,10 +966,10 @@
 	sb->s_bdev = NULL;
 	sb->s_dev = 0;
 	sb->s_type = NULL;
-fsput_and_out:
-	put_filesystem(fs_type);
 	if (bdev)
 		bdput(bdev);
+fsput_and_out:
+	put_filesystem(fs_type);
 dput_and_out:
 	dput(dir_d);
 	up(&mount_sem);
@@ -1134,7 +1134,7 @@
 		dentry = namei(dev_name);
 		retval = PTR_ERR(dentry);
 		if (IS_ERR(dentry))
-			goto out;
+			goto fs_out;
 
 		inode = dentry->d_inode;
 		retval = -ENOTBLK;
@@ -1163,6 +1163,8 @@
 
 dput_and_out:
 	dput(dentry);
+fs_out:
+	put_filesystem(fstype);
 out:
 	unlock_kernel();
 	return retval;
diff -u --recursive --new-file v2.3.51/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.3.51/linux/fs/sysv/inode.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/sysv/inode.c	Mon Mar 13 12:35:39 2000
@@ -1188,22 +1188,17 @@
 
 static DECLARE_FSTYPE_DEV(sysv_fs_type, "sysv", sysv_read_super);
 
-int __init init_sysv_fs(void)
+static int __init init_sysv_fs(void)
 {
 	return register_filesystem(&sysv_fs_type);
 }
 
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
-	return init_sysv_fs();
-}
-
-void cleanup_module(void)
+static void __exit exit_sysv_fs(void)
 {
 	unregister_filesystem(&sysv_fs_type);
 }
 
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_sysv_fs)
+module_exit(exit_sysv_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/udf/super.c linux/fs/udf/super.c
--- v2.3.51/linux/fs/udf/super.c	Fri Mar 10 16:40:48 2000
+++ linux/fs/udf/super.c	Mon Mar 13 12:35:39 2000
@@ -127,61 +127,21 @@
 	uid_t uid;
 };
 
-#if defined(MODULE)
-	
-/*
- * cleanup_module
- *
- * PURPOSE
- *	Unregister the UDF filesystem type.
- *
- * DESCRIPTION
- *	Clean-up before the module is unloaded.
- *	This routine only applies when compiled as a module.
- *
- * HISTORY
- *	July 1, 1997 - Andrew E. Mileski
- *	Written, tested, and released.
- */
-int
-cleanup_module(void)
-{
-	printk(KERN_NOTICE "udf: unregistering filesystem\n");
-	return unregister_filesystem(&udf_fstype);
-}
 
-/*
- * init_module / init_udf_fs
- *
- * PURPOSE
- *	Register the UDF filesystem type.
- *
- * HISTORY
- *	July 1, 1997 - Andrew E. Mileski
- *	Written, tested, and released.
- */
-int init_module(void)
-#else /* if !defined(MODULE) */
-int __init init_udf_fs(void)
-#endif
+static int __init init_udf_fs(void)
 {
 	printk(KERN_NOTICE "udf: registering filesystem\n");
-	{
-		struct super_block sb;
-		int size;
-
-		size = sizeof(struct super_block) +
-			(long)&sb.u - (long)&sb;
-		if ( size < sizeof(struct udf_sb_info) )
-		{
-			printk(KERN_ERR "udf: Danger! Kernel was compiled without enough room for udf_sb_info\n");
-			printk(KERN_ERR "udf: Kernel has room for %u bytes, udf needs %lu\n",
-				size, (unsigned long)sizeof(struct udf_sb_info));
-			return 0;
-		}
-	}
 	return register_filesystem(&udf_fstype);
 }
+
+static void __exit exit_udf_fs(void)
+{
+	printk(KERN_NOTICE "udf: unregistering filesystem\n");
+	unregister_filesystem(&udf_fstype);
+}
+
+module_init(init_udf_fs)
+module_exit(exit_udf_fs)
 
 /*
  * udf_parse_options
diff -u --recursive --new-file v2.3.51/linux/fs/ufs/dir.c linux/fs/ufs/dir.c
--- v2.3.51/linux/fs/ufs/dir.c	Sat Feb 26 22:31:54 2000
+++ linux/fs/ufs/dir.c	Mon Mar 13 10:23:36 2000
@@ -121,7 +121,7 @@
 				 * version stamp to detect whether or
 				 * not the directory has been modified
 				 * during the copy operation. */
-				unsigned long version = inode->i_version;
+				unsigned long version = filp->f_version;
 
 				UFSD(("filldir(%s,%u)\n", de->d_name, SWAB32(de->d_ino)))
 				UFSD(("namlen %u\n", ufs_get_de_namlen(de)))
@@ -129,7 +129,7 @@
 						filp->f_pos, SWAB32(de->d_ino));
 				if (error)
 					break;
-				if (version != inode->i_version)
+				if (version != filp->f_version)
 					goto revalidate;
 				stored ++;
 			}
diff -u --recursive --new-file v2.3.51/linux/fs/ufs/super.c linux/fs/ufs/super.c
--- v2.3.51/linux/fs/ufs/super.c	Fri Mar 10 16:40:49 2000
+++ linux/fs/ufs/super.c	Mon Mar 13 12:35:39 2000
@@ -951,22 +951,17 @@
 
 static DECLARE_FSTYPE_DEV(ufs_fs_type, "ufs", ufs_read_super);
 
-int __init init_ufs_fs(void)
+static int __init init_ufs_fs(void)
 {
 	return register_filesystem(&ufs_fs_type);
 }
 
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
-	return init_ufs_fs();
-}
-
-void cleanup_module(void)
+static void __exit exit_ufs_fs(void)
 {
 	unregister_filesystem(&ufs_fs_type);
 }
 
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_ufs_fs)
+module_exit(exit_ufs_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v2.3.51/linux/fs/umsdos/inode.c	Fri Mar 10 16:40:49 2000
+++ linux/fs/umsdos/inode.c	Mon Mar 13 12:35:39 2000
@@ -435,22 +435,17 @@
 
 static DECLARE_FSTYPE_DEV(umsdos_fs_type, "umsdos", UMSDOS_read_super);
 
-int __init init_umsdos_fs (void)
+static int __init init_umsdos_fs (void)
 {
 	return register_filesystem (&umsdos_fs_type);
 }
 
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module (void)
-{
-	return init_umsdos_fs ();
-}
-
-void cleanup_module (void)
+static void __exit exit_umsdos_fs (void)
 {
 	unregister_filesystem (&umsdos_fs_type);
 }
 
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_umsdos_fs)
+module_exit(exit_umsdos_fs)
diff -u --recursive --new-file v2.3.51/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.3.51/linux/fs/vfat/namei.c	Fri Mar 10 16:40:49 2000
+++ linux/fs/vfat/namei.c	Mon Mar 13 12:35:39 2000
@@ -1292,16 +1292,3 @@
 
 	return res;
 }
-
-#ifdef MODULE
-int init_module(void)
-{
-	return init_vfat_fs();
-}
-
-void cleanup_module(void)
-{
-	unregister_filesystem(&vfat_fs_type);
-}
-
-#endif /* ifdef MODULE */
diff -u --recursive --new-file v2.3.51/linux/fs/vfat/vfatfs_syms.c linux/fs/vfat/vfatfs_syms.c
--- v2.3.51/linux/fs/vfat/vfatfs_syms.c	Fri Mar 10 16:40:49 2000
+++ linux/fs/vfat/vfatfs_syms.c	Mon Mar 13 12:35:39 2000
@@ -8,6 +8,7 @@
 #define ASC_LINUX_VERSION(V, P, S)	(((V) * 65536) + ((P) * 256) + (S))
 #include <linux/version.h>
 #include <linux/module.h>
+#include <linux/init.h>
 
 #include <linux/mm.h>
 #include <linux/msdos_fs.h>
@@ -22,8 +23,15 @@
 EXPORT_SYMBOL(vfat_read_super);
 EXPORT_SYMBOL(vfat_lookup);
 
-int init_vfat_fs(void)
+static int __init init_vfat_fs(void)
 {
 	return register_filesystem(&vfat_fs_type);
 }
 
+static void __exit exit_vfat_fs(void)
+{
+	unregister_filesystem(&vfat_fs_type);
+}
+
+module_init(init_vfat_fs)
+module_exit(exit_vfat_fs)
diff -u --recursive --new-file v2.3.51/linux/include/asm-alpha/parport.h linux/include/asm-alpha/parport.h
--- v2.3.51/linux/include/asm-alpha/parport.h	Sat Feb 26 22:31:55 2000
+++ linux/include/asm-alpha/parport.h	Tue Mar 14 18:04:50 2000
@@ -1,7 +1,7 @@
 /*
  * parport.h: platform-specific PC-style parport initialisation
  *
- * Copyright (C) 1999  Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000  Tim Waugh <tim@cyberelk.demon.co.uk>
  *
  * This file should only be included by drivers/parport/parport_pc.c.
  */
@@ -9,53 +9,10 @@
 #ifndef _ASM_AXP_PARPORT_H
 #define _ASM_AXP_PARPORT_H 1
 
-#include <linux/config.h>
-
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-	int count = 0, i = 0;
-
-	if (io && *io) {
-		/* Only probe the ports we were given. */
-		user_specified = 1;
-		do {
-			if (!*io_hi) *io_hi = 0x400 + *io;
-			if (parport_pc_probe_port(*(io++), *(io_hi++),
-						  *(irq++), *(dma++), NULL))
-				count++;
-		} while (*io && (++i < PARPORT_PC_MAX_PORTS));
-	} else {
-		count += parport_pc_init_superio ();
-
-		/* Probe all the likely ports. */
-		if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-			count++;
-		count += parport_pc_init_pci (irq[0], dma[0]);
-	}
-
-        return count;
+	return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASM_AXP_PARPORT_H) */
diff -u --recursive --new-file v2.3.51/linux/include/asm-arm/arch-cl7500/system.h linux/include/asm-arm/arch-cl7500/system.h
--- v2.3.51/linux/include/asm-arm/arch-cl7500/system.h	Thu Mar  2 14:36:23 2000
+++ linux/include/asm-arm/arch-cl7500/system.h	Sun Mar 12 19:39:39 2000
@@ -23,6 +23,4 @@
 
 #define arch_power_off()	do { } while (0)
 
-#define arch_power_off()	do { } while (0)
-
 #endif
diff -u --recursive --new-file v2.3.51/linux/include/asm-arm/arch-ebsa285/time.h linux/include/asm-arm/arch-ebsa285/time.h
--- v2.3.51/linux/include/asm-arm/arch-ebsa285/time.h	Thu Feb 10 17:11:20 2000
+++ linux/include/asm-arm/arch-ebsa285/time.h	Sun Mar 12 19:39:39 2000
@@ -203,7 +203,8 @@
 {
 	int irq;
 
-	if (machine_is_co285())
+	if (machine_is_co285() ||
+	    machine_is_personal_server())
 		/*
 		 * Add-in 21285s shouldn't access the RTC
 		 */
@@ -247,7 +248,9 @@
 			rtc_base = 0;
 	}
 
-	if (machine_is_ebsa285() || machine_is_co285()) {
+	if (machine_is_ebsa285() ||
+	    machine_is_co285() ||
+	    machine_is_personal_server()) {
 		gettimeoffset = timer1_gettimeoffset;
 
 		*CSR_TIMER1_CLR  = 0;
diff -u --recursive --new-file v2.3.51/linux/include/asm-arm/parport.h linux/include/asm-arm/parport.h
--- v2.3.51/linux/include/asm-arm/parport.h	Thu Mar  2 14:36:23 2000
+++ linux/include/asm-arm/parport.h	Tue Mar 14 18:04:50 2000
@@ -1,7 +1,7 @@
 /*
  * parport.h: ARM-specific parport initialisation
  *
- * Copyright (C) 1999  Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000  Tim Waugh <tim@cyberelk.demon.co.uk>
  *
  * This file should only be included by drivers/parport/parport_pc.c.
  */
@@ -9,60 +9,10 @@
 #ifndef __ASMARM_PARPORT_H
 #define __ASMARM_PARPORT_H
 
-#include <linux/config.h>
-
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-static int __devinit parport_pc_init_superio(void);
-
-static int user_specified __maybe_initdata = 0;
-
-
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-	int count = 0, i = 0;
-
-	if (io && *io) {
-		/* Only probe the ports we were given. */
-		user_specified = 1;
-		do {
-			if (!*io_hi) *io_hi = 0x400 + *io;
-			if (parport_pc_probe_port(*(io++), *(io_hi++),
-						  *(irq++), *(dma++), NULL))
-				count++;
-		} while (*io && (++i < PARPORT_PC_MAX_PORTS));
-	} else {
-#ifdef CONFIG_PCI
-		count += parport_pc_init_superio ();
-#endif
-
-		/* Probe all the likely ports. */
-		if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-			count++;
-#ifdef CONFIG_PCI
-		count += parport_pc_init_pci (irq[0], dma[0]);
-#endif
-	}
-
-        return count;
+	return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASMARM_PARPORT_H) */
diff -u --recursive --new-file v2.3.51/linux/include/asm-arm/procinfo.h linux/include/asm-arm/procinfo.h
--- v2.3.51/linux/include/asm-arm/procinfo.h	Fri Oct 22 13:21:53 1999
+++ linux/include/asm-arm/procinfo.h	Sun Mar 12 19:39:39 2000
@@ -18,8 +18,10 @@
 /*
  * Note!  struct processor is always defined if we're
  * using MULTI_CPU, otherwise this entry is unused,
- * but still exists.  NOTE! This structure is used
- * by assembler code!  Check:
+ * but still exists.
+ *
+ * NOTE! The following structure is defined by assembly
+ * language, NOT C code.  For more information, check:
  *  arch/arm/mm/proc-*.S and arch/arm/kernel/head-armv.S
  */
 struct proc_info_list {
diff -u --recursive --new-file v2.3.51/linux/include/asm-arm/ptrace.h linux/include/asm-arm/ptrace.h
--- v2.3.51/linux/include/asm-arm/ptrace.h	Fri Oct 22 13:21:53 1999
+++ linux/include/asm-arm/ptrace.h	Sun Mar 12 19:39:39 2000
@@ -3,6 +3,11 @@
 
 #include <asm/proc/ptrace.h>
 
+#define PTRACE_GETREGS		12
+#define PTRACE_SETREGS		13
+#define PTRACE_GETFPREGS	14
+#define PTRACE_SETFPREGS	15
+
 #ifndef __ASSEMBLY__
 #define pc_pointer(v) \
 	((v) & ~PCMASK)
diff -u --recursive --new-file v2.3.51/linux/include/asm-arm/system.h linux/include/asm-arm/system.h
--- v2.3.51/linux/include/asm-arm/system.h	Thu Mar  2 14:36:23 2000
+++ linux/include/asm-arm/system.h	Sun Mar 12 19:39:39 2000
@@ -16,22 +16,23 @@
 extern unsigned int __machine_arch_type;
 
 /* see arch/arm/kernel/setup.c for a description of these */
-#define MACH_TYPE_EBSA110	0
-#define MACH_TYPE_RISCPC	1
-#define MACH_TYPE_NEXUSPCI	3
-#define MACH_TYPE_EBSA285	4
-#define MACH_TYPE_NETWINDER	5
-#define MACH_TYPE_CATS		6
-#define MACH_TYPE_TBOX		7
-#define MACH_TYPE_CO285		8
-#define MACH_TYPE_CLPS7110	9
-#define MACH_TYPE_ARCHIMEDES	10
-#define MACH_TYPE_A5K		11
-#define MACH_TYPE_ETOILE	12
-#define MACH_TYPE_LACIE_NAS	13
-#define MACH_TYPE_CLPS7500	14
-#define MACH_TYPE_SHARK		15
-#define MACH_TYPE_SA1100	16
+#define MACH_TYPE_EBSA110		0
+#define MACH_TYPE_RISCPC		1
+#define MACH_TYPE_NEXUSPCI		3
+#define MACH_TYPE_EBSA285		4
+#define MACH_TYPE_NETWINDER		5
+#define MACH_TYPE_CATS			6
+#define MACH_TYPE_TBOX			7
+#define MACH_TYPE_CO285			8
+#define MACH_TYPE_CLPS7110		9
+#define MACH_TYPE_ARCHIMEDES		10
+#define MACH_TYPE_A5K			11
+#define MACH_TYPE_ETOILE		12
+#define MACH_TYPE_LACIE_NAS		13
+#define MACH_TYPE_CLPS7500		14
+#define MACH_TYPE_SHARK			15
+#define MACH_TYPE_SA1100		16
+#define MACH_TYPE_PERSONAL_SERVER	17
 
 /*
  * Sort out a definition for machine_arch_type
@@ -41,6 +42,11 @@
  * 2. If two or more architectures are selected, then the selected
  *    machine_is_xxx() are variable, and the unselected machine_is_xxx()
  *    are constant zero.
+ *
+ * In general, you should use machine_is_xxxx() in your code, not:
+ *  -  switch (machine_arch_type) { }
+ *  -  if (machine_arch_type = xxxx)
+ *  -  __machine_arch_type
  */
 #ifdef CONFIG_ARCH_EBSA110
 # ifdef machine_arch_type
@@ -114,6 +120,54 @@
 # define machine_is_co285()	(0)
 #endif
 
+#ifdef CONFIG_ARCH_ARC
+# ifdef machine_arch_type
+#  undef machine_arch_type
+#  define machine_arch_type	__machine_arch_type
+# else
+#  define machine_arch_type	MACH_TYPE_ARCHIMEDES
+# endif
+# define machine_is_arc()	(machine_arch_type == MACH_TYPE_ARCHIMEDES)
+#else
+# define machine_is_arc()	(0)
+#endif
+
+#ifdef CONFIG_ARCH_A5K
+# ifdef machine_arch_type
+#  undef machine_arch_type
+#  define machine_arch_type	__machine_arch_type
+# else
+#  define machine_arch_type	MACH_TYPE_A5K
+# endif
+# define machine_is_a5k()	(machine_arch_type == MACH_TYPE_A5K)
+#else
+# define machine_is_a5k()	(0)
+#endif
+
+#ifdef CONFIG_ARCH_CLPS7500
+# ifdef machine_arch_type
+#  undef machine_arch_type
+#  define machine_arch_type	__machine_arch_type
+# else
+#  define machine_arch_type	MACH_TYPE_CLPS7500
+# endif
+# define machine_is_clps7500()	(machine_arch_type == MACH_TYPE_CLPS7500)
+#else
+# define machine_is_clps7500()	(0)
+#endif
+
+#ifdef CONFIG_ARCH_SHARK
+# ifdef machine_arch_type
+#  undef machine_arch_type
+#  define machine_arch_type	__machine_arch_type
+# else
+#  define machine_arch_type	MACH_TYPE_SHARK
+# endif
+# define machine_is_shark()	(machine_arch_type == MACH_TYPE_SHARK)
+#else
+# define machine_is_shark()	(0)
+#endif
+
 #ifdef CONFIG_ARCH_SA1100
 # ifdef machine_arch_type
 #  undef machine_arch_type
@@ -121,9 +175,21 @@
 # else
 #  define machine_arch_type	MACH_TYPE_SA1100
 # endif
-# define machine_is_sa1100()	(machine_arch_type == MACH_TYPE_SA1100
+# define machine_is_sa1100()	(machine_arch_type == MACH_TYPE_SA1100)
 #else
 # define machine_is_sa1100()	(0)
+#endif
+
+#ifdef CONFIG_PERSONAL_SERVER
+# ifdef machine_arch_type
+#  undef machine_arch_type
+#  define machine_arch_type	__machine_arch_type
+# else
+#  define machine_arch_type	MACH_TYPE_PERSONAL_SERVER
+# endif
+# define machine_is_personal_server()	(machine_arch_type == MACH_TYPE_PERSONAL_SERVER)
+#else
+# define machine_is_personal_server()	(0)
 #endif
 
 #ifndef machine_arch_type
diff -u --recursive --new-file v2.3.51/linux/include/asm-i386/mca_dma.h linux/include/asm-i386/mca_dma.h
--- v2.3.51/linux/include/asm-i386/mca_dma.h	Thu Dec 17 09:06:24 1998
+++ linux/include/asm-i386/mca_dma.h	Sun Mar 12 19:18:55 2000
@@ -55,17 +55,41 @@
 #define MCA_DMA_MODE_16    0x40  /* 16 bit xfers */
 
 
+/**
+ *	mca_enable_dma	-	channel to enable DMA on
+ *	@dmanr: DMA channel
+ *
+ *	Enable the MCA bus DMA on a channel. This can be called from
+ *	IRQ context.
+ */
 
 static __inline__ void mca_enable_dma(unsigned int dmanr)
 {
 	outb(MCA_DMA_FN_RESET_MASK | dmanr, MCA_DMA_REG_FN);
 }
 
+/**
+ *	mca_disble_dma	-	channel to disable DMA on
+ *	@dmanr: DMA channel
+ *
+ *	Enable the MCA bus DMA on a channel. This can be called from
+ *	IRQ context.
+ */
+
 static __inline__ void mca_disable_dma(unsigned int dmanr)
 {
 	outb(MCA_DMA_FN_MASK | dmanr, MCA_DMA_REG_FN);
 }
 
+/**
+ *	mca_set_dma_addr -	load a 24bit DMA address
+ *	@dmanr: DMA channel
+ *	@a: 24bit bus address
+ *
+ *	Load the address register in the DMA controller. This has a 24bit
+ *	limitation (16Mb). 
+ */
+
 static __inline__ void mca_set_dma_addr(unsigned int dmanr, unsigned int a)
 {
 	outb(MCA_DMA_FN_SET_ADDR | dmanr, MCA_DMA_REG_FN);
@@ -74,6 +98,14 @@
 	outb((a >> 16) & 0xff, MCA_DMA_REG_EXE);
 }
 
+/**
+ *	mca_get_dma_addr -	load a 24bit DMA address
+ *	@dmanr: DMA channel
+ *
+ *	Read the address register in the DMA controller. This has a 24bit
+ *	limitation (16Mb). The return is a bus address.
+ */
+
 static __inline__ unsigned int mca_get_dma_addr(unsigned int dmanr)
 {
 	unsigned int addr;
@@ -86,6 +118,15 @@
 	return addr;
 }
 
+/**
+ *	mca_set_dma_count -	load a 16bit transfer count
+ *	@dmanr: DMA channel
+ *	@count: count
+ *
+ *	Set the DMA count for this channel. This can be up to 64Kbytes.
+ *	Setting a count of zero will not do what you expect.
+ */
+
 static __inline__ void mca_set_dma_count(unsigned int dmanr, unsigned int count)
 {
 	count--;  /* transfers one more than count -- correct for this */
@@ -95,6 +136,14 @@
 	outb((count >> 8) & 0xff, MCA_DMA_REG_EXE);
 }
 
+/**
+ *	mca_get_dma_residue -	get the remaining bytes to transfer
+ *	@dmanr: DMA channel
+ *
+ *	This function returns the number of bytes left to transfer
+ *	on this DMA channel.
+ */
+
 static __inline__ unsigned int mca_get_dma_residue(unsigned int dmanr)
 {
 	unsigned short count;
@@ -106,6 +155,15 @@
 	return count;
 }
 
+/**
+ *	mca_set_dma_io -	set the port for an I/O transfer
+ *	@dmanr: DMA channel
+ *	@io_addr: an I/O port number
+ *
+ *	Unlike the ISA bus DMA controllers the DMA on MCA bus can transfer
+ *	with an I/O port target.
+ */
+
 static __inline__ void mca_set_dma_io(unsigned int dmanr, unsigned int io_addr)
 {
 	/*
@@ -116,6 +174,24 @@
 	outb(io_addr & 0xff, MCA_DMA_REG_EXE);
 	outb((io_addr >>  8) & 0xff, MCA_DMA_REG_EXE);
 }
+
+/**
+ *	mca_set_dma_mode -	set the DMA mode
+ *	@dmanr: DMA channel
+ *	@mode: The mode to set
+ *
+ *	The DMA controller supports several modes. The mode values you can
+ *	set are 
+ *
+ *	MCA_DMA_MODE_READ when reading from the DMA device.
+ *
+ *	MCA_DMA_MODE_WRITE to writing to the DMA device.
+ *
+ *	MCA_DMA_MODE_IO to do DMA to or from an I/O port.
+ *
+ *	MCA_DMA_MODE_16 to do 16bit transfers.
+ *
+ */
 
 static __inline__ void mca_set_dma_mode(unsigned int dmanr, unsigned int mode)
 {
diff -u --recursive --new-file v2.3.51/linux/include/asm-i386/mman.h linux/include/asm-i386/mman.h
--- v2.3.51/linux/include/asm-i386/mman.h	Sun Oct  6 22:55:48 1996
+++ linux/include/asm-i386/mman.h	Tue Mar 14 17:45:20 2000
@@ -25,6 +25,12 @@
 #define MCL_CURRENT	1		/* lock all current mappings */
 #define MCL_FUTURE	2		/* lock all future mappings */
 
+#define MADV_NORMAL	0x0		/* default page-in behavior */
+#define MADV_RANDOM	0x1		/* page-in minimum required */
+#define MADV_SEQUENTIAL	0x2		/* read-ahead aggressively */
+#define MADV_WILLNEED	0x3		/* pre-fault pages */
+#define MADV_DONTNEED	0x4		/* discard these pages */
+
 /* compatibility flags */
 #define MAP_ANON	MAP_ANONYMOUS
 #define MAP_FILE	0
diff -u --recursive --new-file v2.3.51/linux/include/asm-i386/parport.h linux/include/asm-i386/parport.h
--- v2.3.51/linux/include/asm-i386/parport.h	Thu Mar  2 14:36:23 2000
+++ linux/include/asm-i386/parport.h	Tue Mar 14 18:04:50 2000
@@ -1,7 +1,7 @@
 /*
  * parport.h: ia32-specific parport initialisation
  *
- * Copyright (C) 1999  Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000  Tim Waugh <tim@cyberelk.demon.co.uk>
  *
  * This file should only be included by drivers/parport/parport_pc.c.
  */
@@ -9,43 +9,10 @@
 #ifndef _ASM_I386_PARPORT_H
 #define _ASM_I386_PARPORT_H 1
 
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-static int parport_pc_init_pci(int irq, int dma);
-static int parport_pc_init_superio(void);
-
-static int user_specified __devinitdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-	int count = 0, i = 0;
-
-	if (io && *io) {
-		/* Only probe the ports we were given. */
-		user_specified = 1;
-		do {
-			if (!*io_hi) *io_hi = 0x400 + *io;
-			if (parport_pc_probe_port(*(io++), *(io_hi++),
-						  *(irq++), *(dma++), NULL))
-				count++;
-		} while (*io && (++i < PARPORT_PC_MAX_PORTS));
-	} else {
-		count += parport_pc_init_superio ();
-
-		if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-			count++;
-		
-		/* probe for other PCI parallel devices */
-		count += parport_pc_init_pci (irq[0], dma[0]);
-	}
-
-        return count;
+	return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASM_I386_PARPORT_H) */
diff -u --recursive --new-file v2.3.51/linux/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h
--- v2.3.51/linux/include/asm-i386/unistd.h	Fri Jan 28 15:09:09 2000
+++ linux/include/asm-i386/unistd.h	Tue Mar 14 17:45:20 2000
@@ -222,6 +222,9 @@
 #define __NR_setfsuid32		215
 #define __NR_setfsgid32		216
 #define __NR_pivot_root		217
+#define __NR_mincore		218
+#define __NR_madvise		219
+#define __NR_madvise1		219	/* delete when C lib stub is removed */
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
 
diff -u --recursive --new-file v2.3.51/linux/include/asm-mips/parport.h linux/include/asm-mips/parport.h
--- v2.3.51/linux/include/asm-mips/parport.h	Sat Feb 26 22:31:56 2000
+++ linux/include/asm-mips/parport.h	Tue Mar 14 18:04:50 2000
@@ -2,60 +2,17 @@
  *
  * parport.h: ia32-specific parport initialisation
  *
- * Copyright (C) 1999  Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000  Tim Waugh <tim@cyberelk.demon.co.uk>
  *
  * This file should only be included by drivers/parport/parport_pc.c.
  */
 #ifndef _ASM_PARPORT_H
 #define _ASM_PARPORT_H 1
 
-#include <linux/config.h>
-
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-	int count = 0, i = 0;
-
-	if (io && *io) {
-		/* Only probe the ports we were given. */
-		user_specified = 1;
-		do {
-			if (!*io_hi) *io_hi = 0x400 + *io;
-			if (parport_pc_probe_port(*(io++), *(io_hi++),
-						  *(irq++), *(dma++), NULL))
-				count++;
-		} while (*io && (++i < PARPORT_PC_MAX_PORTS));
-	} else {
-		count += parport_pc_init_superio ();
-
-		/* Probe all the likely ports. */
-		if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-			count++;
-		count += parport_pc_init_pci (irq[0], dma[0]);
-	}
-
-        return count;
+	return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASM_PARPORT_H) */
diff -u --recursive --new-file v2.3.51/linux/include/asm-mips64/parport.h linux/include/asm-mips64/parport.h
--- v2.3.51/linux/include/asm-mips64/parport.h	Sat Feb 26 22:31:57 2000
+++ linux/include/asm-mips64/parport.h	Tue Mar 14 18:04:50 2000
@@ -2,60 +2,17 @@
  *
  * parport.h: ia32-specific parport initialisation
  *
- * Copyright (C) 1999  Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000  Tim Waugh <tim@cyberelk.demon.co.uk>
  *
  * This file should only be included by drivers/parport/parport_pc.c.
  */
 #ifndef _ASM_PARPORT_H
 #define _ASM_PARPORT_H 1
 
-#include <linux/config.h>
-
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-	int count = 0, i = 0;
-
-	if (io && *io) {
-		/* Only probe the ports we were given. */
-		user_specified = 1;
-		do {
-			if (!*io_hi) *io_hi = 0x400 + *io;
-			if (parport_pc_probe_port(*(io++), *(io_hi++),
-						  *(irq++), *(dma++), NULL))
-				count++;
-		} while (*io && (++i < PARPORT_PC_MAX_PORTS));
-	} else {
-		count += parport_pc_init_superio ();
-
-		/* Probe all the likely ports. */
-		if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-			count++;
-		if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-			count++;
-		count += parport_pc_init_pci (irq[0], dma[0]);
-	}
-
-        return count;
+	return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASM_PARPORT_H) */
diff -u --recursive --new-file v2.3.51/linux/include/asm-sparc/ide.h linux/include/asm-sparc/ide.h
--- v2.3.51/linux/include/asm-sparc/ide.h	Fri Mar 10 16:40:49 2000
+++ linux/include/asm-sparc/ide.h	Sun Mar 12 19:11:17 2000
@@ -1,4 +1,4 @@
-/* $Id: ide.h,v 1.3 2000/03/10 04:46:47 davem Exp $
+/* $Id: ide.h,v 1.4 2000/03/12 03:56:12 davem Exp $
  * ide.h: SPARC PCI specific IDE glue.
  *
  * Copyright (C) 1997  David S. Miller (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.3.51/linux/include/asm-sparc/vaddrs.h linux/include/asm-sparc/vaddrs.h
--- v2.3.51/linux/include/asm-sparc/vaddrs.h	Thu Apr 22 19:24:52 1999
+++ linux/include/asm-sparc/vaddrs.h	Sun Mar 12 19:11:17 2000
@@ -1,4 +1,4 @@
-/* $Id: vaddrs.h,v 1.22 1999/04/20 13:22:55 anton Exp $ */
+/* $Id: vaddrs.h,v 1.23 2000/03/12 04:10:46 davem Exp $ */
 #ifndef _SPARC_VADDRS_H
 #define _SPARC_VADDRS_H
 
@@ -20,8 +20,8 @@
 #define  IOBASE_LEN     0x00300000  /* Length of the IO area */
 #define  IOBASE_END     0xfe300000
 #define  DVMA_VADDR     0xfff00000  /* Base area of the DVMA on suns */
-#define  DVMA_LEN       0x00040000  /* Size of the DVMA address space */
-#define  DVMA_END       0xfff40000
+#define  DVMA_LEN       0x000c0000  /* Size of the DVMA address space */
+#define  DVMA_END       0xfffc0000
 
 /* IOMMU Mapping area, must be on a 16MB boundary!  Note this
  * doesn't count the DVMA areas, the prom lives between the
diff -u --recursive --new-file v2.3.51/linux/include/asm-sparc64/parport.h linux/include/asm-sparc64/parport.h
--- v2.3.51/linux/include/asm-sparc64/parport.h	Tue Feb  1 01:35:44 2000
+++ linux/include/asm-sparc64/parport.h	Tue Mar 14 18:04:50 2000
@@ -11,14 +11,6 @@
 #include <asm/ebus.h>
 #include <asm/ns87303.h>
 
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_init
-#define __maybe_initdata
-#else
-#define __maybe_init __init
-#define __maybe_initdata __initdata
-#endif
-
 #define PARPORT_PC_MAX_PORTS	PARPORT_MAX
 
 static struct linux_ebus_dma *sparc_ebus_dmas[PARPORT_PC_MAX_PORTS];
@@ -108,12 +100,7 @@
 	return res;
 }
 
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-
-static int user_specified __initdata = 0;
-
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
 	struct linux_ebus *ebus;
 	struct linux_ebus_device *edev;
@@ -155,7 +142,6 @@
 		}
 	}
 
-	count += parport_pc_init_pci(PARPORT_IRQ_AUTO, PARPORT_DMA_NONE);
 	return count;
 }
 
diff -u --recursive --new-file v2.3.51/linux/include/linux/affs_fs.h linux/include/linux/affs_fs.h
--- v2.3.51/linux/include/linux/affs_fs.h	Thu Mar  2 14:36:23 2000
+++ linux/include/linux/affs_fs.h	Mon Mar 13 12:35:39 2000
@@ -89,7 +89,6 @@
 /* super.c */
 
 extern int			 affs_fs(void);
-extern int			 init_affs_fs(void);
 
 /* file.c */
 
diff -u --recursive --new-file v2.3.51/linux/include/linux/bfs_fs.h linux/include/linux/bfs_fs.h
--- v2.3.51/linux/include/linux/bfs_fs.h	Sat Feb 26 22:32:02 2000
+++ linux/include/linux/bfs_fs.h	Mon Mar 13 12:35:39 2000
@@ -78,9 +78,6 @@
 
 #ifdef __KERNEL__
 
-/* inode.c */
-extern int init_bfs_fs(void);
-
 /* file.c */
 extern struct inode_operations bfs_file_inops;
 extern struct file_operations bfs_file_operations;
diff -u --recursive --new-file v2.3.51/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.3.51/linux/include/linux/blk.h	Wed Feb 16 17:03:52 2000
+++ linux/include/linux/blk.h	Tue Mar 14 18:38:08 2000
@@ -18,17 +18,7 @@
  * NOTE that writes may use only the low 2/3 of these: reads
  * take precedence.
  */
-#define NR_REQUEST	128
-
-/*
- * This is used in the elevator algorithm.  We don't prioritise reads
- * over writes any more --- although reads are more time-critical than
- * writes, by treating them equally we increase filesystem throughput.
- * This turns out to give better overall performance.  -- sct
- */
-#define IN_ORDER(s1,s2) \
-((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
-(s1)->sector < (s2)->sector)))
+#define NR_REQUEST	256
 
 /*
  * Initialization functions.
@@ -102,7 +92,7 @@
 	{
 		if (req->cmd == READ)
 			req->q->elevator.read_pendings--;
-		req->q->nr_segments -= req->nr_segments;
+		req->q->elevator.nr_segments -= req->nr_segments;
 		req->q = NULL;
 	}
 	list_del(&req->queue);
diff -u --recursive --new-file v2.3.51/linux/include/linux/blkdev.h linux/include/linux/blkdev.h
--- v2.3.51/linux/include/linux/blkdev.h	Sat Feb 26 22:32:03 2000
+++ linux/include/linux/blkdev.h	Tue Mar 14 18:37:04 2000
@@ -43,6 +43,8 @@
 	request_queue_t * q;
 };
 
+#include <linux/elevator.h>
+
 typedef int (merge_request_fn) (request_queue_t *q, 
 				struct request  *req,
 				struct buffer_head *bh,
@@ -57,21 +59,11 @@
 typedef void (plug_device_fn) (request_queue_t *q, kdev_t device);
 typedef void (unplug_device_fn) (void *q);
 
-typedef struct elevator_s
-{
-	int sequence;
-	int read_latency;
-	int write_latency;
-	int max_bomb_segments;
-	int read_pendings;
-} elevator_t;
-
 struct request_queue
 {
 	struct list_head queue_head;
 	/* together with queue_head for cacheline sharing */
 	elevator_t elevator;
-	unsigned int nr_segments;
 
 	request_fn_proc		* request_fn;
 	merge_request_fn	* back_merge_fn;
@@ -163,8 +155,6 @@
 /* read-ahead in pages.. */
 #define MAX_READAHEAD	31
 #define MIN_READAHEAD	3
-
-#define ELEVATOR_DEFAULTS ((elevator_t) { 0, NR_REQUEST>>1, NR_REQUEST<<5, 4, 0, })
 
 #define blkdev_entry_to_request(entry) list_entry((entry), struct request, queue)
 #define blkdev_entry_next_request(entry) blkdev_entry_to_request((entry)->next)
diff -u --recursive --new-file v2.3.51/linux/include/linux/compatmac.h linux/include/linux/compatmac.h
--- v2.3.51/linux/include/linux/compatmac.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/compatmac.h	Sun Mar 12 19:18:55 2000
@@ -0,0 +1,163 @@
+  /* 
+   * This header tries to allow you to write 2.3-compatible drivers, 
+   * but (using this header) still allows you to run them on 2.2 and 
+   * 2.0 kernels. 
+   *
+   * Sometimes, a #define replaces a "construct" that older kernels
+   * had. For example, 
+   *
+   *       DECLARE_MUTEX(name);
+   *
+   * replaces the older 
+   *
+   *       struct semaphore name = MUTEX;
+   *
+   * This file then declares the DECLARE_MUTEX macro to compile into the 
+   * older version. 
+   * 
+   * In some cases, a macro or function changes the number of arguments.
+   * In that case, there is nothing we can do except define an access 
+   * macro that provides the same functionality on both versions of Linux. 
+   * 
+   * This is the case for example with the "get_user" macro 2.0 kernels use:
+   *
+   *          a = get_user (b);
+   *  
+   * while newer kernels use 
+   * 
+   *          get_user (a,b);
+   *
+   * This is unfortunate. We therefore define "Get_user (a,b)" which looks
+   * almost the same as the 2.2+ construct, and translates into the 
+   * appropriate sequence for earlier constructs. 
+   * 
+   * Supported by this file are the 2.0 kernels, 2.2 kernels, and the 
+   * most recent 2.3 kernel. 2.3 support will be dropped as soon when 2.4
+   * comes out. 2.0 support may someday be dropped. But then again, maybe 
+   * not. 
+   *
+   * I'll try to maintain this, provided that Linus agrees with the setup. 
+   * Feel free to mail updates or suggestions. 
+   *
+   * -- R.E.Wolff@BitWizard.nl
+   *
+   */
+
+#ifndef COMPATMAC_H
+#define COMPATMAC_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < 0x020100    /* Less than 2.1.0 */
+#define TWO_ZERO
+#else
+#if LINUX_VERSION_CODE < 0x020200   /* less than 2.2.x */
+#warning "Please use a 2.2.x kernel. "
+#else
+#if LINUX_VERSION_CODE < 0x020300   /* less than 2.3.x */
+#define TWO_TWO
+#else
+#define TWO_THREE
+#endif
+#endif
+#endif
+
+#ifdef TWO_ZERO
+
+/* Here is the section that makes the 2.2 compatible driver source 
+   work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2, 
+   and provide for compatibility stuff here if possible. */
+
+/* Some 200 days (on intel) */
+#define MAX_SCHEDULE_TIMEOUT     ((long)(~0UL>>1))
+
+#include <linux/bios32.h>
+
+#define Get_user(a,b)                a = get_user(b)
+#define Put_user(a,b)                0,put_user(a,b)
+#define copy_to_user(a,b,c)          memcpy_tofs(a,b,c)
+
+static inline int copy_from_user(void *to,const void *from, int c) 
+{
+  memcpy_fromfs(to, from, c);
+  return 0;
+}
+
+#define pci_present                  pcibios_present
+#define pci_read_config_word         pcibios_read_config_word
+#define pci_read_config_dword        pcibios_read_config_dword
+
+static inline unsigned char get_irq (unsigned char bus, unsigned char fn)
+{
+	unsigned char t; 
+	pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &t);
+	return t;
+}
+
+static inline void *ioremap(unsigned long base, long length)
+{
+	if (base < 0x100000) return (void *)base;
+	return vremap (base, length);
+}
+
+#define my_iounmap(x, b)             (((long)x<0x100000)?0:vfree ((void*)x))
+
+#define capable(x)                   suser()
+
+#define queue_task                   queue_task_irq_off
+#define tty_flip_buffer_push(tty)    queue_task(&tty->flip.tqueue, &tq_timer)
+#define signal_pending(current)      (current->signal & ~current->blocked)
+#define schedule_timeout(to)         do {current->timeout = jiffies + (to);schedule ();} while (0)
+#define time_after(t1,t2)            (((long)t1-t2) > 0)
+
+
+#define test_and_set_bit(nr, addr)   set_bit(nr, addr)
+#define test_and_clear_bit(nr, addr) clear_bit(nr, addr)
+
+/* Not yet implemented on 2.0 */
+#define ASYNC_SPD_SHI  -1
+#define ASYNC_SPD_WARP -1
+
+
+/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it
+   to the "name" field that does exist. As long as the assignments are
+   done in the right order, there is nothing to worry about. */
+#define driver_name           name 
+
+/* Should be in a header somewhere. They are in tty.h on 2.2 */
+#define TTY_HW_COOK_OUT       14 /* Flag to tell ntty what we can handle */
+#define TTY_HW_COOK_IN        15 /* in hardware - output and input       */
+
+/* The return type of a "close" routine. */
+#define INT                   void
+#define NO_ERROR              /* Nothing */
+
+#else
+
+/* The 2.2.x compatibility section. */
+#include <asm/uaccess.h>
+
+
+#define Get_user(a,b)         get_user(a,b)
+#define Put_user(a,b)         put_user(a,b)
+#define get_irq(pdev)         pdev->irq
+
+#define INT                   int
+#define NO_ERROR              0
+
+#define my_iounmap(x,b)       (iounmap((char *)(b)))
+
+#endif
+
+#ifndef TWO_THREE
+/* These are new in 2.3. The source now uses 2.3 syntax, and here is 
+   the compatibility define... */
+#define wait_queue_head_t     struct wait_queue *
+#define DECLARE_MUTEX(name)   struct semaphore name = MUTEX
+#define DECLARE_WAITQUEUE(wait, current) \
+                              struct wait_queue wait = { current, NULL }
+
+#endif
+
+
+#endif
diff -u --recursive --new-file v2.3.51/linux/include/linux/efs_fs.h linux/include/linux/efs_fs.h
--- v2.3.51/linux/include/linux/efs_fs.h	Fri Mar 10 16:40:49 2000
+++ linux/include/linux/efs_fs.h	Mon Mar 13 12:35:39 2000
@@ -49,10 +49,7 @@
 extern struct file_operations efs_dir_operations;
 extern struct address_space_operations efs_symlink_aops;
 
-extern int init_module(void);
-extern void cleanup_module(void);
 extern struct super_block *efs_read_super(struct super_block *, void *, int);
-extern void efs_put_super(struct super_block *);
 extern int efs_statfs(struct super_block *, struct statfs *);
 
 extern void efs_read_inode(struct inode *);
@@ -61,7 +58,4 @@
 extern struct dentry *efs_lookup(struct inode *, struct dentry *);
 extern int efs_bmap(struct inode *, int);
 
-extern int init_efs_fs(void);
-
 #endif /* __EFS_FS_H__ */
-
diff -u --recursive --new-file v2.3.51/linux/include/linux/elevator.h linux/include/linux/elevator.h
--- v2.3.51/linux/include/linux/elevator.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/elevator.h	Sun Mar 12 19:32:58 2000
@@ -0,0 +1,124 @@
+#ifndef _LINUX_ELEVATOR_H
+#define _LINUX_ELEVATOR_H
+
+#define ELEVATOR_DEBUG
+
+struct elevator_s;
+typedef struct elevator_s elevator_t;
+
+typedef void (elevator_fn) (struct request *, elevator_t *,
+			    struct list_head *,
+			    struct list_head *, int);
+
+struct elevator_s
+{
+	int sequence;
+
+	int read_latency;
+	int write_latency;
+	int max_bomb_segments;
+
+	unsigned int nr_segments;
+	int read_pendings;
+
+	elevator_fn * elevator_fn;
+};
+
+#define ELEVATOR_DEFAULTS				\
+((elevator_t) {						\
+	0,			/* sequence */		\
+							\
+	128,			/* read_latency */	\
+	8192,			/* write_latency */	\
+	4,			/* max_bomb_segments */	\
+							\
+	0,			/* nr_segments */	\
+	0,			/* read_pendings */	\
+							\
+	elevator_default,	/* elevator_fn */	\
+	})
+
+
+typedef struct blkelv_ioctl_arg_s {
+	void * queue_ID;
+	int read_latency;
+	int write_latency;
+	int max_bomb_segments;
+} blkelv_ioctl_arg_t;
+
+#define BLKELVGET   _IO(0x12,106)
+#define BLKELVSET   _IO(0x12,107)
+
+extern int blkelvget_ioctl(elevator_t *, blkelv_ioctl_arg_t *);
+extern int blkelvset_ioctl(elevator_t *, const blkelv_ioctl_arg_t *);
+
+
+extern void elevator_init(elevator_t *);
+
+#ifdef ELEVATOR_DEBUG
+extern void elevator_debug(request_queue_t *, kdev_t);
+#else
+#define elevator_debug(a,b) do { } while(0)
+#endif
+
+#define elevator_sequence_after(a,b) ((int)((b)-(a)) < 0)
+#define elevator_sequence_before(a,b) elevator_sequence_after(b,a)
+#define elevator_sequence_after_eq(a,b) ((int)((b)-(a)) <= 0)
+#define elevator_sequence_before_eq(a,b) elevator_sequence_after_eq(b,a)
+
+/*
+ * This is used in the elevator algorithm.  We don't prioritise reads
+ * over writes any more --- although reads are more time-critical than
+ * writes, by treating them equally we increase filesystem throughput.
+ * This turns out to give better overall performance.  -- sct
+ */
+#define IN_ORDER(s1,s2)				\
+	((((s1)->rq_dev == (s2)->rq_dev &&	\
+	   (s1)->sector < (s2)->sector)) ||	\
+	 (s1)->rq_dev < (s2)->rq_dev)
+
+static inline void elevator_merge_requests(elevator_t * e, struct request * req, struct request * next)
+{
+	if (elevator_sequence_before(next->elevator_sequence, req->elevator_sequence))
+		req->elevator_sequence = next->elevator_sequence;
+	if (req->cmd == READ)
+		e->read_pendings--;
+
+}
+
+static inline int elevator_sequence(elevator_t * e, int latency)
+{
+	return latency + e->sequence;
+}
+
+#define elevator_merge_before(q, req, lat)	__elevator_merge((q), (req), (lat), 0)
+#define elevator_merge_after(q, req, lat)	__elevator_merge((q), (req), (lat), 1)
+static inline void __elevator_merge(elevator_t * elevator, struct request * req, int latency, int after)
+{
+	int sequence = elevator_sequence(elevator, latency);
+	if (after)
+		sequence -= req->nr_segments;
+	if (elevator_sequence_before(sequence, req->elevator_sequence))
+		req->elevator_sequence = sequence;
+}
+
+static inline void elevator_account_request(elevator_t * elevator, struct request * req)
+{
+	elevator->sequence++;
+	if (req->cmd == READ)
+		elevator->read_pendings++;
+	elevator->nr_segments++;
+}
+
+static inline int elevator_request_latency(elevator_t * elevator, int rw)
+{
+	int latency;
+
+	latency = elevator->read_latency;
+	if (rw != READ)
+		latency = elevator->write_latency;
+
+	return latency;
+}
+
+#endif
diff -u --recursive --new-file v2.3.51/linux/include/linux/fb.h linux/include/linux/fb.h
--- v2.3.51/linux/include/linux/fb.h	Fri Mar 10 16:40:49 2000
+++ linux/include/linux/fb.h	Mon Mar 13 10:21:50 2000
@@ -232,7 +232,7 @@
 
 #ifdef __KERNEL__
 
-#if 1 /* to go away in 2.4.0 */
+#if 1 /* to go away in 2.5.0 */
 extern int GET_FB_IDX(kdev_t rdev);
 #else
 #define GET_FB_IDX(node)	(MINOR(node))
@@ -283,15 +283,12 @@
     int (*fb_rasterimg)(struct fb_info *info, int start);
 };
 
-/* fb_info flags */
-#define FBINFO_FLAG_MODULE      1       /* Low-level driver is a module */
-#define FBINFO_FLAG_OPEN        2       /* Has this been open already ? */ 
-
 struct fb_info {
    char modename[40];			/* default video mode */
    kdev_t node;
    int flags;
-   int count;                           /* How many using the hardware */
+   int open;                            /* Has this been open already ? */
+#define FBINFO_FLAG_MODULE	1	/* Low-level driver is a module */
    struct fb_var_screeninfo var;        /* Current var */
    struct fb_fix_screeninfo fix;        /* Current fix */
    struct fb_monspecs monspecs;         /* Current Monitor specs */
diff -u --recursive --new-file v2.3.51/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.3.51/linux/include/linux/fs.h	Fri Mar 10 16:40:49 2000
+++ linux/include/linux/fs.h	Tue Mar 14 18:36:40 2000
@@ -160,6 +160,8 @@
 #define BLKSSZGET  _IO(0x12,104)/* get block device sector size */
 #if 0
 #define BLKPG      _IO(0x12,105)/* See blkpg.h */
+#define BLKELVGET   _IO(0x12,106)/* elevator get */
+#define BLKELVSET   _IO(0x12,107)/* elevator set */
 /* This was here just to show that the number is taken -
    probably all these _IO(0x12,*) ioctls should be moved to blkpg.h. */
 #endif
@@ -758,6 +760,7 @@
 	if (!sb->s_op || !sb->s_op->statfs)
 		return -ENOSYS;
 	memset(buf, 0xff, sizeof(struct statfs));
+	buf->f_blocks = 0;	/* Darn GNU df... */
 	return sb->s_op->statfs(sb, buf);
 }
 
diff -u --recursive --new-file v2.3.51/linux/include/linux/generic_serial.h linux/include/linux/generic_serial.h
--- v2.3.51/linux/include/linux/generic_serial.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/generic_serial.h	Sun Mar 12 19:18:55 2000
@@ -0,0 +1,105 @@
+/*
+ *  generic_serial.h
+ *
+ *  Copyright (C) 1998 R.E.Wolff@BitWizard.nl
+ *
+ *  written for the SX serial driver.
+ *     Contains the code that should be shared over all the serial drivers.
+ *
+ *  Version 0.1 -- December, 1998.
+ */
+
+#ifndef GENERIC_SERIAL_H
+#define GENERIC_SERIAL_H
+
+
+
+
+struct real_driver {
+  void                    (*disable_tx_interrupts) (void *);
+  void                    (*enable_tx_interrupts) (void *);
+  void                    (*disable_rx_interrupts) (void *);
+  void                    (*enable_rx_interrupts) (void *);
+  int                     (*get_CD) (void *);
+  void                    (*shutdown_port) (void*);
+  int                     (*set_real_termios) (void*);
+  int                     (*chars_in_buffer) (void*);
+  void                    (*close) (void*);
+  void                    (*hungup) (void*);
+  void                    (*getserial) (void*, struct serial_struct *sp);
+};
+
+
+
+struct gs_port {
+  int                     magic;
+  unsigned char           *xmit_buf; 
+  int                     xmit_head;
+  int                     xmit_tail;
+  int                     xmit_cnt;
+  /*  struct semaphore        port_write_sem; */
+  int                     flags;
+  struct termios          normal_termios;
+  struct termios          callout_termios;
+  wait_queue_head_t       open_wait;
+  wait_queue_head_t       close_wait;
+  long                    session;
+  long                    pgrp;
+  int                     count;
+  int                     blocked_open;
+  struct tty_struct       *tty;
+  int                     event;
+  unsigned short          closing_wait;
+  int                     close_delay;
+  struct real_driver      *rd;
+  int                     wakeup_chars;
+  int                     baud_base;
+  int                     baud;
+  int                     custom_divisor;
+};
+
+
+/* Flags */
+/* Warning: serial.h defines some ASYNC_ flags, they say they are "only"
+   used in serial.c, but they are also used in all other serial drivers. 
+   Make sure they don't clash with these here... */
+#define GS_TX_INTEN      0x00800000
+#define GS_RX_INTEN      0x00400000
+#define GS_ACTIVE        0x00200000
+
+
+
+#define GS_TYPE_NORMAL   1
+#define GS_TYPE_CALLOUT  2
+
+
+#define GS_DEBUG_FLUSH   0x00000001
+#define GS_DEBUG_BTR     0x00000002
+#define GS_DEBUG_TERMIOS 0x00000004
+#define GS_DEBUG_STUFF   0x00000008
+#define GS_DEBUG_CLOSE   0x00000010
+#define GS_DEBUG_FLOW    0x00000020
+
+
+void gs_put_char(struct tty_struct *tty, unsigned char ch);
+int  gs_write(struct tty_struct *tty, int from_user, 
+             const unsigned char *buf, int count);
+int  gs_write_room(struct tty_struct *tty);
+int  gs_chars_in_buffer(struct tty_struct *tty);
+void gs_flush_buffer(struct tty_struct *tty);
+void gs_flush_chars(struct tty_struct *tty);
+void gs_stop(struct tty_struct *tty);
+void gs_start(struct tty_struct *tty);
+void gs_hangup(struct tty_struct *tty);
+void gs_do_softint(void *private_);
+int  block_til_ready(void *port, struct file *filp);
+void gs_close(struct tty_struct *tty, struct file *filp);
+void gs_set_termios (struct tty_struct * tty, 
+                     struct termios * old_termios);
+int  gs_init_port(struct gs_port *port);
+int  gs_setserial(struct gs_port *port, struct serial_struct *sp);
+void gs_getserial(struct gs_port *port, struct serial_struct *sp);
+
+extern int gs_debug;
+
+#endif
diff -u --recursive --new-file v2.3.51/linux/include/linux/hfs_fs.h linux/include/linux/hfs_fs.h
--- v2.3.51/linux/include/linux/hfs_fs.h	Sat Feb 26 22:32:06 2000
+++ linux/include/linux/hfs_fs.h	Mon Mar 13 12:35:39 2000
@@ -302,7 +302,6 @@
 
 /* super.c */
 extern struct super_block *hfs_read_super(struct super_block *,void *,int);
-extern int init_hfs_fs(void);
 
 /* trans.c */
 extern void hfs_colon2mac(struct hfs_name *, const char *, int);
diff -u --recursive --new-file v2.3.51/linux/include/linux/hpfs_fs.h linux/include/linux/hpfs_fs.h
--- v2.3.51/linux/include/linux/hpfs_fs.h	Sun Jan 14 21:59:13 1996
+++ linux/include/linux/hpfs_fs.h	Mon Mar 13 12:35:39 2000
@@ -5,9 +5,4 @@
 
 #define HPFS_SUPER_MAGIC 0xf995e849
 
-/* The entry point for a VFS */
-
-extern struct super_block *hpfs_read_super (struct super_block *, void *, int);
-extern int init_hpfs_fs(void);
-
 #endif
diff -u --recursive --new-file v2.3.51/linux/include/linux/if_ppp.h linux/include/linux/if_ppp.h
--- v2.3.51/linux/include/linux/if_ppp.h	Fri Aug  6 10:44:11 1999
+++ linux/include/linux/if_ppp.h	Sun Mar 12 21:12:37 2000
@@ -21,7 +21,7 @@
  */
 
 /*
- *  ==FILEVERSION 990806==
+ *  ==FILEVERSION 20000115==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the above date.
@@ -60,8 +60,10 @@
 #define SC_ENABLE_IP	0x00000100	/* IP packets may be exchanged */
 #define SC_LOOP_TRAFFIC	0x00000200	/* send traffic to pppd */
 #define SC_MULTILINK	0x00000400	/* do multilink encapsulation */
+#define SC_MP_SHORTSEQ	0x00000800	/* use short MP sequence numbers */
 #define SC_COMP_RUN	0x00001000	/* compressor has been inited */
 #define SC_DECOMP_RUN	0x00002000	/* decompressor has been inited */
+#define SC_MP_XSHORTSEQ	0x00004000	/* transmit short MP seq numbers */
 #define SC_DEBUG	0x00010000	/* enable debug messages */
 #define SC_LOG_INPKT	0x00020000	/* log contents of good pkts recvd */
 #define SC_LOG_OUTPKT	0x00040000	/* log contents of pkts sent */
@@ -133,7 +135,11 @@
 #define PPPIOCGIDLE	_IOR('t', 63, struct ppp_idle) /* get idle time */
 #define PPPIOCNEWUNIT	_IOWR('t', 62, int)	/* create new ppp unit */
 #define PPPIOCATTACH	_IOW('t', 61, int)	/* attach to ppp unit */
-#define PPPIOCDETACH	_IOW('t', 60, int)	/* detach from ppp unit */
+#define PPPIOCDETACH	_IOW('t', 60, int)	/* detach from ppp unit/chan */
+#define PPPIOCSMRRU	_IOW('t', 59, int)	/* set multilink MRU */
+#define PPPIOCCONNECT	_IOW('t', 58, int)	/* connect channel to unit */
+#define PPPIOCDISCONN	_IO('t', 57)		/* disconnect channel */
+#define PPPIOCATTCHAN	_IOW('t', 56, int)	/* attach to ppp channel */
 
 #define SIOCGPPPSTATS   (SIOCDEVPRIVATE + 0)
 #define SIOCGPPPVER     (SIOCDEVPRIVATE + 1)	/* NEVER change this!! */
diff -u --recursive --new-file v2.3.51/linux/include/linux/iso_fs.h linux/include/linux/iso_fs.h
--- v2.3.51/linux/include/linux/iso_fs.h	Sat Feb 26 22:32:06 2000
+++ linux/include/linux/iso_fs.h	Tue Mar 14 18:36:53 2000
@@ -186,7 +186,6 @@
 extern struct dentry *isofs_lookup(struct inode *, struct dentry *);
 extern int isofs_get_block(struct inode *, long, struct buffer_head *, int);
 extern int isofs_bmap(struct inode *, int);
-extern int init_iso9660_fs(void);
 extern int isofs_lookup_grandparent(struct inode *, int);
 
 extern struct inode_operations isofs_dir_inode_operations;
diff -u --recursive --new-file v2.3.51/linux/include/linux/kmod.h linux/include/linux/kmod.h
--- v2.3.51/linux/include/linux/kmod.h	Fri Mar 10 16:40:49 2000
+++ linux/include/linux/kmod.h	Tue Mar 14 18:36:40 2000
@@ -10,7 +10,7 @@
 extern int exec_usermodehelper(char *program_path, char *argv[], char *envp[]);
 #else
 static inline int request_module(const char * name) { return -ENOSYS; }
-extern inline int exec_usermodehelper(char *program_path, char *argv[], char *envp[])
+static inline int exec_usermodehelper(char *program_path, char *argv[], char *envp[])
 {
 	return -EACCES;
 }
diff -u --recursive --new-file v2.3.51/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h
--- v2.3.51/linux/include/linux/minix_fs.h	Sat Feb 26 22:32:06 2000
+++ linux/include/linux/minix_fs.h	Mon Mar 13 12:35:39 2000
@@ -100,7 +100,6 @@
 extern struct buffer_head * minix_bread(struct inode *, int, int);
 
 extern void minix_truncate(struct inode *);
-extern int init_minix_fs(void);
 extern int minix_sync_inode(struct inode *);
 extern int minix_sync_file(struct file *, struct dentry *);
 
diff -u --recursive --new-file v2.3.51/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.3.51/linux/include/linux/mm.h	Thu Mar  2 14:36:23 2000
+++ linux/include/linux/mm.h	Tue Mar 14 18:36:41 2000
@@ -60,6 +60,7 @@
 	struct vm_operations_struct * vm_ops;
 	unsigned long vm_pgoff;		/* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
 	struct file * vm_file;
+	unsigned long vm_raend;
 	void * vm_private_data;		/* was vm_pte (shared mem) */
 };
 
@@ -83,10 +84,19 @@
 
 #define VM_EXECUTABLE	0x00001000
 #define VM_LOCKED	0x00002000
-#define VM_IO           0x00004000  /* Memory mapped I/O or similar */
+#define VM_IO           0x00004000	/* Memory mapped I/O or similar */
+
+#define VM_SEQ_READ	0x00008000	/* App will access data sequentially */
+#define VM_RAND_READ	0x00010000	/* App will not benefit from clustered reads */
 
 #define VM_STACK_FLAGS	0x00000177
 
+#define VM_READHINTMASK			(VM_SEQ_READ | VM_RAND_READ)
+#define VM_ClearReadHint(v)		(v)->vm_flags &= ~VM_READHINTMASK
+#define VM_NormalReadHint(v)		(!((v)->vm_flags & VM_READHINTMASK))
+#define VM_SequentialReadHint(v)	((v)->vm_flags & VM_SEQ_READ)
+#define VM_RandomReadHint(v)		((v)->vm_flags & VM_RAND_READ)
+
 /*
  * mapping from the currently active vm_flags protection bits (the
  * low four bits) to a page protection mask..
@@ -105,7 +115,6 @@
 	void (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
 	void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot);
 	int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags);
-	void (*advise)(struct vm_area_struct *area, unsigned long, size_t, unsigned int advise);
 	struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
 	struct page * (*wppage)(struct vm_area_struct * area, unsigned long address, struct page * page);
 	int (*swapout)(struct page *, struct file *);
diff -u --recursive --new-file v2.3.51/linux/include/linux/mmzone.h linux/include/linux/mmzone.h
--- v2.3.51/linux/include/linux/mmzone.h	Sat Feb 26 22:32:06 2000
+++ linux/include/linux/mmzone.h	Tue Mar 14 18:36:40 2000
@@ -15,8 +15,8 @@
 #define MAX_ORDER 10
 
 typedef struct free_area_struct {
-	struct list_head free_list;
-	unsigned int * map;
+	struct list_head	free_list;
+	unsigned int		*map;
 } free_area_t;
 
 struct pglist_data;
@@ -25,30 +25,31 @@
 	/*
 	 * Commonly accessed fields:
 	 */
-	spinlock_t lock;
-	unsigned long offset;
-	unsigned long free_pages;
-	char low_on_memory;
-	char zone_wake_kswapd;
-	unsigned long pages_min, pages_low, pages_high;
+	spinlock_t		lock;
+	unsigned long		offset;
+	unsigned long		free_pages;
+	char			low_on_memory;
+	char			zone_wake_kswapd;
+	unsigned long		pages_min, pages_low, pages_high;
+	struct list_head	lru_cache;
 
 	/*
 	 * free areas of different sizes
 	 */
-	free_area_t free_area[MAX_ORDER];
+	free_area_t		free_area[MAX_ORDER];
 
 	/*
 	 * rarely used fields:
 	 */
-	char * name;
-	unsigned long size;
+	char			*name;
+	unsigned long		size;
 	/*
 	 * Discontig memory support fields.
 	 */
-	struct pglist_data *zone_pgdat;
-	unsigned long zone_start_paddr;
-	unsigned long zone_start_mapnr;
-	struct page * zone_mem_map;
+	struct pglist_data	*zone_pgdat;
+	unsigned long		zone_start_paddr;
+	unsigned long		zone_start_mapnr;
+	struct page		*zone_mem_map;
 } zone_t;
 
 #define ZONE_DMA		0
diff -u --recursive --new-file v2.3.51/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v2.3.51/linux/include/linux/msdos_fs.h	Fri Mar 10 16:40:49 2000
+++ linux/include/linux/msdos_fs.h	Mon Mar 13 12:35:39 2000
@@ -272,14 +272,6 @@
 extern ssize_t fat_file_write(struct file *, const char *, size_t, loff_t *);
 extern void fat_truncate(struct inode *inode);
 
-/* vfat.c */
-extern int init_vfat_fs(void);
-
-
-/* msdosfs_syms.c */
-extern int init_msdos_fs(void);
-extern struct file_system_type msdos_fs_type;
-
 /* msdos.c */
 extern struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent);
 
@@ -294,7 +286,6 @@
 			struct inode *new_dir,struct dentry *new_dentry);
 
 /* nls.c */
-extern int init_fat_nls(void);
 extern struct fat_nls_table *fat_load_nls(int codepage);
 
 /* tables.c */
diff -u --recursive --new-file v2.3.51/linux/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h
--- v2.3.51/linux/include/linux/ncp_fs.h	Sat Feb 26 22:32:06 2000
+++ linux/include/linux/ncp_fs.h	Mon Mar 13 12:35:39 2000
@@ -228,7 +228,6 @@
 struct inode *ncp_iget(struct super_block *, struct ncp_entry_info *);
 void ncp_update_inode(struct inode *, struct ncp_entry_info *);
 void ncp_update_inode2(struct inode *, struct ncp_entry_info *);
-extern int init_ncp_fs(void);
 
 /* linux/fs/ncpfs/dir.c */
 extern struct inode_operations ncp_dir_inode_operations;
diff -u --recursive --new-file v2.3.51/linux/include/linux/nls.h linux/include/linux/nls.h
--- v2.3.51/linux/include/linux/nls.h	Wed Dec  8 14:11:28 1999
+++ linux/include/linux/nls.h	Tue Mar 14 18:36:53 2000
@@ -1,3 +1,5 @@
+#include <linux/init.h>
+
 struct nls_unicode {
 	unsigned char uni1;
 	unsigned char uni2;
@@ -9,16 +11,13 @@
 	struct nls_unicode *charset2uni;
 	unsigned char *charset2lower;
 	unsigned char *charset2upper;
-	void (*inc_use_count) (void);
-	void (*dec_use_count) (void);
+	struct module *owner;
 	struct nls_table *next;
 };
 
 /* nls.c */
-extern int init_nls(void);
 extern int register_nls(struct nls_table *);
 extern int unregister_nls(struct nls_table *);
-extern struct nls_table *find_nls(char *);
 extern struct nls_table *load_nls(char *);
 extern void unload_nls(struct nls_table *);
 extern struct nls_table *load_nls_default(void);
@@ -27,31 +26,3 @@
 extern int utf8_mbstowcs(__u16 *, const __u8 *, int);
 extern int utf8_wctomb(__u8 *, __u16, int);
 extern int utf8_wcstombs(__u8 *, const __u16 *, int);
-
-extern int init_nls_iso8859_1(void);
-extern int init_nls_iso8859_2(void);
-extern int init_nls_iso8859_3(void);
-extern int init_nls_iso8859_4(void);
-extern int init_nls_iso8859_5(void);
-extern int init_nls_iso8859_6(void);
-extern int init_nls_iso8859_7(void);
-extern int init_nls_iso8859_8(void);
-extern int init_nls_iso8859_9(void);
-extern int init_nls_iso8859_15(void);
-extern int init_nls_cp437(void);
-extern int init_nls_cp737(void);
-extern int init_nls_cp775(void);
-extern int init_nls_cp850(void);
-extern int init_nls_cp852(void);
-extern int init_nls_cp855(void);
-extern int init_nls_cp857(void);
-extern int init_nls_cp860(void);
-extern int init_nls_cp861(void);
-extern int init_nls_cp862(void);
-extern int init_nls_cp863(void);
-extern int init_nls_cp864(void);
-extern int init_nls_cp865(void);
-extern int init_nls_cp866(void);
-extern int init_nls_cp869(void);
-extern int init_nls_cp874(void);
-extern int init_nls_koi8_r(void);
diff -u --recursive --new-file v2.3.51/linux/include/linux/ntfs_fs.h linux/include/linux/ntfs_fs.h
--- v2.3.51/linux/include/linux/ntfs_fs.h	Sun Dec 21 16:41:26 1997
+++ linux/include/linux/ntfs_fs.h	Mon Mar 13 12:35:39 2000
@@ -1,7 +1,3 @@
 #ifndef _LINUX_NTFS_FS_H
 #define _LINUX_NTFS_FS_H
-
-int init_ntfs_fs(void);
-
 #endif
-
diff -u --recursive --new-file v2.3.51/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.3.51/linux/include/linux/pci.h	Sat Feb 26 22:32:07 2000
+++ linux/include/linux/pci.h	Tue Mar 14 18:37:49 2000
@@ -577,6 +577,9 @@
 extern inline void pci_set_master(struct pci_dev *dev) { }
 extern inline int pci_enable_device(struct pci_dev *dev) { return -EIO; }
 extern inline int pci_module_init(struct pci_driver *drv) { return -ENODEV; }
+extern inline int pci_assign_resource(struct pci_dev *dev, int i) { return -EBUSY;}
+extern inline int pci_register_driver(struct pci_driver *) { return 0;}
+extern inline void pci_unregister_driver(struct pci_driver *) { }
 
 #else
 
diff -u --recursive --new-file v2.3.51/linux/include/linux/personality.h linux/include/linux/personality.h
--- v2.3.51/linux/include/linux/personality.h	Tue Sep  7 12:14:07 1999
+++ linux/include/linux/personality.h	Mon Mar 13 12:35:39 2000
@@ -54,6 +54,8 @@
 extern struct exec_domain *lookup_exec_domain(unsigned long personality);
 extern int register_exec_domain(struct exec_domain *it);
 extern int unregister_exec_domain(struct exec_domain *it);
+#define put_exec_domain(it) \
+	if (it && it->module) __MOD_DEC_USE_COUNT(it->module);
 asmlinkage long sys_personality(unsigned long personality);
 
 #endif /* _PERSONALITY_H */
diff -u --recursive --new-file v2.3.51/linux/include/linux/ppp_channel.h linux/include/linux/ppp_channel.h
--- v2.3.51/linux/include/linux/ppp_channel.h	Thu Feb 10 17:11:22 2000
+++ linux/include/linux/ppp_channel.h	Sun Mar 12 21:12:37 2000
@@ -16,11 +16,9 @@
  *  as published by the Free Software Foundation; either version
  *  2 of the License, or (at your option) any later version.
  *
- * ==FILEVERSION 990909==
+ * ==FILEVERSION 20000225==
  */
 
-/* $Id: ppp_channel.h,v 1.3 2000/01/31 01:42:48 davem Exp $ */
-
 #include <linux/list.h>
 #include <linux/skbuff.h>
 
@@ -30,18 +28,19 @@
 	/* Send a packet (or multilink fragment) on this channel.
 	   Returns 1 if it was accepted, 0 if not. */
 	int	(*start_xmit)(struct ppp_channel *, struct sk_buff *);
-
+	/* Handle an ioctl call that has come in via /dev/ppp. */
+	int	(*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
 };
 
 struct ppp_channel {
 	void		*private;	/* channel private data */
 	struct ppp_channel_ops *ops;	/* operations for this channel */
-	int		xmit_qlen;	/* length of transmit queue (bytes) */
-	int		speed;		/* transfer rate (bytes/second) */
-	int		latency;	/* overhead time in milliseconds */
+	int		mtu;		/* max transmit packet size */
 	int		hdrlen;		/* amount of headroom channel needs */
-	struct list_head list;		/* link in list of channels per unit */
 	void		*ppp;		/* opaque to channel */
+	/* the following are not used at present */
+	int		speed;		/* transfer rate (bytes/second) */
+	int		latency;	/* overhead time in milliseconds */
 };
 
 #ifdef __KERNEL__
@@ -57,10 +56,32 @@
 extern void ppp_input_error(struct ppp_channel *, int code);
 
 /* Attach a channel to a given PPP unit. */
-extern int ppp_register_channel(struct ppp_channel *, int unit);
+extern int ppp_register_channel(struct ppp_channel *);
 
 /* Detach a channel from its PPP unit (e.g. on hangup). */
 extern void ppp_unregister_channel(struct ppp_channel *);
+
+/* Get the channel number for a channel */
+extern int ppp_channel_index(struct ppp_channel *);
+
+/*
+ * SMP locking notes:
+ * The channel code must ensure that when it calls ppp_unregister_channel,
+ * nothing is executing in any of the procedures above, for that
+ * channel.  The generic layer will ensure that nothing is executing
+ * in the start_xmit and ioctl routines for the channel by the time
+ * that ppp_unregister_channel returns.
+ */
+
+/* The following are temporary compatibility stuff */
+ssize_t ppp_channel_read(struct ppp_channel *chan, struct file *file,
+			 char *buf, size_t count);
+ssize_t ppp_channel_write(struct ppp_channel *chan, const char *buf,
+			  size_t count);
+unsigned int ppp_channel_poll(struct ppp_channel *chan, struct file *file,
+			      poll_table *wait);
+int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
+		      unsigned long arg);
 
 #endif /* __KERNEL__ */
 #endif
diff -u --recursive --new-file v2.3.51/linux/include/linux/ppp_defs.h linux/include/linux/ppp_defs.h
--- v2.3.51/linux/include/linux/ppp_defs.h	Thu Jan 14 22:56:06 1999
+++ linux/include/linux/ppp_defs.h	Sun Mar 12 21:12:37 2000
@@ -28,7 +28,7 @@
  */
 
 /*
- *  ==FILEVERSION 990114==
+ *  ==FILEVERSION 20000114==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the above date.
@@ -70,12 +70,15 @@
 #define PPP_IPX		0x2b	/* IPX protocol */
 #define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
 #define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
+#define PPP_MP		0x3d	/* Multilink protocol */
 #define PPP_IPV6	0x57	/* Internet Protocol Version 6 */
+#define PPP_COMPFRAG	0xfb	/* fragment compressed below bundle */
 #define PPP_COMP	0xfd	/* compressed packet */
 #define PPP_IPCP	0x8021	/* IP Control Protocol */
 #define PPP_ATCP	0x8029	/* AppleTalk Control Protocol */
 #define PPP_IPXCP	0x802b	/* IPX Control Protocol */
 #define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
+#define PPP_CCPFRAG	0x80fb	/* CCP at link level (below MP bundle) */
 #define PPP_CCP		0x80fd	/* Compression Control Protocol */
 #define PPP_LCP		0xc021	/* Link Control Protocol */
 #define PPP_PAP		0xc023	/* Password Authentication Protocol */
diff -u --recursive --new-file v2.3.51/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.3.51/linux/include/linux/proc_fs.h	Sat Feb 26 22:32:07 2000
+++ linux/include/linux/proc_fs.h	Tue Mar 14 18:36:52 2000
@@ -98,7 +98,6 @@
 
 extern struct super_block *proc_super_blocks;
 extern struct super_block *proc_read_super(struct super_block *,void *,int);
-extern int init_proc_fs(void);
 extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_entry *);
 
 extern int proc_match(int, const char *,struct proc_dir_entry *);
diff -u --recursive --new-file v2.3.51/linux/include/linux/qnx4_fs.h linux/include/linux/qnx4_fs.h
--- v2.3.51/linux/include/linux/qnx4_fs.h	Sat Feb 26 22:32:07 2000
+++ linux/include/linux/qnx4_fs.h	Mon Mar 13 12:35:39 2000
@@ -104,7 +104,6 @@
 extern struct buffer_head *qnx4_getblk(struct inode *, int, int);
 extern struct buffer_head *qnx4_bread(struct inode *, int, int);
 
-extern int init_qnx4_fs(void);
 extern int qnx4_create(struct inode *dir, struct dentry *dentry, int mode);
 extern struct inode_operations qnx4_file_inode_operations;
 extern struct inode_operations qnx4_dir_inode_operations;
diff -u --recursive --new-file v2.3.51/linux/include/linux/romfs_fs.h linux/include/linux/romfs_fs.h
--- v2.3.51/linux/include/linux/romfs_fs.h	Tue Jan 14 02:51:18 1997
+++ linux/include/linux/romfs_fs.h	Mon Mar 13 12:35:39 2000
@@ -56,7 +56,6 @@
 #ifdef __KERNEL__
 
 /* Not much now */
-extern int init_romfs_fs(void);
 
 #endif /* __KERNEL__ */
 #endif
diff -u --recursive --new-file v2.3.51/linux/include/linux/smb_fs.h linux/include/linux/smb_fs.h
--- v2.3.51/linux/include/linux/smb_fs.h	Sat Feb 26 22:32:07 2000
+++ linux/include/linux/smb_fs.h	Mon Mar 13 12:35:39 2000
@@ -120,7 +120,6 @@
 int  smb_notify_change(struct dentry *, struct iattr *);
 unsigned long smb_invent_inos(unsigned long);
 struct inode *smb_iget(struct super_block *, struct smb_fattr *);
-extern int init_smb_fs(void);
 
 /* linux/fs/smbfs/proc.c */
 __u32 smb_len(unsigned char *);
diff -u --recursive --new-file v2.3.51/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.3.51/linux/include/linux/swap.h	Fri Jan  7 19:13:23 2000
+++ linux/include/linux/swap.h	Tue Mar 14 18:36:40 2000
@@ -67,7 +67,6 @@
 FASTCALL(unsigned int nr_free_buffer_pages(void));
 FASTCALL(unsigned int nr_free_highpages(void));
 extern int nr_lru_pages;
-extern struct list_head lru_cache;
 extern atomic_t nr_async_pages;
 extern struct address_space swapper_space;
 extern atomic_t page_cache_size;
@@ -167,7 +166,7 @@
 #define	lru_cache_add(page)			\
 do {						\
 	spin_lock(&pagemap_lru_lock);		\
-	list_add(&(page)->lru, &lru_cache);	\
+	list_add(&(page)->lru, &page->zone->lru_cache);	\
 	nr_lru_pages++;				\
 	spin_unlock(&pagemap_lru_lock);		\
 } while (0)
diff -u --recursive --new-file v2.3.51/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v2.3.51/linux/include/linux/sysv_fs.h	Sat Feb 26 22:32:07 2000
+++ linux/include/linux/sysv_fs.h	Mon Mar 13 12:35:39 2000
@@ -377,7 +377,6 @@
 extern struct buffer_head * sysv_file_bread(struct inode *, int, int);
 
 extern void sysv_truncate(struct inode *);
-extern int init_sysv_fs(void);
 extern void sysv_write_inode(struct inode *);
 extern int sysv_sync_inode(struct inode *);
 extern int sysv_sync_file(struct file *, struct dentry *);
diff -u --recursive --new-file v2.3.51/linux/include/linux/telephony.h linux/include/linux/telephony.h
--- v2.3.51/linux/include/linux/telephony.h	Tue Jan  4 13:57:21 2000
+++ linux/include/linux/telephony.h	Sun Mar 12 19:18:55 2000
@@ -158,6 +158,16 @@
 	WSS = 12
 } phone_codec;
 
+struct phone_codec_data
+{
+	phone_codec type;
+	unsigned short buf_min, buf_opt, buf_max;
+};
+
+#define PHONE_QUERY_CODEC		_IOWR ('q', 0xA7, struct phone_codec_data *)
+#define PHONE_PSTN_LINETEST		_IO ('q', 0xA8)
+
+
 /******************************************************************************
 *
 * The exception structure allows us to multiplex multiple events onto the
diff -u --recursive --new-file v2.3.51/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.3.51/linux/include/linux/tty.h	Fri Jan 21 18:19:17 2000
+++ linux/include/linux/tty.h	Tue Mar 14 18:36:40 2000
@@ -303,6 +303,7 @@
 	unsigned long canon_head;
 	unsigned int canon_column;
 	struct semaphore atomic_read;
+	spinlock_t read_lock;
 };
 
 /* tty magic number */
diff -u --recursive --new-file v2.3.51/linux/include/linux/udf_fs.h linux/include/linux/udf_fs.h
--- v2.3.51/linux/include/linux/udf_fs.h	Thu Mar  2 14:36:23 2000
+++ linux/include/linux/udf_fs.h	Mon Mar 13 12:35:39 2000
@@ -60,7 +60,6 @@
 /*
  * Function prototypes (all other prototypes included in udfdecl.h)
  */
-extern int init_udf_fs(void);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.51/linux/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h
--- v2.3.51/linux/include/linux/ufs_fs.h	Sat Feb 26 22:32:07 2000
+++ linux/include/linux/ufs_fs.h	Tue Mar 14 18:36:40 2000
@@ -546,7 +546,6 @@
 extern void ufs_warning (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
 extern void ufs_error (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
 extern void ufs_panic (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
-extern int init_ufs_fs(void);
 extern void ufs_write_super (struct super_block *);
 
 /* symlink.c */
diff -u --recursive --new-file v2.3.51/linux/include/linux/umsdos_fs.h linux/include/linux/umsdos_fs.h
--- v2.3.51/linux/include/linux/umsdos_fs.h	Sat Feb 26 22:32:07 2000
+++ linux/include/linux/umsdos_fs.h	Mon Mar 13 12:35:39 2000
@@ -177,7 +177,6 @@
 extern struct inode_operations umsdos_rdir_inode_operations;
 extern struct file_operations umsdos_dir_operations;
 extern struct file_operations umsdos_rdir_operations;
-extern int init_umsdos_fs (void);
 
 #include <linux/umsdos_fs.p>
 
diff -u --recursive --new-file v2.3.51/linux/include/linux/usb.h linux/include/linux/usb.h
--- v2.3.51/linux/include/linux/usb.h	Fri Mar 10 16:40:50 2000
+++ linux/include/linux/usb.h	Tue Mar 14 18:09:51 2000
@@ -364,16 +364,20 @@
  */
 typedef int (*usb_device_irq)(int, void *, int, void *);
 
-/* --------------------------------------------------------------------------* 
- * New USB Structures                                                        *
- * --------------------------------------------------------------------------*/
-
-#define USB_DISABLE_SPD		1
-#define USB_ISO_ASAP		2
-#define USB_URB_EARLY_COMPLETE	4
-#define USB_ASYNC_UNLINK	8
+/* -------------------------------------------------------------------------------------* 
+ * New USB Structures                                                                   *
+ * -------------------------------------------------------------------------------------*/
 
-typedef struct {
+
+#define USB_DISABLE_SPD         0x0001
+#define USB_ISO_ASAP            0x0002
+#define USB_URB_EARLY_COMPLETE  0x0004
+#define USB_ASYNC_UNLINK        0x0008
+#define USB_QUEUE_BULK          0x0010
+#define USB_TIMEOUT_KILLED	0x1000	// only set by HCD!
+
+typedef struct
+{
 	unsigned int offset;
 	unsigned int length;		// expected length
 	unsigned int actual_length;
@@ -402,6 +406,7 @@
 	int number_of_packets;		// number of packets in this request (iso/irq only)
 	int interval;                   // polling interval (irq only)
 	int error_count;		// number of errors in this transfer (iso only)
+	int timeout;			// timeout (in jiffies)
 	//
 	void *context;			// context for completion routine
 	usb_complete_t complete;	// pointer to completion routine
@@ -445,6 +450,31 @@
 	(a)->start_frame=-1;\
     } while (0)
 
+#define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \
+    do {\
+	spin_lock_init(&(a)->lock);\
+	(a)->dev=aa;\
+	(a)->pipe=b;\
+	(a)->setup_packet=c;\
+	(a)->transfer_buffer=d;\
+	(a)->transfer_buffer_length=e;\
+	(a)->complete=f;\
+	(a)->context=g;\
+	(a)->timeout=h;\
+    } while (0)
+
+#define FILL_BULK_URB_TO(a,aa,b,c,d,e,f,g) \
+    do {\
+	spin_lock_init(&(a)->lock);\
+	(a)->dev=aa;\
+	(a)->pipe=b;\
+	(a)->transfer_buffer=c;\
+	(a)->transfer_buffer_length=d;\
+	(a)->complete=e;\
+	(a)->context=f;\
+	(a)->timeout=g;\
+    } while (0)
+    
 purb_t usb_alloc_urb(int iso_packets);
 void usb_free_urb (purb_t purb);
 int usb_submit_urb(purb_t purb);
diff -u --recursive --new-file v2.3.51/linux/ipc/shm.c linux/ipc/shm.c
--- v2.3.51/linux/ipc/shm.c	Fri Mar 10 16:40:50 2000
+++ linux/ipc/shm.c	Tue Mar 14 17:37:20 2000
@@ -435,6 +435,8 @@
 	default:
 		down(&shm_ids.sem);
 		for (; nr-2 <= shm_ids.max_id; nr++ ) {
+			if (nr-2 == zero_id)
+				continue;
 			if (!(shp = shm_get (nr-2))) 
 				continue;
 			if (shp->shm_perm.mode & SHM_DEST)
@@ -462,8 +464,10 @@
 
 	down(&shm_ids.sem);
 	for(i = 0; i <= shm_ids.max_id; i++) {
+		if (i == zero_id)
+			continue;
 		if (!(shp = shm_lock(i)))
-		    continue;
+			continue;
 		if (!(shp->shm_perm.mode & SHM_DEST) &&
 		    dent->d_name.len == shp->shm_namelen &&
 		    strncmp(dent->d_name.name, shp->shm_name, shp->shm_namelen) == 0)
@@ -517,7 +521,7 @@
 {
 	unsigned short dir  = pages / PTRS_PER_PTE;
 	unsigned short last = pages % PTRS_PER_PTE;
-	pte_t **ret, **ptr;
+	pte_t **ret, **ptr, *pte;
 
 	if (pages == 0)
 		return NULL;
@@ -531,7 +535,8 @@
 		*ptr = (pte_t *)__get_free_page (GFP_KERNEL);
 		if (!*ptr)
 			goto free;
-		memset (*ptr, 0, PAGE_SIZE); 
+		for (pte = *ptr; pte < *ptr + PTRS_PER_PTE; pte++)
+			pte_clear (pte);
 	}
 
 	/* The last one is probably not of PAGE_SIZE: we use kmalloc */
@@ -539,7 +544,8 @@
 		*ptr = kmalloc (last*sizeof(pte_t), GFP_KERNEL);
 		if (!*ptr)
 			goto free;
-		memset (*ptr, 0, last*sizeof(pte_t));
+		for (pte = *ptr; pte < *ptr + last; pte++)
+			pte_clear (pte);
 	}
 	return ret;
 
@@ -696,9 +702,11 @@
 {
 	struct shmid_kernel *shp;
 	int err, id = 0;
+	static int count=0;
 
 	if (!shm_sb) {
-		printk ("shmget: shm filesystem not mounted\n");
+		if(count++<5)
+			printk(KERN_WARNING "shmget: shm filesystem not mounted\n");
 		return -EINVAL;
 	}
 
@@ -886,9 +894,11 @@
 	struct shm_setbuf setbuf;
 	struct shmid_kernel *shp;
 	int err, version;
+	static int count;
 
 	if (!shm_sb) {
-		printk ("shmctl: shm filesystem not mounted\n");
+		if(count++<5)
+			printk (KERN_WARNING "shmctl: shm filesystem not mounted\n");
 		return -EINVAL;
 	}
 
@@ -1119,11 +1129,13 @@
 	if (IS_ERR (name))
 		return PTR_ERR (name);
 
+	lock_kernel();
 	file = filp_open (name, O_RDWR, 0);
 	putname (name);
-	if (IS_ERR (file))
+	if (IS_ERR (file)) {
+		unlock_kernel();
 		goto bad_file;
-	lock_kernel();
+	}
 	*raddr = do_mmap (file, addr, file->f_dentry->d_inode->i_size,
 			  (shmflg & SHM_RDONLY ? PROT_READ :
 			   PROT_READ | PROT_WRITE), flags, 0);
diff -u --recursive --new-file v2.3.51/linux/kernel/exec_domain.c linux/kernel/exec_domain.c
--- v2.3.51/linux/kernel/exec_domain.c	Thu Feb 10 17:11:23 2000
+++ linux/kernel/exec_domain.c	Mon Mar 13 12:35:39 2000
@@ -23,7 +23,7 @@
 };
 
 static struct exec_domain *exec_domains = &default_exec_domain;
-
+static spinlock_t exec_domains_lock = SPIN_LOCK_UNLOCKED;
 
 static asmlinkage void no_lcall7(int segment, struct pt_regs * regs)
 {
@@ -32,15 +32,10 @@
    * personality set incorrectly.  Check to see whether SVr4 is available,
    * and use it, otherwise give the user a SEGV.
    */
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_DEC_USE_COUNT(current->exec_domain->module);
-
+	put_exec_domain(current->exec_domain);
 	current->personality = PER_SVR4;
 	current->exec_domain = lookup_exec_domain(current->personality);
 
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_INC_USE_COUNT(current->exec_domain->module);
-
 	if (current->exec_domain && current->exec_domain->handler
 	&& current->exec_domain->handler != no_lcall7) {
 		current->exec_domain->handler(segment, regs);
@@ -55,10 +50,15 @@
 	unsigned long pers = personality & PER_MASK;
 	struct exec_domain *it;
 
+	spin_lock(&exec_domains_lock);
 	for (it=exec_domains; it; it=it->next)
-		if (pers >= it->pers_low
-		&& pers <= it->pers_high)
+		if (pers >= it->pers_low && pers <= it->pers_high) {
+			if (!try_inc_mod_count(it->module))
+				continue;
+			spin_unlock(&exec_domains_lock);
 			return it;
+		}
+	spin_unlock(&exec_domains_lock);
 
 	/* Should never get this far. */
 	printk(KERN_ERR "No execution domain for personality 0x%02lx\n", pers);
@@ -73,11 +73,15 @@
 		return -EINVAL;
 	if (it->next)
 		return -EBUSY;
+	spin_lock(&exec_domains_lock);
 	for (tmp=exec_domains; tmp; tmp=tmp->next)
-		if (tmp == it)
+		if (tmp == it) {
+			spin_unlock(&exec_domains_lock);
 			return -EBUSY;
+		}
 	it->next = exec_domains;
 	exec_domains = it;
+	spin_unlock(&exec_domains_lock);
 	return 0;
 }
 
@@ -86,14 +90,17 @@
 	struct exec_domain ** tmp;
 
 	tmp = &exec_domains;
+	spin_lock(&exec_domains_lock);
 	while (*tmp) {
 		if (it == *tmp) {
 			*tmp = it->next;
 			it->next = NULL;
+			spin_unlock(&exec_domains_lock);
 			return 0;
 		}
 		tmp = &(*tmp)->next;
 	}
+	spin_unlock(&exec_domains_lock);
 	return -EINVAL;
 }
 
@@ -113,12 +120,9 @@
 		goto out;
 
 	old_personality = current->personality;
-	if (current->exec_domain && current->exec_domain->module)
-		__MOD_DEC_USE_COUNT(current->exec_domain->module);
+	put_exec_domain(current->exec_domain);
 	current->personality = personality;
 	current->exec_domain = it;
-	if (current->exec_domain->module)
-		__MOD_INC_USE_COUNT(current->exec_domain->module);
 	ret = old_personality;
 out:
 	unlock_kernel();
@@ -130,9 +134,11 @@
 	int len = 0;
 	struct exec_domain * e;
 
+	spin_lock(&exec_domains_lock);
 	for (e=exec_domains; e && len < PAGE_SIZE - 80; e=e->next)
 		len += sprintf(page+len, "%d-%d\t%-16s\t[%s]\n",
 			e->pers_low, e->pers_high, e->name,
 			e->module ? e->module->name : "kernel");
+	spin_unlock(&exec_domains_lock);
 	return len;
 }
diff -u --recursive --new-file v2.3.51/linux/kernel/exit.c linux/kernel/exit.c
--- v2.3.51/linux/kernel/exit.c	Fri Mar 10 16:40:50 2000
+++ linux/kernel/exit.c	Mon Mar 13 14:56:06 2000
@@ -55,8 +55,8 @@
 		 * was given away by the parent in the first place.)
 		 */
 		current->counter += p->counter;
-		if (current->counter > current->priority)
-			current->counter = current->priority;
+		if (current->counter >= current->priority*2)
+			current->counter = current->priority*2-1;
 		free_task_struct(p);
 	} else {
 		printk("task releasing itself\n");
@@ -425,8 +425,7 @@
 	tsk->exit_code = code;
 	exit_notify();
 	task_unlock(tsk);
-	if (tsk->exec_domain && tsk->exec_domain->module)
-		__MOD_DEC_USE_COUNT(tsk->exec_domain->module);
+	put_exec_domain(tsk->exec_domain);
 	if (tsk->binfmt && tsk->binfmt->module)
 		__MOD_DEC_USE_COUNT(tsk->binfmt->module);
 	schedule();
diff -u --recursive --new-file v2.3.51/linux/kernel/fork.c linux/kernel/fork.c
--- v2.3.51/linux/kernel/fork.c	Fri Mar 10 16:40:50 2000
+++ linux/kernel/fork.c	Mon Mar 13 16:33:10 2000
@@ -647,8 +647,11 @@
 	p->run_list.next = NULL;
 	p->run_list.prev = NULL;
 
-	if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT))
-		p->p_pptr = p->p_opptr = current;
+	if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT)) {
+		p->p_opptr = current;
+		if (!(current->flags & PF_PTRACED))
+			p->p_pptr = current;
+	}
 	p->p_cptr = NULL;
 	init_waitqueue_head(&p->wait_chldexit);
 	p->vfork_sem = NULL;
@@ -748,8 +751,7 @@
 bad_fork_cleanup_files:
 	exit_files(p); /* blocking */
 bad_fork_cleanup:
-	if (p->exec_domain && p->exec_domain->module)
-		__MOD_DEC_USE_COUNT(p->exec_domain->module);
+	put_exec_domain(p->exec_domain);
 	if (p->binfmt && p->binfmt->module)
 		__MOD_DEC_USE_COUNT(p->binfmt->module);
 bad_fork_cleanup_count:
diff -u --recursive --new-file v2.3.51/linux/kernel/module.c linux/kernel/module.c
--- v2.3.51/linux/kernel/module.c	Fri Mar 10 16:40:50 2000
+++ linux/kernel/module.c	Sat Mar 11 08:08:46 2000
@@ -1038,4 +1038,9 @@
 	return -ENOSYS;
 }
 
+int try_inc_mod_count(struct module *mod)
+{
+	return 1;
+}
+
 #endif	/* CONFIG_MODULES */
diff -u --recursive --new-file v2.3.51/linux/kernel/pm.c linux/kernel/pm.c
--- v2.3.51/linux/kernel/pm.c	Sat Feb 26 22:32:07 2000
+++ linux/kernel/pm.c	Tue Mar 14 17:54:42 2000
@@ -28,9 +28,17 @@
 static spinlock_t pm_devs_lock = SPIN_LOCK_UNLOCKED;
 static LIST_HEAD(pm_devs);
 
-/*
- * Register a device with power management
+/**
+ *	pm_register - register a device with power management
+ *	@type: The device type 
+ *	@id: Device ID
+ *	@callback: Callback function
+ *
+ *	Add a device to the list of devices that wish to be notified about
+ *	power management events. A pm_dev structure is returnd on success,
+ *	on failure the return is NULL
  */
+ 
 struct pm_dev *pm_register(pm_dev_t type,
 			   unsigned long id,
 			   pm_callback callback)
@@ -51,9 +59,14 @@
 	return dev;
 }
 
-/*
- * Unregister a device with power management
+/**
+ *	pm_unregister -  unregister a device with power management
+ *	@dev: device to unregister
+ *
+ *	Remove a device from the power management notification lists. The
+ *	dev passed must be a handle previously returned by pm_register.
  */
+ 
 void pm_unregister(struct pm_dev *dev)
 {
 	if (dev) {
@@ -67,9 +80,16 @@
 	}
 }
 
-/*
- * Unregister all devices with matching callback
+/**
+ *	pm_unregister_all - unregister all devices with matching callback
+ *	@callback: callback function pointer
+ *
+ *	Unregister every device that would call the callback passed. This
+ *	is primarily meant as a helper function for loadable modules. It
+ *	enables a module to give up all its managed devices without keeping
+ *	its own private list.
  */
+ 
 void pm_unregister_all(pm_callback callback)
 {
 	struct list_head *entry;
@@ -86,9 +106,21 @@
 	}
 }
 
-/*
- * Send request to a single device
+/**
+ *	pm_send - send request to a single device
+ *	@dev: device to send to
+ *	@rqst: power management request
+ *	@data: data for the callback
+ *
+ *	Issue a power management request to a given device. The 
+ *	PM_SUSPEND and PM_RESUME events are handled specially. The
+ *	data field must hold the intented next state. No call is made
+ *	if the state matches.
+ *
+ *	BUGS: what stops two power management requests occuring in parallel
+ *	and conflicting.
  */
+ 
 int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
 {
 	int status = 0;
@@ -138,9 +170,26 @@
 	}
 }
 
-/*
- * Send a request to all devices
+/**
+ *	pm_send - send request to all managed device
+ *	@rqst: power management request
+ *	@data: data for the callback
+ *
+ *	Issue a power management request to a all devices. The 
+ *	PM_SUSPEND events are handled specially. Any device is 
+ *	permitted to fail a suspend by returning a non zero (error)
+ *	value from its callback function. If any device vetoes a 
+ *	suspend request then all other devices that have suspended 
+ *	during the processing of this request are restored to their
+ *	previous state.
+ *
+ *	Zero is returned on success. If a suspend fails then the status
+ *	from the device that vetoes the suspend is returned.
+ *
+ *	BUGS: what stops two power management requests occuring in parallel
+ *	and conflicting.
  */
+ 
 int pm_send_all(pm_request_t rqst, void *data)
 {
 	struct list_head *entry = pm_devs.next;
@@ -162,9 +211,19 @@
 	return 0;
 }
 
-/*
- * Find a device
+/**
+ *	pm_find  - find a device
+ *	@type: type of device
+ *	@from: Where to start looking
+ *
+ *	Scan the power management list for devices of a specific type. The
+ *	return value for a matching device may be passed to further calls
+ *	to this function to find further matches. A NULL indicates the end
+ *	of the list. 
+ *
+ *	To search from the beginning pass NULL as the from value.
  */
+ 
 struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from)
 {
 	struct list_head *entry = from ? from->entry.next:pm_devs.next;
diff -u --recursive --new-file v2.3.51/linux/mm/filemap.c linux/mm/filemap.c
--- v2.3.51/linux/mm/filemap.c	Fri Mar 10 16:40:50 2000
+++ linux/mm/filemap.c	Tue Mar 14 17:45:20 2000
@@ -25,6 +25,7 @@
 
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
+#include <asm/mman.h>
 
 #include <linux/highmem.h>
 
@@ -220,15 +221,18 @@
 	struct list_head * page_lru, * dispose;
 	struct page * page;
 
+	if (!zone)
+		BUG();
+
 	count = nr_lru_pages / (priority+1);
 
 	spin_lock(&pagemap_lru_lock);
 
-	while (count > 0 && (page_lru = lru_cache.prev) != &lru_cache) {
+	while (count > 0 && (page_lru = zone->lru_cache.prev) != &zone->lru_cache) {
 		page = list_entry(page_lru, struct page, lru);
 		list_del(page_lru);
 
-		dispose = &lru_cache;
+		dispose = &zone->lru_cache;
 		if (test_and_clear_bit(PG_referenced, &page->flags))
 			/* Roll the page at the top of the lru list,
 			 * we could also be more aggressive putting
@@ -355,8 +359,8 @@
 	nr_lru_pages--;
 
 out:
-	list_splice(&young, &lru_cache);
-	list_splice(&old, lru_cache.prev);
+	list_splice(&young, &zone->lru_cache);
+	list_splice(&old, zone->lru_cache.prev);
 
 	spin_unlock(&pagemap_lru_lock);
 
@@ -1294,6 +1298,61 @@
 }
 
 /*
+ * Read-ahead and flush behind for MADV_SEQUENTIAL areas.  Since we are
+ * sure this is sequential access, we don't need a flexible read-ahead
+ * window size -- we can always use a large fixed size window.
+ */
+static void nopage_sequential_readahead(struct vm_area_struct * vma,
+	unsigned long pgoff, unsigned long filesize)
+{
+	unsigned long ra_window;
+
+	ra_window = get_max_readahead(vma->vm_file->f_dentry->d_inode);
+	ra_window = CLUSTER_OFFSET(ra_window + CLUSTER_PAGES - 1);
+
+	/* vm_raend is zero if we haven't read ahead in this area yet.  */
+	if (vma->vm_raend == 0)
+		vma->vm_raend = vma->vm_pgoff + ra_window;
+
+	/*
+	 * If we've just faulted the page half-way through our window,
+	 * then schedule reads for the next window, and release the
+	 * pages in the previous window.
+	 */
+	if ((pgoff + (ra_window >> 1)) == vma->vm_raend) {
+		unsigned long start = vma->vm_pgoff + vma->vm_raend;
+		unsigned long end = start + ra_window;
+
+		if (end > ((vma->vm_end >> PAGE_SHIFT) + vma->vm_pgoff))
+			end = (vma->vm_end >> PAGE_SHIFT) + vma->vm_pgoff;
+		if (start > end)
+			return;
+
+		while ((start < end) && (start < filesize)) {
+			if (read_cluster_nonblocking(vma->vm_file,
+							start, filesize) < 0)
+				break;
+			start += CLUSTER_PAGES;
+		}
+		run_task_queue(&tq_disk);
+
+		/* if we're far enough past the beginning of this area,
+		   recycle pages that are in the previous window. */
+		if (vma->vm_raend > (vma->vm_pgoff + ra_window + ra_window)) {
+			unsigned long window = ra_window << PAGE_SHIFT;
+
+			end = vma->vm_start + (vma->vm_raend << PAGE_SHIFT);
+			end -= window + window;
+			filemap_sync(vma, end - window, window, MS_INVALIDATE);
+		}
+
+		vma->vm_raend += ra_window;
+	}
+
+	return;
+}
+
+/*
  * filemap_nopage() is invoked via the vma operations vector for a
  * mapped memory region to read in file data during a page fault.
  *
@@ -1339,6 +1398,12 @@
 		goto page_not_uptodate;
 
 success:
+ 	/*
+	 * Try read-ahead for sequential areas.
+	 */
+	if (VM_SequentialReadHint(area))
+		nopage_sequential_readahead(area, pgoff, size);
+
 	/*
 	 * Found the page and have a reference on it, need to check sharing
 	 * and possibly copy it over to another page..
@@ -1355,7 +1420,7 @@
 		page_cache_release(page);
 		return new_page;
 	}
-		
+
 	flush_page_to_ram(old_page);
 	return old_page;
 
@@ -1367,7 +1432,7 @@
 	 * Otherwise, we're off the end of a privately mapped file,
 	 * so we need to map a zero page.
 	 */
-	if (pgoff < size)
+	if ((pgoff < size) && !VM_RandomReadHint(area))
 		error = read_cluster_nonblocking(file, pgoff, size);
 	else
 		error = page_cache_read(file, pgoff);
@@ -1646,7 +1711,6 @@
 	return 0;
 }
 
-
 /*
  * The msync() system call.
  */
@@ -1723,6 +1787,505 @@
 	}
 out:
 	unlock_kernel();
+	up(&current->mm->mmap_sem);
+	return error;
+}
+
+static inline void setup_read_behavior(struct vm_area_struct * vma,
+	int behavior)
+{
+	VM_ClearReadHint(vma);
+	switch(behavior) {
+		case MADV_SEQUENTIAL:
+			vma->vm_flags |= VM_SEQ_READ;
+			break;
+		case MADV_RANDOM:
+			vma->vm_flags |= VM_RAND_READ;
+			break;
+		default:
+			break;
+	}
+	return;
+}
+
+static long madvise_fixup_start(struct vm_area_struct * vma,
+	unsigned long end, int behavior)
+{
+	struct vm_area_struct * n;
+
+	n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+	if (!n)
+		return -EAGAIN;
+	*n = *vma;
+	n->vm_end = end;
+	setup_read_behavior(n, behavior);
+	n->vm_raend = 0;
+	get_file(n->vm_file);
+	if (n->vm_ops && n->vm_ops->open)
+		n->vm_ops->open(n);
+	vmlist_modify_lock(vma->vm_mm);
+	vma->vm_pgoff += (end - vma->vm_start) >> PAGE_SHIFT;
+	vma->vm_start = end;
+	insert_vm_struct(current->mm, n);
+	vmlist_modify_unlock(vma->vm_mm);
+	return 0;
+}
+
+static long madvise_fixup_end(struct vm_area_struct * vma,
+	unsigned long start, int behavior)
+{
+	struct vm_area_struct * n;
+
+	n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+	if (!n)
+		return -EAGAIN;
+	*n = *vma;
+	n->vm_start = start;
+	n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
+	setup_read_behavior(n, behavior);
+	n->vm_raend = 0;
+	get_file(n->vm_file);
+	if (n->vm_ops && n->vm_ops->open)
+		n->vm_ops->open(n);
+	vmlist_modify_lock(vma->vm_mm);
+	vma->vm_end = start;
+	insert_vm_struct(current->mm, n);
+	vmlist_modify_unlock(vma->vm_mm);
+	return 0;
+}
+
+static long madvise_fixup_middle(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, int behavior)
+{
+	struct vm_area_struct * left, * right;
+
+	left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+	if (!left)
+		return -EAGAIN;
+	right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+	if (!right) {
+		kmem_cache_free(vm_area_cachep, left);
+		return -EAGAIN;
+	}
+	*left = *vma;
+	*right = *vma;
+	left->vm_end = start;
+	right->vm_start = end;
+	right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
+	left->vm_raend = 0;
+	right->vm_raend = 0;
+	atomic_add(2, &vma->vm_file->f_count);
+
+	if (vma->vm_ops && vma->vm_ops->open) {
+		vma->vm_ops->open(left);
+		vma->vm_ops->open(right);
+	}
+	vmlist_modify_lock(vma->vm_mm);
+	vma->vm_pgoff += (start - vma->vm_start) >> PAGE_SHIFT;
+	vma->vm_start = start;
+	vma->vm_end = end;
+	setup_read_behavior(vma, behavior);
+	vma->vm_raend = 0;
+	insert_vm_struct(current->mm, left);
+	insert_vm_struct(current->mm, right);
+	vmlist_modify_unlock(vma->vm_mm);
+	return 0;
+}
+
+/*
+ * We can potentially split a vm area into separate
+ * areas, each area with its own behavior.
+ */
+static long madvise_behavior(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, int behavior)
+{
+	int error = 0;
+
+	/* This caps the number of vma's this process can own */
+	if (vma->vm_mm->map_count > MAX_MAP_COUNT)
+		return -ENOMEM;
+
+	if (start == vma->vm_start) {
+		if (end == vma->vm_end) {
+			setup_read_behavior(vma, behavior);
+			vma->vm_raend = 0;
+		} else
+			error = madvise_fixup_start(vma, end, behavior);
+	} else {
+		if (end == vma->vm_end)
+			error = madvise_fixup_end(vma, start, behavior);
+		else
+			error = madvise_fixup_middle(vma, start, end, behavior);
+	}
+
+	return error;
+}
+
+/*
+ * Schedule all required I/O operations, then run the disk queue
+ * to make sure they are started.  Do not wait for completion.
+ */
+static long madvise_willneed(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end)
+{
+	long error = -EBADF;
+	struct file * file;
+	unsigned long size, rlim_rss;
+
+	/* Doesn't work if there's no mapped file. */
+	if (!vma->vm_file)
+		return error;
+	file = vma->vm_file;
+	size = (file->f_dentry->d_inode->i_size + PAGE_CACHE_SIZE - 1) >>
+							PAGE_CACHE_SHIFT;
+
+	start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+	if (end > vma->vm_end)
+		end = vma->vm_end;
+	end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+
+	/* Make sure this doesn't exceed the process's max rss. */
+	error = -EIO;
+	rlim_rss = current->rlim ?  current->rlim[RLIMIT_RSS].rlim_cur :
+				LONG_MAX; /* default: see resource.h */
+	if ((vma->vm_mm->rss + (end - start)) > rlim_rss)
+		return error;
+
+	/* round to cluster boundaries if this isn't a "random" area. */
+	if (!VM_RandomReadHint(vma)) {
+		start = CLUSTER_OFFSET(start);
+		end = CLUSTER_OFFSET(end + CLUSTER_PAGES - 1);
+
+		while ((start < end) && (start < size)) {
+			error = read_cluster_nonblocking(file, start, size);
+			start += CLUSTER_PAGES;
+			if (error < 0)
+				break;
+		}
+	} else {
+		while ((start < end) && (start < size)) {
+			error = page_cache_read(file, start);
+			start++;
+			if (error < 0)
+				break;
+		}
+	}
+
+	/* Don't wait for someone else to push these requests. */
+	run_task_queue(&tq_disk);
+
+	return error;
+}
+
+/*
+ * Application no longer needs these pages.  If the pages are dirty,
+ * it's OK to just throw them away.  The app will be more careful about
+ * data it wants to keep.  Be sure to free swap resources too.  The
+ * zap_page_range call sets things up for shrink_mmap to actually free
+ * these pages later if no one else has touched them in the meantime,
+ * although we could add these pages to a global reuse list for
+ * shrink_mmap to pick up before reclaiming other pages.
+ *
+ * NB: This interface discards data rather than pushes it out to swap,
+ * as some implementations do.  This has performance implications for
+ * applications like large transactional databases which want to discard
+ * pages in anonymous maps after committing to backing store the data
+ * that was kept in them.  There is no reason to write this data out to
+ * the swap area if the application is discarding it.
+ *
+ * An interface that causes the system to free clean pages and flush
+ * dirty pages is already available as msync(MS_INVALIDATE).
+ */
+static long madvise_dontneed(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end)
+{
+	if (vma->vm_flags & VM_LOCKED)
+		return -EINVAL;
+
+	lock_kernel();	/* is this really necessary? */
+
+	flush_cache_range(vma->vm_mm, start, end);
+	zap_page_range(vma->vm_mm, start, end - start);
+	flush_tlb_range(vma->vm_mm, start, end);
+
+	unlock_kernel();
+	return 0;
+}
+
+static long madvise_vma(struct vm_area_struct * vma, unsigned long start,
+	unsigned long end, int behavior)
+{
+	long error = -EBADF;
+
+	switch (behavior) {
+	case MADV_NORMAL:
+	case MADV_SEQUENTIAL:
+	case MADV_RANDOM:
+		error = madvise_behavior(vma, start, end, behavior);
+		break;
+
+	case MADV_WILLNEED:
+		error = madvise_willneed(vma, start, end);
+		break;
+
+	case MADV_DONTNEED:
+		error = madvise_dontneed(vma, start, end);
+		break;
+
+	default:
+		error = -EINVAL;
+		break;
+	}
+		
+	return error;
+}
+
+/*
+ * The madvise(2) system call.
+ *
+ * Applications can use madvise() to advise the kernel how it should
+ * handle paging I/O in this VM area.  The idea is to help the kernel
+ * use appropriate read-ahead and caching techniques.  The information
+ * provided is advisory only, and can be safely disregarded by the
+ * kernel without affecting the correct operation of the application.
+ *
+ * behavior values:
+ *  MADV_NORMAL - the default behavior is to read clusters.  This
+ *		results in some read-ahead and read-behind.
+ *  MADV_RANDOM - the system should read the minimum amount of data
+ *		on any access, since it is unlikely that the appli-
+ *		cation will need more than what it asks for.
+ *  MADV_SEQUENTIAL - pages in the given range will probably be accessed
+ *		once, so they can be aggressively read ahead, and
+ *		can be freed soon after they are accessed.
+ *  MADV_WILLNEED - the application is notifying the system to read
+ *		some pages ahead.
+ *  MADV_DONTNEED - the application is finished with the given range,
+ *		so the kernel can free resources associated with it.
+ *
+ * return values:
+ *  zero    - success
+ *  -EINVAL - start + len < 0, start is not page-aligned,
+ *		"behavior" is not a valid value, or application
+ *		is attempting to release locked or shared pages.
+ *  -ENOMEM - addresses in the specified range are not currently
+ *		mapped, or are outside the AS of the process.
+ *  -EIO    - an I/O error occurred while paging in data.
+ *  -EBADF  - map exists, but area maps something that isn't a file.
+ *  -EAGAIN - a kernel resource was temporarily unavailable.
+ */
+asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior)
+{
+	unsigned long end;
+	struct vm_area_struct * vma;
+	int unmapped_error = 0;
+	int error = -EINVAL;
+
+	down(&current->mm->mmap_sem);
+
+	if (start & ~PAGE_MASK)
+		goto out;
+	len = (len + ~PAGE_MASK) & PAGE_MASK;
+	end = start + len;
+	if (end < start)
+		goto out;
+
+	error = 0;
+	if (end == start)
+		goto out;
+
+	/*
+	 * If the interval [start,end) covers some unmapped address
+	 * ranges, just ignore them, but return -ENOMEM at the end.
+	 */
+	vma = find_vma(current->mm, start);
+	for (;;) {
+		/* Still start < end. */
+		error = -ENOMEM;
+		if (!vma)
+			goto out;
+
+		/* Here start < vma->vm_end. */
+		if (start < vma->vm_start) {
+			unmapped_error = -ENOMEM;
+			start = vma->vm_start;
+		}
+
+		/* Here vma->vm_start <= start < vma->vm_end. */
+		if (end <= vma->vm_end) {
+			if (start < end) {
+				error = madvise_vma(vma, start, end,
+							behavior);
+				if (error)
+					goto out;
+			}
+			error = unmapped_error;
+			goto out;
+		}
+
+		/* Here vma->vm_start <= start < vma->vm_end < end. */
+		error = madvise_vma(vma, start, vma->vm_end, behavior);
+		if (error)
+			goto out;
+		start = vma->vm_end;
+		vma = vma->vm_next;
+	}
+
+out:
+	up(&current->mm->mmap_sem);
+	return error;
+}
+
+/*
+ * Later we can get more picky about what "in core" means precisely.
+ * For now, simply check to see if the page is in the page cache,
+ * and is up to date; i.e. that no page-in operation would be required
+ * at this time if an application were to map and access this page.
+ */
+static unsigned char mincore_page(struct vm_area_struct * vma,
+	unsigned long pgoff)
+{
+	unsigned char present = 0;
+	struct address_space * as = &vma->vm_file->f_dentry->d_inode->i_data;
+	struct page * page, ** hash = page_hash(as, pgoff);
+
+	spin_lock(&pagecache_lock);
+	page = __find_page_nolock(as, pgoff, *hash);
+	if ((page) && (Page_Uptodate(page)))
+		present = 1;
+	spin_unlock(&pagecache_lock);
+
+	return present;
+}
+
+static long mincore_vma(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, unsigned char * vec)
+{
+	long error, i, remaining;
+	unsigned char * tmp;
+
+	error = -ENOMEM;
+	if (!vma->vm_file)
+		return error;
+
+	start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+	if (end > vma->vm_end)
+		end = vma->vm_end;
+	end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+
+	error = -EAGAIN;
+	tmp = (unsigned char *) __get_free_page(GFP_KERNEL);
+	if (!tmp)
+		return error;
+
+	/* (end - start) is # of pages, and also # of bytes in "vec */
+	remaining = (end - start),
+
+	error = 0;
+	for (i = 0; remaining > 0; remaining -= PAGE_SIZE, i++) {
+		int j = 0;
+		long thispiece = (remaining < PAGE_SIZE) ?
+						remaining : PAGE_SIZE;
+
+		while (j < thispiece)
+			tmp[j++] = mincore_page(vma, start++);
+
+		if (copy_to_user(vec + PAGE_SIZE * i, tmp, thispiece)) {
+			error = -EFAULT;
+			break;
+		}
+	}
+
+	free_page((unsigned long) tmp);
+	return error;
+}
+
+/*
+ * The mincore(2) system call.
+ *
+ * mincore() returns the memory residency status of the pages in the
+ * current process's address space specified by [addr, addr + len).
+ * The status is returned in a vector of bytes.  The least significant
+ * bit of each byte is 1 if the referenced page is in memory, otherwise
+ * it is zero.
+ *
+ * Because the status of a page can change after mincore() checks it
+ * but before it returns to the application, the returned vector may
+ * contain stale information.  Only locked pages are guaranteed to
+ * remain in memory.
+ *
+ * return values:
+ *  zero    - success
+ *  -EFAULT - vec points to an illegal address
+ *  -EINVAL - addr is not a multiple of PAGE_CACHE_SIZE,
+ *		or len has a nonpositive value
+ *  -ENOMEM - Addresses in the range [addr, addr + len] are
+ *		invalid for the address space of this process, or
+ *		specify one or more pages which are not currently
+ *		mapped
+ *  -EAGAIN - A kernel resource was temporarily unavailable.
+ */
+asmlinkage long sys_mincore(unsigned long start, size_t len,
+	unsigned char * vec)
+{
+	int index = 0;
+	unsigned long end;
+	struct vm_area_struct * vma;
+	int unmapped_error = 0;
+	long error = -EINVAL;
+
+	down(&current->mm->mmap_sem);
+
+	if (start & ~PAGE_MASK)
+		goto out;
+	len = (len + ~PAGE_MASK) & PAGE_MASK;
+	end = start + len;
+	if (end < start)
+		goto out;
+
+	error = 0;
+	if (end == start)
+		goto out;
+
+	/*
+	 * If the interval [start,end) covers some unmapped address
+	 * ranges, just ignore them, but return -ENOMEM at the end.
+	 */
+	vma = find_vma(current->mm, start);
+	for (;;) {
+		/* Still start < end. */
+		error = -ENOMEM;
+		if (!vma)
+			goto out;
+
+		/* Here start < vma->vm_end. */
+		if (start < vma->vm_start) {
+			unmapped_error = -ENOMEM;
+			start = vma->vm_start;
+		}
+
+		/* Here vma->vm_start <= start < vma->vm_end. */
+		if (end <= vma->vm_end) {
+			if (start < end) {
+				error = mincore_vma(vma, start, end,
+							&vec[index]);
+				if (error)
+					goto out;
+			}
+			error = unmapped_error;
+			goto out;
+		}
+
+		/* Here vma->vm_start <= start < vma->vm_end < end. */
+		error = mincore_vma(vma, start, vma->vm_end, &vec[index]);
+		if (error)
+			goto out;
+		index += (vma->vm_end - start) >> PAGE_CACHE_SHIFT;
+		start = vma->vm_end;
+		vma = vma->vm_next;
+	}
+
+out:
 	up(&current->mm->mmap_sem);
 	return error;
 }
diff -u --recursive --new-file v2.3.51/linux/mm/mlock.c linux/mm/mlock.c
--- v2.3.51/linux/mm/mlock.c	Thu Feb 10 17:11:23 2000
+++ linux/mm/mlock.c	Tue Mar 14 17:45:20 2000
@@ -31,6 +31,7 @@
 	*n = *vma;
 	n->vm_end = end;
 	n->vm_flags = newflags;
+	n->vm_raend = 0;
 	if (n->vm_file)
 		get_file(n->vm_file);
 	if (n->vm_ops && n->vm_ops->open)
@@ -55,6 +56,7 @@
 	n->vm_start = start;
 	n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
 	n->vm_flags = newflags;
+	n->vm_raend = 0;
 	if (n->vm_file)
 		get_file(n->vm_file);
 	if (n->vm_ops && n->vm_ops->open)
@@ -85,6 +87,8 @@
 	right->vm_start = end;
 	right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
 	vma->vm_flags = newflags;
+	left->vm_raend = 0;
+	right->vm_raend = 0;
 	if (vma->vm_file)
 		atomic_add(2, &vma->vm_file->f_count);
 
@@ -97,6 +101,7 @@
 	vma->vm_start = start;
 	vma->vm_end = end;
 	vma->vm_flags = newflags;
+	vma->vm_raend = 0;
 	insert_vm_struct(current->mm, left);
 	insert_vm_struct(current->mm, right);
 	vmlist_modify_unlock(vma->vm_mm);
diff -u --recursive --new-file v2.3.51/linux/mm/mmap.c linux/mm/mmap.c
--- v2.3.51/linux/mm/mmap.c	Fri Mar 10 16:40:50 2000
+++ linux/mm/mmap.c	Tue Mar 14 17:45:21 2000
@@ -249,6 +249,9 @@
 	vma->vm_flags = vm_flags(prot,flags) | mm->def_flags;
 
 	if (file) {
+		VM_ClearReadHint(vma);
+		vma->vm_raend = 0;
+
 		if (file->f_mode & 1)
 			vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
 		if (flags & MAP_SHARED) {
@@ -549,6 +552,7 @@
 		mpnt->vm_end = area->vm_end;
 		mpnt->vm_page_prot = area->vm_page_prot;
 		mpnt->vm_flags = area->vm_flags;
+		mpnt->vm_raend = 0;
 		mpnt->vm_ops = area->vm_ops;
 		mpnt->vm_pgoff = area->vm_pgoff + ((end - area->vm_start) >> PAGE_SHIFT);
 		mpnt->vm_file = area->vm_file;
diff -u --recursive --new-file v2.3.51/linux/mm/mprotect.c linux/mm/mprotect.c
--- v2.3.51/linux/mm/mprotect.c	Thu Feb 10 17:11:23 2000
+++ linux/mm/mprotect.c	Tue Mar 14 17:45:21 2000
@@ -105,6 +105,7 @@
 	*n = *vma;
 	n->vm_end = end;
 	n->vm_flags = newflags;
+	n->vm_raend = 0;
 	n->vm_page_prot = prot;
 	if (n->vm_file)
 		get_file(n->vm_file);
@@ -131,6 +132,7 @@
 	n->vm_start = start;
 	n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
 	n->vm_flags = newflags;
+	n->vm_raend = 0;
 	n->vm_page_prot = prot;
 	if (n->vm_file)
 		get_file(n->vm_file);
@@ -162,6 +164,8 @@
 	left->vm_end = start;
 	right->vm_start = end;
 	right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
+	left->vm_raend = 0;
+	right->vm_raend = 0;
 	if (vma->vm_file)
 		atomic_add(2,&vma->vm_file->f_count);
 	if (vma->vm_ops && vma->vm_ops->open) {
@@ -173,6 +177,7 @@
 	vma->vm_start = start;
 	vma->vm_end = end;
 	vma->vm_flags = newflags;
+	vma->vm_raend = 0;
 	vma->vm_page_prot = prot;
 	insert_vm_struct(current->mm, left);
 	insert_vm_struct(current->mm, right);
diff -u --recursive --new-file v2.3.51/linux/mm/mremap.c linux/mm/mremap.c
--- v2.3.51/linux/mm/mremap.c	Sun Feb 20 21:12:40 2000
+++ linux/mm/mremap.c	Tue Mar 14 17:45:21 2000
@@ -135,8 +135,8 @@
 			*new_vma = *vma;
 			new_vma->vm_start = new_addr;
 			new_vma->vm_end = new_addr+new_len;
-			new_vma->vm_pgoff = vma->vm_pgoff;
 			new_vma->vm_pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
+			new_vma->vm_raend = 0;
 			if (new_vma->vm_file)
 				get_file(new_vma->vm_file);
 			if (new_vma->vm_ops && new_vma->vm_ops->open)
diff -u --recursive --new-file v2.3.51/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.3.51/linux/mm/page_alloc.c	Tue Mar  7 14:32:26 2000
+++ linux/mm/page_alloc.c	Mon Mar 13 14:54:54 2000
@@ -26,7 +26,6 @@
 
 int nr_swap_pages = 0;
 int nr_lru_pages;
-LIST_HEAD(lru_cache);
 pg_data_t *pgdat_list = (pg_data_t *)0;
 
 static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };
@@ -59,6 +58,19 @@
  */
 #define BAD_RANGE(zone,x) (((zone) != (x)->zone) || (((x)-mem_map) < (zone)->offset) || (((x)-mem_map) >= (zone)->offset+(zone)->size))
 
+static inline unsigned long classfree(zone_t *zone)
+{
+	unsigned long free = 0;
+	zone_t *z = zone->zone_pgdat->node_zones;
+
+	while (z != zone) {
+		free += z->free_pages;
+		z++;
+	}
+	free += zone->free_pages;
+	return(free);
+}
+
 /*
  * Buddy system. Hairy. You really aren't expected to understand this
  *
@@ -135,6 +147,9 @@
 	memlist_add_head(&(base + page_idx)->list, &area->free_list);
 
 	spin_unlock_irqrestore(&zone->lock, flags);
+
+	if (classfree(zone) > zone->pages_high)
+		zone->zone_wake_kswapd = 0;
 }
 
 #define MARK_USED(index, order, area) \
@@ -201,19 +216,6 @@
 	return NULL;
 }
 
-static inline unsigned long classfree(zone_t *zone)
-{
-	unsigned long free = 0;
-	zone_t *z = zone->zone_pgdat->node_zones;
-
-	while (z != zone) {
-		free += z->free_pages;
-		z++;
-	}
-	free += zone->free_pages;
-	return(free);
-}
-
 static inline int zone_balance_memory (zone_t *zone, int gfp_mask)
 {
 	int freed;
@@ -263,21 +265,12 @@
 		{
 			unsigned long free = classfree(z);
 
-			if (free > z->pages_high)
-			{
-				if (z->low_on_memory)
-					z->low_on_memory = 0;
-				z->zone_wake_kswapd = 0;
-			}
-			else
+			if (free <= z->pages_high)
 			{
 				extern wait_queue_head_t kswapd_wait;
 
-				if (free <= z->pages_low) {
-					z->zone_wake_kswapd = 1;
-					wake_up_interruptible(&kswapd_wait);
-				} else
-					z->zone_wake_kswapd = 0;
+				z->zone_wake_kswapd = 1;
+				wake_up_interruptible(&kswapd_wait);
 
 				if (free <= z->pages_min)
 					z->low_on_memory = 1;
@@ -585,6 +578,7 @@
 			unsigned long bitmap_size;
 
 			memlist_init(&zone->free_area[i].free_list);
+			memlist_init(&zone->lru_cache);
 			mask += mask;
 			size = (size + ~mask) & mask;
 			bitmap_size = size >> i;
diff -u --recursive --new-file v2.3.51/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.3.51/linux/mm/vmscan.c	Thu Mar  2 14:36:23 2000
+++ linux/mm/vmscan.c	Mon Mar 13 14:54:54 2000
@@ -504,8 +504,7 @@
 			while (pgdat) {
 				for (i = 0; i < MAX_NR_ZONES; i++) {
 					zone = pgdat->node_zones + i;
-					if ((!zone->size) || 
-							(!zone->zone_wake_kswapd))
+					if ((!zone->size) || (!zone->zone_wake_kswapd))
 						continue;
 					do_try_to_free_pages(GFP_KSWAPD, zone);
 				}
diff -u --recursive --new-file v2.3.51/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.3.51/linux/net/appletalk/ddp.c	Fri Mar 10 16:40:50 2000
+++ linux/net/appletalk/ddp.c	Mon Mar 13 22:18:11 2000
@@ -1378,11 +1378,12 @@
 
 		if (n < 0)
 			return n;
-	} else
+	} else {
 		sk->protinfo.af_at.src_port = addr->sat_port;
 
-	if (atalk_find_or_insert_socket(sk, addr) != NULL)
-		return -EADDRINUSE;
+		if (atalk_find_or_insert_socket(sk, addr) != NULL)
+			return -EADDRINUSE;
+	}
 
 	sk->zapped = 0;
 
diff -u --recursive --new-file v2.3.51/linux/net/atm/Makefile linux/net/atm/Makefile
--- v2.3.51/linux/net/atm/Makefile	Fri Sep 10 23:57:37 1999
+++ linux/net/atm/Makefile	Sun Mar 12 19:27:22 2000
@@ -58,4 +58,4 @@
 include $(TOPDIR)/Rules.make
 
 mpoa.o: mpc.o mpoa_caches.o mpoa_proc.o
-	ld -r -o mpoa.o mpc.o mpoa_caches.o mpoa_proc.o
+	$(LD) -r -o mpoa.o mpc.o mpoa_caches.o mpoa_proc.o
diff -u --recursive --new-file v2.3.51/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.3.51/linux/net/ax25/af_ax25.c	Wed Feb 16 17:03:52 2000
+++ linux/net/ax25/af_ax25.c	Sun Mar 12 19:21:26 2000
@@ -96,6 +96,10 @@
  *      AX.25 038       Matthias(DG2FEF)        Small fixes to the syscall interface to make kernel
  *                                              independent of AX25_MAX_DIGIS used by applications.
  *                      Tomi(OH2BNS)            Fixed ax25_getname().
+ *			Joerg(DL1BKE)		Starting to phase out the support for full_sockaddr_ax25
+ *						with only 6 digipeaters and sockaddr_ax25 in ax25_bind(),
+ *						ax25_connect() and ax25_sendmsg()
+ *			Joerg(DL1BKE)		Added support for SO_BINDTODEVICE
  */
 
 #include <linux/config.h>
@@ -624,6 +628,8 @@
 static int ax25_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
 {
 	struct sock *sk = sock->sk;
+	struct net_device *dev;
+	char devname[IFNAMSIZ];
 	int opt;
 
 	if (level != SOL_AX25)
@@ -702,6 +708,22 @@
 			sk->protinfo.ax25->paclen = opt;
 			return 0;
 
+		case SO_BINDTODEVICE:
+			if (optlen > IFNAMSIZ) optlen=IFNAMSIZ;
+			if (copy_from_user(devname, optval, optlen))
+				return -EFAULT;
+
+			dev = dev_get_by_name(devname);
+			if (dev == NULL) return -ENODEV;
+
+			if (sk->type == SOCK_SEQPACKET && 
+			   (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN))
+				return -EADDRNOTAVAIL;
+		
+			sk->protinfo.ax25->ax25_dev = ax25_dev_ax25dev(dev);
+			ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
+			return 0;
+
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -710,14 +732,23 @@
 static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
 {
 	struct sock *sk = sock->sk;
+	struct ax25_dev *ax25_dev;
+	char devname[IFNAMSIZ];
+	void *valptr;
 	int val = 0;
-	int len;
+	int maxlen, length;
 
 	if (level != SOL_AX25)
 		return -ENOPROTOOPT;
 
-	if (get_user(len, optlen))
+	if (get_user(maxlen, optlen))
 		return -EFAULT;
+		
+	if (maxlen < 1)
+		return -EFAULT;
+
+	valptr = (void *) &val;
+	length = min(maxlen, sizeof(int));
 
 	switch (optname) {
 		case AX25_WINDOW:
@@ -763,17 +794,30 @@
 		case AX25_PACLEN:
 			val = sk->protinfo.ax25->paclen;
 			break;
+			
+		case SO_BINDTODEVICE:
+			ax25_dev = sk->protinfo.ax25->ax25_dev;
+
+			if (ax25_dev != NULL && ax25_dev->dev != NULL) {
+				strncpy(devname, ax25_dev->dev->name, IFNAMSIZ);
+				length = min(strlen(ax25_dev->dev->name)+1, maxlen);
+				devname[length-1] = '\0';
+			} else {
+				*devname = '\0';
+				length = 1;
+			}
+
+			valptr = (void *) devname;
+			break;
 
 		default:
 			return -ENOPROTOOPT;
 	}
 
-	len = min(len, sizeof(int));
-
-	if (put_user(len, optlen))
+	if (put_user(length, optlen))
 		return -EFAULT;
 
-	if (copy_to_user(optval, &val, len))
+	if (copy_to_user(optval, valptr, length))
 		return -EFAULT;
 
 	return 0;
@@ -997,9 +1041,9 @@
 
 /*
  *	We support a funny extension here so you can (as root) give any callsign
- *	digipeated via a local address as source. This is a hack until we add
- *	BSD 4.4 ADDIFADDR type support. It is however small and trivially backward
- *	compatible 8)
+ *	digipeated via a local address as source. This hack is obsolete now
+ *	that we've implemented support for SO_BINDTODEVICE. It is however small 
+ *	and trivially backward compatible.
  */
 static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
@@ -1011,11 +1055,16 @@
 	if (sk->zapped == 0)
 		return -EINVAL;
 
-	if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
-		return -EINVAL;
+	if (addr_len != sizeof(struct sockaddr_ax25) && 
+	    addr_len != sizeof(struct full_sockaddr_ax25)) {
+		/* support for old structure may go away some time */
+		if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
+		    (addr_len > sizeof(struct full_sockaddr_ax25)))
+			return -EINVAL;
 
-	if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25)))
-		return -EINVAL;
+		printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n",
+			current->comm);
+	}
 
 	if (addr->fsa_ax25.sax25_family != AF_AX25)
 		return -EINVAL;
@@ -1029,34 +1078,28 @@
 	else
 		sk->protinfo.ax25->source_addr = *call;
 
-	SOCK_DEBUG(sk, "AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->source_addr));
+	/*
+	 * User already set interface with SO_BINDTODEVICE
+	 */
+
+	if (sk->protinfo.ax25->ax25_dev != NULL)
+		goto done;
 
 	if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
-		if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) {
-			ax25_dev = NULL;
-			SOCK_DEBUG(sk, "AX25: bound to any device\n");
-		} else {
-			if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) {
-				SOCK_DEBUG(sk, "AX25: bind failed - no device\n");
-				return -EADDRNOTAVAIL;
-			}
-			SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name);
-		}
-	} else {
-		if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) {
-			SOCK_DEBUG(sk, "AX25: bind failed - no device\n");
+		if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 &&
+		    (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL)
+			return -EADDRNOTAVAIL;
+	}  else {
+		if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL)
 			return -EADDRNOTAVAIL;
-		}
-		SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name);
 	}
 
 	if (ax25_dev != NULL)
 		ax25_fillin_cb(sk->protinfo.ax25, ax25_dev);
 
+done:
 	ax25_insert_socket(sk->protinfo.ax25);
-
 	sk->zapped = 0;
-	SOCK_DEBUG(sk, "AX25: socket is bound\n");
 	return 0;
 }
 
@@ -1095,8 +1138,21 @@
 	/*
 	 * some sanity checks. code further down depends on this
 	 */
-	if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
-		return -EINVAL;
+
+	if (addr_len == sizeof(struct sockaddr_ax25)) {
+		/* support for this will go away in early 2.5.x */
+		printk(KERN_WARNING "ax25_connect(): %s uses obsolete socket structure\n",
+			current->comm);
+	}
+	else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
+		/* support for old structure may go away some time */
+		if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
+		    (addr_len > sizeof(struct full_sockaddr_ax25)))
+			return -EINVAL;
+
+		printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n",
+			current->comm);
+	}
 
 	if (fsa->fsa_ax25.sax25_family != AF_AX25)
 		return -EINVAL;
@@ -1105,7 +1161,7 @@
 		kfree(sk->protinfo.ax25->digipeat);
 		sk->protinfo.ax25->digipeat = NULL;
 	}
-
+	
 	/*
 	 *	Handle digi-peaters to be used.
 	 */
@@ -1138,6 +1194,9 @@
 	 *	been filled in, error if it hasn't.
 	 */
 	if (sk->zapped) {
+		/* check if we can remove this feature. It is broken. */
+		printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@poboxes.com\n",
+			current->comm);
 		if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0)
 			return err;
 		ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
@@ -1330,10 +1389,20 @@
 	if (usax != NULL) {
 		if (usax->sax25_family != AF_AX25)
 			return -EINVAL;
-		if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
-			return -EINVAL;
-		if (addr_len < (usax->sax25_ndigis * AX25_ADDR_LEN + sizeof(struct sockaddr_ax25)))
-			return -EINVAL;
+
+		if (addr_len == sizeof(struct sockaddr_ax25)) {
+			printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n",
+				current->comm);
+		}
+		else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
+			/* support for old structure may go away some time */
+			if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
+		    	    (addr_len > sizeof(struct full_sockaddr_ax25)))
+		    		return -EINVAL;
+
+			printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n",
+				current->comm);
+		}
 
 		if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) {
 			int ct           = 0;
diff -u --recursive --new-file v2.3.51/linux/net/econet/af_econet.c linux/net/econet/af_econet.c
--- v2.3.51/linux/net/econet/af_econet.c	Fri Mar 10 16:40:51 2000
+++ linux/net/econet/af_econet.c	Mon Mar 13 22:18:11 2000
@@ -51,12 +51,13 @@
    how you count) it makes sense to use a simple lookup table. */
 static struct net_device *net2dev_map[256];
 
+#define EC_PORT_IP	0xd2
+
 #ifdef CONFIG_ECONET_AUNUDP
 static spinlock_t aun_queue_lock;
 static struct socket *udpsock;
 #define AUN_PORT	0x8000
 
-#define EC_PORT_IP	0xd2
 
 struct aunhdr
 {
@@ -750,6 +751,28 @@
 	return NULL;
 }
 
+/*
+ *	Queue a received packet for a socket.
+ */
+
+static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
+			   unsigned char stn, unsigned char net,
+			   unsigned char cb, unsigned char port)
+{
+	struct ec_cb *eb = (struct ec_cb *)&skb->cb;
+	struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
+
+	memset(sec, 0, sizeof(struct sockaddr_ec));
+	sec->sec_family = AF_ECONET;
+	sec->type = ECTYPE_PACKET_RECEIVED;
+	sec->port = port;
+	sec->cb = cb;
+	sec->addr.net = net;
+	sec->addr.station = stn;
+
+	return sock_queue_rcv_skb(sk, skb);
+}
+
 #ifdef CONFIG_ECONET_AUNUDP
 
 /*
@@ -792,27 +815,6 @@
 	set_fs(oldfs);
 }
 
-/*
- *	Queue a received packet for a socket.
- */
-
-static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
-			   unsigned char stn, unsigned char net,
-			   unsigned char cb, unsigned char port)
-{
-	struct ec_cb *eb = (struct ec_cb *)&skb->cb;
-	struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
-
-	memset(sec, 0, sizeof(struct sockaddr_ec));
-	sec->sec_family = AF_ECONET;
-	sec->type = ECTYPE_PACKET_RECEIVED;
-	sec->port = port;
-	sec->cb = cb;
-	sec->addr.net = net;
-	sec->addr.station = stn;
-
-	return sock_queue_rcv_skb(sk, skb);
-}
 
 /*
  *	Handle incoming AUN packets.  Work out if anybody wants them,
@@ -1029,7 +1031,7 @@
  *	Receive an Econet frame from a device.
  */
 
-static int econet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
 {
 	struct ec_framehdr *hdr = (struct ec_framehdr *)skb->data;
 	struct sock *sk;
@@ -1132,9 +1134,9 @@
 int __init econet_proto_init(struct net_proto *pro)
 {
 	extern void econet_sysctl_register(void);
-	spin_lock_init(&aun_queue_lock);
 	sock_register(&econet_family_ops);
 #ifdef CONFIG_ECONET_AUNUDP
+	spin_lock_init(&aun_queue_lock);
 	aun_udp_initialise();
 #endif
 #ifdef CONFIG_ECONET_NATIVE
diff -u --recursive --new-file v2.3.51/linux/net/packet/af_packet.c linux/net/packet/af_packet.c
--- v2.3.51/linux/net/packet/af_packet.c	Sat Feb 26 22:32:08 2000
+++ linux/net/packet/af_packet.c	Mon Mar 13 22:18:11 2000
@@ -5,7 +5,7 @@
  *
  *		PACKET - implements raw packet sockets.
  *
- * Version:	$Id: af_packet.c,v 1.32 2000/02/21 16:25:55 davem Exp $
+ * Version:	$Id: af_packet.c,v 1.33 2000/03/13 22:11:50 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1439,6 +1439,7 @@
 		case SIOCGIFBR:
 		case SIOCSIFBR:
 #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+#ifdef CONFIG_INET
 #ifdef CONFIG_KMOD
 			if (br_ioctl_hook == NULL)
 				request_module("bridge");
@@ -1446,6 +1447,7 @@
 			if (br_ioctl_hook != NULL)
 				return br_ioctl_hook(arg);
 #endif
+#endif				
 
 			return -ENOPKG;
 			
diff -u --recursive --new-file v2.3.51/linux/net/sched/sch_ingress.c linux/net/sched/sch_ingress.c
--- v2.3.51/linux/net/sched/sch_ingress.c	Fri Jan 21 18:19:17 2000
+++ linux/net/sched/sch_ingress.c	Mon Mar 13 09:35:06 2000
@@ -229,7 +229,6 @@
 {
 	{ NULL, NULL},
 	ing_hook,
-	NULL,
 	PF_INET,
 	NF_IP_PRE_ROUTING,
 	1
diff -u --recursive --new-file v2.3.51/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.3.51/linux/net/x25/af_x25.c	Tue Nov 23 22:42:21 1999
+++ linux/net/x25/af_x25.c	Sun Mar 12 19:11:17 2000
@@ -16,6 +16,7 @@
  *	X.25 001	Jonathan Naylor	Started coding.
  *	X.25 002	Jonathan Naylor	Centralised disconnect handling.
  *					New timer architecture.
+ *	2000-11-03	Henner Eisen	MSG_EOR handling more POSIX compliant.
  */
 
 #include <linux/config.h>
@@ -851,7 +852,7 @@
 	if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_OOB | MSG_EOR))
 		return -EINVAL;
 
-	/* we currently don't support segments at the user interface */ 
+	/* we currently don't support segmented records at the user interface */
 	if (!(msg->msg_flags & MSG_EOR))
 		return -EINVAL;
 
@@ -1033,6 +1034,9 @@
 		copied = size;
 		msg->msg_flags |= MSG_TRUNC;
 	}
+
+	/* Currently, each datagram always contains a complete record */ 
+	msg->msg_flags |= MSG_EOR;
 
 	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
diff -u --recursive --new-file v2.3.51/linux/net/x25/x25_link.c linux/net/x25/x25_link.c
--- v2.3.51/linux/net/x25/x25_link.c	Thu Aug 26 13:05:47 1999
+++ linux/net/x25/x25_link.c	Sun Mar 12 19:11:17 2000
@@ -73,18 +73,25 @@
 	del_timer(&neigh->t20timer);
 }
 
+static int x25_t20timer_pending(struct x25_neigh *neigh)
+{
+	return timer_pending(&neigh->t20timer);
+}
+
 /*
  *	This handles all restart and diagnostic frames.
  */
 void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned short frametype)
 {
 	struct sk_buff *skbn;
+	int confirm;
 
 	switch (frametype) {
 		case X25_RESTART_REQUEST:
+			confirm = !x25_t20timer_pending(neigh);
 			x25_stop_t20timer(neigh);
 			neigh->state = X25_LINK_STATE_3;
-			x25_transmit_restart_confirmation(neigh);
+			if (confirm) x25_transmit_restart_confirmation(neigh);
 			break;
 
 		case X25_RESTART_CONFIRMATION:
diff -u --recursive --new-file v2.3.51/linux/scripts/Makefile linux/scripts/Makefile
--- v2.3.51/linux/scripts/Makefile	Thu Aug  6 11:14:53 1998
+++ linux/scripts/Makefile	Sun Mar 12 19:39:47 2000
@@ -33,6 +33,12 @@
 tkparse.o tkcond.o tkgen.o:
 	$(HOSTCC) $(HOSTCFLAGS) -c -o $@ $(@:.o=.c)
 
+docproc.o: docproc.c
+	$(HOSTCC) $(HOSTCFLAGS) -c -o $@ $(@:.o=.c)
+
+docproc: docproc.o
+	${HOSTCC} -o docproc docproc.o
+
 clean:
 	rm -f *~ kconfig.tk *.o tkparse mkdep split-include
 
diff -u --recursive --new-file v2.3.51/linux/scripts/docgen linux/scripts/docgen
--- v2.3.51/linux/scripts/docgen	Wed Dec 31 16:00:00 1969
+++ linux/scripts/docgen	Tue Mar 14 17:54:42 2000
@@ -0,0 +1,3 @@
+#!/bin/sh
+X=`$TOPDIR/scripts/gen-all-syms "$*"`
+$TOPDIR/scripts/docproc $X
diff -u --recursive --new-file v2.3.51/linux/scripts/docproc.c linux/scripts/docproc.c
--- v2.3.51/linux/scripts/docproc.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/docproc.c	Sun Mar 12 19:39:47 2000
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/*
+ *	A simple filter for the templates
+ */
+
+int main(int argc, char *argv[])
+{
+	char buf[1024];
+	char *vec[1024];
+	char type[64];
+	int i;
+	int vp=2;
+	pid_t pid;
+
+
+	if(chdir(getenv("TOPDIR")))
+	{
+		perror("chdir");
+		exit(1);
+	}
+	
+	/*
+	 *	Build the exec array ahead of time.
+	 */
+	vec[0]="kernel-doc";
+	vec[1]="-docbook";
+	for(i=1;vp<1021;i++)
+	{
+		if(argv[i]==NULL)
+			break;
+		vec[vp++]=type;
+		vec[vp++]=argv[i];
+	}
+	vec[vp++]=buf+2;
+	vec[vp++]=NULL;
+	
+	/*
+	 *	Now process the template
+	 */
+	 
+	while(fgets(buf, 1024, stdin))
+	{
+		if(*buf!='!')
+			printf("%s", buf);
+		else
+		{
+			fflush(stdout);
+			if(buf[1]=='E')
+				strcpy(type, "-function");
+			else if(buf[1]=='I')
+				strcpy(type, "-nofunction");	
+			else
+			{
+				fprintf(stderr, "Unknown ! escape.\n");
+				exit(1);
+			}
+			switch(pid=fork())
+			{
+				case -1:
+					perror("fork");
+					exit(1);
+				case  0:
+					execvp("scripts/kernel-doc", vec);
+					perror("exec");
+					exit(1);
+				default:
+					waitpid(pid, NULL,0);
+			}
+		}
+	}
+	exit(0);
+}
diff -u --recursive --new-file v2.3.51/linux/scripts/gen-all-syms linux/scripts/gen-all-syms
--- v2.3.51/linux/scripts/gen-all-syms	Wed Dec 31 16:00:00 1969
+++ linux/scripts/gen-all-syms	Sun Mar 12 19:39:47 2000
@@ -0,0 +1,7 @@
+#!/bin/sh
+for i in $*
+do
+	grep "EXPORT_SYMBOL.*(.*)" "$i" \
+		| sed -e "s/EXPORT_SYMBOL.*(/  /" \
+		| sed -e "s/).*$//"
+done
diff -u --recursive --new-file v2.3.51/linux/scripts/kernel-doc linux/scripts/kernel-doc
--- v2.3.51/linux/scripts/kernel-doc	Wed Dec 31 16:00:00 1969
+++ linux/scripts/kernel-doc	Mon Mar 13 09:43:37 2000
@@ -0,0 +1,865 @@
+#!/usr/bin/perl
+
+## Copyright (c) 1998 Michael Zucchi, All Rights Reserved        ##
+##                                                               ##
+## This software falls under the GNU Public License. Please read ##
+##              the COPYING file for more information            ##
+
+#
+# This will read a 'c' file and scan for embedded comments in the
+# style of gnome comments (+minor extensions - see below).
+#
+
+# Note: This only supports 'c'.
+
+# usage:
+# kerneldoc [ -docbook | -html | -text | -man ]
+#           [ -function funcname [ -function funcname ...] ] c file(s)s > outputfile
+# or
+#           [ -nofunction funcname [ -function funcname ...] ] c file(s)s > outputfile
+#
+#  Set output format using one of -docbook -html -text or -man.  Default is man.
+#
+#  -function funcname
+#	If set, then only generate documentation for the given function(s).  All
+#	other functions are ignored.
+#
+#  -nofunction funcname
+#	If set, then only generate documentation for the other function(s).  All
+#	other functions are ignored. Cannot be used with -function together
+#	(yes thats a bug - perl hackers can fix it 8))
+#
+#  c files - list of 'c' files to process
+#
+#  All output goes to stdout, with errors to stderr.
+
+#
+# format of comments.
+# In the following table, (...)? signifies optional structure.
+#                         (...)* signifies 0 or more structure elements
+# /**
+#  * function_name(:)? (- short description)?
+# (* @parameterx: (description of parameter x)?)*
+# (* a blank line)?
+#  * (Description:)? (Description of function)?
+#  * (section header: (section description)? )*
+#  (*)?*/
+#
+# So .. the trivial example would be:
+#
+# /**
+#  * my_function
+#  **/
+#
+# If the Description: header tag is ommitted, then there must be a blank line
+# after the last parameter specification.
+# e.g.
+# /**
+#  * my_function - does my stuff
+#  * @my_arg: its mine damnit
+#  *
+#  * Does my stuff explained. 
+#  */
+#
+#  or, could also use:
+# /**
+#  * my_function - does my stuff
+#  * @my_arg: its mine damnit
+#  * Description: Does my stuff explained. 
+#  */
+# etc.
+#
+# All descriptions can be multiline, apart from the short function description.
+#
+# All descriptive text is further processed, scanning for the following special
+# patterns, which are highlighted appropriately.
+#
+# 'funcname()' - function
+# '$ENVVAR' - environmental variable
+# '&struct_name' - name of a structure
+# '@parameter' - name of a parameter
+# '%CONST' - name of a constant.
+
+# match expressions used to find embedded type information
+$type_constant = "\\\%(\\w+)";
+$type_func = "(\\w+\\(\\))";
+$type_param = "\\\@(\\w+)";
+$type_struct = "\\\&(\\w+)";
+$type_env = "(\\\$\\w+)";
+
+
+# Output conversion substitutions.
+#  One for each output format
+
+# these work fairly well
+%highlights_html = ( $type_constant, "<i>\$1</i>",
+		     $type_func, "<b>\$1</b>",
+		     $type_struct, "<i>\$1</i>",
+		     $type_param, "<tt><b>\$1</b></tt>" );
+$blankline_html = "<p>";
+
+# sgml, docbook format
+%highlights_sgml = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
+		     $type_func, "<function>\$1</function>",
+		     $type_struct, "<structname>\$1</structname>",
+		     $type_env, "<envar>\$1</envar>",
+		     $type_param, "<parameter>\$1</parameter>" );
+$blankline_sgml = "</para><para>\n";
+
+# gnome, docbook format
+%highlights_gnome = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
+		     $type_func, "<function>\$1</function>",
+		     $type_struct, "<structname>\$1</structname>",
+		     $type_env, "<envar>\$1</envar>",
+		     $type_param, "<parameter>\$1</parameter>" );
+$blankline_gnome = "</para><para>\n";
+
+# these are pretty rough
+%highlights_man = ( $type_constant, "\\n.I \\\"\$1\\\"\\n",
+		    $type_func, "\\n.B \\\"\$1\\\"\\n",
+		    $type_struct, "\\n.I \\\"\$1\\\"\\n",
+		    $type_param."([\.\, ]*)\n?", "\\n.I \\\"\$1\$2\\\"\\n" );
+$blankline_man = "";
+
+# text-mode
+%highlights_text = ( $type_constant, "\$1",
+		     $type_func, "\$1",
+		     $type_struct, "\$1",
+		     $type_param, "\$1" );
+$blankline_text = "";
+
+
+sub usage {
+    print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man ]\n";
+    print "         [ -function funcname [ -function funcname ...] ]\n";
+    print "         [ -nofunction funcname [ -nofunction funcname ...] ]\n";
+    print "         c source file(s) > outputfile\n";
+    exit 1;
+}
+
+# read arguments
+if ($#ARGV==-1) {
+    usage();
+}
+
+$verbose = 0;
+$output_mode = "man";
+%highlights = %highlights_man;
+$blankline = $blankline_man;
+$modulename = "API Documentation";
+$function_only = 0;
+while ($ARGV[0] =~ m/^-(.*)/) {
+    $cmd = shift @ARGV;
+    if ($cmd eq "-html") {
+	$output_mode = "html";
+	%highlights = %highlights_html;
+	$blankline = $blankline_html;
+    } elsif ($cmd eq "-man") {
+	$output_mode = "man";
+	%highlights = %highlights_man;
+	$blankline = $blankline_man;
+    } elsif ($cmd eq "-text") {
+	$output_mode = "text";
+	%highlights = %highlights_text;
+	$blankline = $blankline_text;
+    } elsif ($cmd eq "-docbook") {
+	$output_mode = "sgml";
+	%highlights = %highlights_sgml;
+	$blankline = $blankline_sgml;
+    } elsif ($cmd eq "-gnome") {
+	$output_mode = "gnome";
+	%highlights = %highlights_gnome;
+	$blankline = $blankline_gnome;
+    } elsif ($cmd eq "-module") { # not needed for sgml, inherits from calling document
+	$modulename = shift @ARGV;
+    } elsif ($cmd eq "-function") { # to only output specific functions
+	$function_only = 1;
+	$function = shift @ARGV;
+	$function_table{$function} = 1;
+    } elsif ($cmd eq "-nofunction") { # to only output specific functions
+	$function_only = 2;
+	$function = shift @ARGV;
+	$function_table{$function} = 1;
+    } elsif ($cmd eq "-v") {
+	$verbose = 1;
+    } elsif (($cmd eq "-h") || ($cmd eq "--help")) {
+	usage();
+    }
+}
+
+
+# generate a sequence of code that will splice in highlighting information
+# using the s// operator.
+$dohighlight = "";
+foreach $pattern (keys %highlights) {
+#    print "scanning pattern $pattern ($highlights{$pattern})\n";
+    $dohighlight .=  "\$contents =~ s:$pattern:$highlights{$pattern}:gs;\n";
+}
+
+##
+# dumps section contents to arrays/hashes intended for that purpose.
+#
+sub dump_section {
+    my $name = shift @_;
+    my $contents = join "\n", @_;
+
+    if ($name =~ m/$type_constant/) {
+	$name = $1;
+#	print STDERR "constant section '$1' = '$contents'\n";
+	$constants{$name} = $contents;
+    } elsif ($name =~ m/$type_param/) {
+#	print STDERR "parameter def '$1' = '$contents'\n";
+	$name = $1;
+	$parameters{$name} = $contents;
+    } else {
+#	print STDERR "other section '$name' = '$contents'\n";
+	$sections{$name} = $contents;
+	push @sectionlist, $name;
+    }
+}
+
+##
+# output function
+#
+# parameters, a hash.
+#  function => "function name"
+#  parameterlist => @list of parameters
+#  parameters => %parameter descriptions
+#  sectionlist => @list of sections
+#  sections => %descriont descriptions
+#  
+
+sub output_highlight {
+    my $contents = join "\n", @_;
+    my $line;
+
+    eval $dohighlight;
+    foreach $line (split "\n", $contents) {
+	if ($line eq ""){
+	    print $lineprefix, $blankline;
+	} else {
+	    print $lineprefix, $line;
+	}
+	print "\n";
+    }
+}
+
+
+# output in html
+sub output_html {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+    my $count;
+    print "<h2>Function</h2>\n";
+
+    print "<i>".$args{'functiontype'}."</i>\n";
+    print "<b>".$args{'function'}."</b>\n";
+    print "(";
+    $count = 0;
+    foreach $parameter (@{$args{'parameterlist'}}) {
+	print "<i>".$args{'parametertypes'}{$parameter}."</i> <b>".$parameter."</b>\n";
+	if ($count != $#{$args{'parameterlist'}}) {
+	    $count++;
+	    print ", ";
+	}
+    }
+    print ")\n";
+
+    print "<h3>Arguments</h3>\n";
+    print "<dl>\n";
+    foreach $parameter (@{$args{'parameterlist'}}) {
+	print "<dt><i>".$args{'parametertypes'}{$parameter}."</i> <b>".$parameter."</b>\n";
+	print "<dd>";
+	output_highlight($args{'parameters'}{$parameter});
+    }
+    print "</dl>\n";
+    foreach $section (@{$args{'sectionlist'}}) {
+	print "<h3>$section</h3>\n";
+	print "<ul>\n";
+	output_highlight($args{'sections'}{$section});
+	print "</ul>\n";
+    }
+    print "<hr>\n";
+}
+
+
+# output in html
+sub output_intro_html {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+    my $count;
+
+    foreach $section (@{$args{'sectionlist'}}) {
+	print "<h3>$section</h3>\n";
+	print "<ul>\n";
+	output_highlight($args{'sections'}{$section});
+	print "</ul>\n";
+    }
+    print "<hr>\n";
+}
+
+
+
+# output in sgml DocBook
+sub output_sgml {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+    my $count;
+    my $id;
+
+    $id = $args{'module'}."-".$args{'function'};
+    $id =~ s/[^A-Za-z0-9]/-/g;
+
+    print "<refentry>\n";
+    print "<refmeta>\n";
+    print "<refentrytitle><phrase id=\"$id\">".$args{'function'}."</phrase></refentrytitle>\n";
+    print "</refmeta>\n";
+    print "<refnamediv>\n";
+    print " <refname>".$args{'function'}."</refname>\n";
+    print " <refpurpose>\n";
+    print "  ".$args{'purpose'}."\n";
+    print " </refpurpose>\n";
+    print "</refnamediv>\n";
+
+    print "<refsynopsisdiv>\n";
+    print " <title>Synopsis</title>\n";
+    print "  <funcsynopsis>\n";
+    print "   <funcdef>".$args{'functiontype'}." ";
+    print "<function>".$args{'function'}." ";
+    print "</function></funcdef>\n";
+
+#    print "<refsect1>\n";
+#    print " <title>Synopsis</title>\n";
+#    print "  <funcsynopsis>\n";
+#    print "   <funcdef>".$args{'functiontype'}." ";
+#    print "<function>".$args{'function'}." ";
+#    print "</function></funcdef>\n";
+
+    $count = 0;
+    if ($#{$args{'parameterlist'}} >= 0) {
+	foreach $parameter (@{$args{'parameterlist'}}) {
+	    print "   <paramdef>".$args{'parametertypes'}{$parameter};
+	    print " <parameter>$parameter</parameter></paramdef>\n";
+	}
+    } else {
+	print "  <void>\n";
+    }
+    print "  </funcsynopsis>\n";
+    print "</refsynopsisdiv>\n";
+#    print "</refsect1>\n";
+
+    # print parameters
+    print "<refsect1>\n <title>Arguments</title>\n";
+#    print "<para>\nArguments\n";
+    if ($#{$args{'parameterlist'}} >= 0) {
+	print " <variablelist>\n";
+	foreach $parameter (@{$args{'parameterlist'}}) {
+	    print "  <varlistentry>\n   <term><parameter>$parameter</parameter></term>\n";
+	    print "   <listitem>\n    <para>\n";
+	    $lineprefix="     ";
+	    output_highlight($args{'parameters'}{$parameter});
+	    print "    </para>\n   </listitem>\n  </varlistentry>\n";
+	}
+	print " </variablelist>\n";
+    } else {
+	print " <para>\n  None\n </para>\n";
+    }
+    print "</refsect1>\n";
+
+    # print out each section
+    $lineprefix="   ";
+    foreach $section (@{$args{'sectionlist'}}) {
+	print "<refsect1>\n <title>$section</title>\n <para>\n";
+#	print "<para>\n$section\n";
+	if ($section =~ m/EXAMPLE/i) {
+	    print "<example><para>\n";
+	}
+	output_highlight($args{'sections'}{$section});
+#	print "</para>";
+	if ($section =~ m/EXAMPLE/i) {
+	    print "</para></example>\n";
+	}
+	print " </para>\n</refsect1>\n";
+    }
+
+    print "</refentry>\n\n";
+}
+
+# output in sgml DocBook
+sub output_intro_sgml {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+    my $count;
+    my $id;
+
+    $id = $args{'module'};
+    $id =~ s/[^A-Za-z0-9]/-/g;
+
+    # print out each section
+    $lineprefix="   ";
+    foreach $section (@{$args{'sectionlist'}}) {
+	print "<refsect1>\n <title>$section</title>\n <para>\n";
+#	print "<para>\n$section\n";
+	if ($section =~ m/EXAMPLE/i) {
+	    print "<example><para>\n";
+	}
+	output_highlight($args{'sections'}{$section});
+#	print "</para>";
+	if ($section =~ m/EXAMPLE/i) {
+	    print "</para></example>\n";
+	}
+	print " </para>\n</refsect1>\n";
+    }
+
+    print "\n\n";
+}
+
+# output in sgml DocBook
+sub output_gnome {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+    my $count;
+    my $id;
+
+    $id = $args{'module'}."-".$args{'function'};
+    $id =~ s/[^A-Za-z0-9]/-/g;
+
+    print "<sect2>\n";
+    print " <title id=\"$id\">".$args{'function'}."</title>\n";
+
+#    print "<simplesect>\n";
+#    print " <title>Synopsis</title>\n";
+    print "  <funcsynopsis>\n";
+    print "   <funcdef>".$args{'functiontype'}." ";
+    print "<function>".$args{'function'}." ";
+    print "</function></funcdef>\n";
+
+    $count = 0;
+    if ($#{$args{'parameterlist'}} >= 0) {
+	foreach $parameter (@{$args{'parameterlist'}}) {
+	    print "   <paramdef>".$args{'parametertypes'}{$parameter};
+	    print " <parameter>$parameter</parameter></paramdef>\n";
+	}
+    } else {
+	print "  <void>\n";
+    }
+    print "  </funcsynopsis>\n";
+#    print "</simplesect>\n";
+#    print "</refsect1>\n";
+
+    # print parameters
+#    print "<simplesect>\n <title>Arguments</title>\n";
+#    if ($#{$args{'parameterlist'}} >= 0) {
+#	print " <variablelist>\n";
+#	foreach $parameter (@{$args{'parameterlist'}}) {
+#	    print "  <varlistentry>\n   <term><parameter>$parameter</parameter></term>\n";
+#	    print "   <listitem>\n    <para>\n";
+#	    $lineprefix="     ";
+#	    output_highlight($args{'parameters'}{$parameter});
+#	    print "    </para>\n   </listitem>\n  </varlistentry>\n";
+#	}
+#	print " </variablelist>\n";
+#    } else {
+#	print " <para>\n  None\n </para>\n";
+#    }
+#    print "</simplesect>\n";
+
+#    print "<simplesect>\n <title>Arguments</title>\n";
+    if ($#{$args{'parameterlist'}} >= 0) {
+	print " <informaltable pgwide=\"1\" frame=\"none\" role=\"params\">\n";
+	print "<tgroup cols=\"2\">\n";
+	print "<colspec colwidth=\"2*\">\n";
+	print "<colspec colwidth=\"8*\">\n";
+	print "<tbody>\n";
+	foreach $parameter (@{$args{'parameterlist'}}) {
+	    print "  <row><entry align=\"right\"><parameter>$parameter</parameter></entry>\n";
+	    print "   <entry>\n";
+	    $lineprefix="     ";
+	    output_highlight($args{'parameters'}{$parameter});
+	    print "    </entry></row>\n";
+	}
+	print " </tbody></tgroup></informaltable>\n";
+    } else {
+	print " <para>\n  None\n </para>\n";
+    }
+#    print "</simplesect>\n";
+
+    # print out each section
+    $lineprefix="   ";
+    foreach $section (@{$args{'sectionlist'}}) {
+	print "<simplesect>\n <title>$section</title>\n";
+#	print "<para>\n$section\n";
+	if ($section =~ m/EXAMPLE/i) {
+	    print "<example><programlisting>\n";
+	} else {
+	}
+	print "<para>\n";
+	output_highlight($args{'sections'}{$section});
+#	print "</para>";
+	print "</para>\n";
+	if ($section =~ m/EXAMPLE/i) {
+	    print "</programlisting></example>\n";
+	} else {
+	}
+	print " </simplesect>\n";
+    }
+
+    print "</sect2>\n\n";
+}
+
+##
+# output in man
+sub output_man {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+    my $count;
+
+    print ".TH \"$args{'module'}\" \"$args{'function'}\" \"25 May 1998\" \"API Manual\" LINUX\n";
+
+    print ".SH Function\n";
+
+    print ".I \"".$args{'functiontype'}."\"\n";
+    print ".B \"".$args{'function'}."\"\n";
+    print "(\n";
+    $count = 0;
+    foreach $parameter (@{$args{'parameterlist'}}) {
+	print ".I \"".$args{'parametertypes'}{$parameter}."\"\n.B \"".$parameter."\"\n";
+	if ($count != $#{$args{'parameterlist'}}) {
+	    $count++;
+	    print ",\n";
+	}
+    }
+    print ")\n";
+
+    print ".SH Arguments\n";
+    foreach $parameter (@{$args{'parameterlist'}}) {
+	print ".IP \"".$args{'parametertypes'}{$parameter}." ".$parameter."\" 12\n";
+	output_highlight($args{'parameters'}{$parameter});
+    }
+    foreach $section (@{$args{'sectionlist'}}) {
+	print ".SH \"$section\"\n";
+	output_highlight($args{'sections'}{$section});
+    }
+}
+
+sub output_intro_man {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+    my $count;
+
+    print ".TH \"$args{'module'}\" \"$args{'module'}\" \"25 May 1998\" \"API Manual\" LINUX\n";
+
+    foreach $section (@{$args{'sectionlist'}}) {
+	print ".SH \"$section\"\n";
+	output_highlight($args{'sections'}{$section});
+    }
+}
+
+##
+# output in text
+sub output_text {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+
+    print "Function = ".$args{'function'}."\n";
+    print "  return type: ".$args{'functiontype'}."\n\n";
+    foreach $parameter (@{$args{'parameterlist'}}) {
+	print " ".$args{'parametertypes'}{$parameter}." ".$parameter."\n";
+	print "    -> ".$args{'parameters'}{$parameter}."\n";
+    }
+    foreach $section (@{$args{'sectionlist'}}) {
+	print " $section:\n";
+	print "    -> ";
+	output_highlight($args{'sections'}{$section});
+    }
+}
+
+sub output_intro_text {
+    my %args = %{$_[0]};
+    my ($parameter, $section);
+
+    foreach $section (@{$args{'sectionlist'}}) {
+	print " $section:\n";
+	print "    -> ";
+	output_highlight($args{'sections'}{$section});
+    }
+}
+
+##
+# generic output function - calls the right one based
+# on current output mode.
+sub output_function {
+#    output_html(@_);
+    eval "output_".$output_mode."(\@_);";
+}
+
+##
+# generic output function - calls the right one based
+# on current output mode.
+sub output_intro {
+#    output_html(@_);
+    eval "output_intro_".$output_mode."(\@_);";
+}
+
+
+##
+# takes a function prototype and spits out all the details
+# stored in the global arrays/hsahes.
+sub dump_function {
+    my $prototype = shift @_;
+
+    $prototype =~ s/^static+ //;
+    $prototype =~ s/^extern+ //;
+    $prototype =~ s/^inline+ //;
+    $prototype =~ s/^__inline__+ //;
+
+    if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
+	$prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
+	$prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
+	$prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
+	$prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/)  {
+	$return_type = $1;
+	$function_name = $2;
+	$args = $3;
+
+#	print STDERR "ARGS = '$args'\n";
+
+	foreach $arg (split ',', $args) {
+	    # strip leading/trailing spaces
+	    $arg =~ s/^\s*//;
+	    $arg =~ s/\s*$//;
+#	    print STDERR "SCAN ARG: '$arg'\n";
+	    @args = split('\s', $arg);
+
+#	    print STDERR " -> @args\n";
+	    $param = pop @args;
+#	    print STDERR " -> @args\n";
+	    if ($param =~ m/^(\*+)(.*)/) {
+		$param = $2;
+		push @args, $1;
+	    }
+	    $type = join " ", @args;
+
+	    if ($type eq "")
+	    {
+		$type="";
+		$param="void";
+		$parameters{void} = "no arguments";
+	    }
+            if ($parameters{$param} eq "") {
+	        $parameters{$param} = "-- undescribed --";
+	        print STDERR "Warning($lineno): Function parameter '$param' not described in '$function_name'\n";
+	    }
+
+	    push @parameterlist, $param;
+	    $parametertypes{$param} = $type;
+#	    print STDERR "param = '$param', type = '$type'\n";
+	}
+    } else {
+	print STDERR "Error($lineno): cannot understand prototype: '$prototype'\n";
+	return;
+    }
+
+    if ($function_only==0 || 
+     ( $function_only == 1 && defined($function_table{$function_name})) || 
+     ( $function_only == 2 && !defined($function_table{$function_name})))
+    {
+	output_function({'function' => $function_name,
+			 'module' => $modulename,
+			 'functiontype' => $return_type,
+			 'parameterlist' => \@parameterlist,
+			 'parameters' => \%parameters,
+			 'parametertypes' => \%parametertypes,
+			 'sectionlist' => \@sectionlist,
+			 'sections' => \%sections,
+			 'purpose' => $function_purpose
+			 });
+    }
+}
+
+######################################################################
+# main
+# states
+# 0 - normal code
+# 1 - looking for function name
+# 2 - scanning field start.
+# 3 - scanning prototype.
+$state = 0;
+$section = "";
+
+$doc_special = "\@\%\$\&";
+
+$doc_start = "^/\\*\\*\$";
+$doc_end = "\\*/";
+$doc_com = "\\s*\\*\\s*";
+$doc_func = $doc_com."(\\w+):?";
+$doc_sect = $doc_com."([".$doc_special."]?[\\w ]+):(.*)";
+$doc_content = $doc_com."(.*)";
+$doc_block = $doc_com."DOC:\\s*(.*)?";
+
+%constants = ();
+%parameters = ();
+@parameterlist = ();
+%sections = ();
+@sectionlist = ();
+
+$contents = "";
+$section_default = "Description";	# default section
+$section_intro = "Introduction";
+$section = $section_default;
+
+$lineno = 0;
+foreach $file (@ARGV) {
+    if (!open(IN,"<$file")) {
+	print STDERR "Error: Cannot open file $file\n";
+	next;
+    }
+    while (<IN>) {
+	$lineno++;
+
+	if ($state == 0) {
+	    if (/$doc_start/o) {
+		$state = 1;		# next line is always the function name
+	    }
+	} elsif ($state == 1) {	# this line is the function name (always)
+	    if (/$doc_block/o) {
+		$state = 4;
+		$contents = "";
+		if ( $1 eq "" ) {
+			$section = $section_intro;
+		} else {
+			$section = $1;
+		}
+            }
+	    elsif (/$doc_func/o) {
+		$function = $1;
+		$state = 2;
+		if (/-(.*)/) {
+		    $function_purpose = $1;
+		} else {
+		    $function_purpose = "";
+		}
+		if ($verbose) {
+		    print STDERR "Info($lineno): Scanning doc for $function\n";
+		}
+	    } else {
+		print STDERR "WARN($lineno): Cannot understand $_ on line $lineno",
+		" - I thought it was a doc line\n";
+		$state = 0;
+	    }
+	} elsif ($state == 2) {	# look for head: lines, and include content
+	    if (/$doc_sect/o) {
+		$newsection = $1;
+		$newcontents = $2;
+
+		if ($contents ne "") {
+		    dump_section($section, $contents);
+		    $section = $section_default;
+		}
+
+		$contents = $newcontents;
+		if ($contents ne "") {
+		    $contents .= "\n";
+		}
+		$section = $newsection;
+	    } elsif (/$doc_end/) {
+
+		if ($contents ne "") {
+		    dump_section($section, $contents);
+		    $section = $section_default;
+		    $contents = "";
+		}
+
+#	    print STDERR "end of doc comment, looking for prototype\n";
+		$prototype = "";
+		$state = 3;
+	    } elsif (/$doc_content/) {
+		# miguel-style comment kludge, look for blank lines after
+		# @parameter line to signify start of description
+		if ($1 eq "" && $section =~ m/^@/) {
+		    dump_section($section, $contents);
+		    $section = $section_default;
+		    $contents = "";
+		} else {
+		    $contents .= $1."\n";
+		}
+	    } else {
+		# i dont know - bad line?  ignore.
+		print STDERR "WARNING($lineno): bad line: $_"; 
+	    }
+	} elsif ($state == 3) {	# scanning for function { (end of prototype)
+	    if (m#\s*/\*\s+MACDOC\s*#io) {
+	      # do nothing
+	    }
+	    elsif (/([^\{]*)/) {
+		$prototype .= $1;
+	    }
+	    if (/\{/) {
+		$prototype =~ s@/\*.*?\*/@@gos;	# strip comments.
+		$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
+		$prototype =~ s@^ +@@gos; # strip leading spaces
+		dump_function($prototype);
+
+		$function = "";
+		%constants = ();
+		%parameters = ();
+		%parametertypes = ();
+		@parameterlist = ();
+		%sections = ();
+		@sectionlist = ();
+		$prototype = "";
+
+		$state = 0;
+	    }
+	} elsif ($state == 4) {
+		# Documentation block
+	        if (/$doc_block/) {
+			dump_section($section, $contents);
+			output_intro({'sectionlist' => \@sectionlist,
+				      'sections' => \%sections });
+			$contents = "";
+			$function = "";
+			%constants = ();
+			%parameters = ();
+			%parametertypes = ();
+			@parameterlist = ();
+			%sections = ();
+			@sectionlist = ();
+			$prototype = "";
+			if ( $1 eq "" ) {
+				$section = $section_intro;
+			} else {
+				$section = $1;
+			}
+                }
+		elsif (/$doc_end/)
+		{
+			dump_section($section, $contents);
+			output_intro({'sectionlist' => \@sectionlist,
+				      'sections' => \%sections });
+			$contents = "";
+			$function = "";
+			%constants = ();
+			%parameters = ();
+			%parametertypes = ();
+			@parameterlist = ();
+			%sections = ();
+			@sectionlist = ();
+			$prototype = "";
+			$state = 0;
+		}
+		elsif (/$doc_content/)
+		{
+			if ( $1 eq "" )
+			{
+				$contents .= $blankline;
+			}
+			else
+			{
+				$contents .= $1 . "\n";
+			}	
+        	}
+          }
+    }
+}
+
