]> git.neil.brown.name Git - history.git/commitdiff
Import 2.0.35pre2 2.0.35pre2
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:47 +0000 (15:11 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:47 +0000 (15:11 -0500)
113 files changed:
CREDITS
Documentation/Configure.help
Documentation/networking/tlan.README
Documentation/paride.txt [new file with mode: 0644]
MAINTAINERS
Makefile
arch/i386/kernel/head.S
arch/i386/kernel/ptrace.c
arch/i386/kernel/setup.c
arch/sparc/config.in
drivers/block/Config.in
drivers/block/Makefile
drivers/block/linear.c
drivers/block/ll_rw_blk.c
drivers/block/md.c
drivers/block/paride/Config.in [new file with mode: 0644]
drivers/block/paride/Makefile [new file with mode: 0644]
drivers/block/paride/aten.c [new file with mode: 0644]
drivers/block/paride/bpck.c [new file with mode: 0644]
drivers/block/paride/comm.c [new file with mode: 0644]
drivers/block/paride/dstr.c [new file with mode: 0644]
drivers/block/paride/epat.c [new file with mode: 0644]
drivers/block/paride/epia.c [new file with mode: 0644]
drivers/block/paride/fit2.c [new file with mode: 0644]
drivers/block/paride/fit3.c [new file with mode: 0644]
drivers/block/paride/frpw.c [new file with mode: 0644]
drivers/block/paride/kbic.c [new file with mode: 0644]
drivers/block/paride/ktti.c [new file with mode: 0644]
drivers/block/paride/mkd [new file with mode: 0644]
drivers/block/paride/on20.c [new file with mode: 0644]
drivers/block/paride/on26.c [new file with mode: 0644]
drivers/block/paride/paride.c [new file with mode: 0644]
drivers/block/paride/paride.h [new file with mode: 0644]
drivers/block/paride/pcd.c [new file with mode: 0644]
drivers/block/paride/pd.c [new file with mode: 0644]
drivers/block/paride/pf.c [new file with mode: 0644]
drivers/block/paride/pg.c [new file with mode: 0644]
drivers/block/paride/pseudo.h [new file with mode: 0644]
drivers/block/paride/pt.c [new file with mode: 0644]
drivers/block/paride/setup.h [new file with mode: 0644]
drivers/block/paride/spinlock.h [new file with mode: 0644]
drivers/block/raid0.c
drivers/block/raid1.c [new file with mode: 0644]
drivers/block/raid5.c [new file with mode: 0644]
drivers/block/rd.c
drivers/cdrom/cm206.c
drivers/cdrom/mcd.c
drivers/cdrom/sonycd535.c
drivers/char/ftape/ftape-bsm.h
drivers/char/ftape/ftape-ctl.h
drivers/char/ftape/kernel-interface.h
drivers/isdn/isdn_net.c
drivers/isdn/sc/interrupt.c
drivers/net/3c503.c
drivers/net/3c515.c
drivers/net/3c59x.c
drivers/net/Changelog.tlan
drivers/net/dgrs.c
drivers/net/e2100.c
drivers/net/eth16i.c
drivers/net/hp-plus.c
drivers/net/hp.c
drivers/net/ne2k-pci.c
drivers/net/ni52.c
drivers/net/smc-ultra.c
drivers/net/smc-ultra32.c
drivers/net/tlan.c
drivers/net/tlan.h
drivers/net/wd.c
drivers/net/yellowfin.c
drivers/scsi/AM53C974.c
drivers/scsi/AM53C974.h
drivers/scsi/FlashPoint.c
drivers/scsi/NCR53c406a.c
drivers/scsi/NCR53c406a.h
drivers/scsi/advansys.c
drivers/scsi/aha152x.c
drivers/scsi/aha1542.c
drivers/scsi/fdomain.c
drivers/scsi/fdomain.h
drivers/scsi/hosts.c
drivers/scsi/pas16.c
drivers/scsi/qlogicfas.c
drivers/scsi/qlogicfas.h
drivers/scsi/scsi.c
drivers/scsi/scsi.h
drivers/scsi/sd.c
drivers/scsi/t128.c
drivers/scsi/wd7000.c
drivers/scsi/wd7000.h
fs/Config.in
fs/buffer.c
fs/isofs/inode.c
fs/proc/array.c
fs/super.c
fs/sysv/inode.c
include/asm-i386/ptrace.h
include/linux/blk.h
include/linux/blkdev.h
include/linux/fs.h
include/linux/md.h
include/linux/pg.h [new file with mode: 0644]
include/linux/raid1.h [new file with mode: 0644]
include/linux/raid5.h [new file with mode: 0644]
include/linux/socket.h
init/main.c
kernel/ksyms.c
kernel/sched.c
mm/page_alloc.c
net/ipv4/rarp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_output.c
net/socket.c

diff --git a/CREDITS b/CREDITS
index bc06a6f0943fc923b7d224d65a1589ba2d41b90c..1780cb1514d75ee27925171d02d77c82113eafdd 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -1594,11 +1594,11 @@ S: Fin-00150 Helsinki
 S: Finland
 
 N: Roger E. Wolff
-E: wolff@dutecai.et.tudelft.nl
+E: R.E.Wolff@BitWizard.nl
 D: Written kmalloc/kfree
 D: Written Specialix IO8+ driver
-S: Oosterstraat 23
-S: 2611 TT Delft
+S: Van Bronckhorststraat 12 
+S: 2612 XV  Delft
 S: The Netherlands
 
 N: Frank Xia
index 36f9e4c06863e54a847de0422af739b0c5a42975..9031256d1eb79eb7f38925f7f1f3eb3f32ff0054 100644 (file)
@@ -320,6 +320,220 @@ CONFIG_BLK_DEV_XD
   Documentation/modules.txt.
   It's pretty unlikely that you have one of these: say N.
 
+Parallel port IDE device support
+CONFIG_PARIDE
+  There are many external CD-ROM and disk devices that connect through
+  your computer's parallel port. Most of them are actually IDE devices
+  using a parallel port IDE adapter. This option enables the PARIDE
+  subsystem which contains drivers for many of these external drives.
+  Read linux/Documentation/paride.txt for more information.  If you 
+  built PARIDE support into your kernel,  you may still build the 
+  individual protocol modules and high-level drivers as loadable 
+  modules. If you build this support as a module, it will be called 
+  paride.o.  To use the PARIDE support, you must say Y or M here 
+  and also to at least one high-level driver (e.g. "Parallel port 
+  IDE disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI 
+  disks" etc.) and to at least one protocol driver (e.g. "ATEN 
+  EH-100 protocol", "MicroSolutions backpack protocol", "DataStor 
+  Commuter protocol" etc.).
+
+Parallel port IDE disks
+CONFIG_PARIDE_PD 
+  This option enables the high-level driver for IDE-type disk devices 
+  connected through a parallel port.  If you chose to build PARIDE 
+  support into your kernel, you may answer Y here to build in the 
+  parallel port IDE driver, otherwise you should answer M to build 
+  it as a loadable module.  The module will be called pd.o.  You 
+  must also have at least one parallel port protocol driver in your 
+  system.  Among the devices supported by this driver are the SyQuest 
+  EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack
+  hard drives from MicroSolutions.
+
+Parallel port ATAPI CD-ROMs
+CONFIG_PARIDE_PCD 
+  This option enables the high-level driver for ATAPI CD-ROM devices
+  connected through a parallel port. If you chose to build PARIDE
+  support into your kernel, you may answer Y here to build in the
+  parallel port ATAPI CD-ROM driver, otherwise you should answer M to
+  build it as a loadable module. The module will be called pcd.o. You
+  must also have at least one parallel port protocol driver in your
+  system. Among the devices supported by this driver are the
+  MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If
+  you have such a CD-ROM drive, you should also say Y to "ISO9660
+  cdrom filesystem support" below, because that's the filesystem used
+  on CDROMs.
+
+Parallel port ATAPI disks
+CONFIG_PARIDE_PF 
+  This option enables the high-level driver for ATAPI disk devices
+  connected through a parallel port.  If you chose to build PARIDE
+  support into your kernel, you may answer Y here to build in the
+  parallel port ATAPI disk driver, otherwise you should answer M
+  to build it as a loadable module.  The module will be called pf.o.
+  You must also have at least one parallel port protocol driver in
+  your system.  Among the devices supported by this driver are the
+  MicroSolutions backpack PD/CD drive and the Imation Superdisk
+  LS-120 drive.
+
+Parallel port ATAPI tapes
+CONFIG_PARIDE_PT
+  This option enables the high-level driver for ATAPI tape devices
+  connected through a parallel port.  If you chose to build PARIDE
+  support into your kernel, you may answer Y here to build in the
+  parallel port ATAPI tape driver, otherwise you should answer M
+  to build it as a loadable module.  The module will be called pt.o.
+  You must also have at least one parallel port protocol driver in
+  your system.  Among the devices supported by this driver is the
+  parallel port version of the HP 5GB drive.
+
+Parallel port generic ATAPI devices
+CONFIG_PARIDE_PG
+  This option enables a special high-level driver for generic ATAPI
+  devices connected through a parallel port. The driver allows user
+  programs, such as cdrecord, to send ATAPI commands directly to a
+  device. If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the parallel port generic ATAPI driver, 
+  otherwise you should answer M to build it as a loadable module.
+  The module will be called pg.o.  You must also have at least one 
+  parallel port protocol driver in your system.  This driver 
+  implements an API loosely related to the generic SCSI driver.  
+  See /usr/include/linux/pg.h for details, or visit 
+  http://www.torque.net/parport/cdr.html for more information and
+  the required patches to cdrecord.
+
+ATEN EH-100 protocol
+CONFIG_PARIDE_ATEN 
+  This option enables support for the ATEN EH-100 parallel port IDE
+  protocol.  This protocol is used in some inexpensive low performance 
+  parallel port kits made in Hong Kong. If you chose to build PARIDE 
+  support into your kernel, you may answer Y here to build in the 
+  protocol driver, otherwise you should answer M to build it as a 
+  loadable module.  The module will be called aten.o.  You must also 
+  have a high-level driver for the type of device that you want to 
+  support.
+
+MicroSolutions backpack protocol
+CONFIG_PARIDE_BPCK 
+  This option enables support for the MicroSolutions backpack 
+  parallel port IDE protocol.  If you chose to build PARIDE support
+  into your kernel, you may answer Y here to build in the protocol
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called bpck.o.  You must also have
+  a high-level driver for the type of device that you want to support.
+
+DataStor Commuter protocol
+CONFIG_PARIDE_COMM 
+  This option enables support for the Commuter parallel port IDE 
+  protocol from DataStor.  If you chose to build PARIDE support
+  into your kernel, you may answer Y here to build in the protocol
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called comm.o.  You must also have
+  a high-level driver for the type of device that you want to support.
+
+DataStor EP-2000 protocol
+CONFIG_PARIDE_DSTR 
+  This option enables support for the EP-2000 parallel port IDE 
+  protocol from DataStor.  If you chose to build PARIDE support
+  into your kernel, you may answer Y here to build in the protocol
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called dstr.o.  You must also have
+  a high-level driver for the type of device that you want to support.
+
+Shuttle EPAT/EPEZ protocol
+CONFIG_PARIDE_EPAT 
+  This option enables support for the EPAT parallel port IDE 
+  protocol.  EPAT is a parallel port IDE adapter manufactured by
+  Shuttle Technology and widely used in devices from major vendors
+  such as Hewlett-Packard, SyQuest, Imation and Avatar. If you 
+  chose to build PARIDE support into your kernel, you may answer Y 
+  here to build in the protocol driver, otherwise you should answer M 
+  to build it as a loadable module.  The module will be called epat.o.  
+  You must also have a high-level driver for the type of device that 
+  you want to support.
+
+Shuttle EPIA protocol
+CONFIG_PARIDE_EPIA 
+  This option enables support for the (obsolete) EPIA parallel port 
+  IDE protocol from Shuttle Technology.  This adapter can still be found
+  in some no-name kits. If you chose to build PARIDE support into your 
+  kernel, you may answer Y here to build in the protocol driver,
+  otherwise you should answer M to build it as a loadable module.  
+  The module will be called epia.o.  You must also have a high-level 
+  driver for the type of device that you want to support.
+
+FIT TD-2000 protocol
+CONFIG_PARIDE_FIT2
+  This option enables support for the TD-2000 parallel port IDE protocol
+  from Fidelity International Technology.  This is a simple (low speed) 
+  adapter that is used in some portable hard drives.  If you chose to 
+  build PARIDE support into your kernel, you may answer Y here to 
+  build in the protocol driver, otherwise you should answer M to 
+  build it as a loadable module.  The module will be called fit2.o.
+  You must also have a high-level driver for the type of device 
+  that you want to support.
+
+FIT TD-3000 protocol
+CONFIG_PARIDE_FIT3
+  This option enables support for the TD-3000 parallel port IDE protocol
+  from Fidelity International Technology. This protocol is used in newer
+  models of their portable disk, CD-ROM and PD/CD devices.   If you chose
+  to build PARIDE support into your kernel, you may answer Y here to
+  build in the protocol driver, otherwise you should answer M to
+  build it as a loadable module.  The module will be called fit3.o.
+  You must also have a high-level driver for the type of device
+  that you want to support.
+
+FreeCom power protocol
+CONFIG_PARIDE_FRPW 
+  This option enables support for the Freecom power parallel port IDE 
+  protocol.  If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you 
+  should answer M to build it as a loadable module.  The module will be 
+  called frpw.o.  You must also have a high-level driver for the type 
+  of device that you want to support.
+
+KingByte KBIC-951A/971A protocols
+CONFIG_PARIDE_KBIC 
+  This option enables support for the KBIC-951A and KBIC-971A parallel 
+  port IDE protocols from KingByte Information Corp.  KingByte's adapters
+  appear in many no-name portable disk and CD-ROM products, especially 
+  in Europe. If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you should 
+  answer M to build it as a loadable module.  The module will be called 
+  kbic.o.  You must also have a high-level driver for the type of device 
+  that you want to support.
+
+KT PHd protocol
+CONFIG_PARIDE_KTTI
+  This option enables support for the "PHd" parallel port IDE protocol
+  from KT Technology.  This is a simple (low speed) adapter that is
+  used in some 2.5" portable hard drives.  If you chose to build PARIDE 
+  support into your kernel, you may answer Y here to build in the 
+  protocol driver, otherwise you should answer M to build it as a 
+  loadable module.  The module will be called ktti.o.  You must also 
+  have a high-level driver for the type of device that you want to
+  support.
+
+OnSpec 90c20 protocol
+CONFIG_PARIDE_ON20 
+  This option enables support for the (obsolete) 90c20 parallel port 
+  IDE protocol from OnSpec (often marketed under the ValuStore brand
+  name).  If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you 
+  should answer M to build it as a loadable module.  The module will 
+  be called on20.o.  You must also have a high-level driver for the 
+  type of device that you want to support.
+
+OnSpec 90c26 protocol
+CONFIG_PARIDE_ON26 
+  This option enables support for the 90c26 parallel port IDE protocol 
+  from OnSpec Electronics (often marketed under the ValuStore brand
+  name).  If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you 
+  should answer M to build it as a loadable module.  The module will 
+  be called on26.o.  You must also have a high-level driver for the 
+  type of device that you want to support.
+
 Multiple devices driver support
 CONFIG_BLK_DEV_MD
   This driver lets you combine several harddisk partitions into one
@@ -349,6 +563,31 @@ CONFIG_MD_STRIPED
   in and removed from the running kernel whenever you want), say M
   here and read Documentation/modules.txt. If unsure, say Y.
 
+RAID-1 (mirroring) mode
+CONFIG_MD_MIRRORING
+  A RAID-1 set consists of several disk drives which are exact copies
+  of each other. In the event of a mirror failture, the RAID driver
+  will continue to use the operational mirrors in the set, providing
+  an error free MD device to the higher levels of the kernel. In
+  a set with N drives, the available space is the capacity of a single
+  drive, and the set protects against a failture of (N - 1) drives.
+  raidtools, a set of user-space tools which create and maintain
+  RAID1/4/5 sets, is available at:
+  http://luthien.nuclecu.unam.mx/~miguel/raid
+
+RAID-4/RAID-5 mode
+CONFIG_MD_RAID5
+  A RAID-5 set of N drives with a capacity of C MB per drive provides
+  the capacity of C * (N - 1) drives, and protects against a failture
+  of a single drive. For a given sector (row) number, (N - 1) drives
+  contain data sectors, and one drive contains the parity protection.
+  For a RAID-4 set, the parity blocks are present on a single drive,
+  while a RAID-5 set distributes the parity accross the drives in one
+  of the available parity distribution methods.
+  raidtools, a set of user-space tools which create and maintain
+  RAID1/4/5 sets, is available at:
+  http://luthien.nuclecu.unam.mx/~miguel/raid
+
 Support for Deskstation RPC44
 CONFIG_DESKSTATION_RPC44
   This is a machine with a R4400 100 MHz CPU. To compile a Linux
index a4818a3e663cbd2f48ade9adb794e633ca81300b..5e9b68e63f3f2082058673d877f93ff869f66469 100644 (file)
@@ -1,6 +1,11 @@
-Caldera TLAN driver for Linux, version 0.42
+TLAN driver for Linux, version 0.43
 README
 
+Note: I (James) am not maintaining this driver anymore, as I no longer
+       have the equipment to do so.  So it is available to anyone who
+       wishes to take it over ;)  If someone needs to reach me about
+       it, my new email address is james@sovereign.org.
+
 
 I.  Supported Devices.
 
@@ -8,20 +13,32 @@ I.  Supported Devices.
 
     Supported:
     Vendor ID  Device ID       Name
-    0e11       ae32            Compaq Netelligent 10/100 TX
-    0e11       ae34            Compaq Netelligent 10 T
+    0e11       ae32            Compaq Netelligent 10/100 TX PCI UTP
+    0e11       ae34            Compaq Netelligent 10 T PCI UTP
     0e11       ae35            Compaq Integrated NetFlex 3/P
-    0e11       ae43            Compaq ProLiant Integrated Netelligent 10/100 TX
-    0e11       ae40            Compaq Dual Port Netelligent 10/100 TX
-    0e11       b011            Compaq Deskpro 4000 5233MMX
+    0e11       ae40            Compaq Netelligent Dual 10/100 TX PCI UTP
+    0e11       ae43            Compaq Netelligent Integrated 10/100 TX UTP
+    0e11       b011            Compaq Netelligent 10/100 TX Embedded UTP
+    0e11       b012            Compaq Netelligent 10 T/2 PCI UTP/Coax
+    0e11       b030            Compaq Netelligent 10/100 TX UTP
     0e11       f130            Compaq NetFlex 3/P
     0e11       f150            Compaq NetFlex 3/P
+    108d       0012            Olicom OC-2325  
+    108d       0013            Olicom OC-2183
     108d       0014            Olicom OC-2326  
 
+
     Caveats:
     
-    I don't believe 100BaseTX daughterboards will work.  I am interested
-    in any reports.
+    I am not sure if 100BaseTX daughterboards (for those cards which
+    support such things) will work.  I haven't had any solid evidence
+    either way.
+
+    However, if a card supports 100BaseTx without requiring an add
+    on daughterboard, it should work with 100BaseTx.
+
+    The "Netelligent 10 T/2 PCI UTP/Coax" (b012) device is untested,
+    but I do not expect any problems.
     
 
 II.  Building the Driver.
@@ -77,6 +94,14 @@ III.  Driver Options
           device that does not have an AUI/BNC connector will probably
           cause it to not function correctly.)
 
+       4. You can set duplex=1 to force half duplex, and duplex=2 to
+          force full duplex.
+
+       5. You can set speed=10 to force 10Mbs operation, and speed=100Mbs
+          to force 100Mbs operation. (I'm not sure what will happen
+          if a card which only supports 10Mbs is forced into 100Mbs
+          mode.)
+
        3. If the driver is built into the kernel, you can use the 3rd
           and 4th parameters to set aui and debug respectively.  For
           example:
@@ -90,6 +115,10 @@ III.  Driver Options
 
                0x01 = aui
                0x02 = use SA_INTERRUPT flag when reserving the irq.
+               0x04 = use half duplex
+               0x08 = use full duplex
+               0x10 = use 10BaseT
+               0x20 = use 100BaseTx
 
 
 IV.  Things to try if you have problems.
@@ -98,33 +127,8 @@ IV.  Things to try if you have problems.
        1. Make sure routing is correct.
        2. If you are using a 2.1.x kernel, try to duplicate the
           problem on a 2.0.x (preferably 2.0.29 or 2.0.30) kernel.
-       3. Set debug to 7, either in tlan.c or through insmod as in
-          section III.1 above.
-       4. Make sure klog is running so the kernel messages are
-          being recorded somewhere.
-       5. Run the following sequence of programs in order (you
-          may want to do this within an xterm, as background
-          traffic may cause a lot of TLAN RECEIVED: messages
-          on the console):
 
-          ifconfig eth0 your.ip.address netmask your.net.mask up
-          route add -net local.net.address eth0
-          ifconfig
-          ping some.computer.on.local.net
-          ifconfig eth0 down
-
-       6. Mail the log of what occurred to me.  Also include the
-          kernel version and what media/connector type (eg,
-          10 BaseT/RJ45, 100 BaseTX/RJ45, Thinnet/BNC, etc).
-
-
-
-Please e-mail me with any comments, successes, or failures.  Thanks.
 
 There is also a tlan mailing list which you can join by sending "subscribe tlan"
-in the body of an email to majordomo@vuser.vu.union.edu.  I will announce new
-releases of the TLAN driver there.
-
-James
-james.banks@caldera.com
+in the body of an email to majordomo@vuser.vu.union.edu.
 
diff --git a/Documentation/paride.txt b/Documentation/paride.txt
new file mode 100644 (file)
index 0000000..951d960
--- /dev/null
@@ -0,0 +1,360 @@
+
+               Linux and parallel port IDE devices
+
+
+PARIDE-2.0.35   (c) 1997-8  Grant Guenther <grant@torque.net>
+
+*************************************************************************
+
+Special notes for the 2.0.35 version:
+
+(i)   This is the paride from 2.1.107 retrofitted to work with 2.0.34.
+     
+(ii)  PARPORT is _not_ supported.  If you obtain the PARPORT patches
+      for 2.0 and try to use them, it might work.  I have not tried
+      it.
+
+(iii) There is no guarantee of any ongoing support or future development
+      for this special retrofit.
+
+(iv)  I have not built or tested PARIDE with SMP support in 2.0.35,
+      use it at your own risk.
+
+*************************************************************************
+
+1. Introduction
+
+Owing to the simplicity and near universality of the parallel port interface
+to personal computers, many external devices such as portable hard-disk,
+CD-ROM, LS-120 and tape drives use the parallel port to connect to their
+host computer.  While some devices (notably scanners) use ad-hoc methods
+to pass commands and data through the parallel port interface, most 
+external devices are actually identical to an internal model, but with
+a parallel-port adapter chip added in.  Some of the original parallel port
+adapters were little more than mechanisms for multiplexing a SCSI bus.
+(The Iomega PPA-3 adapter used in the ZIP drives is an example of this
+approach).  Most current designs, however, take a different approach.
+The adapter chip reproduces a small ISA or IDE bus in the external device
+and the communication protocol provides operations for reading and writing
+device registers, as well as data block transfer functions.  Sometimes,
+the device being addressed via the parallel cable is a standard SCSI
+controller like an NCR 5380.  The "ditto" family of external tape
+drives use the ISA replicator to interface a floppy disk controller,
+which is then connected to a floppy-tape mechanism.  The vast majority
+of external parallel port devices, however, are now based on standard
+IDE type devices, which require no intermediate controller.  If one
+were to open up a parallel port CD-ROM drive, for instance, one would
+find a standard ATAPI CD-ROM drive, a power supply, and a single adapter
+that interconnected a standard PC parallel port cable and a standard
+IDE cable.  It is usually possible to exchange the CD-ROM device with
+any other device using the IDE interface. 
+
+The document describes the support in Linux for parallel port IDE
+devices.  It does not cover parallel port SCSI devices, "ditto" tape
+drives or scanners.  Many different devices are supported by the 
+parallel port IDE subsystem, including:
+
+       MicroSolutions backpack CD-ROM
+       MicroSolutions backpack PD/CD
+       MicroSolutions backpack hard-drives
+       MicroSolutions backpack 8000t tape drive
+       SyQuest EZ-135, EZ-230 & SparQ drives
+       Avatar Shark
+       Imation Superdisk LS-120
+       FreeCom Power CD
+       Hewlett-Packard 5GB tape drive
+       Hewlett-Packard 7100 and 7200 CD-RW drives
+
+as well as most of the clone and no-name products on the market.
+
+To support such a wide range of devices, PARIDE, the parallel port IDE
+subsystem, is actually structured in three parts.   There is a base
+paride module which provides a registry and some common methods for
+accessing the parallel ports.  The second component is a set of 
+high-level drivers for each of the different type of supported device: 
+
+       pd      IDE disk
+       pcd     ATAPI CD-ROM
+       pf      ATAPI disk
+       pt      ATAPI tape
+       pg      ATAPI generic
+
+(Currently, the pg driver is only used with CD-R drives).
+
+The high-level drivers function according to the relevant standards.
+The third component of PARIDE is a set of low-level protocol drivers
+for each of the parallel port IDE adapter chips.  Thanks to the interest
+and encouragement of Linux users from many parts of the world, 
+support is available for almost all known adapter protocols:
+
+        aten    ATEN EH-100                            (HK)
+        bpck    Microsolutions backpack                (US)
+        comm    DataStor (old-type) "commuter" adapter (TW)
+        dstr    DataStor EP-2000                       (TW)
+        epat    Shuttle EPAT                           (UK)
+        epia    Shuttle EPIA                           (UK)
+       fit2    FIT TD-2000                            (US)
+       fit3    FIT TD-3000                            (US)
+        frpw    Freecom Power                          (DE)
+        kbic    KingByte KBIC-951A and KBIC-971A       (TW)
+       ktti    KT Technology PHd adapter              (SG)
+        on20    OnSpec 90c20                           (US)
+        on26    OnSpec 90c26                           (US)
+
+
+2. Using the PARIDE subsystem
+
+While configuring the Linux kernel, you may choose either to build
+the PARIDE drivers into your kernel, or to build them as modules.
+
+In either case, you will need to select "Parallel port IDE device support"
+as well as at least one of the high-level drivers and at least one
+of the parallel port communication protocols.  If you do not know
+what kind of parallel port adapter is used in your drive, you could
+begin by checking the file names and any text files on your DOS 
+installation floppy.  Alternatively, you can look at the markings on
+the adapter chip itself.  That's usually sufficient to identify the
+correct device.  
+
+You can actually select all the protocol modules, and allow the PARIDE
+subsystem to try them all for you.
+
+For the "brand-name" products listed above, here are the protocol
+and high-level drivers that you would use:
+
+       Manufacturer            Model           Driver  Protocol
+       
+       MicroSolutions          CD-ROM          pcd     bpck
+       MicroSolutions          PD drive        pf      bpck
+       MicroSolutions          hard-drive      pd      bpck
+       MicroSolutions          8000t tape      pt      bpck
+       SyQuest                 EZ, SparQ       pd      epat
+       Imation                 Superdisk       pf      epat
+       Avatar                  Shark           pd      epat
+       FreeCom                 CD-ROM          pcd     frpw
+       Hewlett-Packard         5GB Tape        pt      epat
+       Hewlett-Packard         7100/7200       pg      epat
+
+2.1  Configuring built-in drivers
+
+We recommend that you get to know how the drivers work and how to
+configure them as loadable modules, before attempting to compile a
+kernel with the drivers built-in.
+
+If you built all of your PARIDE support directly into your kernel,
+and you have just a single parallel port IDE device, your kernel should
+locate it automatically for you.  If you have more than one device,
+you may need to give some command line options to your bootloader
+(eg: LILO), how to do that is beyond the scope of this document.
+
+The high-level drivers accept a number of command line parameters, all
+of which are documented in the source files in linux/drivers/block/paride.
+By default, each driver will automatically try all parallel ports it
+can find, and all protocol types that have been installed, until it finds
+a parallel port IDE adapter.  Once it finds one, the probe stops.  So,
+if you have more than one device, you will need to tell the drivers
+how to identify them.  This requires specifying the port address, the
+protocol identification number and, for some devices, the drive's
+chain ID.  While your system is booting, a number of messages are
+displayed on the console.  Like all such messages, they can be
+reviewed with the 'dmesg' command.  Among those messages will be
+some lines like:
+
+       paride: bpck registered as protocol 0
+       paride: epat registered as protocol 1
+
+The numbers will always be the same until you build a new kernel with
+different protocol selections.  You should note these numbers as you
+will need them to identify the devices.
+
+If you happen to be using a MicroSolutions backpack device, you will
+also need to know the unit ID number for each drive.  This is usually
+the last two digits of the drive's serial number (but read MicroSolutions'
+documentation about this).
+
+As an example, let's assume that you have a MicroSolutions PD/CD drive
+with unit ID number 36 connected to the parallel port at 0x378, a SyQuest 
+EZ-135 connected to the chained port on the PD/CD drive and also an 
+Imation Superdisk connected to port 0x278.  You could give the following 
+options on your boot command:
+
+       pd.drive0=0x378,1 pf.drive0=0x278,1 pf.drive1=0x378,0,36
+
+In the last option, pf.drive1 configures device /dev/pf1, the 0x378
+is the parallel port base address, the 0 is the protocol registration
+number and 36 is the chain ID.
+
+This (2.0.34) version of PARIDE does not support chained devices on the
+same parallel port.
+
+2.2  Loading and configuring PARIDE as modules
+
+It is much faster and simpler to get to understand the PARIDE drivers
+if you use them as loadable kernel modules.   
+
+Note:  using these drivers with the "kerneld" automatic module loading
+system is not recommended, and is not documented here.  
+
+To use PARIDE, you must begin by 
+
+       insmod paride
+
+this loads a base module which provides a registry for the protocols,
+among other tasks.
+
+Then, load as many of the protocol modules as you think you might need.
+As you load each module, it will register the protocols that it supports,
+and print a log message to your kernel log file and your console. For 
+example:
+
+       # insmod epat
+       paride: epat registered as protocol 0
+       # insmod kbic
+       paride: k951 registered as protocol 1
+        paride: k971 registered as protocol 2
+
+Finally, you can load high-level drivers for each kind of device that
+you have connected.  By default, each driver will autoprobe for a single 
+device, but you can support up to four similar devices by giving their
+individual co-ordinates when you load the driver.
+
+For example, if you had two no-name CD-ROM drives both using the
+KingByte KBIC-951A adapter, one on port 0x378 and the other on 0x3bc
+you could give the following command:
+
+       # insmod pcd drive0=0x378,1 drive1=0x3bc,1
+
+For most adapters, giving a port address and protocol number is sufficient,
+but check the source files in linux/drivers/block/paride for more 
+information.  (Hopefully someone will write some man pages one day !).
+
+As another example, here's what happens when PARPORT is installed, and
+a SyQuest EZ-135 is attached to port 0x378:
+
+       # insmod paride
+       paride: version 1.0 installed
+       # insmod epat
+       paride: epat registered as protocol 0
+       # insmod pd
+       pd: pd version 1.0, major 45, cluster 64, nice 0
+       pda: Sharing parport1 at 0x378
+       pda: epat 1.0, Shuttle EPAT chip c3 at 0x378, mode 5 (EPP-32), delay 1
+       pda: SyQuest EZ135A, 262144 blocks [128M], (512/16/32), removable media
+        pda: pda1
+
+Note that the last line is the output from the generic partition table
+scanner - in this case it reports that it has found a disk with one partition.
+
+2.3  Using a PARIDE device
+
+Once the drivers have been loaded, you can access PARIDE devices in the
+same way as their traditional counterparts.  You will probably need to
+create the device "special files".  Here is a simple script that you can
+cut to a file and execute:
+
+#!/bin/bash
+#
+# mkd -- a script to create the device special files for the PARIDE subsystem
+#
+function mkdev {
+  mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
+}
+#
+function pd {
+  D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
+  mkdev pd$D b 45 $[ $1 * 16 ]
+  for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+  do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
+  done
+}
+#
+cd /dev
+#
+for u in 0 1 2 3 ; do pd $u ; done
+for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done 
+for u in 0 1 2 3 ; do mkdev pf$u  b 47 $u ; done 
+for u in 0 1 2 3 ; do mkdev pt$u  c 96 $u ; done 
+for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done 
+for u in 0 1 2 3 ; do mkdev pg$u  c 97 $u ; done 
+#
+# end of mkd
+
+With the device files and drivers in place, you can access PARIDE devices
+like any other Linux device.   For example, to mount a CD-ROM in pcd0, use:
+
+       mount /dev/pcd0 /cdrom
+
+If you have a fresh Avatar Shark cartridge, and the drive is pda, you
+might do something like:
+
+       fdisk /dev/pda          -- make a new partition table with
+                                  partition 1 of type 83
+
+       mke2fs /dev/pda1        -- to build the file system
+
+       mkdir /shark            -- make a place to mount the disk
+
+       mount /dev/pda1 /shark
+
+Devices like the Imation superdisk work in the same way, except that
+they do not have a partition table.  For example to make a 120MB
+floppy that you could share with a DOS system:
+
+       mkdosfs /dev/pf0
+       mount /dev/pf0 /mnt
+
+2.4  Using the pg driver
+
+The pg driver can be used in conjunction with the cdrecord program
+to create CD-ROMs.  For more information, and the required patches 
+to cdrecord, please visit http://www.torque.net/parport/cdr.html .
+
+3. Troubleshooting
+
+While a lot of testing has gone into these drivers to make them work
+as smoothly as possible, problems will arise.  If you do have problems,
+please check all the obvious things first:  does the drive work in
+DOS with the manufacturer's drivers ?  If that doesn't yield any useful
+clues, then please make sure that only one drive is hooked to your system,
+and that no other device driver is using your parallel port (check in 
+/proc/ioports).  Then, load the appropriate drivers (you can load several 
+protocol modules if you want) as in:
+
+       # insmod paride
+       # insmod epat
+       # insmod bpck
+       # insmod kbic
+       ...
+       # insmod pd verbose=1
+
+(using the correct driver for the type of device you have, of course).
+The verbose=1 parameter will cause the drivers to log a trace of their
+activity as they attempt to locate your drive.
+
+Use 'dmesg' to capture a log of all the PARIDE messages (any messages
+beginning with paride:, a protocol module's name or a driver's name) and
+include that with your bug report.  You can submit a bug report in one
+of two ways.  Either send it directly to the author of the PARIDE suite,
+by e-mail to grant@torque.net, or join the linux-parport mailing list
+and post your report there.
+
+You can join the linux-parport mailing list by sending a mail message
+to 
+               linux-parport-request@torque.net
+
+with the single word 
+
+               subscribe
+
+in the body of the mail message (not in the subject line).   Please be
+sure that your mail program is correctly set up when you do this,  as
+the list manager is a robot that will subscribe you using the reply
+address in your mail headers.  REMOVE any anti-spam gimmicks you may
+have in your mail headers, when sending mail to the list server.
+
+You might also find some useful information on the linux-parport
+web pages (although they are not always up to date) at
+
+       http://www.torque.net/parport/
+
+
index 5900b12d839fc743a7973b1ed3b8db2daca3ab7a..e2552bc2347ad4c97ef9c14995891e3ddabac2e5 100644 (file)
@@ -389,6 +389,12 @@ M: Juergen Fischer <fischer@et-inf.fho-emden.de>
 L:     linux-scsi@vger.rutgers.edu
 S:     Maintained
 
+GSCD CDROM DRIVER
+P:     Oliver Raupach
+M:     oliver@mm.gop.de
+L:     linux-kernel@vger.rutgers.edu
+S:     Maintained
+
 SBPCD CDROM DRIVER
 P:     Eberhard Moenkeberg
 M:     emoenke@gwdg.de
index 4c2db3eae6b9ea26f065d6d3762864a19f4ec961..97e1ea9f60bb67b1a14b1e8cdee17beefb4e28d4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -146,6 +146,10 @@ ifdef CONFIG_SBUS
 DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
 endif
 
+ifeq ($(CONFIG_PARIDE),y)
+DRIVERS := $(DRIVERS) drivers/block/paride/paride.a
+endif
+
 include arch/$(ARCH)/Makefile
 
 ifdef SMP
index 4c3d6c5b8060837a090dda03d6b5761a4e94487f..3fd3af0be1fe7fd94c1ffd0901a068b95e8a4f65 100644 (file)
@@ -103,16 +103,17 @@ startup_32:
 checkCPUtype:
 #endif
 
-/* check if it is 486 or 386. */
+/* check Processor type: 386, 486, 6x86(L) or CPUID capable processor */
 /*
  * XXX - this does a lot of unnecessary setup.  Alignment checks don't
  * apply at our cpl of 0 and the stack ought to be aligned already, and
  * we don't need to preserve eflags.
  */
+
        movl $3, SYMBOL_NAME(x86)
        pushfl                  # push EFLAGS
        popl %eax               # get EFLAGS
-       movl %eax,%ecx          # save original EFLAGS
+       movl %eax,%ecx          # save original EFLAGS in ecx
        xorl $0x40000,%eax      # flip AC bit in EFLAGS
        pushl %eax              # copy to EFLAGS
        popfl                   # set EFLAGS
@@ -127,10 +128,11 @@ checkCPUtype:
        pushl %eax
        popfl                   # if we are on a straight 486DX, SX, or
        pushfl                  # 487SX we can't change it
-       popl %eax
-       xorl %ecx,%eax
+       popl %eax               # Also if we are on a Cyrix 6x86(L)
+       xorl %ecx,%eax          # OTOH 6x86MXs and MIIs check OK
        andl $0x200000,%eax
-       je is486
+       je is486x
+
 isnew: pushl %ecx              # restore original EFLAGS
        popfl
        incl SYMBOL_NAME(have_cpuid)    # we have CPUID
@@ -168,7 +170,72 @@ isnew:     pushl %ecx              # restore original EFLAGS
        andl $0x80000011,%eax   # Save PG,PE,ET
        orl $0x50022,%eax       # set AM, WP, NE and MP
        jmp 2f
-is486: pushl %ecx              # restore original EFLAGS
+
+/* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid
+ * clobbering the new BX chipset used with the Pentium II, which has a register
+ * at the same addresses as those used to access the Cyrix special configuration
+ * registers (CCRs).
+ */
+       /*
+        * A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2
+        * (and it _must_ be 5 divided by 2) while other CPUs change
+        * them in undefined ways. We need to know this since we may
+        * need to enable the CPUID instruction at least.
+        * We couldn't use this test before since the PPro and PII behave
+        * like Cyrix chips in this respect.
+        */
+is486x:        xor %ax,%ax
+       sahf
+       movb $5,%ax
+       movb $2,%bx
+       div %bl
+       lahf
+       cmpb $2,%ah
+       jne ncyrix
+       /*
+        * N.B. The pattern of accesses to 0x22 and 0x23 is *essential*
+        *      so do not try to "optimize" it! For the same reason we
+        *      do all this with interrupts off.
+        */
+#define setCx86(reg, val) \
+       movb reg,%ax;   \
+       outb %ax,$0x22; \
+       movb val,%ax;   \
+       outb %ax,$0x23
+
+#define getCx86(reg) \
+       movb reg,%ax;   \
+       outb %ax,$0x22; \
+       inb $0x23,%ax
+
+       cli
+       getCx86($0xc3)          # get CCR3
+       movb %ax,%cx            # Save old value
+       movb %ax,%bx
+       andb $0x0f,%bx          # Enable access to all config registers
+       orb $0x10,%bx           # by setting bit 4
+       setCx86($0xc3,%bx)
+
+       getCx86($0xe8)          # now we can get CCR4
+       orb $0x80,%ax           # and set bit 7 (CPUIDEN)
+       movb %ax,%bx            # to enable CPUID execution
+       setCx86($0xe8,%bx)
+
+        getCx86($0xfe)          # DIR0 : let's check this is a 6x86(L)
+        andb $0xf0,%ax         # should be 3xh
+       cmpb $0x30,%ax          # 
+       jne n6x86
+        getCx86($0xe9)          # CCR5 : we reset the SLOP bit
+        andb $0xfd,%ax         # so that udelay calculation
+        movb %ax,%bx           # is correct on 6x86(L) CPUs
+        setCx86($0xe9,%bx)
+       setCx86($0xc3,%cx)      # Restore old CCR3
+       sti
+       jmp isnew               # We enabled CPUID now
+
+n6x86: setCx86($0xc3,%cx)      # Restore old CCR3
+       sti
+ncyrix:        pushl %ecx              # restore original EFLAGS
        popfl
        movl %cr0,%eax          # 486
        andl $0x80000011,%eax   # Save PG,PE,ET
index be1f46467de23ca048674d80194ea68161f47fe7..5d68a9d359fc09878d8cb5929259063d26a8a699 100644 (file)
@@ -417,6 +417,55 @@ static unsigned long get_fpreg_word(struct task_struct *child,
        return tmp;
 }
 
+static int putreg(struct task_struct *child,
+       unsigned long regno, unsigned long value)
+{
+       switch (regno >> 2) {
+               case ORIG_EAX:
+                       return -EIO;
+               case FS:
+               case GS:
+               case DS:
+               case ES:
+                       if (value && (value & 3) != 3)
+                               return -EIO;
+                       value &= 0xffff;
+                       break;
+               case SS:
+               case CS:
+                       if ((value & 3) != 3)
+                               return -EIO;
+                       value &= 0xffff;
+                       break;
+               case EFL:
+                       value &= FLAG_MASK;
+                       value |= get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~FLAG_MASK;
+       }
+       put_stack_long(child, regno - sizeof(struct pt_regs), value);
+       return 0;
+}
+
+static unsigned long getreg(struct task_struct *child,
+       unsigned long regno)
+{
+       unsigned long retval = ~0UL;
+
+       switch (regno >> 2) {
+               case FS:
+               case GS:
+               case DS:
+               case ES:
+               case SS:
+               case CS:
+                       retval = 0xffff;
+                       /* fall through */
+               default:
+                       regno = regno - sizeof(struct pt_regs);
+                       retval &= get_stack_long(child, regno);
+       }
+       return retval;
+}
+
 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
 {
        struct task_struct *child;
@@ -490,39 +539,23 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
                        unsigned long tmp;
                        int res;
                        
-                       if ((addr & 3 && 
-                            (addr < (long) (&dummy->i387) ||
-                             addr > (long) (&dummy->i387.st_space[20]) )) ||
-                           addr < 0 || addr > sizeof(struct user) - 3)
+                       if ((addr & 3) || addr < 0
+                           || addr > sizeof(struct user) - 3)
                                return -EIO;
                        
                        res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
                        if (res)
                                return res;
                        tmp = 0;  /* Default return condition */
-                       if (addr >= (long) (&dummy->i387) &&
-                           addr < (long) (&dummy->i387.st_space[20]) ) {
-#ifndef CONFIG_MATH_EMULATION
-                               if (!hard_math)
-                                       return -EIO;
-#endif /* defined(CONFIG_MATH_EMULATION) */
-                               tmp = get_fpreg_word(child, addr);
-                       } 
-                       if(addr < 17*sizeof(long)) {
-                         addr = addr >> 2; /* temporary hack. */
-
-                         tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER);
-                         if (addr == DS || addr == ES ||
-                             addr == FS || addr == GS ||
-                             addr == CS || addr == SS)
-                           tmp &= 0xffff;
-                       };
-                       if(addr >= (long) &dummy->u_debugreg[0] &&
-                          addr <= (long) &dummy->u_debugreg[7]){
+                       if(addr < 17*sizeof(long))
+                               tmp = getreg(child, addr);
+                       else if(addr >= (long) &dummy->u_debugreg[0]
+                               && addr <= (long) &dummy->u_debugreg[7])
+                       {
                                addr -= (long) &dummy->u_debugreg[0];
                                addr = addr >> 2;
                                tmp = child->debugreg[addr];
-                       };
+                       }
                        put_fs_long(tmp,(unsigned long *) data);
                        return 0;
                }
@@ -533,50 +566,18 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
                        return write_long(child,addr,data);
 
                case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
-                       if ((addr & 3 &&
-                            (addr < (long) (&dummy->i387.st_space[0]) ||
-                             addr > (long) (&dummy->i387.st_space[20]) )) ||
-                           addr < 0 || 
-                           addr > sizeof(struct user) - 3)
+                       if ((addr & 3) || addr < 0
+                           || addr > sizeof(struct user) - 3)
                                return -EIO;
-                               
-                       if (addr >= (long) (&dummy->i387.st_space[0]) &&
-                           addr < (long) (&dummy->i387.st_space[20]) ) {
-#ifndef CONFIG_MATH_EMULATION
-                               if (!hard_math)
-                                       return -EIO;
-#endif /* defined(CONFIG_MATH_EMULATION) */
-                               return put_fpreg_word(child, addr, data);
-                       }
-                       addr = addr >> 2; /* temporary hack. */
-                       
-                       if (addr == ORIG_EAX)
-                               return -EIO;
-                       if (addr == DS || addr == ES ||
-                           addr == FS || addr == GS ||
-                           addr == CS || addr == SS) {
-                               data &= 0xffff;
-                               if (data && (data & 3) != 3)
-                                       return -EIO;
-                       }
-                       if (addr == EFL) {   /* flags. */
-                               data &= FLAG_MASK;
-                               data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER)  & ~FLAG_MASK;
-                       }
-                 /* Do not allow the user to set the debug register for kernel
-                    address space */
-                 if(addr < 17){
-                         if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))
-                               return -EIO;
-                       return 0;
-                       };
+
+                       if(addr < 17*sizeof(long))
+                               return putreg(child, addr, data);
 
                  /* We need to be very careful here.  We implicitly
                     want to modify a portion of the task_struct, and we
                     have to be selective about what portions we allow someone
                     to modify. */
 
-                 addr = addr << 2;  /* Convert back again */
                  if(addr >= (long) &dummy->u_debugreg[0] &&
                     addr <= (long) &dummy->u_debugreg[7]){
 
@@ -666,6 +667,108 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
                        return 0;
                }
 
+               case PTRACE_GETREGS: { /* Get all gp regs from the child. */
+#ifdef CONFIG_MATH_EMULATION
+                       if (!hard_math)
+                               /* Not supported. */
+                               return -EIO;
+#endif
+
+                       if (verify_area(VERIFY_WRITE, (void *) data,
+                                       17*sizeof(long)))
+                         return -EIO;
+                       for (i = 0; i < 17*sizeof(long);
+                            i += sizeof(long), data += sizeof(long))
+                         put_fs_long (getreg(child, i), (unsigned long *) data);
+                       return 0;
+                 };
+
+               case PTRACE_SETREGS: { /* Set all gp regs in the child. */
+                       unsigned long tmp;
+
+#ifdef CONFIG_MATH_EMULATION
+                       if (!hard_math)
+                               /* Not supported. */
+                               return -EIO;
+#endif
+
+                       if (verify_area(VERIFY_READ, (void *) data,
+                                       17*sizeof(long)))
+                         return -EIO;
+                       for (i = 0; i < 17*sizeof(long);
+                            i += sizeof(long), data += sizeof(long))
+                         {
+                           tmp = get_fs_long ((unsigned long *) data);
+                           putreg(child, i, tmp);
+                         }
+                       return 0;
+                 };
+
+               case PTRACE_GETFPREGS: { /* Get the child FPU state. */
+                       unsigned long *tmp;
+
+#ifdef CONFIG_MATH_EMULATION
+                       if (!hard_math)
+                               /* Not supported. */
+                               return -EIO;
+#endif
+
+                       if (verify_area(VERIFY_WRITE, (void *) data,
+                                       sizeof(struct user_i387_struct)))
+                         return -EIO;
+                       if ( !child->used_math ) {
+                         /* Simulate an empty FPU. */
+                         child->tss.i387.hard.cwd = 0xffff037f;
+                         child->tss.i387.hard.swd = 0xffff0000;
+                         child->tss.i387.hard.twd = 0xffffffff;
+                       }
+                       if (last_task_used_math == child)
+                         {
+                           clts();
+                           __asm__("fnsave %0; fwait":"=m" (child->tss.i387.hard));
+                           last_task_used_math = NULL;
+                           stts();
+                         }
+                       tmp = (unsigned long *) &child->tss.i387.hard;
+                       for ( i = 0; i < sizeof(struct user_i387_struct); i += sizeof(long) )
+                         {
+                           put_fs_long (*tmp, (unsigned long *) data);
+                           data += sizeof(long);
+                           tmp++;
+                         }
+
+                       return 0;
+                 };
+
+               case PTRACE_SETFPREGS: { /* Set the child FPU state. */
+                       unsigned long *tmp;
+
+#ifdef CONFIG_MATH_EMULATION
+                       if (!hard_math)
+                               /* Not supported. */
+                               return -EIO;
+#endif
+
+                       if (verify_area(VERIFY_READ, (void *) data,
+                                       sizeof(struct user_i387_struct)))
+                         return -EIO;
+                       child->used_math = 1;
+                       if (last_task_used_math == child)
+                         {
+                           /* Discard the state of the FPU */
+                           last_task_used_math = NULL;
+                         }
+                       tmp = (unsigned long *) &child->tss.i387.hard;
+                       for ( i = 0; i < sizeof(struct user_i387_struct); i += sizeof(long) )
+                         {
+                           *tmp = get_fs_long ((unsigned long *) data);
+                           data += sizeof(long);
+                           tmp++;
+                         }
+                       child->flags &= ~PF_USEDFPU;
+                       return 0;
+                 };
+
                default:
                        return -EIO;
        }
index 1d3cd6311f66652fd6ff81ca33e4319ab685eaaa..643863bca9d583b35af20311efd91364f12eb86f 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/smp.h>
+#include <asm/io.h>
 
 /*
  * Tell us the machine setup..
@@ -224,9 +225,7 @@ static const char * i486model(unsigned int nr)
 static const char * i586model(unsigned int nr)
 {
        static const char *model[] = {
-               "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83",
-               "Pentium MMX", NULL, NULL, "Mobile Pentium 75+", 
-               "Mobile Pentium MMX"
+               "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83"
        };
        if (nr < sizeof(model)/sizeof(char *))
                return model[nr];
@@ -237,7 +236,7 @@ static const char * Cx86model(void)
 {
        unsigned char nr6x86 = 0;
        static const char *model[] = {
-               "unknown", "6x86", "6x86L", "6x86MX", "6x86MXi"
+               "unknown", "6x86", "6x86L", "6x86MX", "MII"
        };
        switch (x86) {
                case 5:
@@ -249,6 +248,10 @@ static const char * Cx86model(void)
                default:
                        nr6x86 = 0;
        }
+
+       /* We must get the stepping number by reading DIR1 */
+       outb(0xff, 0x22); x86_mask=inb(0x23);
+
        switch (x86_mask) {
                case 0x03:
                        Cx86_step =  1; /* 6x86MX Rev 1.3 */
@@ -256,20 +259,26 @@ static const char * Cx86model(void)
                case 0x04:
                        Cx86_step =  2; /* 6x86MX Rev 1.4 */
                        break;
+               case 0x05:
+                       Cx86_step =  3; /* 6x86MX Rev 1.5 */
+                       break;
+               case 0x06:
+                       Cx86_step =  4; /* 6x86MX Rev 1.6 */
+                       break;
                case 0x14:
-                       Cx86_step =  3; /* 6x86 Rev 2.4 */
+                       Cx86_step =  5; /* 6x86 Rev 2.4 */
                        break;
                case 0x15:
-                       Cx86_step =  4; /* 6x86 Rev 2.5 */
+                       Cx86_step =  6; /* 6x86 Rev 2.5 */
                        break;
                case 0x16:
-                       Cx86_step =  5; /* 6x86 Rev 2.6 */
+                       Cx86_step =  7; /* 6x86 Rev 2.6 */
                        break;
                case 0x17:
-                       Cx86_step =  6; /* 6x86 Rev 2.7 or 3.7 */
+                       Cx86_step =  8; /* 6x86 Rev 2.7 or 3.7 */
                        break;
                case 0x22:
-                       Cx86_step =  7; /* 6x86L Rev 4.2 */
+                       Cx86_step =  9; /* 6x86L Rev 4.2 */
                        break;
                default:
                        Cx86_step = 0;
index 9829bbb17ad58e8068a387a2ff142a5c81dcad9a..e7db9ab5235aae7d4aaf6c37c76c635a78c1b5b9 100644 (file)
@@ -40,6 +40,8 @@ bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
+  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-4/RAID-5 mode' CONFIG_MD_RAID5
 fi
 
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
index 5f6ea38142c88bd64c46fca7c0552c94683653d4..1436e59c214189a336bf7f18167190ee28ee2c65 100644 (file)
@@ -45,6 +45,8 @@ bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
+  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-4/RAID-5 mode' CONFIG_MD_RAID5
 fi
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
 if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
@@ -52,6 +54,10 @@ if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
 fi
 tristate 'XT harddisk support' CONFIG_BLK_DEV_XD
 
+tristate 'Parallel port IDE device support' CONFIG_PARIDE 
+if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
+  source drivers/block/paride/Config.in
+fi
 
 if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
   define_bool CONFIG_BLK_DEV_HD y
index a2ed99202c048a6af8217487b3c7e843f250d949..5f6b91b828ff27255e9a145099405ccb228a79e2 100644 (file)
@@ -109,6 +109,15 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_PARIDE),y)
+SUB_DIRS        += paride
+MOD_SUB_DIRS    += paride
+else
+  ifeq ($(CONFIG_PARIDE),m)
+  MOD_SUB_DIRS  += paride
+  endif
+endif
+
 ifeq ($(CONFIG_BLK_DEV_MD),y)
 LX_OBJS += md.o
 
@@ -128,25 +137,21 @@ else
   endif
 endif
 
-#ifeq ($(CONFIG_MD_RAID1),y)
-#L_OBJS += raid1.o
-#else
-#  ifeq ($(CONFIG_MD_SUPPORT_RAID1),y)
-#    ifeq ($(CONFIG_MD_RAID1),m)
-#    M_OBJS += raid1.o
-#    endif
-#  endif
-#endif
-#
-#ifeq ($(CONFIG_MD_RAID5),y)
-#L_OBJS += raid5.o
-#else
-#  ifeq ($(CONFIG_MD_SUPPORT_RAID5),y)
-#    ifeq ($(CONFIG_MD_RAID5),m)
-#    M_OBJS += raid5.o
-#    endif
-#  endif
-#endif
+ifeq ($(CONFIG_MD_MIRRORING),y)
+L_OBJS += raid1.o
+else
+  ifeq ($(CONFIG_MD_MIRRORING),m)
+  M_OBJS += raid1.o
+  endif
+endif
+
+ifeq ($(CONFIG_MD_RAID5),y)
+L_OBJS += raid5.o
+else
+  ifeq ($(CONFIG_MD_RAID5),m)
+  M_OBJS += raid5.o
+  endif
+endif
 
 endif
 
index dfbe6f31f2ddb90f7be7dd65e59c5df22ad24304..47f44d70cf77b825c37632acc6577605ba5cf063 100644 (file)
@@ -161,6 +161,7 @@ static int linear_status (char *page, int minor, struct md_dev *mddev)
 
   sz+=sprintf (page+sz, "\n");
 #endif
+  sz+=sprintf (page+sz, " %dk rounding", 1<<FACTOR_SHIFT(FACTOR(mddev)));
   return sz;
 }
 
@@ -169,6 +170,8 @@ static struct md_personality linear_personality=
 {
   "linear",
   linear_map,
+  NULL,
+  NULL,
   linear_run,
   linear_stop,
   linear_status,
index 510d7225939bbc125a325346e5a2bb172737502d..1eb8d63c8e7e7cb61d16dc88a3dcb758ae40edef 100644 (file)
@@ -280,7 +280,30 @@ void add_request(struct blk_dev_struct * dev, struct request * req)
        sti();
 }
 
-static void make_request(int major,int rw, struct buffer_head * bh)
+#define MAX_SECTORS 244
+
+static inline void attempt_merge (struct request *req)
+{
+       struct request *next = req->next;
+
+       if (!next)
+               return;
+       if (req->sector + req->nr_sectors != next->sector)
+               return;
+       if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS)
+               return;
+#if 0
+       printk ("%s: merge %ld, %ld + %ld == %ld\n", kdevname(req->rq_dev), req->sector, req->nr_sectors, next->nr_sectors, req->nr_sectors + next->nr_sectors);
+#endif 
+       req->bhtail->b_reqnext = next->bh;
+       req->bhtail = next->bhtail;
+       req->nr_sectors += next->nr_sectors;
+       next->rq_status = RQ_INACTIVE;
+       req->next = next->next;
+       wake_up (&wait_for_request);
+}
+
+void make_request(int major,int rw, struct buffer_head * bh)
 {
        unsigned int sector, count;
        struct request * req;
@@ -290,8 +313,12 @@ static void make_request(int major,int rw, struct buffer_head * bh)
        sector = bh->b_rsector;
 
        /* Uhhuh.. Nasty dead-lock possible here.. */
-       if (buffer_locked(bh))
+       if (buffer_locked(bh)) {
+#if 0
+               printk("make_request(): buffer already locked\n");
+#endif
                return;
+       }
        /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
 
        lock_buffer(bh);
@@ -319,6 +346,9 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                        rw = READ;      /* drop into READ */
                case READ:
                        if (buffer_uptodate(bh)) {
+#if 0
+                               printk ("make_request(): buffer uptodate for READ\n");
+#endif
                                unlock_buffer(bh); /* Hmmph! Already have it */
                                return;
                        }
@@ -330,6 +360,9 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                        rw = WRITE;     /* drop into WRITE */
                case WRITE:
                        if (!buffer_dirty(bh)) {
+#if 0
+                               printk ("make_request(): buffer clean for WRITE\n");
+#endif
                                unlock_buffer(bh); /* Hmmph! Nothing to write */
                                return;
                        }
@@ -391,7 +424,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                                continue;
                        if (req->cmd != rw)
                                continue;
-                       if (req->nr_sectors >= 244)
+                       if (req->nr_sectors >= MAX_SECTORS)
                                continue;
                        if (req->rq_dev != bh->b_rdev)
                                continue;
@@ -399,6 +432,9 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                        if (req->sector + req->nr_sectors == sector) {
                                req->bhtail->b_reqnext = bh;
                                req->bhtail = bh;
+                               req->nr_sectors += count;
+                               /* Can we now merge this req with the next? */
+                               attempt_merge(req);
                        /* or to the beginning? */
                        } else if (req->sector - count == sector) {
                                bh->b_reqnext = req->bh;
@@ -406,10 +442,10 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                                req->buffer = bh->b_data;
                                req->current_nr_sectors = count;
                                req->sector = sector;
+                               req->nr_sectors += count;
                        } else
                                continue;
 
-                       req->nr_sectors += count;
                        mark_buffer_clean(bh);
                        sti();
                        return;
@@ -512,6 +548,12 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
        for (i = 0; i < nr; i++) {
                if (bh[i]) {
                        set_bit(BH_Req, &bh[i]->b_state);
+#ifdef CONFIG_BLK_DEV_MD
+                       if (MAJOR(bh[i]->b_dev) == MD_MAJOR) {
+                               md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]);
+                               continue;
+                       }
+#endif
                        make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]);
                }
        }
@@ -646,6 +688,9 @@ int blk_dev_init(void)
 #ifdef CONFIG_BLK_DEV_XD
        xd_init();
 #endif
+#ifdef CONFIG_PARIDE
+        { extern void paride_init(void); paride_init(); };
+#endif
 #ifdef CONFIG_BLK_DEV_FD
        floppy_init();
 #else
index dfc0cede1267f254994fb9bc0a1021db27ee9422..98ffe3a7e12c079e1317867e720639523e17f956 100644 (file)
@@ -9,6 +9,9 @@
 
    kerneld support by Boris Tobotras <boris@xtalk.msk.su>
 
+   RAID-1/RAID-5 extensions by:
+        Ingo Molnar, Miguel de Icaza, Gadi Oxman
+   
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, or (at your option)
 #include <linux/proc_fs.h>
 #include <linux/blkdev.h>
 #include <linux/genhd.h>
+#include <linux/smp_lock.h>
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
 #include <linux/errno.h>
+/*
+ * For kernel_thread()
+ */
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
 
 #define MAJOR_NR MD_MAJOR
 #define MD_DRIVER
 
 #include <linux/blk.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
 
 static struct hd_struct md_hd_struct[MAX_MD_DEV];
 static int md_blocksizes[MAX_MD_DEV];
+static struct md_thread md_threads[MAX_MD_THREADS];
 
 int md_size[MAX_MD_DEV]={0, };
 
@@ -91,8 +103,7 @@ char *partition_name (kdev_t dev)
 
   if (!hd)
   {
-    printk ("No gendisk entry for dev %s\n", kdevname(dev));
-    sprintf (name, "dev %s", kdevname(dev));
+    sprintf (name, "[dev %s]", kdevname(dev));
     return (name);
   }
 
@@ -117,23 +128,196 @@ static void set_ra (void)
   read_ahead[MD_MAJOR]=minra;
 }
 
+static int legacy_raid_sb (int minor, int pnum)
+{
+       int i, factor;
+
+       factor = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+
+       /*****
+        * do size and offset calculations.
+        */
+       for (i=0; i<md_dev[minor].nb_dev; i++) {
+               md_dev[minor].devices[i].size &= ~(factor - 1);
+               md_size[minor] += md_dev[minor].devices[i].size;
+               md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + 
+                                                       md_dev[minor].devices[i-1].size) : 0;
+       }
+       return 0;
+}
+
+static void free_sb (struct md_dev *mddev)
+{
+       int i;
+       struct real_dev *realdev;
+
+       if (mddev->sb) {
+               free_page((unsigned long) mddev->sb);
+               mddev->sb = NULL;
+       }
+       for (i = 0; i <mddev->nb_dev; i++) {
+               realdev = mddev->devices + i;
+               if (realdev->sb) {
+                       free_page((unsigned long) realdev->sb);
+                       realdev->sb = NULL;
+               }
+       }
+}
+
+static int analyze_sb (int minor, int pnum)
+{
+       int i;
+       struct md_dev *mddev = md_dev + minor;
+       struct buffer_head *bh;
+       kdev_t dev;
+       struct real_dev *realdev;
+       u32 sb_offset, device_size;
+       md_superblock_t *sb = NULL;
+
+       /*
+        * raid-0 and linear don't use a raid superblock
+        */
+       if (pnum == RAID0 >> PERSONALITY_SHIFT || pnum == LINEAR >> PERSONALITY_SHIFT)
+               return legacy_raid_sb(minor, pnum);
+       
+       /*
+        * Verify the raid superblock on each real device
+        */
+       for (i = 0; i < mddev->nb_dev; i++) {
+               realdev = mddev->devices + i;
+               dev = realdev->dev;
+               device_size = blk_size[MAJOR(dev)][MINOR(dev)];
+               realdev->sb_offset = sb_offset = MD_NEW_SIZE_BLOCKS(device_size);
+               set_blocksize(dev, MD_SB_BYTES);
+               bh = bread(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES);
+               if (bh) {
+                       sb = (md_superblock_t *) bh->b_data;
+                       if (sb->md_magic != MD_SB_MAGIC) {
+                               printk("md: %s: invalid raid superblock magic (%x) on block %u\n", kdevname(dev), sb->md_magic, sb_offset);
+                               goto abort;
+                       }
+                       if (!mddev->sb) {
+                               mddev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL);
+                               if (!mddev->sb)
+                                       goto abort;
+                               memcpy(mddev->sb, sb, MD_SB_BYTES);
+                       }
+                       realdev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL);
+                       if (!realdev->sb)
+                               goto abort;
+                       memcpy(realdev->sb, bh->b_data, MD_SB_BYTES);
+
+                       if (memcmp(mddev->sb, sb, MD_SB_GENERIC_CONSTANT_WORDS * 4)) {
+                               printk(KERN_ERR "md: superblock inconsistenty -- run ckraid\n");
+                               goto abort;
+                       }
+                       /*
+                        * Find the newest superblock version
+                        */
+                       if (sb->utime != mddev->sb->utime) {
+                               printk(KERN_ERR "md: superblock update time inconsistenty -- using the most recent one\n");
+                               if (sb->utime > mddev->sb->utime)
+                                       memcpy(mddev->sb, sb, MD_SB_BYTES);
+                       }
+                       realdev->size = sb->size;
+               } else
+                       printk(KERN_ERR "md: disabled device %s\n", kdevname(dev));
+       }
+       if (!mddev->sb) {
+               printk(KERN_ERR "md: couldn't access raid array %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+               goto abort;
+       }
+       sb = mddev->sb;
+
+       /*
+        * Check if we can support this raid array
+        */
+       if (sb->major_version != MD_MAJOR_VERSION || sb->minor_version > MD_MINOR_VERSION) {
+               printk("md: %s: unsupported raid array version %d.%d.%d\n", kdevname(MKDEV(MD_MAJOR, minor)),
+               sb->major_version, sb->minor_version, sb->patch_version);
+               goto abort;
+       }
+       if (sb->state != (1 << MD_SB_CLEAN)) {
+               printk(KERN_ERR "md: %s: raid array is not clean -- run ckraid\n", kdevname(MKDEV(MD_MAJOR, minor)));
+               goto abort;
+       }
+       switch (sb->level) {
+               case 1:
+                       md_size[minor] = sb->size;
+                       break;
+               case 4:
+               case 5:
+                       md_size[minor] = sb->size * (sb->raid_disks - 1);
+                       break;
+               default:
+                       printk(KERN_ERR "md: %s: unsupported raid level %d\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+                       goto abort;
+       }
+       return 0;
+abort:
+       free_sb(mddev);
+       return 1;
+}
+
+int md_update_sb(int minor)
+{
+       struct md_dev *mddev = md_dev + minor;
+       struct buffer_head *bh;
+       md_superblock_t *sb = mddev->sb;
+       struct real_dev *realdev;
+       kdev_t dev;
+       int i;
+       u32 sb_offset;
+
+       sb->utime = CURRENT_TIME;
+       for (i = 0; i < mddev->nb_dev; i++) {
+               realdev = mddev->devices + i;
+               if (!realdev->sb)
+                       continue;
+               dev = realdev->dev;
+               sb_offset = realdev->sb_offset;
+               set_blocksize(dev, MD_SB_BYTES);
+               printk("md: updating raid superblock on device %s, sb_offset == %u\n", kdevname(dev), sb_offset);
+               bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES);
+               if (bh) {
+                       sb = (md_superblock_t *) bh->b_data;
+                       memcpy(sb, mddev->sb, MD_SB_BYTES);
+                       memcpy(&sb->descriptor, sb->disks + realdev->sb->descriptor.number, MD_SB_DESCRIPTOR_WORDS * 4);
+                       mark_buffer_uptodate(bh, 1);
+                       mark_buffer_dirty(bh, 1);
+                       ll_rw_block(WRITE, 1, &bh);
+                       wait_on_buffer(bh);
+                       bforget(bh);
+                       fsync_dev(dev);
+                       invalidate_buffers(dev);
+               } else
+                       printk(KERN_ERR "md: getblk failed for device %s\n", kdevname(dev));
+       }
+       return 0;
+}
 
 static int do_md_run (int minor, int repart)
 {
-  int pnum, i, min, current_ra, err;
-  
+  int pnum, i, min, factor, current_ra, err;
+
   if (!md_dev[minor].nb_dev)
     return -EINVAL;
   
   if (md_dev[minor].pers)
     return -EBUSY;
-  
+
   md_dev[minor].repartition=repart;
   
-  if ((pnum=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT))
+  if ((pnum=PERSONALITY(&md_dev[minor]) >> (PERSONALITY_SHIFT))
       >= MAX_PERSONALITY)
     return -EINVAL;
-  
+
+  /* Only RAID-1 and RAID-5 can have MD devices as underlying devices */
+  if (pnum != (RAID1 >> PERSONALITY_SHIFT) && pnum != (RAID5 >> PERSONALITY_SHIFT)){
+         for (i = 0; i < md_dev [minor].nb_dev; i++)
+                 if (MAJOR (md_dev [minor].devices [i].dev) == MD_MAJOR)
+                         return -EINVAL;
+  }
   if (!pers[pnum])
   {
 #ifdef CONFIG_KERNELD
@@ -145,7 +329,7 @@ static int do_md_run (int minor, int repart)
       return -EINVAL;
   }
   
-  min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+  factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
   
   for (i=0; i<md_dev[minor].nb_dev; i++)
     if (md_dev[minor].devices[i].size<min)
@@ -154,26 +338,37 @@ static int do_md_run (int minor, int repart)
              partition_name (md_dev[minor].devices[i].dev), min);
       return -EINVAL;
     }
+
+  for (i=0; i<md_dev[minor].nb_dev; i++) {
+    fsync_dev(md_dev[minor].devices[i].dev);
+    invalidate_buffers(md_dev[minor].devices[i].dev);
+  }
   
   /* Resize devices according to the factor. It is used to align
      partitions size on a given chunk size. */
   md_size[minor]=0;
-  
-  for (i=0; i<md_dev[minor].nb_dev; i++)
-  {
-    md_dev[minor].devices[i].size &= ~(min - 1);
-    md_size[minor] += md_dev[minor].devices[i].size;
-    md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + md_dev[minor].devices[i-1].size) : 0;
-  }
+
+  /*
+   * Analyze the raid superblock
+   */ 
+  if (analyze_sb(minor, pnum))
+    return -EINVAL;
 
   md_dev[minor].pers=pers[pnum];
   
   if ((err=md_dev[minor].pers->run (minor, md_dev+minor)))
   {
     md_dev[minor].pers=NULL;
+    free_sb(md_dev + minor);
     return (err);
   }
-  
+
+  if (pnum != RAID0 >> PERSONALITY_SHIFT && pnum != LINEAR >> PERSONALITY_SHIFT)
+  {
+    md_dev[minor].sb->state &= ~(1 << MD_SB_CLEAN);
+    md_update_sb(minor);
+  }
+
   /* FIXME : We assume here we have blocks
      that are twice as large as sectors.
      THIS MAY NOT BE TRUE !!! */
@@ -191,7 +386,6 @@ static int do_md_run (int minor, int repart)
   
   read_ahead[MD_MAJOR]=current_ra;
   
-  printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name);
   return (0);
 }
 
@@ -211,38 +405,40 @@ static int do_md_stop (int minor, struct inode *inode)
     /*  The device won't exist anymore -> flush it now */
     fsync_dev (inode->i_rdev);
     invalidate_buffers (inode->i_rdev);
+    if (md_dev[minor].sb)
+    {
+      md_dev[minor].sb->state |= 1 << MD_SB_CLEAN;
+      md_update_sb(minor);
+    }
     md_dev[minor].pers->stop (minor, md_dev+minor);
   }
   
   /* Remove locks. */
+  if (md_dev[minor].sb)
+    free_sb(md_dev + minor);
   for (i=0; i<md_dev[minor].nb_dev; i++)
     clear_inode (md_dev[minor].devices[i].inode);
-  
+
   md_dev[minor].nb_dev=md_size[minor]=0;
   md_hd_struct[minor].nr_sects=0;
   md_dev[minor].pers=NULL;
   
   set_ra ();                   /* calculate new read_ahead */
   
-  printk ("STOP_DEV md%x\n", minor);
   return (0);
 }
 
 
 static int do_md_add (int minor, kdev_t dev)
 {
-  struct gendisk *gen_real;
   int i;
-  
-  if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL)
+
+  if (md_dev[minor].nb_dev==MAX_REAL)
     return -EINVAL;
   
   if (!fs_may_mount (dev) || md_dev[minor].pers)
     return -EBUSY;
-  
-  if (!(gen_real=find_gendisk (dev)))
-    return -ENOENT;
-  
+
   i=md_dev[minor].nb_dev++;
   md_dev[minor].devices[i].dev=dev;
   
@@ -258,7 +454,13 @@ static int do_md_add (int minor, kdev_t dev)
   
   /* Sizes are now rounded at run time */
   
-  md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)];
+/*  md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; HACKHACK*/
+
+  if (blk_size[MAJOR(dev)][MINOR(dev)] == 0) {
+       printk("md_add(): zero device size, huh, bailing out.\n");
+  }
+
+  md_dev[minor].devices[i].size=blk_size[MAJOR(dev)][MINOR(dev)];
 
   printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
   return (0);
@@ -420,6 +622,27 @@ int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size)
   return (md_dev[minor].pers->map(md_dev+minor, rdev, rsector, size));
 }
   
+int md_make_request (int minor, int rw, struct buffer_head * bh)
+{
+       if (md_dev [minor].pers->make_request) {
+               if (buffer_locked(bh))
+                       return 0;
+               if (rw == WRITE || rw == WRITEA) {
+                       if (!buffer_dirty(bh))
+                               return 0;
+                       set_bit(BH_Lock, &bh->b_state);
+               }
+               if (rw == READ || rw == READA) {
+                       if (buffer_uptodate(bh))
+                               return 0;
+                       set_bit (BH_Lock, &bh->b_state);
+               }
+               return (md_dev[minor].pers->make_request(md_dev+minor, rw, bh));
+       } else {
+               make_request (MAJOR(bh->b_rdev), rw, bh);
+               return 0;
+       }
+}
 
 static void do_md_request (void)
 {
@@ -427,6 +650,40 @@ static void do_md_request (void)
   return;
 }  
 
+/*
+ * We run MAX_MD_THREADS from md_init() and arbitrate them in run time.
+ * This is not so elegant, but how can we use kernel_thread() from within
+ * loadable modules?
+ */
+struct md_thread *md_register_thread (void (*run) (void *), void *data)
+{
+       int i;
+       for (i = 0; i < MAX_MD_THREADS; i++) {
+               if (md_threads[i].run == NULL) {
+                       md_threads[i].run = run;
+                       md_threads[i].data = data;
+                       return md_threads + i;
+               }
+       }
+       return NULL;
+}
+
+
+void md_unregister_thread (struct md_thread *thread)
+{
+       thread->run = NULL;
+       thread->data = NULL;
+       thread->flags = 0;
+}
+
+void md_wakeup_thread(struct md_thread *thread)
+{
+       set_bit(THREAD_WAKEUP, &thread->flags);
+       wake_up(&thread->wqueue);
+}
+
+struct buffer_head *efind_buffer(kdev_t dev, int block, int size);
+
 static struct symbol_table md_symbol_table=
 {
 #include <linux/symtab_begin.h>
@@ -435,11 +692,18 @@ static struct symbol_table md_symbol_table=
   X(register_md_personality),
   X(unregister_md_personality),
   X(partition_name),
+  X(md_dev),
+  X(md_error),
+  X(md_register_thread),
+  X(md_unregister_thread),
+  X(md_update_sb),
+  X(md_map),
+  X(md_wakeup_thread),
+  X(efind_buffer),
 
 #include <linux/symtab_end.h>
 };
 
-
 static void md_geninit (struct gendisk *gdisk)
 {
   int i;
@@ -463,6 +727,17 @@ static void md_geninit (struct gendisk *gdisk)
              });
 }
 
+int md_error (kdev_t mddev, kdev_t rdev)
+{
+    unsigned int minor = MINOR (mddev);
+    if (MAJOR(mddev) != MD_MAJOR || minor > MAX_MD_DEV)
+       panic ("md_error gets unknown device\n");
+    if (!md_dev [minor].pers)
+       panic ("md_error gets an error for an unknown device\n");
+    if (md_dev [minor].pers->error_handler)
+       return (md_dev [minor].pers->error_handler (md_dev+minor, rdev));
+    return 0;
+}
 
 int get_md_status (char *page)
 {
@@ -495,9 +770,13 @@ int get_md_status (char *page)
                   partition_name(md_dev[i].devices[j].dev));
       size+=md_dev[i].devices[j].size;
     }
-    
-    if (md_dev[i].nb_dev)
-      sz+=sprintf (page+sz, " %d blocks", size);
+
+    if (md_dev[i].nb_dev) {
+      if (md_dev[i].pers)
+        sz+=sprintf (page+sz, " %d blocks", md_size[i]);
+      else
+        sz+=sprintf (page+sz, " %d blocks", size);
+    }
 
     if (!md_dev[i].pers)
     {
@@ -508,11 +787,8 @@ int get_md_status (char *page)
     if (md_dev[i].pers->max_invalid_dev)
       sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i));
 
-    sz+=sprintf (page+sz, " %dk %s\n", 1<<FACTOR_SHIFT(FACTOR(md_dev+i)),
-                md_dev[i].pers == pers[LINEAR>>PERSONALITY_SHIFT] ?
-                "rounding" : "chunks");
-
     sz+=md_dev[i].pers->status (page+sz, i, md_dev+i);
+    sz+=sprintf (page+sz, "\n");
   }
 
   return (sz);
@@ -545,6 +821,32 @@ int unregister_md_personality (int p_num)
   return 0;
 } 
 
+int md_thread(void * arg)
+{
+       struct md_thread *thread = arg;
+
+       current->session = 1;
+       current->pgrp = 1;
+       sprintf(current->comm, "md_thread");
+
+#ifdef __SMP__
+       lock_kernel();
+       syscall_count++;
+#endif
+       for (;;) {
+               sti();
+               clear_bit(THREAD_WAKEUP, &thread->flags);
+               if (thread->run) {
+                       thread->run(thread->data);
+                       run_task_queue(&tq_disk);
+               }
+               current->signal = 0;
+               cli();
+               if (!test_bit(THREAD_WAKEUP, &thread->flags))
+                       interruptible_sleep_on(&thread->wqueue);
+       }
+}
+
 void linear_init (void);
 void raid0_init (void);
 void raid1_init (void);
@@ -552,7 +854,11 @@ void raid5_init (void);
 
 int md_init (void)
 {
-  printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL);
+  int i;
+
+  printk ("md driver %d.%d.%d MAX_MD_DEV=%d, MAX_REAL=%d\n",
+    MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION,
+    MAX_MD_DEV, MAX_REAL);
 
   if (register_blkdev (MD_MAJOR, "md", &md_fops))
   {
@@ -560,9 +866,17 @@ int md_init (void)
     return (-1);
   }
 
+  for (i = 0; i < MAX_MD_THREADS; i++) {
+    md_threads[i].run = NULL;
+    init_waitqueue(&md_threads[i].wqueue);
+    md_threads[i].flags = 0;
+    kernel_thread (md_thread, md_threads + i, 0);
+  }
+
   blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST;
   blk_dev[MD_MAJOR].current_request=NULL;
   read_ahead[MD_MAJOR]=INT_MAX;
+  memset(md_dev, 0, MAX_MD_DEV * sizeof (struct md_dev));
   md_gendisk.next=gendisk_head;
 
   gendisk_head=&md_gendisk;
@@ -573,6 +887,12 @@ int md_init (void)
 #ifdef CONFIG_MD_STRIPED
   raid0_init ();
 #endif
+#ifdef CONFIG_MD_MIRRORING
+  raid1_init ();
+#endif
+#ifdef CONFIG_MD_RAID5
+  raid5_init ();
+#endif
   
   return (0);
 }
diff --git a/drivers/block/paride/Config.in b/drivers/block/paride/Config.in
new file mode 100644 (file)
index 0000000..00dd9c8
--- /dev/null
@@ -0,0 +1,24 @@
+#
+# PARIDE configuration
+#
+comment 'Parallel IDE high-level drivers'
+dep_tristate '  Parallel port IDE disks' CONFIG_PARIDE_PD $CONFIG_PARIDE
+dep_tristate '  Parallel port ATAPI CD-ROMs' CONFIG_PARIDE_PCD $CONFIG_PARIDE
+dep_tristate '  Parallel port ATAPI disks' CONFIG_PARIDE_PF $CONFIG_PARIDE
+dep_tristate '  Parallel port ATAPI tapes' CONFIG_PARIDE_PT $CONFIG_PARIDE
+dep_tristate '  Parallel port generic ATAPI devices' CONFIG_PARIDE_PG $CONFIG_PARIDE
+comment 'Parallel IDE protocol modules'
+dep_tristate '    ATEN EH-100 protocol' CONFIG_PARIDE_ATEN $CONFIG_PARIDE
+dep_tristate '    MicroSolutions backpack protocol' CONFIG_PARIDE_BPCK $CONFIG_PARIDE
+dep_tristate '    DataStor Commuter protocol' CONFIG_PARIDE_COMM $CONFIG_PARIDE
+dep_tristate '    DataStor EP-2000 protocol' CONFIG_PARIDE_DSTR $CONFIG_PARIDE
+dep_tristate '    FIT TD-2000 protocol' CONFIG_PARIDE_FIT2 $CONFIG_PARIDE
+dep_tristate '    FIT TD-3000 protocol' CONFIG_PARIDE_FIT3 $CONFIG_PARIDE
+dep_tristate '    Shuttle EPAT/EPEZ protocol' CONFIG_PARIDE_EPAT $CONFIG_PARIDE
+dep_tristate '    Shuttle EPIA protocol' CONFIG_PARIDE_EPIA $CONFIG_PARIDE
+dep_tristate '    FreeCom power protocol' CONFIG_PARIDE_FRPW $CONFIG_PARIDE
+dep_tristate '    KingByte KBIC-951A/971A protocols' CONFIG_PARIDE_KBIC $CONFIG_PARIDE
+dep_tristate '    KT PHd protocol' CONFIG_PARIDE_KTTI $CONFIG_PARIDE
+dep_tristate '    OnSpec 90c20 protocol' CONFIG_PARIDE_ON20 $CONFIG_PARIDE
+dep_tristate '    OnSpec 90c26 protocol' CONFIG_PARIDE_ON26 $CONFIG_PARIDE
+#
diff --git a/drivers/block/paride/Makefile b/drivers/block/paride/Makefile
new file mode 100644 (file)
index 0000000..abb45d2
--- /dev/null
@@ -0,0 +1,174 @@
+#
+# Makefile for PARIDE
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := paride.a
+MX_OBJS  :=
+LX_OBJS  := 
+MI_OBJS  :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PARIDE),y)
+  LX_OBJS += paride.o
+else
+  ifeq ($(CONFIG_PARIDE),m)
+    MX_OBJS += paride.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PD),y)
+  LX_OBJS += pd.o
+else
+  ifeq ($(CONFIG_PARIDE_PD),m)
+    MX_OBJS += pd.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PCD),y)
+  LX_OBJS += pcd.o
+else
+  ifeq ($(CONFIG_PARIDE_PCD),m)
+    MX_OBJS += pcd.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PF),y)
+  LX_OBJS += pf.o
+else
+  ifeq ($(CONFIG_PARIDE_PF),m)
+    MX_OBJS += pf.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PT),y)
+  LX_OBJS += pt.o
+else
+  ifeq ($(CONFIG_PARIDE_PT),m)
+    MX_OBJS += pt.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PG),y)
+  LX_OBJS += pg.o
+else
+  ifeq ($(CONFIG_PARIDE_PG),m)
+    MX_OBJS += pg.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ATEN),y)
+  LX_OBJS += aten.o
+else
+  ifeq ($(CONFIG_PARIDE_ATEN),m)
+    MX_OBJS += aten.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_BPCK),y)
+  LX_OBJS += bpck.o
+else
+  ifeq ($(CONFIG_PARIDE_BPCK),m)
+    MX_OBJS += bpck.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_COMM),y)
+  LX_OBJS += comm.o
+else
+  ifeq ($(CONFIG_PARIDE_COMM),m)
+    MX_OBJS += comm.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_DSTR),y)
+  LX_OBJS += dstr.o
+else
+  ifeq ($(CONFIG_PARIDE_DSTR),m)
+    MX_OBJS += dstr.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_KBIC),y)
+  LX_OBJS += kbic.o
+else
+  ifeq ($(CONFIG_PARIDE_KBIC),m)
+    MX_OBJS += kbic.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPAT),y)
+  LX_OBJS += epat.o
+else
+  ifeq ($(CONFIG_PARIDE_EPAT),m)
+    MX_OBJS += epat.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPIA),y)
+  LX_OBJS += epia.o
+else
+  ifeq ($(CONFIG_PARIDE_EPIA),m)
+    MX_OBJS += epia.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FIT2),y)
+  LX_OBJS += fit2.o
+else
+  ifeq ($(CONFIG_PARIDE_FIT2),m)
+    MX_OBJS += fit2.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FIT3),y)
+  LX_OBJS += fit3.o
+else
+  ifeq ($(CONFIG_PARIDE_FIT3),m)
+    MX_OBJS += fit3.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FRPW),y)
+  LX_OBJS += frpw.o
+else
+  ifeq ($(CONFIG_PARIDE_FRPW),m)
+    MX_OBJS += frpw.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON20),y)
+  LX_OBJS += on20.o
+else
+  ifeq ($(CONFIG_PARIDE_ON20),m)
+    MX_OBJS += on20.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON26),y)
+  LX_OBJS += on26.o
+else
+  ifeq ($(CONFIG_PARIDE_ON26),m)
+    MX_OBJS += on26.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_KTTI),y)
+  LX_OBJS += ktti.o
+else
+  ifeq ($(CONFIG_PARIDE_KTTI),m)
+    MX_OBJS += ktti.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/block/paride/aten.c b/drivers/block/paride/aten.c
new file mode 100644 (file)
index 0000000..6f80be5
--- /dev/null
@@ -0,0 +1,172 @@
+/* 
+        aten.c  (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+       aten.c is a low-level protocol driver for the ATEN EH-100
+       parallel port adapter.  The EH-100 supports 4-bit and 8-bit
+        modes only.  There is also an EH-132 which supports EPP mode
+        transfers.  The EH-132 is not yet supported.
+
+*/
+
+/* Changes:
+
+       1.01    GRG 1998.05.05  init_proto, release_proto
+
+*/
+
+#define ATEN_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x20 };
+
+static void  aten_write_regr( PIA *pi, int cont, int regr, int val)
+
+{      int r;
+
+       r = regr + cont_map[cont] + 0x80;
+
+       w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc);
+}
+
+static int aten_read_regr( PIA *pi, int cont, int regr )
+
+{      int  a, b, r;
+
+        r = regr + cont_map[cont] + 0x40;
+
+       switch (pi->mode) {
+
+        case 0: w0(r); w2(0xe); w2(6); 
+               w2(7); w2(6); w2(0);
+               a = r1(); w0(0x10); b = r1(); w2(0xc);
+               return j44(a,b);
+
+        case 1: r |= 0x10;
+               w0(r); w2(0xe); w2(6); w0(0xff); 
+               w2(0x27); w2(0x26); w2(0x20);
+               a = r0();
+               w2(0x26); w2(0xc);
+               return a;
+       }
+       return -1;
+}
+
+static void aten_read_block( PIA *pi, char * buf, int count )
+
+{      int  k, a, b, c, d;
+
+       switch (pi->mode) {
+
+       case 0: w0(0x48); w2(0xe); w2(6);
+               for (k=0;k<count/2;k++) {
+                       w2(7); w2(6); w2(2);
+                       a = r1(); w0(0x58); b = r1();
+                       w2(0); d = r1(); w0(0x48); c = r1();
+                       buf[2*k] = j44(c,d);
+                       buf[2*k+1] = j44(a,b);
+               }
+               w2(0xc);
+               break;
+
+       case 1: w0(0x58); w2(0xe); w2(6);
+               for (k=0;k<count/2;k++) {
+                       w2(0x27); w2(0x26); w2(0x22);
+                       a = r0(); w2(0x20); b = r0();
+                       buf[2*k] = b; buf[2*k+1] = a;
+               }
+               w2(0x26); w2(0xc);
+               break;
+       }
+}
+
+static void aten_write_block( PIA *pi, char * buf, int count )
+
+{      int k;
+
+       w0(0x88); w2(0xe); w2(6);
+       for (k=0;k<count/2;k++) {
+               w0(buf[2*k+1]); w2(0xe); w2(6);
+               w0(buf[2*k]); w2(7); w2(6);
+       }
+       w2(0xc);
+}
+
+static void aten_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+       w2(0xc);        
+}
+
+static void aten_disconnect ( PIA *pi )
+
+{       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void aten_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[2] = {"4-bit","8-bit"};
+
+        printk("%s: aten %s, ATEN EH-100 at 0x%x, ",
+                pi->device,ATEN_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void aten_init_proto( PIA *pi )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void aten_release_proto( PIA *pi )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol aten = {"aten",0,2,2,1,1,
+                           aten_write_regr,
+                           aten_read_regr,
+                           aten_write_block,
+                           aten_read_block,
+                           aten_connect,
+                           aten_disconnect,
+                           0,
+                           0,
+                           0,
+                           aten_log_adapter,
+                           aten_init_proto,
+                           aten_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &aten ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &aten );
+}
+
+#endif
+
+/* end of aten.c */
diff --git a/drivers/block/paride/bpck.c b/drivers/block/paride/bpck.c
new file mode 100644 (file)
index 0000000..4b241c7
--- /dev/null
@@ -0,0 +1,482 @@
+/* 
+       bpck.c  (c) 1996-8  Grant R. Guenther <grant@torque.net>
+                           Under the terms of the GNU public license.
+
+       bpck.c is a low-level protocol driver for the MicroSolutions 
+       "backpack" parallel port IDE adapter.  
+
+*/
+
+/* Changes:
+
+       1.01    GRG 1998.05.05 init_proto, release_proto, pi->delay 
+
+*/
+
+#define        BPCK_VERSION    "1.01" 
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#undef r2
+#undef w2
+
+#define PC                     pi->private
+#define r2()                   (PC=(in_p(2) & 0xff))
+#define w2(byte)               {out_p(2,byte); PC = byte;}
+#define t2(pat)                {PC ^= pat; out_p(2,PC);}
+#define e2()                   {PC &= 0xfe; out_p(2,PC);}
+#define o2()                   {PC |= 1; out_p(2,PC);}
+
+#define j44(l,h)     (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+   cont = 2 - use internal bpck register addressing
+*/
+
+static int  cont_map[3] = { 0x40, 0x48, 0 };
+
+static int bpck_read_regr( PIA *pi, int cont, int regr )
+
+{       int r, l, h;
+
+       r = regr + cont_map[cont];
+
+       switch (pi->mode) {
+
+       case 0: w0(r & 0xf); w0(r); t2(2); t2(4);
+               l = r1();
+               t2(4);
+               h = r1();
+               return j44(l,h);
+
+       case 1: w0(r & 0xf); w0(r); t2(2);
+               e2(); t2(0x20);
+               t2(4); h = r0();
+               t2(1); t2(0x20);
+               return h;
+
+       case 2:
+       case 3:
+       case 4: w0(r); w2(9); w2(0); w2(0x20);
+               h = r4();
+               w2(0);
+               return h;
+
+       }
+       return -1;
+}      
+
+static void bpck_write_regr( PIA *pi, int cont, int regr, int val )
+
+{      int     r;
+
+        r = regr + cont_map[cont];
+
+       switch (pi->mode) {
+
+       case 0:
+       case 1: w0(r);
+               t2(2);
+               w0(val);
+               o2(); t2(4); t2(1);
+               break;
+
+       case 2:
+       case 3:
+       case 4: w0(r); w2(9); w2(0);
+               w0(val); w2(1); w2(3); w2(0);
+               break;
+
+       }
+}
+
+/* These macros access the bpck registers in native addressing */
+
+#define WR(r,v)                bpck_write_regr(pi,2,r,v)
+#define RR(r)          (bpck_read_regr(pi,2,r))
+
+static void bpck_write_block( PIA *pi, char * buf, int count )
+
+{      int i;
+
+       switch (pi->mode) {
+
+       case 0: WR(4,0x40);
+               w0(0x40); t2(2); t2(1);
+               for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
+               WR(4,0);
+               break;
+
+       case 1: WR(4,0x50);
+                w0(0x40); t2(2); t2(1);
+                for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
+                WR(4,0x10);
+               break;
+
+       case 2: WR(4,0x48);
+               w0(0x40); w2(9); w2(0); w2(1);
+               for (i=0;i<count;i++) w4(buf[i]);
+               w2(0);
+               WR(4,8);
+               break;
+
+        case 3: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(1);
+                for (i=0;i<count/2;i++) w4w(((u16 *)buf)[i]);
+                w2(0);
+                WR(4,8);
+                break;
+        case 4: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(1);
+                for (i=0;i<count/4;i++) w4l(((u32 *)buf)[i]);
+                w2(0);
+                WR(4,8);
+                break;
+       }
+}
+
+static void bpck_read_block( PIA *pi, char * buf, int count )
+
+{      int i, l, h;
+
+       switch (pi->mode) {
+
+       case 0: WR(4,0x40);
+               w0(0x40); t2(2);
+               for (i=0;i<count;i++) {
+                   t2(4); l = r1();
+                   t2(4); h = r1();
+                   buf[i] = j44(l,h);
+               }
+               WR(4,0);
+               break;
+
+       case 1: WR(4,0x50);
+               w0(0x40); t2(2); t2(0x20);
+               for(i=0;i<count;i++) { t2(4); buf[i] = r0(); }
+               t2(1); t2(0x20);
+               WR(4,0x10);
+               break;
+
+       case 2: WR(4,0x48);
+               w0(0x40); w2(9); w2(0); w2(0x20);
+               for (i=0;i<count;i++) buf[i] = r4();
+               w2(0);
+               WR(4,8);
+               break;
+
+        case 3: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(0x20);
+                for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+                w2(0);
+                WR(4,8);
+                break;
+
+        case 4: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(0x20);
+                for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
+                w2(0);
+                WR(4,8);
+                break;
+
+       }
+}
+
+static int bpck_probe_unit ( PIA *pi )
+
+{      int o1, o0, f7, id;
+       int t, s;
+
+       id = pi->unit;
+       s = 0;
+       w2(4); w2(0xe); r2(); t2(2); 
+       o1 = r1()&0xf8;
+       o0 = r0();
+       w0(255-id); w2(4); w0(id);
+       t2(8); t2(8); t2(8);
+       t2(2); t = r1()&0xf8;
+       f7 = ((id % 8) == 7);
+       if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; }
+       if ((t == o1) && ((!f7) || (s == o1)))  {
+               w2(0x4c); w0(o0);
+               return 0;       
+       }
+       t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
+       return 1;
+}
+       
+static void bpck_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+       w0(0xff-pi->unit); w2(4); w0(pi->unit);
+       t2(8); t2(8); t2(8); 
+       t2(2); t2(2);
+       
+       switch (pi->mode) {
+
+       case 0: t2(8); WR(4,0);
+               break;
+
+       case 1: t2(8); WR(4,0x10);
+               break;
+
+       case 2:
+        case 3:
+       case 4: w2(0); WR(4,8);
+               break;
+
+       }
+
+       WR(5,8);
+
+       if (pi->devtype == PI_PCD) {
+               WR(0x46,0x10);          /* fiddle with ESS logic ??? */
+               WR(0x4c,0x38);
+               WR(0x4d,0x88);
+               WR(0x46,0xa0);
+               WR(0x41,0);
+               WR(0x4e,8);
+               }
+}
+
+static void bpck_disconnect ( PIA *pi )
+
+{      w0(0); 
+       if (pi->mode >= 2) { w2(9); w2(0); } else t2(2);
+       w2(0x4c); w0(pi->saved_r0);
+} 
+
+static void bpck_force_spp ( PIA *pi )
+
+/* This fakes the EPP protocol to turn off EPP ... */
+
+{       pi->saved_r0 = r0();
+        w0(0xff-pi->unit); w2(4); w0(pi->unit);
+        t2(8); t2(8); t2(8); 
+        t2(2); t2(2);
+
+        w2(0); 
+        w0(4); w2(9); w2(0); 
+        w0(0); w2(1); w2(3); w2(0);     
+        w0(0); w2(9); w2(0);
+        w2(0x4c); w0(pi->saved_r0);
+}
+
+#define TEST_LEN  16
+
+static int bpck_test_proto( PIA *pi, char * scratch, int verbose )
+
+{      int i, e, l, h, om;
+       char buf[TEST_LEN];
+
+       bpck_force_spp(pi);
+
+       switch (pi->mode) {
+
+       case 0: bpck_connect(pi);
+               WR(0x13,0x7f);
+               w0(0x13); t2(2);
+               for(i=0;i<TEST_LEN;i++) {
+                    t2(4); l = r1();
+                    t2(4); h = r1();
+                    buf[i] = j44(l,h);
+               }
+               bpck_disconnect(pi);
+               break;
+
+        case 1: bpck_connect(pi);
+               WR(0x13,0x7f);
+                w0(0x13); t2(2); t2(0x20);
+                for(i=0;i<TEST_LEN;i++) { t2(4); buf[i] = r0(); }
+                t2(1); t2(0x20);
+               bpck_disconnect(pi);
+               break;
+
+       case 2:
+       case 3:
+       case 4: om = pi->mode;
+               pi->mode = 0;
+               bpck_connect(pi);
+               WR(7,3);
+               WR(4,8);
+               bpck_disconnect(pi);
+
+               pi->mode = om;
+               bpck_connect(pi);
+               w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
+
+               switch (pi->mode) {
+                 case 2: for (i=0;i<TEST_LEN;i++) buf[i] = r4();
+                         break;
+                 case 3: for (i=0;i<TEST_LEN/2;i++) ((u16 *)buf)[i] = r4w();
+                          break;
+                 case 4: for (i=0;i<TEST_LEN/4;i++) ((u32 *)buf)[i] = r4l();
+                          break;
+               }
+
+               w2(0);
+               WR(7,0);
+               bpck_disconnect(pi);
+
+               break;
+
+       }
+
+       if (verbose) {
+           printk("%s: bpck: 0x%x unit %d mode %d: ",
+                  pi->device,pi->port,pi->unit,pi->mode);
+           for (i=0;i<TEST_LEN;i++) printk("%3d",buf[i]);
+           printk("\n");
+       }
+
+       e = 0;
+       for (i=0;i<TEST_LEN;i++) if (buf[i] != (i+1)) e++;
+       return e;
+}
+
+static void bpck_read_eeprom ( PIA *pi, char * buf )
+
+{       int i,j,k,n,p,v,f, om, od;
+
+       bpck_force_spp(pi);
+
+       om = pi->mode;  od = pi->delay;
+       pi->mode = 0; pi->delay = 6;
+
+       bpck_connect(pi);
+       
+       n = 0;
+       WR(4,0);
+       for (i=0;i<64;i++) {
+           WR(6,8);  
+           WR(6,0xc);
+           p = 0x100;
+           for (k=0;k<9;k++) {
+               f = (((i + 0x180) & p) != 0) * 2;
+               WR(6,f+0xc); 
+               WR(6,f+0xd); 
+               WR(6,f+0xc);
+               p = (p >> 1);
+           }
+           for (j=0;j<2;j++) {
+               v = 0;
+               for (k=0;k<8;k++) {
+                   WR(6,0xc); 
+                   WR(6,0xd); 
+                   WR(6,0xc); 
+                   f = RR(0);
+                   v = 2*v + (f == 0x84);
+               }
+               buf[2*i+1-j] = v;
+           }
+       }
+       WR(6,8);
+       WR(6,0);
+       WR(5,8);
+
+       bpck_disconnect(pi);
+
+        if (om >= 2) {
+                bpck_connect(pi);
+                WR(7,3);
+                WR(4,8);
+                bpck_disconnect(pi);
+        }
+
+       pi->mode = om; pi->delay = od;
+}
+
+static int bpck_test_port ( PIA *pi )  /* check for 8-bit port */
+
+{      int     i, r, m;
+
+       w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
+       m = -1;
+       if (r == i) m = 2;
+       if (r == (255-i)) m = 0;
+
+       w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i);
+       if (r != (255-i)) m = -1;
+       
+       if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); }
+       if (m == 2) { w2(0x26); w2(0xc); }
+
+       if (m == -1) return 0;
+       return 5;
+}
+
+static void bpck_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{      char    *mode_string[5] = { "4-bit","8-bit","EPP-8",
+                                   "EPP-16","EPP-32" };
+
+#ifdef DUMP_EEPROM
+       int i;
+#endif
+
+       bpck_read_eeprom(pi,scratch);
+
+#ifdef DUMP_EEPROM
+       if (verbose) {
+          for(i=0;i<128;i++)
+               if ((scratch[i] < ' ') || (scratch[i] > '~'))
+                   scratch[i] = '.';
+          printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch);
+          printk("%s:              %64.64s\n",pi->device,&scratch[64]);
+       }
+#endif
+
+       printk("%s: bpck %s, backpack %8.8s unit %d",
+               pi->device,BPCK_VERSION,&scratch[110],pi->unit);
+       printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port,
+               pi->mode,mode_string[pi->mode],pi->delay);
+}
+
+static void bpck_init_proto( PIA *pi)
+
+{      MOD_INC_USE_COUNT;
+}
+
+static void bpck_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol bpck = { "bpck",0,5,2,1,256,
+                         bpck_write_regr,
+                         bpck_read_regr,
+                         bpck_write_block,
+                         bpck_read_block,
+                         bpck_connect,
+                         bpck_disconnect,
+                         bpck_test_port,
+                         bpck_probe_unit,
+                         bpck_test_proto,
+                         bpck_log_adapter,
+                         bpck_init_proto,
+                         bpck_release_proto
+                       };
+
+#ifdef MODULE
+
+int    init_module(void)
+
+{      return pi_register(&bpck) - 1;
+}
+
+void   cleanup_module(void)
+
+{      pi_unregister(&bpck);
+}
+
+#endif
+
+/* end of bpck.c */
diff --git a/drivers/block/paride/comm.c b/drivers/block/paride/comm.c
new file mode 100644 (file)
index 0000000..99660e2
--- /dev/null
@@ -0,0 +1,228 @@
+/* 
+        comm.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+       comm.c is a low-level protocol driver for some older models
+       of the DataStor "Commuter" parallel to IDE adapter.  Some of
+       the parallel port devices marketed by Arista currently
+       use this adapter.
+*/
+
+/* Changes:
+
+       1.01    GRG 1998.05.05  init_proto, release_proto
+
+*/
+
+#define COMM_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+*/
+
+#define j44(a,b)       (((a>>3)&0x0f)|((b<<1)&0xf0))
+
+#define P1     w2(5);w2(0xd);w2(0xd);w2(5);w2(4);
+#define P2     w2(5);w2(7);w2(7);w2(5);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x10 };
+
+static int comm_read_regr( PIA *pi, int cont, int regr )
+
+{       int     l, h, r;
+
+        r = regr + cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0: w0(r); P1; w0(0);
+               w2(6); l = r1(); w0(0x80); h = r1(); w2(4);
+                return j44(l,h);
+
+        case 1: w0(r+0x20); P1; 
+               w0(0); w2(0x26); h = r0(); w2(4);
+                return h;
+
+       case 2:
+       case 3:
+        case 4: w3(r+0x20); r1(); 
+               w2(0x24); h = r4(); w2(4);
+                return h;
+
+        }
+        return -1;
+}       
+
+static void comm_write_regr( PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = regr + cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(r); P1; w0(val); P2;
+               break;
+
+       case 2:
+       case 3:
+        case 4: w3(r); r1(); w4(val); 
+                break;
+        }
+}
+
+static void comm_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); w0(0xff); w2(6);
+        w2(4); w0(0xaa); w2(6);
+        w2(4); w0(0x00); w2(6);
+        w2(4); w0(0x87); w2(6);
+        w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4);
+}
+
+static void comm_disconnect ( PIA *pi )
+
+{       w2(0); w2(0); w2(0); w2(4); 
+       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void comm_read_block( PIA *pi, char * buf, int count )
+
+{       int     i, l, h;
+
+        switch (pi->mode) {
+        
+        case 0: w0(0x48); P1;
+                for(i=0;i<count;i++) {
+                        w0(0); w2(6); l = r1();
+                        w0(0x80); h = r1(); w2(4);
+                        buf[i] = j44(l,h);
+                }
+                break;
+
+        case 1: w0(0x68); P1; w0(0);
+                for(i=0;i<count;i++) {
+                        w2(0x26); buf[i] = r0(); w2(0x24);
+                }
+               w2(4);
+               break;
+               
+       case 2: w3(0x68); r1(); w2(0x24);
+               for (i=0;i<count;i++) buf[i] = r4();
+               w2(4);
+               break;
+
+        case 3: w3(0x68); r1(); w2(0x24);
+                for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+                w2(4);
+                break;
+
+        case 4: w3(0x68); r1(); w2(0x24);
+                for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
+                w2(4);
+                break;
+               
+       }
+}
+
+/* NB: Watch out for the byte swapped writes ! */
+
+static void comm_write_block( PIA *pi, char * buf, int count )
+
+{       int    k;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1: w0(0x68); P1;
+               for (k=0;k<count;k++) {
+                        w2(5); w0(buf[k^1]); w2(7);
+                }
+                w2(5); w2(4);
+                break;
+
+        case 2: w3(0x48); r1();
+                for (k=0;k<count;k++) w4(buf[k^1]);
+                break;
+
+        case 3: w3(0x48); r1();
+                for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+                break;
+
+        case 4: w3(0x48); r1();
+                for (k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
+                break;
+
+
+        }
+}
+
+static void comm_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: comm %s, DataStor Commuter at 0x%x, ",
+                pi->device,COMM_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void comm_init_proto(PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void comm_release_proto(PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol comm = {"comm",0,5,2,1,1,
+                           comm_write_regr,
+                           comm_read_regr,
+                           comm_write_block,
+                           comm_read_block,
+                           comm_connect,
+                           comm_disconnect,
+                           0,
+                           0,
+                           0,
+                           comm_log_adapter,
+                           comm_init_proto,
+                           comm_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &comm ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &comm );
+}
+
+#endif
+
+/* end of comm.c */
diff --git a/drivers/block/paride/dstr.c b/drivers/block/paride/dstr.c
new file mode 100644 (file)
index 0000000..cc522fa
--- /dev/null
@@ -0,0 +1,243 @@
+/* 
+        dstr.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+        dstr.c is a low-level protocol driver for the 
+        DataStor EP2000 parallel to IDE adapter chip.
+
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define DSTR_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+               3  EPP-16
+               4  EPP-32
+*/
+
+#define j44(a,b)  (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80))
+
+#define P1     w2(5);w2(0xd);w2(5);w2(4);
+#define P2     w2(5);w2(7);w2(5);w2(4);
+#define P3      w2(6);w2(4);w2(6);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x20, 0x40 };
+
+static int dstr_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+        r = regr + cont_map[cont];
+
+       w0(0x81); P1;
+       if (pi->mode) { w0(0x11); } else { w0(1); }
+       P2; w0(r); P1;
+
+        switch (pi->mode)  {
+
+        case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4);
+                return j44(a,b);
+
+        case 1: w0(0); w2(0x26); a = r0(); w2(4);
+                return a;
+
+       case 2:
+       case 3:
+        case 4: w2(0x24); a = r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void dstr_write_regr(  PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = regr + cont_map[cont];
+
+       w0(0x81); P1; 
+       if (pi->mode >= 2) { w0(0x11); } else { w0(1); }
+       P2; w0(r); P1;
+       
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(val); w2(5); w2(7); w2(5); w2(4);
+               break;
+
+       case 2:
+       case 3:
+        case 4: w4(val); 
+                break;
+        }
+}
+
+#define  CCP(x)  w0(0xff);w2(0xc);w2(4);\
+                w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\
+                w0(x);w2(5);w2(4);
+
+static void dstr_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); CCP(0xe0); w0(0xff);
+}
+
+static void dstr_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void dstr_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        w0(0x81); P1;
+        if (pi->mode) { w0(0x19); } else { w0(9); }
+       P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+        switch (pi->mode) {
+
+        case 0: for (k=0;k<count;k++) {
+                        w2(6); a = r1(); w2(4);
+                        w2(6); b = r1(); w2(4);
+                        buf[k] = j44(a,b);
+                } 
+                break;
+
+        case 1: w0(0);
+                for (k=0;k<count;k++) {
+                        w2(0x26); buf[k] = r0(); w2(0x24);
+                }
+                w2(4);
+                break;
+
+        case 2: w2(0x24); 
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4);
+                break;
+
+        case 3: w2(0x24); 
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4);
+                break;
+
+        case 4: w2(0x24); 
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4);
+                break;
+
+        }
+}
+
+static void dstr_write_block( PIA *pi, char * buf, int count )
+
+{       int    k;
+
+        w0(0x81); P1;
+        if (pi->mode) { w0(0x19); } else { w0(9); }
+        P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1: for (k=0;k<count;k++) {
+                        w2(5); w0(buf[k]); w2(7);
+                }
+                w2(5); w2(4);
+                break;
+
+        case 2: w2(0xc5);
+                for (k=0;k<count;k++) w4(buf[k]);
+               w2(0xc4);
+                break;
+
+        case 3: w2(0xc5);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        case 4: w2(0xc5);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        }
+}
+
+
+static void dstr_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8",
+                                  "EPP-16","EPP-32"};
+
+        printk("%s: dstr %s, DataStor EP2000 at 0x%x, ",
+                pi->device,DSTR_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void dstr_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void dstr_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol dstr = {"dstr",0,5,2,1,1,
+                           dstr_write_regr,
+                           dstr_read_regr,
+                           dstr_write_block,
+                           dstr_read_block,
+                           dstr_connect,
+                           dstr_disconnect,
+                           0,
+                           0,
+                           0,
+                           dstr_log_adapter,
+                           dstr_init_proto,
+                           dstr_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &dstr ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &dstr );
+}
+
+#endif
+
+/* end of dstr.c */
diff --git a/drivers/block/paride/epat.c b/drivers/block/paride/epat.c
new file mode 100644 (file)
index 0000000..980eb69
--- /dev/null
@@ -0,0 +1,321 @@
+/* 
+        epat.c  (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+       This is the low level protocol driver for the EPAT parallel
+        to IDE adapter from Shuttle Technologies.  This adapter is
+        used in many popular parallel port disk products such as the
+        SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk.
+       
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define EPAT_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)               (((a>>4)&0x0f)+(b&0xf0))
+#define j53(a,b)               (((a>>3)&0x1f)+((b<<4)&0xe0))
+
+/* cont =  0   IDE register file
+   cont =  1   IDE control registers
+   cont =  2   internal EPAT registers
+*/
+
+static int cont_map[3] = { 0x18, 0x10, 0 };
+
+static void epat_write_regr( PIA *pi, int cont, int regr, int val)
+
+{      int r;
+
+       r = regr + cont_map[cont];
+
+       switch (pi->mode) {
+
+       case 0:
+       case 1:
+       case 2: w0(0x60+r); w2(1); w0(val); w2(4);
+               break;
+
+       case 3:
+       case 4:
+       case 5: w3(0x40+r); w4(val);
+               break;
+
+       }
+}
+
+static int epat_read_regr( PIA *pi, int cont, int regr )
+
+{      int  a, b, r;
+
+       r = regr + cont_map[cont];
+
+       switch (pi->mode) {
+
+       case 0: w0(r); w2(1); w2(3); 
+               a = r1(); w2(4); b = r1();
+               return j44(a,b);
+
+       case 1: w0(0x40+r); w2(1); w2(4);
+               a = r1(); b = r2(); w0(0xff);
+               return j53(a,b);
+
+       case 2: w0(0x20+r); w2(1); w2(0x25);
+               a = r0(); w2(4);
+               return a;
+
+       case 3:
+       case 4:
+       case 5: w3(r); w2(0x24); a = r4(); w2(4);
+               return a;
+
+       }
+       return -1;      /* never gets here */
+}
+
+static void epat_read_block( PIA *pi, char * buf, int count )
+
+{      int  k, ph, a, b;
+
+       switch (pi->mode) {
+
+       case 0: w0(7); w2(1); w2(3); w0(0xff);
+               ph = 0;
+               for(k=0;k<count;k++) {
+                       if (k == count-1) w0(0xfd);
+                       w2(6+ph); a = r1();
+                       if (a & 8) b = a; 
+                         else { w2(4+ph); b = r1(); }
+                       buf[k] = j44(a,b);
+                       ph =  1 - ph;
+               }
+               w0(0); w2(4);
+               break;
+
+       case 1: w0(0x47); w2(1); w2(5); w0(0xff);
+               ph = 0;
+               for(k=0;k<count;k++) {
+                       if (k == count-1) w0(0xfd); 
+                       w2(4+ph);
+                       a = r1(); b = r2();
+                       buf[k] = j53(a,b);
+                       ph = 1 - ph;
+               }
+               w0(0); w2(4);
+               break;
+
+       case 2: w0(0x27); w2(1); w2(0x25); w0(0);
+               ph = 0;
+               for(k=0;k<count-1;k++) {
+                       w2(0x24+ph);
+                       buf[k] = r0();
+                       ph = 1 - ph;
+               }
+               w2(0x26); w2(0x27); buf[count-1] = r0(); 
+               w2(0x25); w2(4);
+               break;
+
+       case 3: w3(0x80); w2(0x24);
+               for(k=0;k<count-1;k++) buf[k] = r4();
+               w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+               w2(4);
+               break;
+
+       case 4: w3(0x80); w2(0x24);
+               for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
+               buf[count-2] = r4();
+               w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+               w2(4);
+               break;
+
+       case 5: w3(0x80); w2(0x24);
+               for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
+               for(k=count-4;k<count-1;k++) buf[k] = r4();
+               w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+               w2(4);
+               break;
+
+       }
+}
+
+static void epat_write_block( PIA *pi, char * buf, int count )   
+
+{      int ph, k;
+
+       switch (pi->mode) {
+
+       case 0:
+       case 1:
+       case 2: w0(0x67); w2(1); w2(5);
+               ph = 0;
+               for(k=0;k<count;k++) {
+                       w0(buf[k]);
+                       w2(4+ph);
+                       ph = 1 - ph;
+               }
+               w2(7); w2(4);
+               break;
+
+       case 3: w3(0xc0); 
+               for(k=0;k<count;k++) w4(buf[k]);
+               w2(4);
+               break;
+
+       case 4: w3(0xc0); 
+               for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]);
+               w2(4);
+               break;
+
+       case 5: w3(0xc0); 
+               for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]);
+               w2(4);
+               break;
+
+       }
+}
+
+/* these macros access the EPAT registers in native addressing */
+
+#define        WR(r,v)         epat_write_regr(pi,2,r,v)
+#define        RR(r)           (epat_read_regr(pi,2,r))
+
+/* and these access the IDE task file */
+
+#define WRi(r,v)         epat_write_regr(pi,0,r,v)
+#define RRi(r)           (epat_read_regr(pi,0,r))
+
+/* FIXME:  the CCP stuff should be fixed to handle multiple EPATs on a chain */
+
+#define CCP(x)         w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+                w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
+
+static void epat_connect ( PIA *pi )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+       CCP(0); CCP(0xe0);
+       w0(0); w2(1); w2(4);
+       if (pi->mode >= 3) {
+               w0(0); w2(1); w2(4); w2(0xc);
+               w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4);
+       }
+       WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10);
+}
+
+static void epat_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static int epat_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     k, j, f, cc;
+       int     e[2] = {0,0};
+
+        epat_connect(pi);
+       cc = RR(0xd);
+       epat_disconnect(pi);
+
+       epat_connect(pi);
+       for (j=0;j<2;j++) {
+           WRi(6,0xa0+j*0x10);
+            for (k=0;k<256;k++) {
+                WRi(2,k^0xaa);
+                WRi(3,k^0x55);
+                if (RRi(2) != (k^0xaa)) e[j]++;
+                }
+           }
+        epat_disconnect(pi);
+
+        f = 0;
+        epat_connect(pi);
+        WR(0x13,1); WR(0x13,0); WR(0xa,0x11);
+        epat_read_block(pi,scratch,512);
+       
+        for (k=0;k<256;k++) {
+            if ((scratch[2*k] & 0xff) != k) f++;
+            if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++;
+        }
+        epat_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n",
+                  pi->device,pi->port,pi->mode,cc,e[0],e[1],f);
+       }
+       
+        return (e[0] && e[1]) || f;
+}
+
+static void epat_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{      int     ver;
+        char    *mode_string[6] = 
+                  {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"};
+
+       epat_connect(pi);
+       WR(0xa,0x38);           /* read the version code */
+        ver = RR(0xb);
+        epat_disconnect(pi);
+
+       printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ",
+               pi->device,EPAT_VERSION,ver,pi->port);
+       printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void epat_init_proto( PIA *pi)
+
+{      MOD_INC_USE_COUNT;
+}
+
+static void epat_release_proto( PIA *pi)
+
+{      MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epat = {"epat",0,6,3,1,1,
+                          epat_write_regr,
+                          epat_read_regr,
+                          epat_write_block,
+                          epat_read_block,
+                          epat_connect,
+                          epat_disconnect,
+                          0,
+                          0,
+                          epat_test_proto,
+                          epat_log_adapter,
+                          epat_init_proto,
+                          epat_release_proto
+                         };
+
+
+#ifdef MODULE
+
+int    init_module(void)
+
+{      return pi_register( &epat) - 1;
+}
+
+void   cleanup_module(void)
+
+{      pi_unregister( &epat);
+}
+
+#endif
+
+/* end of epat.c */
diff --git a/drivers/block/paride/epia.c b/drivers/block/paride/epia.c
new file mode 100644 (file)
index 0000000..4bdf657
--- /dev/null
@@ -0,0 +1,326 @@
+/* 
+        epia.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+        epia.c is a low-level protocol driver for Shuttle Technologies 
+       EPIA parallel to IDE adapter chip.  This device is now obsolete
+       and has been replaced with the EPAT chip, which is supported
+       by epat.c, however, some devices based on EPIA are still
+       available.
+
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+       1.02    GRG 1998.06.17 support older versions of EPIA
+
+*/
+
+#define EPIA_VERSION      "1.02"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads on port 1, 8-bit writes
+                1  5/3 reads on ports 1 & 2, 8-bit writes
+                2  8-bit reads and writes
+                3  8-bit EPP mode
+               4  16-bit EPP
+               5  32-bit EPP
+*/
+
+#define j44(a,b)                (((a>>4)&0x0f)+(b&0xf0))
+#define j53(a,b)                (((a>>3)&0x1f)+((b<<4)&0xe0))
+
+/* cont =  0   IDE register file
+   cont =  1   IDE control registers
+*/
+
+static int cont_map[2] = { 0, 0x80 };
+
+static int epia_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+       regr += cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0: r = regr^0x39;
+                w0(r); w2(1); w2(3); w0(r);
+                a = r1(); w2(1); b = r1(); w2(4);
+                return j44(a,b);
+
+        case 1: r = regr^0x31;
+                w0(r); w2(1); w0(r&0x37); 
+                w2(3); w2(5); w0(r|0xf0);
+                a = r1(); b = r2(); w2(4);
+                return j53(a,b);
+
+        case 2: r = regr^0x29;
+                w0(r); w2(1); w2(0X21); w2(0x23); 
+                a = r0(); w2(4);
+                return a;
+
+       case 3:
+       case 4:
+        case 5: w3(regr); w2(0x24); a = r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void epia_write_regr( PIA *pi, int cont, int regr, int val)
+
+{       int  r;
+
+       regr += cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1:
+        case 2: r = regr^0x19;
+                w0(r); w2(1); w0(val); w2(3); w2(4);
+                break;
+
+       case 3:
+       case 4:
+        case 5: r = regr^0x40;
+                w3(r); w4(val); w2(4);
+                break;
+        }
+}
+
+#define WR(r,v)         epia_write_regr(pi,0,r,v)
+#define RR(r)           (epia_read_regr(pi,0,r))
+
+/* The use of register 0x84 is entirely unclear - it seems to control
+   some EPP counters ...  currently we know about 3 different block
+   sizes:  the standard 512 byte reads and writes, 12 byte writes and 
+   2048 byte reads (the last two being used in the CDrom drivers.
+*/
+
+static void epia_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+        w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
+        w2(1); w2(4);
+        if (pi->mode >= 3) { 
+                w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
+                w2(0x24); w2(0x26); w2(4);
+        }
+        WR(0x86,8);  
+}
+
+static void epia_disconnect ( PIA *pi )
+
+{       /* WR(0x84,0x10); */
+        w0(pi->saved_r0);
+        w2(1); w2(4);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void epia_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, ph, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(0x81); w2(1); w2(3); w0(0xc1);
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(2+ph); a = r1();
+                        w2(4+ph); b = r1();
+                        buf[k] = j44(a,b);
+                        ph = 1 - ph;
+                } 
+                w0(0); w2(4);
+                break;
+
+        case 1: w0(0x91); w2(1); w0(0x10); w2(3); 
+                w0(0x51); w2(5); w0(0xd1); 
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(4+ph);
+                        a = r1(); b = r2();
+                        buf[k] = j53(a,b);
+                        ph = 1 - ph;
+                }
+                w0(0); w2(4);
+                break;
+
+        case 2: w0(0x89); w2(1); w2(0x23); w2(0x21); 
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(0x24+ph);
+                        buf[k] = r0();
+                        ph = 1 - ph;
+                }
+                w2(6); w2(4);
+                break;
+
+        case 3: if (count > 512) WR(0x84,3);
+               w3(0); w2(0x24);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4); WR(0x84,0);
+                break;
+
+        case 4: if (count > 512) WR(0x84,3);
+               w3(0); w2(0x24);
+               for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4); WR(0x84,0);
+                break;
+
+        case 5: if (count > 512) WR(0x84,3);
+               w3(0); w2(0x24);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4); WR(0x84,0);
+                break;
+
+        }
+}
+
+static void epia_write_block( PIA *pi, char * buf, int count )
+
+{       int     ph, k, last, d;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1:
+        case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5);
+                ph = 0;  last = 0x8000;
+                for (k=0;k<count;k++) {
+                        d = buf[k];
+                        if (d != last) { last = d; w0(d); }
+                        w2(4+ph);
+                        ph = 1 - ph;
+                }
+                w2(7); w2(4);
+                break;
+
+        case 3: if (count < 512) WR(0x84,1);
+               w3(0x40);
+                for (k=0;k<count;k++) w4(buf[k]);
+               if (count < 512) WR(0x84,0);
+                break;
+
+        case 4: if (count < 512) WR(0x84,1);
+               w3(0x40);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+               if (count < 512) WR(0x84,0);
+                break;
+
+        case 5: if (count < 512) WR(0x84,1);
+               w3(0x40);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+               if (count < 512) WR(0x84,0);
+                break;
+
+        }
+
+}
+
+static int epia_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k, f;
+       int     e[2] = {0,0};
+
+        epia_connect(pi);
+        for (j=0;j<2;j++) {
+            WR(6,0xa0+j*0x10);
+            for (k=0;k<256;k++) {
+                WR(2,k^0xaa);
+                WR(3,k^0x55);
+                if (RR(2) != (k^0xaa)) e[j]++;
+            }
+           WR(2,1); WR(3,1);
+        }
+        epia_disconnect(pi);
+
+        f = 0;
+        epia_connect(pi);
+        WR(0x84,8);
+        epia_read_block(pi,scratch,512);
+        for (k=0;k<256;k++) {
+            if ((scratch[2*k] & 0xff) != ((k+1) & 0xff)) f++;
+            if ((scratch[2*k+1] & 0xff) != ((-2-k) & 0xff)) f++;
+        }
+        WR(0x84,0);
+        epia_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+                   pi->device,pi->port,pi->mode,e[0],e[1],f);
+        }
+        
+        return (e[0] && e[1]) || f;
+
+}
+
+
+static void epia_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[6] = {"4-bit","5/3","8-bit",
+                                  "EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: epia %s, Shuttle EPIA at 0x%x, ",
+                pi->device,EPIA_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void epia_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void epia_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epia = {"epia",0,6,3,1,1,
+                           epia_write_regr,
+                           epia_read_regr,
+                           epia_write_block,
+                           epia_read_block,
+                           epia_connect,
+                           epia_disconnect,
+                           0,
+                           0,
+                           epia_test_proto,
+                           epia_log_adapter,
+                           epia_init_proto,
+                           epia_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &epia ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &epia );
+}
+
+#endif
+
+/* end of epia.c */
+
diff --git a/drivers/block/paride/fit2.c b/drivers/block/paride/fit2.c
new file mode 100644 (file)
index 0000000..b93fff0
--- /dev/null
@@ -0,0 +1,161 @@
+/* 
+        fit2.c        (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+       fit2.c is a low-level protocol driver for the older version
+        of the Fidelity International Technology parallel port adapter.  
+       This adapter is used in their TransDisk 2000 and older TransDisk
+       3000 portable hard-drives.  As far as I can tell, this device
+       supports 4-bit mode _only_.  
+
+       Newer models of the FIT products use an enhanced protocol.
+       The "fit3" protocol module should support current drives.
+
+*/
+
+#define FIT2_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                (((a>>4)&0x0f)|(b&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+
+NB:  The FIT adapter does not appear to use the control registers.
+So, we map ALT_STATUS to STATUS and NO-OP writes to the device
+control register - this means that IDE reset will not work on these
+devices.
+
+*/
+
+static void  fit2_write_regr( PIA *pi, int cont, int regr, int val)
+
+{      if (cont == 1) return;
+       w2(0xc); w0(regr); w2(4); w0(val); w2(5); w0(0); w2(4);
+}
+
+static int fit2_read_regr( PIA *pi, int cont, int regr )
+
+{      int  a, b, r;
+
+       if (cont) {
+         if (regr != 6) return 0xff;
+         r = 7;
+       } else r = regr + 0x10;
+
+       w2(0xc); w0(r); w2(4); w2(5); 
+                w0(0); a = r1();
+                w0(1); b = r1();
+       w2(4);
+
+       return j44(a,b);
+
+}
+
+static void fit2_read_block( PIA *pi, char * buf, int count )
+
+{      int  k, a, b, c, d;
+
+       w2(0xc); w0(0x10);
+
+       for (k=0;k<count/4;k++) {
+
+               w2(4); w2(5);
+               w0(0); a = r1(); w0(1); b = r1();
+               w0(3); c = r1(); w0(2); d = r1(); 
+               buf[4*k+0] = j44(a,b);
+               buf[4*k+1] = j44(d,c);
+
+                w2(4); w2(5);
+                       a = r1(); w0(3); b = r1();
+                w0(1); c = r1(); w0(0); d = r1(); 
+                buf[4*k+2] = j44(d,c);
+                buf[4*k+3] = j44(a,b);
+
+       }
+
+       w2(4);
+
+}
+
+static void fit2_write_block( PIA *pi, char * buf, int count )
+
+{      int k;
+
+
+       w2(0xc); w0(0); 
+       for (k=0;k<count/2;k++) {
+               w2(4); w0(buf[2*k]); 
+               w2(5); w0(buf[2*k+1]);
+       }
+       w2(4);
+}
+
+static void fit2_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+       w2(0xcc); 
+}
+
+static void fit2_disconnect ( PIA *pi )
+
+{       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void fit2_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       printk("%s: fit2 %s, FIT 2000 adapter at 0x%x, delay %d\n",
+                pi->device,FIT2_VERSION,pi->port,pi->delay);
+
+}
+
+static void fit2_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void fit2_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol fit2 = {"fit2",0,1,2,1,1,
+                           fit2_write_regr,
+                           fit2_read_regr,
+                           fit2_write_block,
+                           fit2_read_block,
+                           fit2_connect,
+                           fit2_disconnect,
+                           0,
+                           0,
+                           0,
+                           fit2_log_adapter,
+                           fit2_init_proto,
+                           fit2_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &fit2 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &fit2 );
+}
+
+#endif
+
+/* end of fit2.c */
diff --git a/drivers/block/paride/fit3.c b/drivers/block/paride/fit3.c
new file mode 100644 (file)
index 0000000..f449bcf
--- /dev/null
@@ -0,0 +1,221 @@
+/* 
+        fit3.c        (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+       fit3.c is a low-level protocol driver for newer models 
+        of the Fidelity International Technology parallel port adapter.  
+       This adapter is used in their TransDisk 3000 portable 
+       hard-drives, as well as CD-ROM, PD-CD and other devices.
+
+       The TD-2000 and certain older devices use a different protocol.
+       Try the fit2 protocol module with them.
+
+        NB:  The FIT adapters do not appear to support the control 
+       registers.  So, we map ALT_STATUS to STATUS and NO-OP writes 
+       to the device control register - this means that IDE reset 
+       will not work on these devices.
+
+*/
+
+#define FIT3_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                (((a>>3)&0x0f)|((b<<1)&0xf0))
+
+#define w7(byte)                {out_p(7,byte);}
+#define r7()                    (in_p(7) & 0xff)
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+
+*/
+
+static void  fit3_write_regr( PIA *pi, int cont, int regr, int val)
+
+{      if (cont == 1) return;
+
+       switch (pi->mode) {
+
+       case 0:
+       case 1: w2(0xc); w0(regr); w2(0x8); w2(0xc); 
+               w0(val); w2(0xd); 
+               w0(0);   w2(0xc);
+               break;
+
+       case 2: w2(0xc); w0(regr); w2(0x8); w2(0xc);
+               w4(val); w4(0);
+               w2(0xc);
+               break;
+
+       }
+}
+
+static int fit3_read_regr( PIA *pi, int cont, int regr )
+
+{      int  a, b;
+
+       if (cont) {
+         if (regr != 6) return 0xff;
+         regr = 7;
+       } 
+
+       switch (pi->mode) {
+
+       case 0: w2(0xc); w0(regr + 0x10); w2(0x8); w2(0xc);
+               w2(0xd); a = r1();
+               w2(0xf); b = r1(); 
+               w2(0xc);
+               return j44(a,b);
+
+       case 1: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
+               w2(0xec); w2(0xee); w2(0xef); a = r0(); 
+               w2(0xc);
+               return a;
+
+       case 2: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc); 
+               w2(0xec); 
+               a = r4(); b = r4(); 
+               w2(0xc);
+               return a;
+
+       }
+       return -1; 
+
+}
+
+static void fit3_read_block( PIA *pi, char * buf, int count )
+
+{      int  k, a, b, c, d;
+
+       switch (pi->mode) {
+
+       case 0: w2(0xc); w0(0x10); w2(0x8); w2(0xc);
+               for (k=0;k<count/2;k++) {
+                   w2(0xd); a = r1();
+                   w2(0xf); b = r1();
+                   w2(0xc); c = r1();
+                   w2(0xe); d = r1();
+                   buf[2*k  ] = j44(a,b);
+                   buf[2*k+1] = j44(c,d);
+               }
+               w2(0xc);
+               break;
+
+       case 1: w2(0xc); w0(0x90); w2(0x8); w2(0xc); 
+               w2(0xec); w2(0xee);
+               for (k=0;k<count/2;k++) {
+                   w2(0xef); a = r0();
+                   w2(0xee); b = r0();
+                    buf[2*k  ] = a;
+                    buf[2*k+1] = b;
+               }
+               w2(0xec); 
+               w2(0xc);
+               break;
+
+       case 2: w2(0xc); w0(0x90); w2(0x8); w2(0xc); 
+                w2(0xec);
+               for (k=0;k<count;k++) buf[k] = r4();
+                w2(0xc);
+               break;
+
+       }
+}
+
+static void fit3_write_block( PIA *pi, char * buf, int count )
+
+{      int k;
+
+        switch (pi->mode) {
+
+       case 0:
+        case 1: w2(0xc); w0(0); w2(0x8); w2(0xc);
+                for (k=0;k<count/2;k++) {
+                   w0(buf[2*k  ]); w2(0xd);
+                   w0(buf[2*k+1]); w2(0xc);
+               }
+               break;
+
+        case 2: w2(0xc); w0(0); w2(0x8); w2(0xc); 
+                for (k=0;k<count;k++) w4(buf[k]);
+                w2(0xc);
+               break;
+       }
+}
+
+static void fit3_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+       w2(0xc); w0(0); w2(0xa);
+       if (pi->mode == 2) { 
+               w2(0xc); w0(0x9); w2(0x8); w2(0xc); 
+               }
+}
+
+static void fit3_disconnect ( PIA *pi )
+
+{       w2(0xc); w0(0xa); w2(0x8); w2(0xc);
+       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void fit3_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[3] = {"4-bit","8-bit","EPP"};
+
+       printk("%s: fit3 %s, FIT 3000 adapter at 0x%x, "
+              "mode %d (%s), delay %d\n",
+                pi->device,FIT3_VERSION,pi->port,
+               pi->mode,mode_string[pi->mode],pi->delay);
+
+}
+
+static void fit3_init_proto(PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void fit3_release_proto(PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol fit3 = {"fit3",0,3,2,1,1,
+                           fit3_write_regr,
+                           fit3_read_regr,
+                           fit3_write_block,
+                           fit3_read_block,
+                           fit3_connect,
+                           fit3_disconnect,
+                           0,
+                           0,
+                           0,
+                           fit3_log_adapter,
+                           fit3_init_proto,
+                           fit3_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &fit3 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &fit3 );
+}
+
+#endif
+
+/* end of fit3.c */
diff --git a/drivers/block/paride/frpw.c b/drivers/block/paride/frpw.c
new file mode 100644 (file)
index 0000000..f965fb9
--- /dev/null
@@ -0,0 +1,310 @@
+/* 
+       frpw.c  (c) 1996-8  Grant R. Guenther <grant@torque.net>
+                           Under the terms of the GNU public license
+
+       frpw.c is a low-level protocol driver for the Freecom "Power"
+       parallel port IDE adapter.
+       
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+                              fix chip detect
+                              added EPP-16 and EPP-32
+
+*/
+
+#define        FRPW_VERSION    "1.01" 
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define cec4           w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
+#define j44(l,h)       (((l>>4)&0x0f)|(h&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x10 };
+
+static int frpw_read_regr( PIA *pi, int cont, int regr )
+
+{      int     h,l,r;
+
+       r = regr + cont_map[cont];
+
+       w2(4);
+       w0(r); cec4;
+       w2(6); l = r1();
+       w2(4); h = r1();
+       w2(4); 
+
+       return j44(l,h);
+
+}
+
+static void frpw_write_regr( PIA *pi, int cont, int regr, int val)
+
+{      int r;
+
+        r = regr + cont_map[cont];
+
+       w2(4); w0(r); cec4; 
+       w0(val);
+       w2(5);w2(7);w2(5);w2(4);
+}
+
+static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr )
+
+{       int     h, l, k, ph;
+
+        switch(pi->mode) {
+
+        case 0: w2(4); w0(regr); cec4;
+                for (k=0;k<count;k++) {
+                        w2(6); l = r1();
+                        w2(4); h = r1();
+                        buf[k] = j44(l,h);
+                }
+                w2(4);
+                break;
+
+        case 1: ph = 2;
+                w2(4); w0(regr + 0xc0); cec4;
+                w0(0xff);
+                for (k=0;k<count;k++) {
+                        w2(0xa4 + ph); 
+                        buf[k] = r0();
+                        ph = 2 - ph;
+                } 
+                w2(0xac); w2(0xa4); w2(4);
+                break;
+
+        case 2: w2(4); w0(regr + 0x80); cec4;
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(0xac); w2(0xa4);
+                w2(4);
+                break;
+
+       case 3: w2(4); w0(regr + 0x80); cec4;
+               for (k=0;k<count-2;k++) buf[k] = r4();
+               w2(0xac); w2(0xa4);
+               buf[count-2] = r4();
+               buf[count-1] = r4();
+               w2(4);
+               break;
+
+       case 4: w2(4); w0(regr + 0x80); cec4;
+                for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
+                w2(0xac); w2(0xa4);
+                buf[count-2] = r4();
+                buf[count-1] = r4();
+                w2(4);
+                break;
+
+       case 5: w2(4); w0(regr + 0x80); cec4;
+                for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
+                buf[count-4] = r4();
+                buf[count-3] = r4();
+                w2(0xac); w2(0xa4);
+                buf[count-2] = r4();
+                buf[count-1] = r4();
+                w2(4);
+                break;
+
+        }
+}
+
+static void frpw_read_block( PIA *pi, char * buf, int count)
+
+{      frpw_read_block_int(pi,buf,count,0x08);
+}
+
+static void frpw_write_block( PIA *pi, char * buf, int count )
+{      int     k;
+
+       switch(pi->mode) {
+
+       case 0:
+       case 1:
+       case 2: w2(4); w0(8); cec4; w2(5);
+               for (k=0;k<count;k++) {
+                       w0(buf[k]);
+                       w2(7);w2(5);
+               }
+               w2(4);
+               break;
+
+       case 3: w2(4); w0(0xc8); cec4; w2(5);
+               for (k=0;k<count;k++) w4(buf[k]);
+               w2(4);
+               break;
+
+        case 4: w2(4); w0(0xc8); cec4; w2(5);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(4);
+                break;
+
+        case 5: w2(4); w0(0xc8); cec4; w2(5);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(4);
+                break;
+       }
+}
+
+static void frpw_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+       w2(4);
+}
+
+static void frpw_disconnect ( PIA *pi )
+
+{       w2(4); w0(0x20); cec4;
+       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+/* Stub logic to see if PNP string is available - used to distinguish
+   between the Xilinx and ASIC implementations of the Freecom adapter.
+*/
+
+static int frpw_test_pnp ( PIA *pi )
+
+/*  returns chip_type:   0 = Xilinx, 1 = ASIC   */
+
+{      int olddelay, a, b;
+
+       olddelay = pi->delay;
+       pi->delay = 10;
+
+       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+       
+       w2(4); w0(4); w2(6); w2(7);
+       a = r1() & 0xff; w2(4); b = r1() & 0xff;
+       w2(0xc); w2(0xe); w2(4);
+
+       pi->delay = olddelay;
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+
+       return ((~a&0x40) && (b&0x40));
+} 
+
+/* We use the pi->private to remember the result of the PNP test.
+   To make this work, private = port*2 + chip.  Yes, I know it's
+   a hack :-(
+*/
+
+static int frpw_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k, r;
+       int     e[2] = {0,0};
+
+       if ((pi->private>>1) != pi->port)
+          pi->private = frpw_test_pnp(pi) + 2*pi->port;
+
+       if (((pi->private%2) == 0) && (pi->mode > 2)) {
+          if (verbose) 
+               printk("%s: frpw: Xilinx does not support mode %d\n",
+                       pi->device, pi->mode);
+          return 1;
+       }
+
+       if (((pi->private%2) == 1) && (pi->mode == 2)) {
+          if (verbose)
+               printk("%s: frpw: ASIC does not support mode 2\n",
+                       pi->device);
+          return 1;
+       }
+
+       frpw_connect(pi);
+       for (j=0;j<2;j++) {
+                frpw_write_regr(pi,0,6,0xa0+j*0x10);
+                for (k=0;k<256;k++) {
+                        frpw_write_regr(pi,0,2,k^0xaa);
+                        frpw_write_regr(pi,0,3,k^0x55);
+                        if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
+                        }
+                }
+       frpw_disconnect(pi);
+
+       frpw_connect(pi);
+        frpw_read_block_int(pi,scratch,512,0x10);
+        r = 0;
+        for (k=0;k<128;k++) if (scratch[k] != k) r++;
+       frpw_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: frpw: port 0x%x, chip %d, mode %d, test=(%d,%d,%d)\n",
+                   pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r);
+        }
+
+        return (r || (e[0] && e[1]));
+}
+
+
+static void frpw_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[6] = {"4-bit","8-bit","EPP",
+                                  "EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device,
+               FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void frpw_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+       pi->private = 0;
+}
+
+static void frpw_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol frpw = {"frpw",0,6,2,2,1,
+                           frpw_write_regr,
+                           frpw_read_regr,
+                           frpw_write_block,
+                           frpw_read_block,
+                           frpw_connect,
+                           frpw_disconnect,
+                           0,
+                           0,
+                           frpw_test_proto,
+                           frpw_log_adapter,
+                           frpw_init_proto,
+                           frpw_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &frpw ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &frpw );
+}
+
+#endif
+
+/* end of frpw.c */
diff --git a/drivers/block/paride/kbic.c b/drivers/block/paride/kbic.c
new file mode 100644 (file)
index 0000000..589ac00
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+        kbic.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+        This is a low-level driver for the KBIC-951A and KBIC-971A
+        parallel to IDE adapter chips from KingByte Information Systems.
+
+       The chips are almost identical, however, the wakeup code 
+       required for the 971A interferes with the correct operation of
+        the 951A, so this driver registers itself twice, once for
+       each chip.
+
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define KBIC_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define r12w()                 (delay_p,inw(pi->port+1)&0xffff) 
+
+#define j44(a,b)                ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+#define j53(w)                  (((w>>3)&0x1f)|((w>>4)&0xe0))
+
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x80, 0x40 };
+
+static int kbic_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, s;
+
+        s = cont_map[cont];
+
+       switch (pi->mode) {
+
+       case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8);
+               a = r1(); w0(0x28); b = r1(); w2(4);
+               return j44(a,b);
+
+       case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8);
+               a = r12w(); w2(4);
+               return j53(a);
+
+       case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1);
+               a = r0(); w2(4);
+                       return a;
+
+       case 3:
+       case 4:
+       case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
+               a = r4(); b = r4(); w2(4); w2(0); w2(4);
+               return a;
+
+       }
+       return -1;
+}       
+
+static void  kbic_write_regr( PIA *pi, int cont, int regr, int val)
+
+{       int  s;
+
+        s = cont_map[cont];
+
+        switch (pi->mode) {
+
+       case 0: 
+        case 1:
+       case 2: w0(regr|0x10|s); w2(4); w2(6); w2(4); 
+               w0(val); w2(5); w2(4);
+               break;
+
+       case 3:
+       case 4:
+       case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
+               w4(val); w4(val);
+               w2(4); w2(0); w2(4);
+                break;
+
+       }
+}
+
+static void k951_connect ( PIA *pi  )
+
+{      pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); 
+}
+
+static void k951_disconnect ( PIA *pi )
+
+{              w0(pi->saved_r0);
+        w2(pi->saved_r2);
+}
+
+#define        CCP(x)  w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\
+               w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff);
+
+static void k971_connect ( PIA *pi  )
+
+{      pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+       CCP(0x20);
+        w2(4); 
+}
+
+static void k971_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+}
+
+/* counts must be congruent to 0 MOD 4, but all known applications
+   have this property.
+*/
+
+static void kbic_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(0x98); w2(4); w2(6); w2(4);
+                for (k=0;k<count/2;k++) {
+                       w2(1); w0(8);    a = r1();
+                              w0(0x28); b = r1();
+                       buf[2*k]   = j44(a,b);
+                       w2(5);           b = r1();
+                              w0(8);    a = r1();
+                       buf[2*k+1] = j44(a,b);
+                       w2(4);
+                } 
+                break;
+
+        case 1: w0(0xb8); w2(4); w2(6); w2(4); 
+                for (k=0;k<count/4;k++) {
+                        w0(0xb8); 
+                       w2(4); w2(5); 
+                        w0(8);    buf[4*k]   = j53(r12w());
+                       w0(0xb8); buf[4*k+1] = j53(r12w());
+                       w2(4); w2(5);
+                                 buf[4*k+3] = j53(r12w());
+                       w0(8);    buf[4*k+2] = j53(r12w());
+                }
+                w2(4);
+                break;
+
+        case 2: w0(0x88); w2(4); w2(6); w2(4);
+                for (k=0;k<count/2;k++) {
+                        w2(0xa0); w2(0xa1); buf[2*k] = r0();
+                        w2(0xa5); buf[2*k+1] = r0();
+                }
+                w2(4);
+                break;
+
+        case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4); w2(0); w2(4);
+                break;
+
+       case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4); w2(0); w2(4);
+                break;
+
+        case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4); w2(0); w2(4);
+                break;
+
+
+        }
+}
+
+static void kbic_write_block( PIA *pi, char * buf, int count )
+
+{       int     k;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1:
+        case 2: w0(0x90); w2(4); w2(6); w2(4); 
+               for(k=0;k<count/2;k++) {
+                       w0(buf[2*k+1]); w2(0); w2(4); 
+                       w0(buf[2*k]);   w2(5); w2(4); 
+               }
+               break;
+
+        case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+               for(k=0;k<count/2;k++) {
+                       w4(buf[2*k+1]); 
+                        w4(buf[2*k]);
+                }
+               w2(4); w2(0); w2(4);
+               break;
+
+       case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for(k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+                w2(4); w2(0); w2(4);
+                break;
+
+        case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for(k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
+                w2(4); w2(0); w2(4);
+                break;
+
+        }
+
+}
+
+static void kbic_log_adapter( PIA *pi, char * scratch, 
+                             int verbose, char * chip )
+
+{       char    *mode_string[6] = {"4-bit","5/3","8-bit",
+                                  "EPP-8","EPP_16","EPP-32"};
+
+        printk("%s: kbic %s, KingByte %s at 0x%x, ",
+                pi->device,KBIC_VERSION,chip,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void k951_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{      kbic_log_adapter(pi,scratch,verbose,"KBIC-951A");
+}
+
+static void k971_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       kbic_log_adapter(pi,scratch,verbose,"KBIC-971A");
+}
+
+static void kbic_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void kbic_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol k951 = {"k951",0,6,3,1,1,
+                           kbic_write_regr,
+                           kbic_read_regr,
+                           kbic_write_block,
+                           kbic_read_block,
+                           k951_connect,
+                           k951_disconnect,
+                           0,
+                           0,
+                           0,
+                           k951_log_adapter,
+                           kbic_init_proto,
+                           kbic_release_proto
+                         };
+
+
+struct pi_protocol k971 = {"k971",0,6,3,1,1,
+                           kbic_write_regr,
+                           kbic_read_regr,
+                           kbic_write_block,
+                           kbic_read_block,
+                           k971_connect,
+                           k971_disconnect,
+                           0,
+                           0,
+                           0,
+                           k971_log_adapter,
+                           kbic_init_proto,
+                           kbic_release_proto
+                          };
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       int s5,s7;
+
+       s5 = pi_register(&k951);
+       s7 = pi_register(&k971);
+
+       return (s5 || s7) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &k951 );
+       pi_unregister( &k971 );
+}
+
+#endif
+
+/* end of kbic.c */
diff --git a/drivers/block/paride/ktti.c b/drivers/block/paride/ktti.c
new file mode 100644 (file)
index 0000000..606bcfd
--- /dev/null
@@ -0,0 +1,138 @@
+/* 
+        ktti.c        (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+       ktti.c is a low-level protocol driver for the KT Technology
+       parallel port adapter.  This adapter is used in the "PHd" 
+        portable hard-drives.  As far as I can tell, this device
+       supports 4-bit mode _only_.  
+
+*/
+
+#define KTTI_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                (((a>>4)&0x0f)|(b&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x10, 0x08 };
+
+static void  ktti_write_regr( PIA *pi, int cont, int regr, int val)
+
+{      int r;
+
+       r = regr + cont_map[cont];
+
+       w0(r); w2(0xb); w2(0xa); w2(3); w2(6); 
+       w0(val); w2(3); w0(0); w2(6); w2(0xb);
+}
+
+static int ktti_read_regr( PIA *pi, int cont, int regr )
+
+{      int  a, b, r;
+
+        r = regr + cont_map[cont];
+
+        w0(r); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9); 
+       a = r1(); w2(0xc);  b = r1(); w2(9); w2(0xc); w2(9);
+       return j44(a,b);
+
+}
+
+static void ktti_read_block( PIA *pi, char * buf, int count )
+
+{      int  k, a, b;
+
+       for (k=0;k<count/2;k++) {
+               w0(0x10); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
+               a = r1(); w2(0xc); b = r1(); w2(9);
+               buf[2*k] = j44(a,b);
+               a = r1(); w2(0xc); b = r1(); w2(9);
+               buf[2*k+1] = j44(a,b);
+       }
+}
+
+static void ktti_write_block( PIA *pi, char * buf, int count )
+
+{      int k;
+
+       for (k=0;k<count/2;k++) {
+               w0(0x10); w2(0xb); w2(0xa); w2(3); w2(6);
+               w0(buf[2*k]); w2(3);
+               w0(buf[2*k+1]); w2(6);
+               w2(0xb);
+       }
+}
+
+static void ktti_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+       w2(0xb); w2(0xa); w0(0); w2(3); w2(6);  
+}
+
+static void ktti_disconnect ( PIA *pi )
+
+{       w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4);
+       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void ktti_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       printk("%s: ktti %s, KT adapter at 0x%x, delay %d\n",
+                pi->device,KTTI_VERSION,pi->port,pi->delay);
+
+}
+
+static void ktti_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void ktti_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol ktti = {"ktti",0,1,2,1,1,
+                           ktti_write_regr,
+                           ktti_read_regr,
+                           ktti_write_block,
+                           ktti_read_block,
+                           ktti_connect,
+                           ktti_disconnect,
+                           0,
+                           0,
+                           0,
+                           ktti_log_adapter,
+                           ktti_init_proto,
+                           ktti_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &ktti ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &ktti );
+}
+
+#endif
+
+/* end of ktti.c */
diff --git a/drivers/block/paride/mkd b/drivers/block/paride/mkd
new file mode 100644 (file)
index 0000000..971f099
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# mkd -- a script to create the device special files for the PARIDE subsystem
+#
+#  block devices:      pd (45), pcd (46), pf (47)
+#  character devices:  pt (96), pg (97)
+#
+function mkdev {
+  mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
+}
+#
+function pd {
+  D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
+  mkdev pd$D b 45 $[ $1 * 16 ]
+  for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+  do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
+  done
+}
+#
+cd /dev
+#
+for u in 0 1 2 3 ; do pd $u ; done
+for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done 
+for u in 0 1 2 3 ; do mkdev pf$u  b 47 $u ; done 
+for u in 0 1 2 3 ; do mkdev pt$u  c 96 $u ; done 
+for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done 
+for u in 0 1 2 3 ; do mkdev pg$u  c 97 $u ; done
+#
+# end of mkd
+
diff --git a/drivers/block/paride/on20.c b/drivers/block/paride/on20.c
new file mode 100644 (file)
index 0000000..749879c
--- /dev/null
@@ -0,0 +1,163 @@
+/* 
+       on20.c  (c) 1996-8  Grant R. Guenther <grant@torque.net>
+                           Under the terms of the GNU public license.
+
+        on20.c is a low-level protocol driver for the
+        Onspec 90c20 parallel to IDE adapter. 
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define        ON20_VERSION    "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define op(f)  w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
+#define vl(v)  w2(4);w0(v);w2(5);w2(7);w2(5);w2(4);
+
+#define j44(a,b)  (((a>>4)&0x0f)|(b&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int on20_read_regr( PIA *pi, int cont, int regr )
+
+{      int h,l, r ;
+
+        r = (regr<<2) + 1 + cont;
+
+        op(1); vl(r); op(0);
+
+       switch (pi->mode)  {
+
+        case 0:  w2(4); w2(6); l = r1();
+                 w2(4); w2(6); h = r1();
+                 w2(4); w2(6); w2(4); w2(6); w2(4);
+                return j44(l,h);
+
+       case 1:  w2(4); w2(0x26); r = r0(); 
+                 w2(4); w2(0x26); w2(4);
+                return r;
+
+       }
+       return -1;
+}      
+
+static void on20_write_regr( PIA *pi, int cont, int regr, int val )
+
+{      int r;
+
+       r = (regr<<2) + 1 + cont;
+
+       op(1); vl(r); 
+       op(0); vl(val); 
+       op(0); vl(val);
+}
+
+static void on20_connect ( PIA *pi)
+
+{      pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+       w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4); 
+       if (pi->mode) { op(2); vl(8); op(2); vl(9); }
+              else   { op(2); vl(0); op(2); vl(8); }
+}
+
+static void on20_disconnect ( PIA *pi )
+
+{      w2(4);w0(7);w2(4);w2(0xc);w2(4);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void on20_read_block( PIA *pi, char * buf, int count )
+
+{      int     k, l, h; 
+
+       op(1); vl(1); op(0);
+
+       for (k=0;k<count;k++) 
+           if (pi->mode) {
+               w2(4); w2(0x26); buf[k] = r0();
+           } else {
+               w2(6); l = r1(); w2(4);
+               w2(6); h = r1(); w2(4);
+               buf[k] = j44(l,h);
+           }
+       w2(4);
+}
+
+static void on20_write_block(  PIA *pi, char * buf, int count )
+
+{      int     k;
+
+       op(1); vl(1); op(0);
+
+       for (k=0;k<count;k++) { w2(5); w0(buf[k]); w2(7); }
+       w2(4);
+}
+
+static void on20_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[2] = {"4-bit","8-bit"};
+
+        printk("%s: on20 %s, OnSpec 90c20 at 0x%x, ",
+                pi->device,ON20_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void on20_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void on20_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on20 = {"on20",0,2,2,1,1,
+                           on20_write_regr,
+                           on20_read_regr,
+                           on20_write_block,
+                           on20_read_block,
+                           on20_connect,
+                           on20_disconnect,
+                           0,
+                           0,
+                           0,
+                           on20_log_adapter,
+                           on20_init_proto,
+                           on20_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &on20 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &on20 );
+}
+
+#endif
+
+/* end of on20.c */
diff --git a/drivers/block/paride/on26.c b/drivers/block/paride/on26.c
new file mode 100644 (file)
index 0000000..89457cc
--- /dev/null
@@ -0,0 +1,267 @@
+/* 
+        on26.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+        on26.c is a low-level protocol driver for the 
+        OnSpec 90c26 parallel to IDE adapter chip.
+
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define ON26_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+               3  EPP-16
+               4  EPP-32
+*/
+
+#define j44(a,b)  (((a>>4)&0x0f)|(b&0xf0))
+
+#define P1     w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
+#define P2     w2(5);w2(7);w2(5);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int on26_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+       r = (regr<<2) + 1 + cont;
+
+        switch (pi->mode)  {
+
+        case 0: w0(1); P1; w0(r); P2; w0(0); P1; 
+               w2(6); a = r1(); w2(4);
+               w2(6); b = r1(); w2(4);
+               w2(6); w2(4); w2(6); w2(4);
+                return j44(a,b);
+
+        case 1: w0(1); P1; w0(r); P2; w0(0); P1;
+               w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
+                return a;
+
+       case 2:
+       case 3:
+        case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
+               w3(0); w3(0); w2(0x24); a = r4(); w2(4);
+               w2(0x24); r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void on26_write_regr( PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = (regr<<2) + 1 + cont;
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(1); P1; w0(r); P2; w0(0); P1;
+               w0(val); P2; w0(val); P2;
+               break;
+
+       case 2:
+       case 3:
+        case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
+               w3(0); w3(0); 
+               w2(5); w4(val); w2(4);
+               w2(5); w4(val); w2(4);
+                break;
+        }
+}
+
+#define  CCP(x)  w0(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+                w0(0x87);w0(0x78);w0(x);w2(4);
+
+static void on26_connect ( PIA *pi )
+
+{       int    x;
+
+       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+        CCP(0x20);
+       w2(0xcd); w2(0xcc); w0(0xff);
+       x = 8; if (pi->mode) x = 9;
+
+       w0(2); P1; w0(8); P2;
+       w0(2); P1; w0(x); P2;
+}
+
+static void on26_disconnect ( PIA *pi )
+
+{       if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); }
+                     else { w0(4); P1; w0(4); P1; }
+       CCP(0x30);
+        w2(0xcd); w2(0xcc); w0(0xff);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void on26_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
+               udelay(10);
+               for (k=0;k<count;k++) {
+                        w2(6); a = r1();
+                        w2(4); b = r1();
+                        buf[k] = j44(a,b);
+                }
+               w0(2); P1; w0(8); P2; 
+                break;
+
+        case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
+               udelay(10);
+                for (k=0;k<count/2;k++) {
+                        w2(0x26); buf[2*k] = r0(); 
+                       w2(0x24); buf[2*k+1] = r0();
+                }
+                w0(2); P1; w0(9); P2;
+                break;
+
+        case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
+               w3(0); w3(0); w2(0x24);
+               udelay(10);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4);
+                break;
+
+        case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0x24);
+                udelay(10);
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4);
+                break;
+
+        case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0x24);
+                udelay(10);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4);
+                break;
+
+        }
+}
+
+static void on26_write_block( PIA *pi, char * buf, int count )
+
+{       int    k;
+
+        switch (pi->mode) {
+
+        case 0: 
+        case 1: w0(1); P1; w0(1); P2; 
+               w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1;
+               udelay(10);
+               for (k=0;k<count/2;k++) {
+                        w2(5); w0(buf[2*k]); 
+                       w2(7); w0(buf[2*k+1]);
+                }
+                w2(5); w2(4);
+               w0(2); P1; w0(8+pi->mode); P2;
+                break;
+
+        case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
+               w3(0); w3(0); w2(0xc5);
+               udelay(10);
+                for (k=0;k<count;k++) w4(buf[k]);
+               w2(0xc4);
+                break;
+
+        case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0xc5);
+                udelay(10);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0xc5);
+                udelay(10);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        }
+
+}
+
+static void on26_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8",
+                                  "EPP-16","EPP-32"};
+
+        printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ",
+                pi->device,ON26_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+               mode_string[pi->mode],pi->delay);
+
+}
+
+static void on26_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void on26_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on26 = {"on26",0,5,2,1,1,
+                           on26_write_regr,
+                           on26_read_regr,
+                           on26_write_block,
+                           on26_read_block,
+                           on26_connect,
+                           on26_disconnect,
+                           0,
+                           0,
+                           0,
+                           on26_log_adapter,
+                           on26_init_proto,
+                           on26_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &on26 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &on26 );
+}
+
+#endif
+
+/* end of on26.c */
+
diff --git a/drivers/block/paride/paride.c b/drivers/block/paride/paride.c
new file mode 100644 (file)
index 0000000..e4ae789
--- /dev/null
@@ -0,0 +1,514 @@
+/* 
+        paride.c  (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+       This is the base module for the family of device drivers
+        that support parallel port IDE devices.  
+
+*/
+
+/* Changes:
+
+       1.01    GRG 1998.05.03  Use spinlocks
+       1.02    GRG 1998.05.05  init_proto, release_proto, ktti
+
+*/
+
+#define PI_VERSION      "1.02"
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include "spinlock.h"
+
+#ifdef CONFIG_PARPORT_MODULE
+#define CONFIG_PARPORT
+#endif
+
+#ifdef CONFIG_PARPORT
+#include <linux/parport.h>
+#endif
+
+#include "paride.h"
+
+#define MAX_PROTOS     32
+
+static struct pi_protocol      *protocols[MAX_PROTOS];
+
+/* spinlock_t  pi_spinlock = SPIN_LOCK_UNLOCKED; */
+
+void pi_write_regr( PIA *pi, int cont, int regr, int val)
+
+{      pi->proto->write_regr(pi,cont,regr,val);
+}
+
+int pi_read_regr( PIA *pi, int cont, int regr)
+
+{      return pi->proto->read_regr(pi,cont,regr);
+}
+
+void pi_write_block( PIA *pi, char * buf, int count)
+
+{      pi->proto->write_block(pi,buf,count);
+}
+
+void pi_read_block( PIA *pi, char * buf, int count)
+
+{       pi->proto->read_block(pi,buf,count);
+}
+
+#ifdef CONFIG_PARPORT
+
+static void pi_wake_up( void *p)
+
+{       PIA  *pi = (PIA *) p;
+       long flags;
+       void (*cont)(void) = NULL;
+
+       spin_lock_irqsave(&pi_spinlock,flags);
+
+       if (pi->claim_cont && !parport_claim(pi->pardev)) {
+               cont = pi->claim_cont;
+               pi->claim_cont = NULL;
+               pi->claimed = 1;
+       }
+
+       spin_unlock_irqrestore(&pi_spinlock,flags);     
+
+       wake_up(&(pi->parq));
+
+       if (cont) cont();
+}
+
+#endif
+
+void pi_do_claimed( PIA *pi, void(*cont)(void))
+
+#ifdef CONFIG_PARPORT
+
+{      long flags;
+
+       spin_lock_irqsave(&pi_spinlock,flags); 
+
+       if (!pi->pardev || !parport_claim(pi->pardev)) {
+               pi->claimed = 1;
+               spin_unlock_irqrestore(&pi_spinlock,flags);
+               cont();
+       } else {
+               pi->claim_cont = cont;
+               spin_unlock_irqrestore(&pi_spinlock,flags);     
+       }
+}
+
+#else
+
+{      cont();
+}
+
+#endif
+
+static void pi_claim( PIA *pi)
+
+{      if (pi->claimed) return;
+       pi->claimed = 1;
+#ifdef CONFIG_PARPORT
+        if (pi->pardev)
+          while (parport_claim((struct pardevice *)(pi->pardev)))
+            sleep_on(&(pi->parq));
+#endif
+}
+
+static void pi_unclaim( PIA *pi)
+
+{      pi->claimed = 0;
+#ifdef CONFIG_PARPORT
+        if (pi->pardev) parport_release((struct pardevice *)(pi->pardev));
+#endif 
+}
+
+void pi_connect( PIA *pi)
+
+{      pi_claim(pi);
+       pi->proto->connect(pi);
+}
+
+void pi_disconnect( PIA *pi)
+
+{      pi->proto->disconnect(pi);
+       pi_unclaim(pi);
+}
+
+static void pi_unregister_parport( PIA *pi)
+
+{
+#ifdef CONFIG_PARPORT
+        if (pi->pardev) {
+           parport_unregister_device((struct pardevice *)(pi->pardev));
+          pi->pardev = NULL;
+       }
+#endif
+}
+
+void pi_release( PIA *pi)
+
+{      pi_unregister_parport(pi);
+       if ((!pi->pardev)&&(pi->reserved)) 
+               release_region(pi->port,pi->reserved);
+       pi->proto->release_proto(pi);
+}
+
+#define WR(r,v)         pi_write_regr(pi,0,r,v)
+#define RR(r)           (pi_read_regr(pi,0,r))
+
+static int pi_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k;
+        int     e[2] = {0,0};
+
+       if (pi->proto->test_proto) {
+               pi_claim(pi);
+               j = pi->proto->test_proto(pi,scratch,verbose);
+               pi_unclaim(pi);
+               return j;
+       }
+
+        pi_connect(pi);
+
+        for (j=0;j<2;j++) {
+                WR(6,0xa0+j*0x10);
+                for (k=0;k<256;k++) {
+                        WR(2,k^0xaa);
+                        WR(3,k^0x55);
+                        if (RR(2) != (k^0xaa)) e[j]++;
+                        }
+                }
+
+        pi_disconnect(pi);
+
+        if (verbose)
+                printk("%s: %s: port 0x%x, mode  %d, test=(%d,%d)\n",
+                        pi->device,pi->proto->name,pi->port,
+                       pi->mode,e[0],e[1]);
+
+        return (e[0] && e[1]);  /* not here if both > 0 */
+}
+
+int  pi_register( PIP *pr)
+
+{      int k;
+
+       for (k=0;k<MAX_PROTOS;k++) 
+          if (protocols[k] && !strcmp(pr->name,protocols[k]->name)) {
+               printk("paride: %s protocol already registered\n",pr->name);
+               return 0;
+          }
+       k = 0;
+       while((k<MAX_PROTOS) && (protocols[k])) k++;
+       if (k == MAX_PROTOS) {
+               printk("paride: protocol table full\n");
+               return 0;
+       }
+       MOD_INC_USE_COUNT;
+       protocols[k] = pr;
+       pr->index = k;
+       printk("paride: %s registered as protocol %d\n",pr->name,k);
+       return 1;
+}      
+
+void pi_unregister( PIP *pr)
+
+{      if (!pr) return;
+       if (protocols[pr->index] != pr) {
+               printk("paride: %s not registered\n",pr->name);
+               return;
+       } 
+       protocols[pr->index] = 0;
+       MOD_DEC_USE_COUNT;
+}
+
+static void pi_register_parport( PIA *pi, int verbose)
+
+{
+#ifdef CONFIG_PARPORT
+
+       struct parport  *pp;
+
+       pp = parport_enumerate();
+
+       while((pp)&&(pp->base != pi->port)) pp = pp->next;
+
+       if (!pp) return;
+
+       pi->pardev = (void *) parport_register_device(
+             pp,pi->device,NULL,pi_wake_up,NULL,0,(void *)pi);
+
+       pi->parq = NULL;
+
+       if (verbose) printk("%s: 0x%x is %s\n",pi->device,pi->port,pp->name);
+       
+       pi->parname = pp->name;
+
+#endif
+}
+
+static int pi_probe_mode( PIA *pi, int max, char * scratch, int verbose)
+
+{      int     best, range;
+
+       if (pi->mode != -1) {
+               if (pi->mode >= max) return 0;
+               range = 3;
+               if (pi->mode >= pi->proto->epp_first) range = 8;
+               if ((range == 8) && (pi->port % 8)) return 0;
+               if ((!pi->pardev) && check_region(pi->port,range)) return 0;
+               pi->reserved = range;
+               return (!pi_test_proto(pi,scratch,verbose));
+       }
+       best = -1;
+       for(pi->mode=0;pi->mode<max;pi->mode++) {
+               range = 3;
+               if (pi->mode >= pi->proto->epp_first) range = 8;
+               if ((range == 8) && (pi->port % 8)) break;
+               if ((!pi->pardev) && check_region(pi->port,range)) break;
+               pi->reserved = range;
+               if (!pi_test_proto(pi,scratch,verbose)) best = pi->mode;
+       }
+       pi->mode = best;
+       return (best > -1);
+}
+
+static int pi_probe_unit( PIA *pi, int unit, char * scratch, int verbose)
+
+{      int max,s,e;
+
+       s = unit; e = s+1;
+
+       if (s == -1) { 
+               s = 0; 
+               e = pi->proto->max_units; 
+       }
+
+       pi_register_parport(pi,verbose);
+
+       if ((!pi->pardev) && check_region(pi->port,3)) return 0;
+
+       if (pi->proto->test_port) {
+               pi_claim(pi);
+               max = pi->proto->test_port(pi);
+               pi_unclaim(pi);
+       }
+       else max = pi->proto->max_mode;
+
+       if (pi->proto->probe_unit) {
+          pi_claim(pi);
+          for (pi->unit=s;pi->unit<e;pi->unit++)
+             if (pi->proto->probe_unit(pi)) {
+                pi_unclaim(pi);
+                if (pi_probe_mode(pi,max,scratch,verbose)) return 1;
+                pi_unregister_parport(pi); 
+                return 0;
+                }
+          pi_unclaim(pi);
+          pi_unregister_parport(pi); 
+          return 0;
+          }
+
+       if (!pi_probe_mode(pi,max,scratch,verbose)) {
+          pi_unregister_parport(pi); 
+          return 0;
+       }
+       return 1;
+               
+}
+
+int pi_init(PIA *pi, int autoprobe, int port, int mode, 
+           int unit, int protocol, int delay, char * scratch, 
+           int devtype, int verbose, char *device )
+
+{      int p,k,s,e;
+       int lpts[7] = {0x3bc,0x378,0x278,0x268,0x27c,0x26c,0};
+
+       s = protocol; e = s+1;
+
+       if (autoprobe) {
+               s = 0; 
+               e = MAX_PROTOS;
+       } else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) ||
+                   (!protocols[s]) || (unit < 0) || 
+                   (unit >= protocols[s]->max_units)) {
+                       printk("%s: Invalid parameters\n",device);
+                       return 0;
+                       }
+
+       for (p=s;p<e;p++) {
+         if (protocols[p]) {
+               pi->proto = protocols[p];
+               pi->private = 0;
+               pi->proto->init_proto(pi);
+               if (delay == -1) pi->delay = pi->proto->default_delay;
+                 else pi->delay = delay;
+               pi->devtype = devtype;
+               pi->device = device;
+
+               pi->parname = NULL;
+               pi->pardev = NULL;
+               pi->parq = NULL;
+               pi->claimed = 0;
+               pi->claim_cont = NULL;
+               
+               pi->mode = mode;
+               if (port != -1) {
+                       pi->port = port;
+                       if (pi_probe_unit(pi,unit,scratch,verbose)) break;
+                       pi->port = 0;
+               } else { 
+                       k = 0;
+                       while ((pi->port = lpts[k++]))
+                          if (pi_probe_unit(pi,unit,scratch,verbose)) break;
+                       if (pi->port) break;
+               }
+               pi->proto->release_proto(pi);
+         }
+       }
+
+       if (!pi->port) {
+               if (autoprobe) printk("%s: Autoprobe failed\n",device);
+               else printk("%s: Adapter not found\n",device);
+               return 0;
+       }
+
+       if (!pi->pardev)
+          request_region(pi->port,pi->reserved,pi->device);
+
+       if (pi->parname)
+          printk("%s: Sharing %s at 0x%x\n",pi->device,
+                       pi->parname,pi->port);
+
+       pi->proto->log_adapter(pi,scratch,verbose);
+
+       return 1;
+}
+
+#ifdef MODULE
+
+int    init_module(void)
+
+{      int k;
+
+       for (k=0;k<MAX_PROTOS;k++) protocols[k] = 0;
+       printk("paride: version %s installed\n",PI_VERSION);
+       return 0;
+}
+
+void   cleanup_module(void)
+
+{
+}
+
+#else
+
+void   paride_init( void )
+
+{
+
+#ifdef CONFIG_PARIDE_ATEN
+       { extern struct pi_protocol aten;
+         pi_register(&aten);
+       };
+#endif
+#ifdef CONFIG_PARIDE_BPCK
+        { extern struct pi_protocol bpck;
+          pi_register(&bpck);
+        };
+#endif
+#ifdef CONFIG_PARIDE_COMM
+        { extern struct pi_protocol comm;
+          pi_register(&comm);
+        };
+#endif
+#ifdef CONFIG_PARIDE_DSTR
+        { extern struct pi_protocol dstr;
+          pi_register(&dstr);
+        };
+#endif
+#ifdef CONFIG_PARIDE_EPAT
+        { extern struct pi_protocol epat;
+          pi_register(&epat);
+        };
+#endif
+#ifdef CONFIG_PARIDE_EPIA
+        { extern struct pi_protocol epia;
+          pi_register(&epia);
+        };
+#endif
+#ifdef CONFIG_PARIDE_FRPW
+        { extern struct pi_protocol frpw;
+          pi_register(&frpw);
+        };
+#endif
+#ifdef CONFIG_PARIDE_FIT2
+        { extern struct pi_protocol fit2;
+          pi_register(&fit2);
+        };
+#endif
+#ifdef CONFIG_PARIDE_FIT3
+        { extern struct pi_protocol fit3;
+          pi_register(&fit3);
+        };
+#endif
+#ifdef CONFIG_PARIDE_KBIC
+        { extern struct pi_protocol k951;
+          extern struct pi_protocol k971;
+          pi_register(&k951);
+          pi_register(&k971);
+        };
+#endif
+#ifdef CONFIG_PARIDE_KTTI
+        { extern struct pi_protocol ktti;
+          pi_register(&ktti);
+        };
+#endif
+#ifdef CONFIG_PARIDE_ON20
+        { extern struct pi_protocol on20;
+          pi_register(&on20);
+        };
+#endif
+#ifdef CONFIG_PARIDE_ON26
+        { extern struct pi_protocol on26;
+          pi_register(&on26);
+        };
+#endif
+
+#ifdef CONFIG_PARIDE_PD
+       { extern int pd_init(void);
+         pd_init();
+       };
+#endif
+#ifdef CONFIG_PARIDE_PCD
+        { extern int pcd_init(void);
+          pcd_init();
+        };
+#endif
+#ifdef CONFIG_PARIDE_PF
+        { extern int pf_init(void);
+          pf_init();
+        };
+#endif
+#ifdef CONFIG_PARIDE_PT
+        { extern int pt_init(void);
+          pt_init();
+        };
+#endif
+#ifdef CONFIG_PARIDE_PG
+        { extern int pg_init(void);
+          pg_init();
+        };
+#endif
+}
+
+#endif
+
+/* end of paride.c */
diff --git a/drivers/block/paride/paride.h b/drivers/block/paride/paride.h
new file mode 100644 (file)
index 0000000..6d37736
--- /dev/null
@@ -0,0 +1,164 @@
+/* 
+       paride.h        (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                                   Under the terms of the GPL.
+
+   This file defines the interface between the high-level parallel
+   IDE device drivers (pd, pf, pcd, pt) and the adapter chips.
+
+*/
+
+/* Changes:
+
+       1.01    GRG 1998.05.05  init_proto, release_proto
+*/
+
+#define PARIDE_H_VERSION       "1.01"
+
+/* Some adapters need to know what kind of device they are in
+
+   Values for devtype:
+*/
+
+#define        PI_PD   0       /* IDE disk */
+#define PI_PCD 1       /* ATAPI CDrom */
+#define PI_PF   2      /* ATAPI disk */
+#define PI_PT  3       /* ATAPI tape */
+#define PI_PG   4       /* ATAPI generic */
+
+/* The paride module contains no state, instead the drivers allocate
+   a pi_adapter data structure and pass it to paride in every operation.
+
+*/
+
+struct pi_adapter  {
+
+       struct pi_protocol *proto;   /* adapter protocol */
+       int     port;                /* base address of parallel port */
+       int     mode;                /* transfer mode in use */
+       int     delay;               /* adapter delay setting */
+       int     devtype;             /* device type: PI_PD etc. */
+       char    *device;             /* name of driver */
+       int     unit;                /* unit number for chained adapters */
+       int     saved_r0;            /* saved port state */
+       int     saved_r2;            /* saved port state */
+       int     reserved;            /* number of ports reserved */
+       int     private;             /* for protocol module */
+
+       struct wait_queue *parq;     /* semaphore for parport sharing */
+       void    *pardev;             /* pointer to pardevice */
+       char    *parname;            /* parport name */
+       int     claimed;             /* parport has already been claimed */
+       void (*claim_cont)(void);    /* continuation for parport wait */
+};
+
+typedef struct pi_adapter PIA;
+
+/* functions exported by paride to the high level drivers */
+
+extern int pi_init(PIA *pi, 
+       int autoprobe,          /* 1 to autoprobe */
+       int port,               /* base port address */
+       int mode,               /* -1 for autoprobe */
+       int unit,               /* unit number, if supported */
+       int protocol,           /* protocol to use */
+       int delay,              /* -1 to use adapter specific default */
+       char * scratch,         /* address of 512 byte buffer */
+       int devtype,            /* device type: PI_PD, PI_PCD, etc ... */
+       int verbose,            /* log verbose data while probing */
+       char *device            /* name of the driver */
+       );                      /* returns 0 on failure, 1 on success */
+
+extern void pi_release(PIA *pi);
+
+/* registers are addressed as (cont,regr)
+
+               cont: 0 for command register file, 1 for control register(s)
+       regr: 0-7 for register number.
+
+*/
+
+extern void pi_write_regr(PIA *pi, int cont, int regr, int val);
+
+extern int pi_read_regr(PIA *pi, int cont, int regr);
+
+extern void pi_write_block(PIA *pi, char * buf, int count);
+
+extern void pi_read_block(PIA *pi, char * buf, int count);
+
+extern void pi_connect(PIA *pi);
+
+extern void pi_disconnect(PIA *pi);
+
+extern void pi_do_claimed(PIA *pi, void (*cont)(void));
+
+/* macros and functions exported to the protocol modules */
+
+#define delay_p                        (pi->delay?udelay(pi->delay):0)
+#define out_p(offs,byte)       outb(byte,pi->port+offs); delay_p;
+#define in_p(offs)             (delay_p,inb(pi->port+offs))
+
+#define w0(byte)                {out_p(0,byte);}
+#define r0()                    (in_p(0) & 0xff)
+#define w1(byte)                {out_p(1,byte);}
+#define r1()                    (in_p(1) & 0xff)
+#define w2(byte)                {out_p(2,byte);}
+#define r2()                    (in_p(2) & 0xff)
+#define w3(byte)                {out_p(3,byte);}
+#define w4(byte)                {out_p(4,byte);}
+#define r4()                    (in_p(4) & 0xff)
+#define w4w(data)              {outw(data,pi->port+4); delay_p;}
+#define w4l(data)              {outl(data,pi->port+4); delay_p;}
+#define r4w()                  (delay_p,inw(pi->port+4)&0xffff)
+#define r4l()                  (delay_p,inl(pi->port+4)&0xffffffff)
+
+static inline u16 pi_swab16( char *b, int k)
+
+{      union { u16 u; char t[2]; } r;
+
+       r.t[0]=b[2*k+1]; r.t[1]=b[2*k];
+        return r.u;
+}
+
+static inline u32 pi_swab32( char *b, int k)
+
+{      union { u32 u; char f[4]; } r;
+
+       r.f[0]=b[4*k+1]; r.f[1]=b[4*k];
+       r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2];
+        return r.u;
+}
+
+struct pi_protocol {
+
+       char    name[8];        /* name for this protocol */
+       int     index;          /* index into protocol table */
+
+       int     max_mode;       /* max mode number */
+       int     epp_first;      /* modes >= this use 8 ports */
+       
+       int     default_delay;  /* delay parameter if not specified */
+       int     max_units;      /* max chained units probed for */
+
+       void (*write_regr)(PIA *,int,int,int);
+       int  (*read_regr)(PIA *,int,int);
+       void (*write_block)(PIA *,char *,int);
+       void (*read_block)(PIA *,char *,int);
+
+       void (*connect)(PIA *);
+       void (*disconnect)(PIA *);
+       
+       int  (*test_port)(PIA *);
+       int  (*probe_unit)(PIA *);
+       int  (*test_proto)(PIA *,char *,int);
+       void (*log_adapter)(PIA *,char *,int);
+       
+       void (*init_proto)(PIA *);
+       void (*release_proto)(PIA *);
+};
+
+typedef struct pi_protocol PIP;
+
+extern int pi_register( PIP * );
+extern void pi_unregister ( PIP * );
+
+/* end of paride.h */
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
new file mode 100644 (file)
index 0000000..038e2c2
--- /dev/null
@@ -0,0 +1,807 @@
+/* 
+       pcd.c   (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                           Under the terms of the GNU public license.
+
+
+       Special 2.0.34 version
+
+
+       This is high-level driver for parallel port ATAPI CDrom
+        drives based on chips supported by the paride module.
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI CDrom drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pcd driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-6 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+               <slv>   ATAPI CDroms can be jumpered to master or slave.
+                       Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                       first drive found.
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+                        
+            major       You may use this parameter to overide the
+                        default major number (46) that this driver
+                        will use.  Be sure to change the device
+                        name as well.
+
+            name        This parameter is a character string that
+                        contains the name the kernel will use for this
+                        device (in /proc output, for instance).
+                        (default "pcd")
+
+            verbose     This parameter controls the amount of logging
+                        that is done while the driver probes for
+                        devices.  Set it to 0 for a quiet load, or 1 to
+                        see all the progress messages.  (default 0)
+
+            nice        This parameter controls the driver's use of
+                        idle CPU time, at the expense of some speed.
+       If this driver is built into the kernel, you can use kernel
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+           pcd.drive0
+           pcd.drive1
+           pcd.drive2
+           pcd.drive3
+           pcd.nice
+
+        In addition, you can use the parameter pcd.disable to disable
+        the driver entirely.
+
+*/
+
+/* Changes:
+
+       1.01    GRG 1997.01.24  Added test unit ready support
+       1.02    GRG 1998.05.06  Changes to pcd_completion, ready_wait,
+                               and loosen interpretation of ATAPI
+                               standard for clearing error status.
+                               Use spinlocks. Eliminate sti().
+       1.03    GRG 1998.06.16  Eliminated an Ugh
+
+*/
+
+#define        PCD_VERSION     "1.03s"
+#define PCD_MAJOR      46
+#define PCD_NAME       "pcd"
+#define PCD_UNITS      4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is off
+   by default.
+
+*/
+
+static int      verbose = 0;
+static int      major = PCD_MAJOR;
+static char     *name = PCD_NAME;
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pcd_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_DLY   5
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/cdrom.h>
+
+#include <asm/segment.h>
+#include "spinlock.h"
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pcd_stt[6] = {{"drive0",6,drive0},
+                         {"drive1",6,drive1},
+                         {"drive2",6,drive2},
+                         {"drive3",6,drive3},
+                        {"disable",1,&disable},
+                         {"nice",1,&nice}};
+
+void pcd_setup( char *str, int *ints)
+
+{       generic_setup(pcd_stt,6,str);
+}
+
+#endif
+
+#include "paride.h"
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR       major
+#define DEVICE_NAME "PCD"
+#define DEVICE_REQUEST do_pcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PCD_RETRIES         5
+#define PCD_TMO                   800          /* timeout in jiffies */
+#define PCD_DELAY           50          /* spin delay in uS */
+#define PCD_READY_TMO      20
+
+#define PCD_SPIN               (10000/PCD_DELAY)*PCD_TMO
+
+#define IDE_ERR                0x01
+#define IDE_DRQ         0x08
+#define IDE_READY       0x40
+#define IDE_BUSY        0x80
+
+int pcd_init(void);
+void cleanup_module( void );
+
+static int     pcd_open(struct inode *inode, struct file *file);
+static void    do_pcd_request(void);
+static void    do_pcd_read(void);
+static int     pcd_ioctl(struct inode *inode,struct file *file,
+                         unsigned int cmd, unsigned long arg);
+
+static void pcd_release (struct inode *inode, struct file *file);
+
+static int     pcd_detect(void);
+static void     pcd_lock(int unit);
+static void     pcd_unlock(int unit);
+static void     pcd_eject(int unit);
+static int      pcd_check_media(int unit);
+static void     do_pcd_read_drq(void);
+
+static int pcd_blocksizes[PCD_UNITS];
+
+#define PCD_NAMELEN    8
+
+struct pcd_unit {
+       struct pi_adapter pia;  /* interface to paride layer */
+       struct pi_adapter *pi;
+       int drive;              /* master/slave */
+       int last_sense;         /* result of last request sense */
+       int access;             /* count of active opens */
+       int present;            /* does this unit exist ? */
+       char name[PCD_NAMELEN]; /* pcd0, pcd1, etc */
+       };
+
+struct pcd_unit pcd[PCD_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PCD pcd[unit]
+#define PI PCD.pi
+
+static char pcd_scratch[64];
+static char pcd_buffer[2048];           /* raw block buffer */
+static int pcd_bufblk = -1;             /* block in buffer, in CD units,
+                                           -1 for nothing there. See also
+                                          pd_unit.
+                                        */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pcd_unit = -1;              /* unit of current request & bufblk */
+static int pcd_retries;                        /* retries on current request */
+static int pcd_busy = 0;               /* request being processed ? */
+static int pcd_sector;                 /* address of next requested sector */
+static int pcd_count;                  /* number of blocks still to do */
+static char * pcd_buf;                 /* buffer for request in progress */
+
+/* kernel glue structures */
+
+static struct file_operations pcd_fops = {
+       NULL,                   /* lseek - default */
+       block_read,             /* read - general block-dev read */
+       block_write,            /* write - general block-dev write */
+       NULL,                   /* readdir - bad */
+       NULL,                   /* select */
+       pcd_ioctl,              /* ioctl */
+       NULL,                   /* mmap */
+       pcd_open,               /* open */
+       pcd_release,            /* release */
+       block_fsync,            /* fsync */
+       NULL,                   /* fasync */
+       NULL,                   /* media change ? */
+       NULL                    /* revalidate new media */
+};
+
+static void pcd_init_units( void )
+
+{       int     unit, j;
+
+        pcd_drive_count = 0;
+        for (unit=0;unit<PCD_UNITS;unit++) {
+                PCD.pi = & PCD.pia;
+                PCD.access = 0;
+                PCD.present = 0;
+               PCD.last_sense = 0;
+                j = 0;
+                while ((j < PCD_NAMELEN-2) && (PCD.name[j]=name[j])) j++;
+                PCD.name[j++] = '0' + unit;
+                PCD.name[j] = 0;
+                PCD.drive = DU[D_SLV];
+                if (DU[D_PRT]) pcd_drive_count++;
+        }
+}
+
+int pcd_init (void)    /* preliminary initialisation */
+
+{       int    i;
+
+       if (disable) return -1;
+
+       pcd_init_units();
+
+       if (pcd_detect()) return -1;
+
+       if (register_blkdev(MAJOR_NR,name,&pcd_fops)) {
+               printk("pcd: unable to get major number %d\n",MAJOR_NR);
+               return -1;
+       }
+       blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+       read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
+
+       for (i=0;i<PCD_UNITS;i++) pcd_blocksizes[i] = 1024;
+        blksize_size[MAJOR_NR] = pcd_blocksizes;
+
+       return 0;
+}
+
+static int pcd_open (struct inode *inode, struct file *file)
+
+{      int unit = DEVICE_NR(inode->i_rdev);
+
+       if  ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+       if (file->f_mode & 2) return -EROFS;  /* wants to write ? */
+
+       MOD_INC_USE_COUNT;
+
+       if (pcd_check_media(unit)) {
+               MOD_DEC_USE_COUNT;
+               return -ENXIO;
+       }
+
+       pcd_lock(unit);
+
+       PCD.access++;
+       return 0;
+}
+
+static void do_pcd_request (void)
+
+{       int unit;
+
+       if (pcd_busy) return;
+        while (1) {
+           if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+           INIT_REQUEST;
+           if (CURRENT->cmd == READ) {
+               unit = MINOR(CURRENT->rq_dev);
+               if (unit != pcd_unit) {
+                       pcd_bufblk = -1;
+                       pcd_unit = unit;
+               }
+               pcd_sector = CURRENT->sector;
+               pcd_count = CURRENT->nr_sectors;
+               pcd_buf = CURRENT->buffer;
+               pcd_busy = 1;
+               ps_set_intr(do_pcd_read,0,0,nice); 
+               return;
+           } 
+           else end_request(0);
+       }
+}
+
+static int pcd_ioctl(struct inode *inode,struct file *file,
+                   unsigned int cmd, unsigned long arg)
+
+/* we currently support only the EJECT ioctl. */
+
+{      int unit = DEVICE_NR(inode->i_rdev);
+       if  ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+       switch (cmd) {
+            case CDROMEJECT: if (PCD.access == 1) {
+                               pcd_eject(unit);
+                               return 0;
+                            }
+           default:
+               return -EINVAL;
+       }
+}
+
+static void pcd_release (struct inode *inode, struct file *file)
+
+{      kdev_t  devp;
+       int     unit;
+
+       devp = inode->i_rdev;
+       unit = DEVICE_NR(devp);
+
+       if  ((unit >= PCD_UNITS) || (PCD.access <= 0)) 
+                       return;
+       
+       PCD.access--;
+
+       if (!PCD.access) { 
+               fsync_dev(devp);
+
+                invalidate_inodes(devp);
+
+               invalidate_buffers(devp);
+               pcd_unlock(unit);
+
+       }
+
+       MOD_DEC_USE_COUNT;
+
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+int    init_module(void)
+
+{      int     err;
+
+       err = pcd_init();
+
+       return err;
+}
+
+void   cleanup_module(void)
+
+{      int unit;
+
+       unregister_blkdev(MAJOR_NR,name);
+       
+        for (unit=0;unit<PCD_UNITS;unit++) 
+           if (PCD.present) pi_release(PI);
+}
+
+#endif
+
+#define WR(c,r,v)       pi_write_regr(PI,c,r,v)
+#define RR(c,r)         (pi_read_regr(PI,c,r))
+
+static int pcd_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{      int j, r, e, s, p;
+
+       j = 0;
+       while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PCD_SPIN))
+               udelay(PCD_DELAY);
+
+       if ((r&(IDE_ERR&stop))||(j>=PCD_SPIN)) {
+          s = RR(0,7);
+          e = RR(0,1);
+          p = RR(0,2);
+                  if (j >= PCD_SPIN) e |= 0x100;
+           if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+                          " loop=%d phase=%d\n",
+                           PCD.name,fun,msg,r,s,e,j,p);
+          return (s<<8)+r;
+       }
+       return 0;
+}
+
+static int pcd_command( int unit, char * cmd, int dlen, char * fun )
+
+{      pi_connect(PI);
+
+        WR(0,6,0xa0 + 0x10*PCD.drive);
+
+       if (pcd_wait(unit,IDE_BUSY|IDE_DRQ,0,fun,"before command")) {
+               pi_disconnect(PI);
+               return -1;
+       }
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pcd_wait(unit,IDE_BUSY,IDE_DRQ,fun,"command DRQ")) {
+               pi_disconnect(PI);
+               return -1;
+       }
+
+        if (RR(0,2) != 1) {
+           printk("%s: %s: command phase error\n",PCD.name,fun);
+          pi_disconnect(PI);
+           return -1;
+        }
+
+       pi_write_block(PI,cmd,12);
+
+       return 0;
+}
+
+static int pcd_completion( int unit, char * buf,  char * fun )
+
+{      int r, s, n;
+
+       r = pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,fun,"completion");
+
+       if ((RR(0,2)&2) && (RR(0,7)&IDE_DRQ)) { 
+               n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc);
+               pi_read_block(PI,buf,n);
+       }
+
+       s = pcd_wait(unit,IDE_BUSY,IDE_READY|IDE_ERR,fun,"data done");
+
+       pi_disconnect(PI); 
+
+       return (r?r:s);
+}
+
+static void pcd_req_sense( int unit, int quiet )
+
+{      char    rs_cmd[12] = { 0x03,0,0,0,16,0,0,0,0,0,0,0 };
+       char    buf[16];
+       int     r;
+
+       r = pcd_command(unit,rs_cmd,16,"Request sense");
+       udelay(1000);
+       if (!r) pcd_completion(unit,buf,"Request sense");
+
+       PCD.last_sense = -1;
+       if (!r) {
+            if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+                              PCD.name,buf[2]&0xf,buf[12],buf[13]);
+           PCD.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
+                                          | ((buf[13]&0xff)<<16) ;
+        } 
+}
+
+static int pcd_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{      int r;
+
+       r = pcd_command(unit,cmd,dlen,fun);
+       udelay(1000);
+       if (!r) r = pcd_completion(unit,buf,fun);
+       if (r) pcd_req_sense(unit,!fun);
+       
+       return r;
+}
+
+#define DBMSG(msg)     NULL
+
+static void pcd_lock(int unit)
+
+{      char    lo_cmd[12] = { 0x1e,0,0,0,1,0,0,0,0,0,0,0 };
+       char    cl_cmd[12] = { 0x1b,0,0,0,3,0,0,0,0,0,0,0 };
+
+       pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd1")); 
+       pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd2"));
+       pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd3"));
+       pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd4"));
+       pcd_atapi(unit,cl_cmd,0,pcd_scratch,"close door");
+
+        pcd_atapi(unit,lo_cmd,0,pcd_scratch,DBMSG("ld"));
+        pcd_atapi(unit,lo_cmd,0,pcd_scratch,"lock door");
+}
+
+static void pcd_unlock( int unit )
+
+{      char    un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 };
+
+       pcd_atapi(unit,un_cmd,0,pcd_scratch,"unlock door");
+}
+
+static void pcd_eject( int unit)
+
+{      char    ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 };
+
+       pcd_unlock(unit);
+       pcd_atapi(unit,ej_cmd,0,pcd_scratch,"eject");
+}
+
+#define PCD_RESET_TMO  30              /* in tenths of a second */
+
+static void pcd_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+static int pcd_reset( int unit )
+
+/* the ATAPI standard actually specifies the contents of all 7 registers
+   after a reset, but the specification is ambiguous concerning the last
+   two bytes, and different drives interpret the standard differently.
+*/
+
+{      int     i, k, flg;
+       int     expect[5] = {1,1,1,0x14,0xeb};
+
+       pi_connect(PI);
+       WR(0,6,0xa0 + 0x10*PCD.drive);
+       WR(0,7,8);
+
+       pcd_sleep(2);           /* delay a bit*/
+
+       k = 0;
+       while ((k++ < PCD_RESET_TMO) && (RR(1,6)&IDE_BUSY))
+               pcd_sleep(10);
+
+       flg = 1;
+       for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+       if (verbose) {
+               printk("%s: Reset (%d) signature = ",PCD.name,k);
+               for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+               if (!flg) printk(" (incorrect)");
+               printk("\n");
+       }
+       
+       pi_disconnect(PI);
+       return flg-1;   
+}
+
+static int pcd_ready_wait( int unit, int tmo )
+
+{       char    tr_cmd[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+        int     k, p;
+
+        k = 0;
+        while (k < tmo) {
+          PCD.last_sense = 0;
+          pcd_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready"));
+          p = PCD.last_sense;
+          if (!p) return 0;
+         if (!(((p & 0xffff) == 0x0402)||((p & 0xff) == 6))) return p;
+          k++;
+          pcd_sleep(100);
+        }
+        return 0x000020;        /* timeout */
+}
+
+static int pcd_check_media( int unit )
+
+{      char    rc_cmd[12] = { 0x25,0,0,0,0,0,0,0,0,0,0,0};
+
+       pcd_ready_wait(unit,PCD_READY_TMO);
+       return (pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("check media")));
+}
+
+static int pcd_identify( int unit, char * id )
+
+{      int k, s;
+       char   id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0};
+
+       pcd_bufblk = -1;
+
+        s = pcd_atapi(unit,id_cmd,36,pcd_buffer,"identify");
+
+       if (s) return -1;
+       if ((pcd_buffer[0] & 0x1f) != 5) {
+         if (verbose) printk("%s: %s is not a CDrom\n",
+                       PCD.name,PCD.drive?"Slave":"Master");
+         return -1;
+       }
+       for (k=0;k<16;k++) id[k] = pcd_buffer[16+k]; id[16] = 0;
+       k = 16; while ((k >= 0) && (id[k] <= 0x20)) { id[k] = 0; k--; }
+
+       printk("%s: %s: %s\n",PCD.name,PCD.drive?"Slave":"Master",id);
+
+       return 0;
+}
+
+static int pcd_probe( int unit, int ms, char * id )
+
+/*     returns  0, with id set if drive is detected
+               -1, if drive detection failed
+*/
+
+{      if (ms == -1) {
+            for (PCD.drive=0;PCD.drive<=1;PCD.drive++)
+              if (!pcd_reset(unit) && !pcd_identify(unit,id)) 
+                 return 0;
+       } else {
+           PCD.drive = ms;
+            if (!pcd_reset(unit) && !pcd_identify(unit,id)) 
+               return 0;
+       }
+       return -1;
+}
+
+static int pcd_detect( void )
+
+{      char    id[18];
+       int     k, unit;
+
+       printk("%s: %s version %s, major %d, nice %d\n",
+               name,name,PCD_VERSION,major,nice);
+
+       k = 0;
+       if (pcd_drive_count == 0) {  /* nothing spec'd - so autoprobe for 1 */
+           unit = 0;
+           if (pi_init(PI,1,-1,-1,-1,-1,-1,pcd_buffer,
+                     PI_PCD,verbose,PCD.name)) {
+               if (!pcd_probe(unit,-1,id)) {
+                       PCD.present = 1;
+                       k++;
+               } else pi_release(PI);
+           }
+
+       } else for (unit=0;unit<PCD_UNITS;unit++) if (DU[D_PRT])
+           if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+                        DU[D_PRO],DU[D_DLY],pcd_buffer,PI_PCD,verbose,
+                        PCD.name)) {
+               if (!pcd_probe(unit,DU[D_SLV],id)) {
+                        PCD.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+       if (k) return 0;
+       
+       printk("%s: No CDrom drive found\n",name);
+       return -1;
+}
+
+/* I/O request processing */
+
+static int pcd_ready( void )
+
+{      int     unit = pcd_unit;
+
+       return (((RR(1,6)&(IDE_BUSY|IDE_DRQ))==IDE_DRQ)) ;
+}
+
+static void pcd_transfer( void )
+
+{      int     k, o;
+
+       while (pcd_count && (pcd_sector/4 == pcd_bufblk)) {
+               o = (pcd_sector % 4) * 512;
+               for(k=0;k<512;k++) pcd_buf[k] = pcd_buffer[o+k];
+               pcd_count--;
+               pcd_buf += 512;
+               pcd_sector++;
+       }
+}
+
+static void pcd_start( void )
+
+{      int     unit = pcd_unit;
+       int     b, i;
+       char    rd_cmd[12] = {0xa8,0,0,0,0,0,0,0,0,1,0,0};
+       long    saved_flags;
+
+       pcd_bufblk = pcd_sector / 4;
+        b = pcd_bufblk;
+       for(i=0;i<4;i++) { 
+          rd_cmd[5-i] = b & 0xff;
+          b = b >> 8;
+       }
+
+       if (pcd_command(unit,rd_cmd,2048,"read block")) {
+               pcd_bufblk = -1; 
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+               pcd_busy = 0;
+               end_request(0);
+               do_pcd_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               return;
+       }
+
+       udelay(1000);
+
+       ps_set_intr(do_pcd_read_drq,pcd_ready,PCD_TMO,nice);
+
+}
+
+static void do_pcd_read( void )
+
+
+{      int     unit = pcd_unit;
+       long    saved_flags;
+
+       pcd_busy = 1;
+       pcd_retries = 0;
+       pcd_transfer();
+       if (!pcd_count) {
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+               end_request(1);
+               pcd_busy = 0;
+               do_pcd_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               return;
+       }
+
+       pi_do_claimed(PI,pcd_start);
+}
+
+static void do_pcd_read_drq( void )
+
+{      int     unit = pcd_unit;
+       long    saved_flags;
+
+       if (pcd_completion(unit,pcd_buffer,"read block")) {
+                if (pcd_retries < PCD_RETRIES) {
+                        udelay(1000);
+                        pcd_retries++;
+                       pi_do_claimed(PI,pcd_start);
+                        return;
+                        }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+               pcd_busy = 0;
+               pcd_bufblk = -1;
+               end_request(0);
+               do_pcd_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               return;
+       }
+
+       do_pcd_read();
+       spin_lock_irqsave(&io_request_lock,saved_flags);
+       do_pcd_request();
+       spin_unlock_irqrestore(&io_request_lock,saved_flags); 
+}
+       
+/* end of pcd.c */
+
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
new file mode 100644 (file)
index 0000000..afb782d
--- /dev/null
@@ -0,0 +1,1107 @@
+/* 
+        pd.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+
+       Special 2.0.34 version.
+
+
+
+        This is the high-level driver for parallel port IDE hard
+        drives based on chips supported by the paride module.
+
+       By default, the driver will autoprobe for a single parallel
+       port IDE drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pd driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+           drive0      These four arguments can be arrays of       
+           drive1      1-8 integers as follows:
+           drive2
+           drive3      <prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<dly>,<slv>
+
+                       Where,
+
+               <prt>   is the base of the parallel port address for
+                       the corresponding drive.  (required)
+
+               <pro>   is the protocol number for the adapter that
+                       supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                       are initialised.  (0 if not given)
+
+               <uni>   for those adapters that support chained
+                       devices, this is the unit selector for the
+                       chain of devices on the given port.  It should
+                       be zero for devices that don't support chaining.
+                       (0 if not given)
+
+               <mod>   this can be -1 to choose the best mode, or one
+                       of the mode numbers supported by the adapter.
+                       (-1 if not given)
+
+               <geo>   this defaults to 0 to indicate that the driver
+                       should use the CHS geometry provided by the drive
+                       itself.  If set to 1, the driver will provide
+                       a logical geometry with 64 heads and 32 sectors
+                       per track, to be consistent with most SCSI
+                       drivers.  (0 if not given)
+
+               <sby>   set this to zero to disable the power saving
+                       standby mode, if needed.  (1 if not given)
+
+               <dly>   some parallel ports require the driver to 
+                       go more slowly.  -1 sets a default value that
+                       should work with the chosen protocol.  Otherwise,
+                       set this to a small integer, the larger it is
+                       the slower the port i/o.  In some cases, setting
+                       this to zero will speed up the device. (default -1)
+
+               <slv>   IDE disks can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+                       
+
+            major       You may use this parameter to overide the
+                        default major number (45) that this driver
+                        will use.  Be sure to change the device
+                        name as well.
+
+            name        This parameter is a character string that
+                        contains the name the kernel will use for this
+                        device (in /proc output, for instance).
+                       (default "pd")
+
+           cluster     The driver will attempt to aggregate requests
+                       for adjacent blocks into larger multi-block
+                       clusters.  The maximum cluster size (in 512
+                       byte sectors) is set with this parameter.
+                       (default 64)
+
+           verbose     This parameter controls the amount of logging
+                       that is done while the driver probes for
+                       devices.  Set it to 0 for a quiet load, or to 1
+                       see all the progress messages.  (default 0)
+
+            nice        This parameter controls the driver's use of
+                        idle CPU time, at the expense of some speed.
+
+        If this driver is built into the kernel, you can use kernel
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pd.drive0
+            pd.drive1
+            pd.drive2
+            pd.drive3
+            pd.cluster
+            pd.nice
+
+        In addition, you can use the parameter pd.disable to disable
+        the driver entirely.
+*/
+
+/* Changes:
+
+       1.01    GRG 1997.01.24  Restored pd_reset()
+                               Added eject ioctl
+       1.02    GRG 1998.05.06  SMP spinlock changes, 
+                               Added slave support
+       1.03    GRG 1998.06.16  Eliminate an Ugh.
+
+*/
+
+#define PD_VERSION      "1.03s"
+#define PD_MAJOR       45
+#define PD_NAME                "pd"
+#define PD_UNITS       4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is off
+   by default.
+
+*/
+
+static int     verbose = 0;
+static int     major = PD_MAJOR;
+static char    *name = PD_NAME;
+static int     cluster = 64;   
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[8] = {0,0,0,-1,0,1,-1,-1};
+static int drive1[8] = {0,0,0,-1,0,1,-1,-1};
+static int drive2[8] = {0,0,0,-1,0,1,-1,-1};
+static int drive3[8] = {0,0,0,-1,0,1,-1,-1};
+
+static int (*drives[4])[8] = {&drive0,&drive1,&drive2,&drive3};
+static int pd_drive_count;
+
+#define D_PRT  0
+#define D_PRO   1
+#define D_UNI  2
+#define D_MOD  3
+#define D_GEO  4
+#define D_SBY  5
+#define D_DLY  6
+#define D_SLV   7
+
+#define        DU              (*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>       /* for the eject ioctl */
+
+#include "spinlock.h"
+#include <asm/segment.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pd_stt[7] = {{"drive0",8,drive0},
+                       {"drive1",8,drive1},
+                       {"drive2",8,drive2},
+                       {"drive3",8,drive3},
+                       {"disable",1,&disable},
+                       {"cluster",1,&cluster},
+                       {"nice",1,&nice}};
+
+void pd_setup( char *str, int *ints)
+
+{      generic_setup(pd_stt,7,str);
+}
+
+#endif
+
+#include "paride.h"
+
+#define PD_BITS    4
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR   major
+#define DEVICE_NAME "PD"
+#define DEVICE_REQUEST do_pd_request
+#define DEVICE_NR(device) (MINOR(device)>>PD_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PD_PARTNS      (1<<PD_BITS)
+#define PD_DEVS                PD_PARTNS*PD_UNITS
+
+/* numbers for "SCSI" geometry */
+
+#define PD_LOG_HEADS    64
+#define PD_LOG_SECTS    32
+
+#define PD_ID_OFF       54
+#define PD_ID_LEN       14
+
+#define PD_MAX_RETRIES  5
+#define PD_TMO          800             /* interrupt timeout in jiffies */
+#define PD_SPIN_DEL     50              /* spin delay in micro-seconds  */
+
+#define PD_SPIN         (10000/PD_SPIN_DEL)*PD_TMO  
+
+#define STAT_ERR        0x00001
+#define STAT_INDEX      0x00002
+#define STAT_ECC        0x00004
+#define STAT_DRQ        0x00008
+#define STAT_SEEK       0x00010
+#define STAT_WRERR      0x00020
+#define STAT_READY      0x00040
+#define STAT_BUSY       0x00080
+
+#define ERR_AMNF        0x00100
+#define ERR_TK0NF       0x00200
+#define ERR_ABRT        0x00400
+#define ERR_MCR         0x00800
+#define ERR_IDNF        0x01000
+#define ERR_MC          0x02000
+#define ERR_UNC         0x04000
+#define ERR_TMO         0x10000
+
+#define IDE_READ               0x20
+#define IDE_WRITE              0x30
+#define IDE_READ_VRFY          0x40
+#define IDE_INIT_DEV_PARMS     0x91
+#define IDE_STANDBY            0x96
+#define IDE_ACKCHANGE          0xdb
+#define IDE_DOORLOCK           0xde
+#define IDE_DOORUNLOCK         0xdf
+#define IDE_IDENTIFY           0xec
+#define IDE_EJECT              0xed
+
+int pd_init(void);
+void pd_setup(char * str, int * ints);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static void pd_geninit(struct gendisk *ignored);
+static int pd_open(struct inode *inode, struct file *file);
+static void do_pd_request(void);
+static int pd_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+static void pd_release (struct inode *inode, struct file *file);
+static int pd_revalidate(kdev_t dev);
+static int pd_detect(void);
+static void do_pd_read(void);
+static void do_pd_read_start(void);
+static void do_pd_write(void);
+static void do_pd_write_start(void);
+static void do_pd_read_drq( void );
+static void do_pd_write_done( void );
+
+static int pd_identify (int unit);
+static void pd_media_check(int unit);
+static void pd_doorlock(int unit, int func);
+static int pd_check_media(kdev_t dev);
+static void pd_eject( int unit);
+
+static struct hd_struct pd_hd[PD_DEVS];
+static int pd_sizes[PD_DEVS];
+static int pd_blocksizes[PD_DEVS];
+
+#define PD_NAMELEN     8
+
+struct pd_unit {
+       struct pi_adapter pia;          /* interface to paride layer */
+       struct pi_adapter *pi;
+       int access;                     /* count of active opens ... */
+       int capacity;                   /* Size of this volume in sectors */
+       int heads;                      /* physical geometry */
+       int sectors;
+       int cylinders;
+       int drive;                      /* master=0 slave=1 */
+       int changed;                    /* Have we seen a disk change ? */
+       int removable;                  /* removable media device  ?  */
+       int standby;
+       int alt_geom;
+       int present;
+       char name[PD_NAMELEN];          /* pda, pdb, etc ... */
+       };
+
+struct pd_unit pd[PD_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PD pd[unit]
+#define PI PD.pi
+
+static int pd_valid = 1;               /* serialise partition checks */
+static char pd_scratch[512];            /* scratch block buffer */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pd_retries = 0;              /* i/o error retry count */
+static int pd_busy = 0;                 /* request being processed ? */
+static int pd_block;                    /* address of next requested block */
+static int pd_count;                    /* number of blocks still to do */
+static int pd_run;                     /* sectors in current cluster */
+static int pd_cmd;                     /* current command READ/WRITE */
+static int pd_unit;                    /* unit of current request */
+static int pd_dev;                     /* minor of current request */
+static int pd_poffs;                   /* partition offset of current minor */
+static char * pd_buf;                   /* buffer for request in progress */
+
+static struct wait_queue *pd_wait_open = NULL;
+
+static char *pd_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR",
+                             "READY","BUSY","AMNF","TK0NF","ABRT","MCR",
+                             "IDNF","MC","UNC","???","TMO"};
+
+/* kernel glue structures */
+
+static struct gendisk pd_gendisk = {
+        PD_MAJOR,       /* Major number */
+        PD_NAME,        /* Major name */
+        PD_BITS,        /* Bits to shift to get real from partition */
+        PD_PARTNS,      /* Number of partitions per real */
+        PD_UNITS,       /* maximum number of real */
+        pd_geninit,     /* init function */
+        pd_hd,          /* hd struct */
+        pd_sizes,       /* block sizes */
+        0,              /* number */
+        NULL,           /* internal */
+        NULL            /* next */
+};
+
+static struct file_operations pd_fops = {
+        NULL,                   /* lseek - default */
+        block_read,             /* read - general block-dev read */
+        block_write,            /* write - general block-dev write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        pd_ioctl,               /* ioctl */
+        NULL,                   /* mmap */
+        pd_open,                /* open */
+        pd_release,             /* release */
+        block_fsync,            /* fsync */
+        NULL,                   /* fasync */
+        pd_check_media,         /* media change ? */
+        pd_revalidate           /* revalidate new media */
+};
+
+void pd_init_units( void )
+
+{      int     unit, j;
+
+       pd_drive_count = 0;
+       for (unit=0;unit<PD_UNITS;unit++) {
+               PD.pi = & PD.pia;
+               PD.access = 0;
+               PD.changed = 1;
+               PD.capacity = 0;
+               PD.drive = DU[D_SLV];
+               PD.present = 0;
+               j = 0;
+               while ((j < PD_NAMELEN-2) && (PD.name[j]=name[j])) j++;
+               PD.name[j++] = 'a' + unit;
+               PD.name[j] = 0;
+               PD.alt_geom = DU[D_GEO];
+               PD.standby = DU[D_SBY];
+               if (DU[D_PRT]) pd_drive_count++;
+       }
+}
+
+int pd_init (void)
+
+{       int i;
+
+       if (disable) return -1;
+
+        if (register_blkdev(MAJOR_NR,name,&pd_fops)) {
+                printk("%s: unable to get major number %d\n",
+                        name,major);
+                return -1;
+        }
+        blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+        read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
+        
+       pd_gendisk.major = major;
+       pd_gendisk.major_name = name;
+       pd_gendisk.next = gendisk_head;
+        gendisk_head = &pd_gendisk;
+
+        for(i=0;i<PD_DEVS;i++) pd_blocksizes[i] = 1024;
+        blksize_size[MAJOR_NR] = pd_blocksizes;
+
+        printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+                name,name,PD_VERSION,major,cluster,nice);
+       
+        return 0;
+}
+
+static void pd_geninit (struct gendisk *ignored)
+
+{      pd_init_units();
+       pd_gendisk.nr_real = pd_detect();
+
+#ifdef MODULE
+        if (!pd_gendisk.nr_real) cleanup_module();
+#endif
+
+}
+
+static int pd_open (struct inode *inode, struct file *file)
+
+{       int unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+        MOD_INC_USE_COUNT;
+
+        while (!pd_valid) sleep_on(&pd_wait_open);
+
+        PD.access++;
+
+        if (PD.removable) {
+               pd_media_check(unit);
+               pd_doorlock(unit,IDE_DOORLOCK);
+       }
+        return 0;
+}
+
+static int pd_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg)
+
+{       struct hd_geometry *geo = (struct hd_geometry *) arg;
+        int dev, err, unit;
+
+        if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+        dev = MINOR(inode->i_rdev);
+       unit = DEVICE_NR(inode->i_rdev);
+        if (dev >= PD_DEVS) return -EINVAL;
+       if (!PD.present) return -ENODEV;
+
+        switch (cmd) {
+           case CDROMEJECT:
+               if (PD.access == 1) pd_eject(unit);
+               return 0;
+            case HDIO_GETGEO:
+                if (!geo) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+                if (err) return err;
+
+               if (PD.alt_geom) {
+                    put_user(PD.capacity/(PD_LOG_HEADS*PD_LOG_SECTS), 
+                               (short *) &geo->cylinders);
+                    put_user(PD_LOG_HEADS, (char *) &geo->heads);
+                    put_user(PD_LOG_SECTS, (char *) &geo->sectors);
+               } else {
+                    put_user(PD.cylinders, (short *) &geo->cylinders);
+                    put_user(PD.heads, (char *) &geo->heads);
+                    put_user(PD.sectors, (char *) &geo->sectors);
+               }
+                put_user(pd_hd[dev].start_sect,(long *)&geo->start);
+                return 0;
+            case BLKRASET:
+                if(!suser()) return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                if(arg > 0xff) return -EINVAL;
+                read_ahead[MAJOR(inode->i_rdev)] = arg;
+                return 0;
+            case BLKRAGET:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+                return (0);
+            case BLKGETSIZE:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(pd_hd[dev].nr_sects,(long *) arg);
+                return (0);
+            case BLKFLSBUF:
+                if(!suser())  return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                fsync_dev(inode->i_rdev);
+                invalidate_buffers(inode->i_rdev);
+                return 0;
+            case BLKRRPART:
+               if (!suser()) return -EACCES;
+                return pd_revalidate(inode->i_rdev);
+            RO_IOCTLS(inode->i_rdev,arg);
+            default:
+                return -EINVAL;
+        }
+}
+
+static void pd_release (struct inode *inode, struct file *file)
+
+{       kdev_t devp;
+       int     unit;
+
+        devp = inode->i_rdev;
+       unit = DEVICE_NR(devp);
+
+       if ((unit >= PD_UNITS) || (PD.access <= 0)) 
+               return;
+
+       PD.access--;
+
+        if (!PD.access)  {
+                fsync_dev(devp);
+
+               invalidate_inodes(devp);
+
+                invalidate_buffers(devp);
+               if (PD.removable) pd_doorlock(unit,IDE_DOORUNLOCK);
+       }
+
+        MOD_DEC_USE_COUNT;
+
+}
+
+static int pd_check_media( kdev_t dev)
+
+{       int r, unit;
+
+       unit = DEVICE_NR(dev);
+       if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+        if (!PD.removable) return 0;
+       pd_media_check(unit);
+       r = PD.changed;
+       PD.changed = 0;
+       return r;
+}
+
+static int pd_revalidate(kdev_t dev)
+
+{       int p, unit, minor;
+        long flags;
+        kdev_t devp;
+
+        unit = DEVICE_NR(dev);
+        if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+        save_flags(flags);
+        cli(); 
+        if (PD.access > 1) {
+                restore_flags(flags);
+                return -EBUSY;
+        }
+        pd_valid = 0;
+        restore_flags(flags);   
+
+        for (p=(PD_PARTNS-1);p>=0;p--) {
+               minor = p + unit*PD_PARTNS;
+                devp = MKDEV(MAJOR_NR, minor);
+                fsync_dev(devp);
+
+                invalidate_inodes(devp);
+
+                invalidate_buffers(devp);
+                pd_hd[minor].start_sect = 0;
+                pd_hd[minor].nr_sects = 0;
+        }
+
+       pd_identify(unit);
+        resetup_one_dev(&pd_gendisk,unit);
+
+        pd_valid = 1;
+        wake_up(&pd_wait_open);
+
+        return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err, unit;
+
+        err = pd_init();
+        if (err) return err;
+
+       pd_geninit(&pd_gendisk);
+
+        if (!pd_gendisk.nr_real)  return -1;
+
+        pd_valid = 0;
+       for (unit=0;unit<PD_UNITS;unit++) 
+          if (PD.present) resetup_one_dev(&pd_gendisk,unit);
+        pd_valid = 1;
+
+        return 0;
+}
+
+void    cleanup_module(void)
+
+{       struct gendisk **gdp;
+       int unit;
+
+        unregister_blkdev(MAJOR_NR,name);
+
+        for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
+                if (*gdp == &pd_gendisk) break;
+        if (*gdp) *gdp = (*gdp)->next;
+
+       for (unit=0;unit<PD_UNITS;unit++) 
+          if (PD.present) pi_release(PI);
+}
+
+#endif
+
+#define        WR(c,r,v)       pi_write_regr(PI,c,r,v)
+#define        RR(c,r)         (pi_read_regr(PI,c,r))
+
+#define DRIVE          (0xa0+0x10*PD.drive)
+
+/*  ide command interface */
+
+static void pd_print_error( int unit, char * msg, int status )
+
+{       int     i;
+
+       printk("%s: %s: status = 0x%x =",PD.name,msg,status);
+        for(i=0;i<18;i++) if (status & (1<<i)) printk(" %s",pd_errs[i]);
+       printk("\n");
+}
+
+static void pd_reset( int unit )    /* called only for MASTER drive */
+
+{       pi_connect(PI);
+       WR(1,6,4);
+        udelay(50);
+        WR(1,6,0);
+       pi_disconnect(PI);
+       udelay(250);
+}
+
+#define DBMSG(msg)     NULL
+
+static int pd_wait_for( int unit, int w, char * msg )    /* polled wait */
+
+{       int     k, r, e;
+
+        k=0;
+        while(k < PD_SPIN) { 
+            r = RR(1,6);
+            k++;
+            if (((r & w) == w) && !(r & STAT_BUSY)) break;
+            udelay(PD_SPIN_DEL);
+        }
+        e = (RR(0,1)<<8) + RR(0,7);
+        if (k >= PD_SPIN)  e |= ERR_TMO;
+        if ((e & (STAT_ERR|ERR_TMO)) && (msg != NULL)) 
+               pd_print_error(unit,msg,e);
+        return e;
+}
+
+static void pd_send_command( int unit, int n, int s, int h, 
+                            int c0, int c1, int func )
+
+{
+        WR(0,6,DRIVE+h);
+        WR(0,1,0);                /* the IDE task file */
+        WR(0,2,n);
+        WR(0,3,s);
+        WR(0,4,c0);
+        WR(0,5,c1);
+        WR(0,7,func);
+
+        udelay(1);
+}
+
+static void pd_ide_command( int unit, int func, int block, int count )
+
+/* Don't use this call if the capacity is zero. */
+
+{       int c1, c0, h, s;
+
+        s  = ( block % PD.sectors) + 1;
+        h  = ( block / PD.sectors) % PD.heads;
+        c0 = ( block / (PD.sectors*PD.heads)) % 256;
+        c1 = ( block / (PD.sectors*PD.heads*256));
+
+        pd_send_command(unit,count,s,h,c0,c1,func);
+}
+
+/* According to the ATA standard, the default CHS geometry should be
+   available following a reset.  Some Western Digital drives come up
+   in a mode where only LBA addresses are accepted until the device
+   parameters are initialised.
+*/
+
+static void pd_init_dev_parms( int unit )
+{       pi_connect(PI);
+        pd_wait_for(unit,0,DBMSG("before init_dev_parms"));
+        pd_send_command(unit,PD.sectors,0,PD.heads-1,0,0,IDE_INIT_DEV_PARMS);
+        udelay(300);
+        pd_wait_for(unit,0,"Initialise device parameters");
+        pi_disconnect(PI);
+}
+
+static void pd_doorlock( int unit, int func )
+
+{       pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"Lock") & STAT_ERR) {
+                pi_disconnect(PI);
+                return;
+        }
+        pd_send_command(unit,1,0,0,0,0,func);
+        pd_wait_for(unit,STAT_READY,"Lock done");
+        pi_disconnect(PI);
+}
+
+static void pd_eject( int unit )
+
+{      pi_connect(PI);
+        pd_wait_for(unit,0,DBMSG("before unlock on eject"));
+        pd_send_command(unit,1,0,0,0,0,IDE_DOORUNLOCK);
+        pd_wait_for(unit,0,DBMSG("after unlock on eject"));
+        pd_wait_for(unit,0,DBMSG("before eject"));
+        pd_send_command(unit,0,0,0,0,0,IDE_EJECT);
+        pd_wait_for(unit,0,DBMSG("after eject"));
+        pi_disconnect(PI);
+}
+
+static void pd_media_check( int unit )
+
+{       int    r;
+
+        pi_connect(PI);
+        r = pd_wait_for(unit,STAT_READY,DBMSG("before media_check"));
+        if (!(r & STAT_ERR)) {
+                pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);  
+                r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after READ_VRFY"));
+        } else PD.changed = 1;   /* say changed if other error */
+        if (r & ERR_MC) {
+                PD.changed = 1;
+                pd_send_command(unit,1,0,0,0,0,IDE_ACKCHANGE);
+                pd_wait_for(unit,STAT_READY,DBMSG("RDY after ACKCHANGE"));
+               pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);
+                r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after VRFY"));
+        }
+        pi_disconnect(PI);
+
+}
+
+static void pd_standby_off( int unit )
+
+{       pi_connect(PI);
+        pd_wait_for(unit,0,DBMSG("before STANDBY"));
+        pd_send_command(unit,0,0,0,0,0,IDE_STANDBY);
+        pd_wait_for(unit,0,DBMSG("after STANDBY"));
+        pi_disconnect(PI);
+}
+
+#define  word_val(n) ((pd_scratch[2*n]&0xff)+256*(pd_scratch[2*n+1]&0xff))
+
+static int pd_identify( int unit )
+
+{       int    j;
+       char id[PD_ID_LEN+1];
+
+/* WARNING:  here there may be dragons.  reset() applies to both drives,
+   but we call it only on probing the MASTER. This should allow most
+   common configurations to work, but be warned that a reset can clear
+   settings on the SLAVE drive.
+*/ 
+
+       if (PD.drive == 0) pd_reset(unit);
+
+        pi_connect(PI);
+       WR(0,6,DRIVE);
+        pd_wait_for(unit,0,DBMSG("before IDENT"));  
+        pd_send_command(unit,1,0,0,0,0,IDE_IDENTIFY);
+
+        if (pd_wait_for(unit,STAT_DRQ,DBMSG("IDENT DRQ")) & STAT_ERR) {
+                pi_disconnect(PI);
+                return 0;
+        }
+        pi_read_block(PI,pd_scratch,512);
+        pi_disconnect(PI);
+        PD.sectors = word_val(6);
+        PD.heads = word_val(3);
+        PD.cylinders  = word_val(1);
+        PD.capacity = PD.sectors*PD.heads*PD.cylinders;
+
+        for(j=0;j<PD_ID_LEN;j++) id[j^1] = pd_scratch[j+PD_ID_OFF];
+        j = PD_ID_LEN-1;
+        while ((j >= 0) && (id[j] <= 0x20)) j--;
+        j++; id[j] = 0;
+
+        PD.removable = (word_val(0) & 0x80);
+        printk("%s: %s, %s, %d blocks [%dM], (%d/%d/%d), %s media\n",
+                    PD.name,id,
+                   PD.drive?"slave":"master",
+                   PD.capacity,PD.capacity/2048,
+                    PD.cylinders,PD.heads,PD.sectors,
+                    PD.removable?"removable":"fixed");
+
+        if (PD.capacity) pd_init_dev_parms(unit);
+        if (!PD.standby) pd_standby_off(unit);
+
+       pd_hd[unit<<PD_BITS].nr_sects = PD.capacity;
+       pd_hd[unit<<PD_BITS].start_sect = 0;
+       
+        return 1;
+}
+
+static int pd_probe_drive( int unit )
+
+{      if (PD.drive == -1) {
+         for (PD.drive=0;PD.drive<=1;PD.drive++)
+            if (pd_identify(unit)) return 1;
+         return 0;
+         }
+       else return pd_identify(unit);
+}
+
+static int pd_detect( void )
+
+{       int    k, unit;
+
+       k = 0;
+       if (pd_drive_count == 0) {  /* nothing spec'd - so autoprobe for 1 */
+           unit = 0;
+           if (pi_init(PI,1,-1,-1,-1,-1,-1,pd_scratch,
+                    PI_PD,verbose,PD.name)) {
+               if (pd_probe_drive(unit)) {
+                       PD.present = 1;
+                       k = 1;
+               } else pi_release(PI);
+           }
+
+       } else for (unit=0;unit<PD_UNITS;unit++) if (DU[D_PRT])
+           if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+                       DU[D_PRO],DU[D_DLY],pd_scratch,
+                       PI_PD,verbose,PD.name)) {
+                if (pd_probe_drive(unit)) {
+                        PD.present = 1;
+                        k = unit+1;
+                } else pi_release(PI);
+            }
+
+/* We lie about the number of drives found, as the generic partition
+   scanner assumes that the drives are numbered sequentially from 0.
+   This can result in some bogus error messages if non-sequential
+   drive numbers are used.
+*/
+       
+       if (k) return k; 
+
+        printk("%s: no valid drive found\n",name);
+        return 0;
+}
+
+/* The i/o request engine */
+
+static int pd_ready( void )
+
+{      int unit = pd_unit;
+
+       return (!(RR(1,6) & STAT_BUSY)) ;
+}
+
+static void do_pd_request (void)
+
+{       struct buffer_head * bh;
+       struct request * req;
+       int     unit;
+
+        if (pd_busy) return;
+repeat:
+        if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+        INIT_REQUEST;
+
+        pd_dev = MINOR(CURRENT->rq_dev);
+       pd_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+        pd_block = CURRENT->sector;
+        pd_count = CURRENT->nr_sectors;
+
+       bh = CURRENT->bh;
+       req = CURRENT;
+       if (bh->b_reqnext)
+               printk("%s: OUCH: b_reqnext != NULL\n",PD.name);
+
+        if ((pd_dev >= PD_DEVS) || 
+           ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) {
+                end_request(0);
+                goto repeat;
+        }
+
+       pd_cmd = CURRENT->cmd;
+       pd_run = pd_count;
+        while ((pd_run <= cluster) &&
+              (req = req->next) && 
+              (pd_block+pd_run == req->sector) &&
+              (pd_cmd == req->cmd) &&
+              (pd_dev == MINOR(req->rq_dev)))
+                       pd_run += req->nr_sectors;
+
+       pd_poffs = pd_hd[pd_dev].start_sect;
+        pd_block += pd_poffs;
+        pd_buf = CURRENT->buffer;
+        pd_retries = 0;
+
+       pd_busy = 1;
+        if (pd_cmd == READ) pi_do_claimed(PI,do_pd_read);
+        else if (pd_cmd == WRITE) pi_do_claimed(PI,do_pd_write);
+        else {  pd_busy = 0;
+               end_request(0);
+                goto repeat;
+        }
+}
+
+static void pd_next_buf( int unit )
+
+{      long    saved_flags;
+
+       spin_lock_irqsave(&io_request_lock,saved_flags);
+       end_request(1);
+       if (!pd_run) {  spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                       return; 
+       }
+       
+/* paranoia */
+
+       if ((!CURRENT) ||
+           (CURRENT->cmd != pd_cmd) ||
+           (MINOR(CURRENT->rq_dev) != pd_dev) ||
+           (CURRENT->rq_status == RQ_INACTIVE) ||
+           (CURRENT->sector+pd_poffs != pd_block)) 
+               printk("%s: OUCH: request list changed unexpectedly\n",
+                       PD.name);
+
+       pd_count = CURRENT->nr_sectors;
+       pd_buf = CURRENT->buffer;
+       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+static void do_pd_read( void )
+
+{      ps_set_intr(do_pd_read_start,0,0,nice);
+}
+
+static void do_pd_read_start( void )
+{       int    unit = pd_unit;
+       long    saved_flags;
+
+       pd_busy = 1;
+
+        pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"do_pd_read") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_read_start);
+                       return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+        pd_ide_command(unit,IDE_READ,pd_block,pd_run);
+        ps_set_intr(do_pd_read_drq,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_read_drq( void )
+
+{       int    unit = pd_unit;
+       long    saved_flags;
+
+       while (1) {
+            if (pd_wait_for(unit,STAT_DRQ,"do_pd_read_drq") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_read_start);
+                        return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+            }
+            pi_read_block(PI,pd_buf,512);
+            pd_count--; pd_run--;
+            pd_buf += 512;
+           pd_block++;
+           if (!pd_run) break;
+           if (!pd_count) pd_next_buf(unit);
+        }
+        pi_disconnect(PI);
+       spin_lock_irqsave(&io_request_lock,saved_flags);
+        end_request(1);
+        pd_busy = 0;
+        do_pd_request();
+       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+static void do_pd_write( void )
+
+{       ps_set_intr(do_pd_write_start,0,0,nice);
+}
+
+static void do_pd_write_start( void )
+
+{       int    unit = pd_unit;
+       long    saved_flags;
+
+       pd_busy = 1;
+
+        pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"do_pd_write") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                       pi_do_claimed(PI,do_pd_write_start);
+                        return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+        pd_ide_command(unit,IDE_WRITE,pd_block,pd_run);
+       while (1) {
+            if (pd_wait_for(unit,STAT_DRQ,"do_pd_write_drq") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_write_start);
+                        return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+                spin_unlock_irqrestore(&io_request_lock,saved_flags);
+               return;
+            }
+            pi_write_block(PI,pd_buf,512);
+           pd_count--; pd_run--;
+           pd_buf += 512;
+           pd_block++;
+           if (!pd_run) break;
+           if (!pd_count) pd_next_buf(unit);
+       }
+        ps_set_intr(do_pd_write_done,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_write_done( void )
+
+{       int    unit = pd_unit;
+       long    saved_flags;
+
+        if (pd_wait_for(unit,STAT_READY,"do_pd_write_done") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_write_start);
+                        return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+        pi_disconnect(PI);
+       spin_lock_irqsave(&io_request_lock,saved_flags);
+        end_request(1);
+        pd_busy = 0;
+        do_pd_request();
+       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+/* end of pd.c */
+
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
new file mode 100644 (file)
index 0000000..3d6f70c
--- /dev/null
@@ -0,0 +1,1080 @@
+/* 
+        pf.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+
+       Special 2.0.34 version
+
+
+        This is the high-level driver for parallel port ATAPI disk
+        drives based on chips supported by the paride module.
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI disk drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pf driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-7 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<lun>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+                <slv>   ATAPI CDroms can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+
+               <lun>   Some ATAPI devices support multiple LUNs.
+                        One example is the ATAPI PD/CD drive from
+                        Matshita/Panasonic.  This device has a 
+                        CD drive on LUN 0 and a PD drive on LUN 1.
+                        By default, the driver will search for the
+                        first LUN with a supported device.  Set 
+                        this parameter to force it to use a specific
+                        LUN.  (default -1)
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+
+           major       You may use this parameter to overide the
+                       default major number (47) that this driver
+                       will use.  Be sure to change the device
+                       name as well.
+
+           name        This parameter is a character string that
+                       contains the name the kernel will use for this
+                       device (in /proc output, for instance).
+                       (default "pf").
+
+            cluster     The driver will attempt to aggregate requests
+                        for adjacent blocks into larger multi-block
+                        clusters.  The maximum cluster size (in 512
+                        byte sectors) is set with this parameter.
+                        (default 64)
+
+            verbose     This parameter controls the amount of logging
+                        that is done while the driver probes for
+                        devices.  Set it to 0 for a quiet load, or 1 to
+                        see all the progress messages.  (default 0)
+
+           nice        This parameter controls the driver's use of
+                       idle CPU time, at the expense of some speed.
+
+        If this driver is built into the kernel, you can use the
+        following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pf.drive0
+            pf.drive1
+            pf.drive2
+            pf.drive3
+           pf.cluster
+            pf.nice
+
+        In addition, you can use the parameter pf.disable to disable
+        the driver entirely.
+
+*/
+
+/* Changes:
+
+       1.01    GRG 1998.05.03  Changes for SMP.  Eliminate sti().
+                               Fix for drives that don't clear STAT_ERR
+                               until after next CDB delivered.
+                               Small change in pf_completion to round
+                               up transfer size.
+       1.02    GRG 1998.06.16  Eliminated an Ugh
+
+*/
+
+#define PF_VERSION      "1.02s"
+#define PF_MAJOR       47
+#define PF_NAME                "pf"
+#define PF_UNITS       4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is off
+   by default.
+
+*/
+
+static int     verbose = 0;
+static int     major = PF_MAJOR;
+static char    *name = PF_NAME;
+static int      cluster = 64;
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[7] = {0,0,0,-1,-1,-1,-1};
+static int drive1[7] = {0,0,0,-1,-1,-1,-1};
+static int drive2[7] = {0,0,0,-1,-1,-1,-1};
+static int drive3[7] = {0,0,0,-1,-1,-1,-1};
+
+static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3};
+static int pf_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_LUN   5
+#define D_DLY   6
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>
+#include "spinlock.h"
+
+#include <asm/segment.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pf_stt[7] = {{"drive0",7,drive0},
+                        {"drive1",7,drive1},
+                        {"drive2",7,drive2},
+                        {"drive3",7,drive3},
+                       {"disable",1,&disable},
+                        {"cluster",1,&cluster},
+                        {"nice",1,&nice}};
+
+void pf_setup( char *str, int *ints)
+
+{       generic_setup(pf_stt,7,str);
+}
+
+#endif
+
+#include "paride.h"
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR   major
+#define DEVICE_NAME "PF"
+#define DEVICE_REQUEST do_pf_request
+#define DEVICE_NR(device) MINOR(device)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+/* constants for faking geometry numbers */
+
+#define PF_FD_MAX      8192            /* use FD geometry under this size */
+#define PF_FD_HDS      2
+#define PF_FD_SPT      18
+#define PF_HD_HDS      64
+#define PF_HD_SPT      32
+
+#define PF_MAX_RETRIES  5
+#define PF_TMO          800             /* interrupt timeout in jiffies */
+#define PF_SPIN_DEL     50              /* spin delay in micro-seconds  */
+
+#define PF_SPIN         (10000/PF_SPIN_DEL)*PF_TMO  
+
+#define STAT_ERR        0x00001
+#define STAT_INDEX      0x00002
+#define STAT_ECC        0x00004
+#define STAT_DRQ        0x00008
+#define STAT_SEEK       0x00010
+#define STAT_WRERR      0x00020
+#define STAT_READY      0x00040
+#define STAT_BUSY       0x00080
+
+#define ATAPI_REQ_SENSE                0x03
+#define ATAPI_LOCK             0x1e
+#define ATAPI_DOOR             0x1b
+#define ATAPI_MODE_SENSE       0x5a
+#define ATAPI_CAPACITY         0x25
+#define ATAPI_IDENTIFY         0x12
+#define ATAPI_READ_10          0x28
+#define ATAPI_WRITE_10         0x2a
+
+int pf_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static int pf_open(struct inode *inode, struct file *file);
+static void do_pf_request(void);
+static int pf_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+
+static void pf_release (struct inode *inode, struct file *file);
+
+static int pf_detect(void);
+static void do_pf_read(void);
+static void do_pf_read_start(void);
+static void do_pf_write(void);
+static void do_pf_write_start(void);
+static void do_pf_read_drq( void );
+static void do_pf_write_done( void );
+
+static int pf_identify (int unit);
+static void pf_lock(int unit, int func);
+static void pf_eject(int unit);
+static int pf_check_media(kdev_t dev);
+
+static int pf_blocksizes[PF_UNITS];
+
+#define PF_NM           0
+#define PF_RO           1
+#define PF_RW           2
+
+#define PF_NAMELEN      8
+
+struct pf_unit {
+       struct pi_adapter pia;    /* interface to paride layer */
+       struct pi_adapter *pi;
+       int removable;            /* removable media device  ?  */
+       int media_status;         /* media present ?  WP ? */
+       int drive;                /* drive */
+       int lun;
+       int access;               /* count of active opens ... */
+       int capacity;             /* Size of this volume in sectors */
+       int present;              /* device present ? */
+       char name[PF_NAMELEN];    /* pf0, pf1, ... */
+       };
+
+struct pf_unit pf[PF_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PF pf[unit]
+#define PI PF.pi
+
+static char pf_scratch[512];            /* scratch block buffer */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pf_retries = 0;              /* i/o error retry count */
+static int pf_busy = 0;                 /* request being processed ? */
+static int pf_block;                    /* address of next requested block */
+static int pf_count;                    /* number of blocks still to do */
+static int pf_run;                     /* sectors in current cluster */
+static int pf_cmd;                     /* current command READ/WRITE */
+static int pf_unit;                    /* unit of current request */
+static int pf_mask;                    /* stopper for pseudo-int */
+static char * pf_buf;                   /* buffer for request in progress */
+
+/* kernel glue structures */
+
+static struct file_operations pf_fops = {
+        NULL,                   /* lseek - default */
+        block_read,             /* read - general block-dev read */
+        block_write,            /* write - general block-dev write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        pf_ioctl,               /* ioctl */
+        NULL,                   /* mmap */
+        pf_open,                /* open */
+        pf_release,             /* release */
+        block_fsync,            /* fsync */
+        NULL,                   /* fasync */
+        pf_check_media,         /* media change ? */
+        NULL                    /* revalidate new media */
+};
+
+void pf_init_units( void )
+
+{       int     unit, j;
+
+        pf_drive_count = 0;
+        for (unit=0;unit<PF_UNITS;unit++) {
+                PF.pi = & PF.pia;
+                PF.access = 0;
+                PF.media_status = PF_NM;
+                PF.capacity = 0;
+                PF.present = 0;
+               PF.drive = DU[D_SLV];
+               PF.lun = DU[D_LUN];
+                j = 0;
+                while ((j < PF_NAMELEN-2) && (PF.name[j]=name[j])) j++;
+                PF.name[j++] = '0' + unit;
+                PF.name[j] = 0;
+                if (DU[D_PRT]) pf_drive_count++;
+        }
+} 
+
+int pf_init (void)      /* preliminary initialisation */
+
+{       int i;
+
+       if (disable) return -1;
+
+       pf_init_units();
+
+       if (pf_detect()) return -1;
+       pf_busy = 0;
+
+        if (register_blkdev(MAJOR_NR,name,&pf_fops)) {
+                printk("pf_init: unable to get major number %d\n",
+                        major);
+                return -1;
+        }
+        blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+        read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
+        
+       for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
+       blksize_size[MAJOR_NR] = pf_blocksizes;
+
+        return 0;
+}
+
+static int pf_open (struct inode *inode, struct file *file)
+
+{       int    unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PF_UNITS) || (!PF.present)) return -ENODEV;
+
+        MOD_INC_USE_COUNT;
+
+       pf_identify(unit);
+
+       if (PF.media_status == PF_NM) {
+               MOD_DEC_USE_COUNT;
+               return -ENODEV;
+               }
+
+       if ((PF.media_status == PF_RO) && (file ->f_mode & 2)) {
+               MOD_DEC_USE_COUNT;
+               return -EROFS;
+               }
+
+        PF.access++;
+        if (PF.removable) pf_lock(unit,1);
+
+        return 0;
+}
+
+static int pf_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg)
+
+{       int err, unit;
+       struct hd_geometry *geo = (struct hd_geometry *) arg;
+
+        if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+        unit = DEVICE_NR(inode->i_rdev);
+        if (unit >= PF_UNITS) return -EINVAL;
+        if (!PF.present) return -ENODEV;
+
+        switch (cmd) {
+           case CDROMEJECT: 
+               if (PF.access == 1) {
+                       pf_eject(unit);
+                       return 0;
+                       }
+           case HDIO_GETGEO:
+                if (!geo) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+                if (err) return err;
+                if (PF.capacity < PF_FD_MAX) {
+                    put_user(PF.capacity/(PF_FD_HDS*PF_FD_SPT),
+                                (short *) &geo->cylinders);
+                    put_user(PF_FD_HDS, (char *) &geo->heads);
+                    put_user(PF_FD_SPT, (char *) &geo->sectors);
+                } else {
+                    put_user(PF.capacity/(PF_HD_HDS*PF_HD_SPT), 
+                               (short *) &geo->cylinders);
+                    put_user(PF_HD_HDS, (char *) &geo->heads);
+                    put_user(PF_HD_SPT, (char *) &geo->sectors);
+                }
+                put_user(0,(long *)&geo->start);
+                return 0;
+            case BLKRASET:
+                if(!suser()) return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                if(arg > 0xff) return -EINVAL;
+                read_ahead[MAJOR(inode->i_rdev)] = arg;
+                return 0;
+            case BLKRAGET:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+                return (0);
+            case BLKGETSIZE:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(PF.capacity,(long *) arg);
+                return (0);
+            case BLKFLSBUF:
+                if(!suser())  return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                fsync_dev(inode->i_rdev);
+                invalidate_buffers(inode->i_rdev);
+                return 0;
+            RO_IOCTLS(inode->i_rdev,arg);
+            default:
+                return -EINVAL;
+        }
+}
+
+
+static void pf_release (struct inode *inode, struct file *file)
+
+{       kdev_t devp;
+       int     unit;
+
+        devp = inode->i_rdev;
+        unit = DEVICE_NR(devp);
+
+        if ((unit >= PF_UNITS) || (PF.access <= 0)) 
+                return;
+
+       PF.access--;
+
+       if (!PF.access) {
+                fsync_dev(devp);
+
+               invalidate_inodes(devp);
+
+                invalidate_buffers(devp);
+               if (PF.removable) pf_lock(unit,0);
+        }
+
+        MOD_DEC_USE_COUNT;
+
+}
+
+static int pf_check_media( kdev_t dev)
+
+{       return 1;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err;
+
+        err = pf_init();
+
+        return err;
+}
+
+void    cleanup_module(void)
+
+{       int unit;
+
+        unregister_blkdev(MAJOR_NR,name);
+
+       for (unit=0;unit<PF_UNITS;unit++)
+         if (PF.present) pi_release(PI);
+}
+
+#endif
+
+#define        WR(c,r,v)       pi_write_regr(PI,c,r,v)
+#define        RR(c,r)         (pi_read_regr(PI,c,r))
+
+#define LUN             (0x20*PF.lun)
+#define DRIVE           (0xa0+0x10*PF.drive)
+
+static int pf_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{       int j, r, e, s, p;
+
+        j = 0;
+        while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PF_SPIN))
+                udelay(PF_SPIN_DEL);
+
+        if ((r&(STAT_ERR&stop))||(j>=PF_SPIN)) {
+           s = RR(0,7);
+           e = RR(0,1);
+           p = RR(0,2);
+           if (j >= PF_SPIN) e |= 0x100;
+           if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+                           " loop=%d phase=%d\n",
+                            PF.name,fun,msg,r,s,e,j,p);
+           return (e<<8)+s;
+        }
+        return 0;
+}
+
+static int pf_command( int unit, char * cmd, int dlen, char * fun )
+
+{       pi_connect(PI);
+
+        WR(0,6,DRIVE);
+
+        if (pf_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pf_wait(unit,STAT_BUSY,STAT_DRQ,fun,"command DRQ")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        if (RR(0,2) != 1) {
+           printk("%s: %s: command phase error\n",PF.name,fun);
+           pi_disconnect(PI);
+           return -1;
+        }
+
+        pi_write_block(PI,cmd,12);
+
+        return 0;
+}
+
+static int pf_completion( int unit, char * buf, char * fun )
+
+{       int r, s, n;
+
+        r = pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+                       fun,"completion");
+
+        if ((RR(0,2)&2) && (RR(0,7)&STAT_DRQ)) { 
+                n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc);
+                pi_read_block(PI,buf,n);
+        }
+
+        s = pf_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+        pi_disconnect(PI); 
+
+        return (r?r:s);
+}
+
+static void pf_req_sense( int unit, int quiet )
+
+{       char    rs_cmd[12] = { ATAPI_REQ_SENSE,LUN,0,0,16,0,0,0,0,0,0,0 };
+        char    buf[16];
+        int     r;
+
+        r = pf_command(unit,rs_cmd,16,"Request sense");
+        udelay(1000);
+        if (!r) pf_completion(unit,buf,"Request sense");
+
+        if ((!r)&&(!quiet)) 
+                printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+                       PF.name,buf[2]&0xf,buf[12],buf[13]);
+}
+
+static int pf_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{       int r;
+
+        r = pf_command(unit,cmd,dlen,fun);
+        udelay(1000);
+        if (!r) r = pf_completion(unit,buf,fun);
+        if (r) pf_req_sense(unit,!fun);
+        
+        return r;
+}
+
+#define DBMSG(msg)      NULL
+
+static void pf_lock(int unit, int func)
+
+{      char    lo_cmd[12] = { ATAPI_LOCK,LUN,0,0,func,0,0,0,0,0,0,0 };
+
+        pf_atapi(unit,lo_cmd,0,pf_scratch,func?"unlock":"lock");
+}
+
+
+static void pf_eject( int unit )
+
+{      char    ej_cmd[12] = { ATAPI_DOOR,LUN,0,0,2,0,0,0,0,0,0,0 };
+
+       pf_lock(unit,0);
+       pf_atapi(unit,ej_cmd,0,pf_scratch,"eject");
+}
+
+#define PF_RESET_TMO   30              /* in tenths of a second */
+
+static void pf_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+
+static int pf_reset( int unit )
+
+/* the ATAPI standard actually specifies the contents of all 7 registers
+   after a reset, but the specification is ambiguous concerning the last
+   two bytes, and different drives interpret the standard differently.
+*/
+
+{      int     i, k, flg;
+       int     expect[5] = {1,1,1,0x14,0xeb};
+
+       pi_connect(PI);
+       WR(0,6,DRIVE);
+       WR(0,7,8);
+
+       pf_sleep(2);
+
+        k = 0;
+        while ((k++ < PF_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+                pf_sleep(10);
+
+       flg = 1;
+       for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+       if (verbose) {
+               printk("%s: Reset (%d) signature = ",PF.name,k);
+               for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+               if (!flg) printk(" (incorrect)");
+               printk("\n");
+       }
+       
+       pi_disconnect(PI);
+       return flg-1;   
+}
+
+static void pf_mode_sense( int unit )
+
+{       char    ms_cmd[12] = { ATAPI_MODE_SENSE,LUN,0,0,0,0,0,0,8,0,0,0};
+       char    buf[8];
+
+        pf_atapi(unit,ms_cmd,8,buf,DBMSG("mode sense"));
+       PF.media_status = PF_RW;
+       if (buf[3] & 0x80) PF.media_status = PF_RO;
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{      int     j,k,l;
+
+       j=0; l=0;
+       for (k=0;k<len;k++) 
+          if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+               l=targ[j++]=buf[k+offs];
+       if (l==0x20) j--; targ[j]=0;
+}
+
+static int xl( char *buf, int offs )
+
+{      int     v,k;
+
+       v=0; 
+       for(k=0;k<4;k++) v=v*256+(buf[k+offs]&0xff);
+       return v;
+}
+
+static void pf_get_capacity( int unit )
+
+{      char    rc_cmd[12] = { ATAPI_CAPACITY,LUN,0,0,0,0,0,0,0,0,0,0};
+       char    buf[8];
+        int    bs;
+
+       if (pf_atapi(unit,rc_cmd,8,buf,DBMSG("get capacity"))) {
+               PF.media_status = PF_NM;
+               return;
+       }
+       PF.capacity = xl(buf,0) + 1;  
+       bs = xl(buf,4);
+       if (bs != 512) {
+               PF.capacity = 0;
+               if (verbose) printk("%s: Drive %d, LUN %d,"
+                                   " unsupported block size %d\n",
+                                   PF.name,PF.drive,PF.lun,bs);
+               }
+}
+
+static int pf_identify( int unit )
+
+{      int     dt, s;
+       char    *ms[2] = {"master","slave"};
+       char    mf[10], id[18];
+       char    id_cmd[12] = { ATAPI_IDENTIFY,LUN,0,0,36,0,0,0,0,0,0,0};
+       char    buf[36];
+
+        s = pf_atapi(unit,id_cmd,36,buf,"identify");
+       if (s) return -1;
+
+       dt = buf[0] & 0x1f;
+       if ((dt != 0) && (dt != 7)) {
+               if (verbose) 
+                  printk("%s: Drive %d, LUN %d, unsupported type %d\n",
+                               PF.name,PF.drive,PF.lun,dt);
+               return -1;
+               }
+
+       xs(buf,mf,8,8);
+       xs(buf,id,16,16);
+
+       PF.removable = (buf[1] & 0x80);
+
+       pf_mode_sense(unit);
+       pf_mode_sense(unit);
+       pf_mode_sense(unit);
+
+       pf_get_capacity(unit);
+
+        printk("%s: %s %s, %s LUN %d, type %d",
+               PF.name,mf,id,ms[PF.drive],PF.lun,dt);
+        if (PF.removable) printk(", removable");
+        if (PF.media_status == PF_NM) 
+                printk(", no media\n");
+        else {  if (PF.media_status == PF_RO) printk(", RO");
+                printk(", %d blocks\n",PF.capacity);
+        }
+
+       return 0;
+}
+
+static int pf_probe( int unit )
+
+/*     returns  0, with id set if drive is detected
+               -1, if drive detection failed
+*/
+
+{      if (PF.drive == -1) {
+          for (PF.drive=0;PF.drive<=1;PF.drive++)
+               if (!pf_reset(unit)) {
+                  if (PF.lun != -1) return pf_identify(unit);
+                  else for (PF.lun=0;PF.lun<8;PF.lun++) 
+                           if (!pf_identify(unit)) return 0;
+               }
+       } else {
+          if (pf_reset(unit)) return -1;
+          if (PF.lun != -1) return pf_identify(unit);
+          for (PF.lun=0;PF.lun<8;PF.lun++) 
+             if (!pf_identify(unit)) return 0;
+       }
+        return -1; 
+}
+
+static int pf_detect( void )
+
+{      int     k, unit;
+
+       printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+               name,name,PF_VERSION,major,cluster,nice);
+
+       k = 0;
+       if (pf_drive_count == 0) {
+           unit = 0;
+           if (pi_init(PI,1,-1,-1,-1,-1,-1,pf_scratch,
+                        PI_PF,verbose,PF.name)) {
+               if (!pf_probe(unit)) {
+                       PF.present = 1;
+                       k++;
+               } else pi_release(PI);
+           }
+
+       } else for (unit=0;unit<PF_UNITS;unit++) if (DU[D_PRT])
+           if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+                       DU[D_PRO],DU[D_DLY],pf_scratch,PI_PF,verbose,
+                       PF.name)) { 
+                if (!pf_probe(unit)) {
+                        PF.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+       if (k) return 0;
+
+       printk("%s: No ATAPI disk detected\n",name);
+       return -1;
+}
+
+/* The i/o request engine */
+
+static int pf_start( int unit, int cmd, int b, int c )
+
+{      int     i;
+       char    io_cmd[12] = {cmd,LUN,0,0,0,0,0,0,0,0,0,0};
+
+       for(i=0;i<4;i++) { 
+          io_cmd[5-i] = b & 0xff;
+          b = b >> 8;
+       }
+       
+       io_cmd[8] = c & 0xff;
+       io_cmd[7] = (c >> 8) & 0xff;
+
+       i = pf_command(unit,io_cmd,c*512,"start i/o");
+
+        udelay(1000);
+
+       return i;       
+}
+
+static int pf_ready( void )
+
+{      int     unit = pf_unit;
+
+       return (((RR(1,6)&(STAT_BUSY|pf_mask)) == pf_mask));
+}
+
+static void do_pf_request (void)
+
+{       struct buffer_head * bh;
+       struct request * req;
+       int unit;
+
+        if (pf_busy) return;
+repeat:
+        if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+        INIT_REQUEST;
+
+        pf_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+        pf_block = CURRENT->sector;
+        pf_count = CURRENT->nr_sectors;
+
+       bh = CURRENT->bh;
+       req = CURRENT;
+       if (bh->b_reqnext)
+               printk("%s: OUCH: b_reqnext != NULL\n",PF.name);
+
+        if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) {
+                end_request(0);
+                goto repeat;
+        }
+
+       pf_cmd = CURRENT->cmd;
+       pf_run = pf_count;
+        while ((pf_run <= cluster) &&
+              (req = req->next) && 
+              (pf_block+pf_run == req->sector) &&
+              (pf_cmd == req->cmd) &&
+              (pf_unit == DEVICE_NR(req->rq_dev)))
+                       pf_run += req->nr_sectors;
+
+        pf_buf = CURRENT->buffer;
+        pf_retries = 0;
+
+       pf_busy = 1;
+        if (pf_cmd == READ) pi_do_claimed(PI,do_pf_read);
+        else if (pf_cmd == WRITE) pi_do_claimed(PI,do_pf_write);
+        else {  pf_busy = 0;
+               end_request(0);
+                goto repeat;
+        }
+}
+
+static void pf_next_buf( int unit )
+
+{      long    saved_flags;
+
+       spin_lock_irqsave(&io_request_lock,saved_flags);
+       end_request(1);
+       if (!pf_run) { spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                      return; 
+       }
+       
+/* paranoia */
+
+       if ((!CURRENT) ||
+           (CURRENT->cmd != pf_cmd) ||
+           (DEVICE_NR(CURRENT->rq_dev) != pf_unit) ||
+           (CURRENT->rq_status == RQ_INACTIVE) ||
+           (CURRENT->sector != pf_block)) 
+               printk("%s: OUCH: request list changed unexpectedly\n",
+                       PF.name);
+
+       pf_count = CURRENT->nr_sectors;
+       pf_buf = CURRENT->buffer;
+       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+static void do_pf_read( void )
+
+/* detach from the calling context - in case the spinlock is held */
+
+{      ps_set_intr(do_pf_read_start,0,0,nice);
+}
+
+static void do_pf_read_start( void )
+
+{       int    unit = pf_unit;
+       long    saved_flags;
+
+       pf_busy = 1;
+
+       if (pf_start(unit,ATAPI_READ_10,pf_block,pf_run)) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_read_start);
+                       return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+       pf_mask = STAT_DRQ;
+        ps_set_intr(do_pf_read_drq,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_read_drq( void )
+
+{       int    unit = pf_unit;
+       long    saved_flags;
+       
+       while (1) {
+            if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+                       "read block","completion") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                       pf_req_sense(unit,0);
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_read_start);
+                        return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+            }
+            pi_read_block(PI,pf_buf,512);
+            pf_count--; pf_run--;
+            pf_buf += 512;
+           pf_block++;
+           if (!pf_run) break;
+           if (!pf_count) pf_next_buf(unit);
+        }
+        pi_disconnect(PI);
+       spin_lock_irqsave(&io_request_lock,saved_flags); 
+        end_request(1);
+        pf_busy = 0;
+        do_pf_request();
+       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+static void do_pf_write( void )
+
+{      ps_set_intr(do_pf_write_start,0,0,nice);
+}
+
+static void do_pf_write_start( void )
+
+{       int    unit = pf_unit;
+       long    saved_flags;
+
+       pf_busy = 1;
+
+       if (pf_start(unit,ATAPI_WRITE_10,pf_block,pf_run)) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_write_start);
+                       return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+
+       while (1) {
+            if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+                       "write block","data wait") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_write_start);
+                        return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+            }
+            pi_write_block(PI,pf_buf,512);
+           pf_count--; pf_run--;
+           pf_buf += 512;
+           pf_block++;
+           if (!pf_run) break;
+           if (!pf_count) pf_next_buf(unit);
+       }
+       pf_mask = 0;
+        ps_set_intr(do_pf_write_done,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_write_done( void )
+
+{       int    unit = pf_unit;
+       long    saved_flags;
+
+        if (pf_wait(unit,STAT_BUSY,0,"write block","done") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                       pi_do_claimed(PI,do_pf_write_start);
+                        return;
+                }
+               spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+               spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+        pi_disconnect(PI);
+       spin_lock_irqsave(&io_request_lock,saved_flags);
+        end_request(1);
+        pf_busy = 0;
+        do_pf_request();
+       spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+/* end of pf.c */
+
diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c
new file mode 100644 (file)
index 0000000..53cecb0
--- /dev/null
@@ -0,0 +1,682 @@
+/* 
+        pg.c    (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+
+       Special 2.0.35 version
+
+
+       The pg driver provides a simple character device interface for
+        sending ATAPI commands to a device.  With the exception of the
+       ATAPI reset operation, all operations are performed by a pair
+        of read and write operations to the appropriate /dev/pgN device.
+       A write operation delivers a command and any outbound data in
+        a single buffer.  Normally, the write will succeed unless the
+        device is offline or malfunctioning, or there is already another
+       command pending.  If the write succeeds, it should be followed
+        immediately by a read operation, to obtain any returned data and
+        status information.  A read will fail if there is no operation
+        in progress.
+
+       As a special case, the device can be reset with a write operation,
+        and in this case, no following read is expected, or permitted.
+
+       There are no ioctl() operations.  Any single operation
+       may transfer at most PG_MAX_DATA bytes.  Note that the driver must
+        copy the data through an internal buffer.  In keeping with all
+       current ATAPI devices, command packets are assumed to be exactly
+       12 bytes in length.
+
+       To permit future changes to this interface, the headers in the
+       read and write buffers contain a single character "magic" flag.
+        Currently this flag must be the character "P".
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI device, but if their individual parameters are
+        specified, the driver can handle up to 4 devices.
+
+       To use this device, you must have the following device 
+       special files defined:
+
+               /dev/pg0 b 97 0
+               /dev/pg1 b 97 1
+               /dev/pg2 b 97 2
+               /dev/pg3 b 97 3
+
+       (You'll need to change the 97 to something else if you use
+       the 'major' parameter to install the driver on a different
+        major number.)
+
+        The behaviour of the pg driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-6 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+                <slv>   ATAPI devices can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+
+           major       You may use this parameter to overide the
+                       default major number (97) that this driver
+                       will use.  Be sure to change the device
+                       name as well.
+
+           name        This parameter is a character string that
+                       contains the name the kernel will use for this
+                       device (in /proc output, for instance).
+                       (default "pg").
+
+            verbose     This parameter controls the amount of logging
+                        that is done by the driver.  Set it to 0 for 
+                       quiet operation, to 1 to enable progress
+                       messages while the driver probes for devices,
+                       or to 2 for full debug logging.  (default 0)
+
+        If this driver is built into the kernel, you can use 
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pg.drive0
+            pg.drive1
+            pg.drive2
+            pg.drive3
+
+        In addition, you can use the parameter pg.disable to disable
+        the driver entirely.
+
+*/
+
+/* Changes:
+
+       1.01    GRG 1998.06.16  Bug fixes
+*/
+
+#define PG_VERSION      "1.01s"
+#define PG_MAJOR       97
+#define PG_NAME                "pg"
+#define PG_UNITS       4
+
+#ifndef PI_PG
+#define PI_PG  4
+#endif
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is 0
+   by default.
+
+*/
+
+static int     verbose = 0;
+static int     major = PG_MAJOR;
+static char    *name = PG_NAME;
+static int      disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pg_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_DLY   5
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/mtio.h>
+#include <linux/pg.h>
+
+#include <asm/segment.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pg_stt[5] = {{"drive0",6,drive0},
+                        {"drive1",6,drive1},
+                        {"drive2",6,drive2},
+                        {"drive3",6,drive3},
+                       {"disable",1,&disable}};
+
+void pg_setup( char *str, int *ints)
+
+{       generic_setup(pg_stt,5,str);
+}
+
+#endif
+
+#include "paride.h"
+
+#define PG_SPIN_DEL     50              /* spin delay in micro-seconds  */
+#define PG_SPIN         200
+#define PG_TMO         HZ
+#define PG_CMD_TMO     3*HZ
+#define PG_RESET_TMO   10*HZ
+
+#define STAT_ERR        0x01
+#define STAT_INDEX      0x02
+#define STAT_ECC        0x04
+#define STAT_DRQ        0x08
+#define STAT_SEEK       0x10
+#define STAT_WRERR      0x20
+#define STAT_READY      0x40
+#define STAT_BUSY       0x80
+
+#define ATAPI_IDENTIFY         0x12
+
+int pg_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+
+static int pg_open(struct inode *inode, struct file *file);
+static void pg_release (struct inode *inode, struct file *file);
+static int pg_read(struct inode *inode, struct file *filp, char *buf, int count);
+static int pg_write(struct inode *inode, struct file *filp, 
+                       const char *buf, int count);
+static int pg_detect(void);
+
+static int pg_identify (int unit, int log);
+
+#define PG_NAMELEN      8
+
+struct pg_unit {
+       struct pi_adapter pia;    /* interface to paride layer */
+       struct pi_adapter *pi;
+       int busy;                 /* write done, read expected */
+       int start;                /* jiffies at command start */
+       int dlen;                 /* transfer size requested */
+       int timeout;              /* timeout requested */
+       int status;               /* last sense key */
+       int drive;                /* drive */
+       int access;               /* count of active opens ... */
+       int present;              /* device present ? */
+       char *bufptr;
+       char name[PG_NAMELEN];    /* pg0, pg1, ... */
+       };
+
+struct pg_unit pg[PG_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PG pg[unit]
+#define PI PG.pi
+
+static char pg_scratch[512];            /* scratch block buffer */
+
+/* kernel glue structures */
+
+static struct file_operations pg_fops = {
+        NULL,                   /* lseek - default */
+        pg_read,                /* read */
+        pg_write,               /* write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        NULL,                   /* ioctl */
+        NULL,                   /* mmap */
+        pg_open,                /* open */
+        pg_release,             /* release */
+        NULL,                   /* fsync */
+        NULL,                   /* fasync */
+        NULL,                   /* media change ? */
+        NULL                    /* revalidate new media */
+};
+
+void pg_init_units( void )
+
+{       int     unit, j;
+
+        pg_drive_count = 0;
+        for (unit=0;unit<PG_UNITS;unit++) {
+                PG.pi = & PG.pia;
+                PG.access = 0;
+                PG.busy = 0;
+                PG.present = 0;
+               PG.bufptr = NULL;
+               PG.drive = DU[D_SLV];
+                j = 0;
+                while ((j < PG_NAMELEN-2) && (PG.name[j]=name[j])) j++;
+                PG.name[j++] = '0' + unit;
+                PG.name[j] = 0;
+                if (DU[D_PRT]) pg_drive_count++;
+        }
+} 
+
+int pg_init (void)      /* preliminary initialisation */
+
+{       int unit;
+
+       if (disable) return -1;
+
+       pg_init_units();
+
+       if (pg_detect()) return -1;
+
+        if (register_chrdev(major,name,&pg_fops)) {
+                printk("pg_init: unable to get major number %d\n",
+                        major);
+               for (unit=0;unit<PG_UNITS;unit++)
+                 if (PG.present) pi_release(PI);
+                return -1;
+        }
+
+        return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err;
+
+        err = pg_init();
+
+        return err;
+}
+
+void    cleanup_module(void)
+
+{       int unit;
+
+        unregister_chrdev(major,name);
+
+       for (unit=0;unit<PG_UNITS;unit++)
+         if (PG.present) pi_release(PI);
+}
+
+#endif
+
+#define        WR(c,r,v)       pi_write_regr(PI,c,r,v)
+#define        RR(c,r)         (pi_read_regr(PI,c,r))
+
+#define DRIVE           (0xa0+0x10*PG.drive)
+
+static void pg_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+static int pg_wait( int unit, int go, int stop, int tmo, char * msg )
+
+{       int j, r, e, s, p;
+
+       PG.status = 0;
+
+        j = 0;
+        while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(jiffies<tmo)) {
+                if (j++ < PG_SPIN) udelay(PG_SPIN_DEL);
+               else pg_sleep(1);
+       }
+
+        if ((r&(STAT_ERR&stop))||(jiffies>=tmo)) {
+           s = RR(0,7);
+           e = RR(0,1);
+           p = RR(0,2);
+           if (verbose > 1)
+            printk("%s: %s: stat=0x%x err=0x%x phase=%d%s\n",
+                   PG.name,msg,s,e,p,(jiffies>=tmo)?" timeout":"");
+
+
+           if (jiffies>=tmo) e |= 0x100;
+          PG.status = (e >> 4) & 0xff;
+           return -1;
+        }
+        return 0;
+}
+
+static int pg_command( int unit, char * cmd, int dlen, int tmo )
+
+{       int k;
+
+       pi_connect(PI);
+
+        WR(0,6,DRIVE);
+
+        if (pg_wait(unit,STAT_BUSY|STAT_DRQ,0,tmo,"before command")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pg_wait(unit,STAT_BUSY,STAT_DRQ,tmo,"command DRQ")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        if (RR(0,2) != 1) {
+           printk("%s: command phase error\n",PG.name);
+           pi_disconnect(PI);
+           return -1;
+        }
+
+        pi_write_block(PI,cmd,12);
+
+       if (verbose > 1) {
+               printk("%s: Command sent, dlen=%d packet= ", PG.name,dlen);
+               for (k=0;k<12;k++) printk("%02x ",cmd[k]&0xff);
+               printk("\n");
+       }
+        return 0;
+}
+
+static int pg_completion( int unit, char * buf, int tmo)
+
+{       int r, d, n, p;
+
+        r = pg_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+                        tmo,"completion");
+
+        PG.dlen = 0;
+
+        while (RR(0,7)&STAT_DRQ) {
+           d = (RR(0,4)+256*RR(0,5));
+           n = ((d+3)&0xfffc);
+           p = RR(0,2)&3;
+           if (p == 0) pi_write_block(PI,buf,n);
+           if (p == 2) pi_read_block(PI,buf,n);
+           if (verbose > 1) printk("%s: %s %d bytes\n",PG.name,
+                                    p?"Read":"Write",n);
+           PG.dlen += (1-p)*d;
+           buf += d;
+           r = pg_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+                        tmo,"completion");
+        }
+
+        pi_disconnect(PI);
+
+        return r;
+}
+
+static int pg_reset( int unit )
+
+{      int     i, k, flg;
+       int     expect[5] = {1,1,1,0x14,0xeb};
+
+       pi_connect(PI);
+       WR(0,6,DRIVE);
+       WR(0,7,8);
+
+       pg_sleep(2);
+
+        k = 0;
+        while ((k++ < PG_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+                pg_sleep(1);
+
+       flg = 1;
+       for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+       if (verbose) {
+               printk("%s: Reset (%d) signature = ",PG.name,k);
+               for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+               if (!flg) printk(" (incorrect)");
+               printk("\n");
+       }
+       
+       pi_disconnect(PI);
+       return flg-1;   
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{      int     j,k,l;
+
+       j=0; l=0;
+       for (k=0;k<len;k++) 
+          if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+               l=targ[j++]=buf[k+offs];
+       if (l==0x20) j--; targ[j]=0;
+}
+
+static int pg_identify( int unit, int log )
+
+{      int     s;
+       char    *ms[2] = {"master","slave"};
+       char    mf[10], id[18];
+       char    id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0};
+       char    buf[36];
+
+        s = pg_command(unit,id_cmd,36,jiffies+PG_TMO);
+       if (s) return -1;
+       s = pg_completion(unit,buf,jiffies+PG_TMO);
+       if (s) return -1;
+
+       if (log) {
+               xs(buf,mf,8,8);
+               xs(buf,id,16,16);
+               printk("%s: %s %s, %s\n",PG.name,mf,id,ms[PG.drive]);
+       }
+
+       return 0;
+}
+
+static int pg_probe( int unit )
+
+/*     returns  0, with id set if drive is detected
+               -1, if drive detection failed
+*/
+
+{      if (PG.drive == -1) {
+          for (PG.drive=0;PG.drive<=1;PG.drive++)
+               if (!pg_reset(unit)) return pg_identify(unit,1);
+       } else {
+          if (!pg_reset(unit)) return pg_identify(unit,1);
+       }
+        return -1; 
+}
+
+static int pg_detect( void )
+
+{      int     k, unit;
+
+       printk("%s: %s version %s, major %d\n",
+               name,name,PG_VERSION,major);
+
+       k = 0;
+       if (pg_drive_count == 0) {
+           unit = 0;
+           if (pi_init(PI,1,-1,-1,-1,-1,-1,pg_scratch,
+                        PI_PG,verbose,PG.name)) {
+               if (!pg_probe(unit)) {
+                       PG.present = 1;
+                       k++;
+               } else pi_release(PI);
+           }
+
+       } else for (unit=0;unit<PG_UNITS;unit++) if (DU[D_PRT])
+           if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+                       DU[D_PRO],DU[D_DLY],pg_scratch,PI_PG,verbose,
+                       PG.name)) { 
+                if (!pg_probe(unit)) {
+                        PG.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+       if (k) return 0;
+
+       printk("%s: No ATAPI device detected\n",name);
+       return -1;
+}
+
+#define DEVICE_NR(dev) (MINOR(dev) % 128)
+
+static int pg_open (struct inode *inode, struct file *file)
+
+{       int    unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PG_UNITS) || (!PG.present)) return -ENODEV;
+
+        PG.access++;
+
+       if (PG.access > 1) {
+               PG.access--;
+               return -EBUSY;
+       }
+
+        MOD_INC_USE_COUNT;
+
+       if (PG.busy) {
+               pg_reset(unit);
+               PG.busy = 0;
+       }
+
+       pg_identify(unit,(verbose>1));
+
+
+       PG.bufptr = kmalloc(PG_MAX_DATA,GFP_KERNEL);
+       if (PG.bufptr == NULL) {
+               PG.access--;
+               MOD_DEC_USE_COUNT;
+               printk("%s: buffer allocation failed\n",PG.name);
+               return -ENOMEM;
+       }
+
+        return 0;
+}
+
+static void pg_release (struct inode *inode, struct file *file)
+{
+        int    unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PG_UNITS) || (PG.access <= 0)) 
+                return;
+
+       PG.access--;
+
+       kfree(PG.bufptr);
+       PG.bufptr = NULL;
+
+        MOD_DEC_USE_COUNT;
+
+}
+
+static int pg_write(struct inode *inode, struct file *filp, 
+               const char *buf, int count)
+
+{       int                     unit = DEVICE_NR(inode->i_rdev);
+        struct pg_write_hdr     hdr;
+        int                     hs = sizeof(hdr);
+
+       if (PG.busy) return -EBUSY;
+       if (count < hs) return -EINVAL;
+       
+       memcpy_fromfs((char *)&hdr,buf,hs);
+
+       if (hdr.magic != PG_MAGIC) return -EINVAL;
+       if (hdr.dlen > PG_MAX_DATA) return -EINVAL;
+       if ((count - hs) > PG_MAX_DATA) return -EINVAL;
+
+       if (hdr.func == PG_RESET) {
+               if (count != hs) return -EINVAL;
+               if (pg_reset(unit)) return -EIO;
+               return count;
+       }
+
+       if (hdr.func != PG_COMMAND) return -EINVAL;
+
+       PG.start = jiffies;
+       PG.timeout = hdr.timeout*HZ + HZ/2 + jiffies;
+
+       if (pg_command(unit,hdr.packet,hdr.dlen,jiffies+PG_CMD_TMO)) {
+               if (PG.status & 0x10) return -ETIME;
+               return -EIO;
+       }
+
+       PG.busy = 1;
+
+       memcpy_fromfs(PG.bufptr,buf+hs,count-hs);
+
+       return count;
+}
+
+static int pg_read(struct inode *inode, struct file *filp, char *buf, int count)
+
+{      int                     unit = DEVICE_NR(inode->i_rdev); 
+       struct pg_read_hdr      hdr;
+       int                     hs = sizeof(hdr);
+       int                     copy;
+
+       if (!PG.busy) return -EINVAL;
+       if (count < hs) return -EINVAL;
+
+       PG.busy = 0;
+
+       if (pg_completion(unit,PG.bufptr,PG.timeout))
+         if (PG.status & 0x10) return -ETIME;
+
+       hdr.magic = PG_MAGIC;
+       hdr.dlen = PG.dlen;
+       copy = 0;
+
+       if (hdr.dlen < 0) {
+               hdr.dlen = -1 * hdr.dlen;
+               copy = hdr.dlen;
+               if (copy > (count - hs)) copy = count - hs;
+       }
+
+       hdr.duration = (jiffies - PG.start + HZ/2) / HZ;
+       hdr.scsi = PG.status & 0x0f;
+
+       memcpy_tofs(buf,(char *)&hdr,hs);
+       if (copy > 0) memcpy_tofs(buf+hs,PG.bufptr,copy);
+       
+       return copy+hs;
+}
+
+/* end of pg.c */
+
diff --git a/drivers/block/paride/pseudo.h b/drivers/block/paride/pseudo.h
new file mode 100644 (file)
index 0000000..97d6fdb
--- /dev/null
@@ -0,0 +1,145 @@
+/* 
+        pseudo.h    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                                Under the terms of the GNU public license.
+
+       This is the "pseudo-interrupt" logic for parallel port drivers.
+
+        This module is #included into each driver.  It makes one
+        function available:
+
+               ps_set_intr( void (*continuation)(void),
+                            int  (*ready)(void),
+                            int timeout,
+                            int nice )
+
+       Which will arrange for ready() to be evaluated frequently and
+       when either it returns true, or timeout jiffies have passed,
+       continuation() will be invoked.
+
+       If nice is true, the test will done approximately once a
+       jiffy.  If nice is 0, the test will also be done whenever
+       the scheduler runs (by adding it to a task queue).
+
+*/
+
+/* Changes:
+
+       1.01    1998.05.03      Switched from cli()/sti() to spinlocks
+
+*/
+       
+#define PS_VERSION     "1.01"
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+
+static void ps_timer_int( unsigned long data);
+static void ps_tq_int( void *data);
+
+static int ps_use_tq = 1;
+static void (* ps_continuation)(void);
+static int (* ps_ready)(void);
+static int ps_then;
+static int ps_timeout;
+static int ps_timer_active = 0;
+static int ps_tq_active = 0;
+
+/* static spinlock_t ps_spinlock = SPIN_LOCK_UNLOCKED; */
+
+static struct timer_list ps_timer = {0,0,0,0,ps_timer_int};
+static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL};
+
+static void ps_set_intr( void (*continuation)(void), 
+                        int (*ready)(void),
+                        int timeout, int nice )
+
+{       long   flags;
+
+       spin_lock_irqsave(&ps_spinlock,flags);
+
+       ps_continuation = continuation;
+       ps_ready = ready;
+        ps_then = jiffies;
+       ps_timeout = jiffies + timeout;
+       ps_use_tq = !nice;
+
+        if (ps_use_tq && !ps_tq_active) {
+#ifdef HAVE_DISABLE_HLT
+                disable_hlt();
+#endif
+               ps_tq_active = 1;
+                queue_task(&ps_tq,&tq_scheduler);
+       }
+
+        if (!ps_timer_active) {
+               ps_timer_active = 1;
+                ps_timer.expires = jiffies;
+                add_timer(&ps_timer);
+        }
+
+       spin_unlock_irqrestore(&ps_spinlock,flags);
+}
+
+static void ps_tq_int( void *data )
+
+{       void (*con)(void);
+       long flags;
+
+       spin_lock_irqsave(&ps_spinlock,flags);
+
+        con = ps_continuation;
+
+#ifdef HAVE_DISABLE_HLT
+        enable_hlt();
+#endif
+
+        ps_tq_active = 0;
+
+        if (!con) {
+               spin_unlock_irqrestore(&ps_spinlock,flags);
+               return;
+       }
+        if (!ps_ready || ps_ready() || (jiffies >= ps_timeout)) {
+                ps_continuation = NULL;
+               spin_unlock_irqrestore(&ps_spinlock,flags);
+                con();
+                return;
+                }
+
+#ifdef HAVE_DISABLE_HLT
+        disable_hlt();
+#endif
+
+        ps_tq_active = 1;
+       queue_task(&ps_tq,&tq_scheduler);
+        spin_unlock_irqrestore(&ps_spinlock,flags);
+}
+
+static void ps_timer_int( unsigned long data)
+
+{       void (*con)(void);
+       long    flags;
+
+       spin_lock_irqsave(&ps_spinlock,flags);
+
+       con = ps_continuation;
+       ps_timer_active = 0;
+       if (!con) {
+               spin_unlock_irqrestore(&ps_spinlock,flags);
+               return;
+       }
+        if (!ps_ready || ps_ready() || (jiffies >= ps_timeout)) {
+                ps_continuation = NULL;
+               spin_unlock_irqrestore(&ps_spinlock,flags);
+                con();
+               return;
+               }
+       ps_timer_active = 1;
+        ps_timer.expires = jiffies;
+        add_timer(&ps_timer);
+        spin_unlock_irqrestore(&ps_spinlock,flags);
+}
+
+/* end of pseudo.h */
+
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
new file mode 100644 (file)
index 0000000..dd1b209
--- /dev/null
@@ -0,0 +1,940 @@
+/* 
+        pt.c    (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+       Special 2.0.35 version
+
+        This is the high-level driver for parallel port ATAPI tape
+        drives based on chips supported by the paride module.
+
+       The driver implements both rewinding and non-rewinding
+       devices, filemarks, and the rewind ioctl.  It allocates
+       a small internal "bounce buffer" for each open device, but
+        otherwise expects buffering and blocking to be done at the
+        user level.  As with most block-structured tapes, short
+       writes are padded to full tape blocks, so reading back a file
+        may return more data than was actually written.
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI tape drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+       The rewinding devices are named /dev/pt0, /dev/pt1, ...
+       while the non-rewinding devices are /dev/npt0, /dev/npt1, etc.
+
+        The behaviour of the pt driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-6 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+                <slv>   ATAPI devices can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+
+           major       You may use this parameter to overide the
+                       default major number (96) that this driver
+                       will use.  Be sure to change the device
+                       name as well.
+
+           name        This parameter is a character string that
+                       contains the name the kernel will use for this
+                       device (in /proc output, for instance).
+                       (default "pt").
+
+            verbose     This parameter controls the amount of logging
+                        that is done while the driver probes for
+                        devices.  Set it to 0 for a quiet load, or 1 to
+                        see all the progress messages.  (default 0)
+
+        If this driver is built into the kernel, you can use 
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pt.drive0
+            pt.drive1
+            pt.drive2
+            pt.drive3
+
+        In addition, you can use the parameter pt.disable to disable
+        the driver entirely.
+
+*/
+
+/*   Changes:
+
+       1.01    GRG 1998.05.06  Round up transfer size, fix ready_wait,
+                               loosed interpretation of ATAPI standard
+                               for clearing error status.
+                               Eliminate sti();
+       1.02    GRG 1998.06.16  Eliminate an Ugh.
+
+*/
+
+#define PT_VERSION      "1.02s"
+#define PT_MAJOR       96
+#define PT_NAME                "pt"
+#define PT_UNITS       4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is on
+   by default.
+
+*/
+
+static int     verbose = 0;
+static int     major = PT_MAJOR;
+static char    *name = PT_NAME;
+static int      disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pt_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_DLY   5
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/mtio.h>
+
+#include <asm/segment.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pt_stt[5] = {{"drive0",6,drive0},
+                        {"drive1",6,drive1},
+                        {"drive2",6,drive2},
+                        {"drive3",6,drive3},
+                       {"disable",1,&disable}};
+
+void pt_setup( char *str, int *ints)
+
+{       generic_setup(pt_stt,5,str);
+}
+
+#endif
+
+#include "paride.h"
+
+#define PT_MAX_RETRIES  5
+#define PT_TMO          800             /* interrupt timeout in jiffies */
+#define PT_SPIN_DEL     50              /* spin delay in micro-seconds  */
+#define PT_RESET_TMO    30             /* 3 seconds */
+#define PT_READY_TMO   60              /* 60 seconds */
+#define PT_REWIND_TMO  1200            /* 20 minutes */
+
+#define PT_SPIN         (10000/PT_SPIN_DEL)*PT_TMO  
+
+#define STAT_ERR        0x00001
+#define STAT_INDEX      0x00002
+#define STAT_ECC        0x00004
+#define STAT_DRQ        0x00008
+#define STAT_SEEK       0x00010
+#define STAT_WRERR      0x00020
+#define STAT_READY      0x00040
+#define STAT_BUSY       0x00080
+#define STAT_SENSE     0x1f000
+
+#define ATAPI_TEST_READY       0x00
+#define ATAPI_REWIND           0x01
+#define ATAPI_REQ_SENSE                0x03
+#define ATAPI_READ_6           0x08
+#define ATAPI_WRITE_6          0x0a
+#define ATAPI_WFM              0x10
+#define ATAPI_IDENTIFY         0x12
+#define ATAPI_MODE_SENSE       0x1a
+#define ATAPI_LOG_SENSE                0x4d
+
+int pt_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+
+static int pt_open(struct inode *inode, struct file *file);
+static int pt_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+static void pt_release (struct inode *inode, struct file *file);
+static int pt_read(struct inode *inode, struct file *filp, char *buf, int count);
+static int pt_write(struct inode *inode, struct file *filp, 
+               const char *buf, int count);
+static int pt_detect(void);
+
+static int pt_identify (int unit);
+
+/* bits in PT.flags */
+
+#define PT_MEDIA       1
+#define PT_WRITE_OK    2
+#define PT_REWIND      4
+#define PT_WRITING      8
+#define PT_READING     16
+#define PT_EOF        32
+
+#define PT_NAMELEN      8
+#define PT_BUFSIZE  16384
+
+struct pt_unit {
+       struct pi_adapter pia;    /* interface to paride layer */
+       struct pi_adapter *pi;
+       int flags;                /* various state flags */
+       int last_sense;           /* result of last request sense */
+       int drive;                /* drive */
+       int access;               /* count of active opens ... */
+       int bs;                   /* block size */
+       int capacity;             /* Size of tape in KB */
+       int present;              /* device present ? */
+       char *bufptr;
+       char name[PT_NAMELEN];    /* pf0, pf1, ... */
+       };
+
+struct pt_unit pt[PT_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PT pt[unit]
+#define PI PT.pi
+
+static char pt_scratch[512];            /* scratch block buffer */
+
+/* kernel glue structures */
+
+static struct file_operations pt_fops = {
+        NULL,                   /* lseek - default */
+        pt_read,                /* read */
+        pt_write,               /* write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        pt_ioctl,               /* ioctl */
+        NULL,                   /* mmap */
+        pt_open,                /* open */
+        pt_release,             /* release */
+        NULL,                   /* fsync */
+        NULL,                   /* fasync */
+        NULL,                   /* media change ? */
+        NULL                    /* revalidate new media */
+};
+
+void pt_init_units( void )
+
+{       int     unit, j;
+
+        pt_drive_count = 0;
+        for (unit=0;unit<PT_UNITS;unit++) {
+                PT.pi = & PT.pia;
+                PT.access = 0;
+                PT.flags = 0;
+               PT.last_sense = 0;
+                PT.present = 0;
+               PT.bufptr = NULL;
+               PT.drive = DU[D_SLV];
+                j = 0;
+                while ((j < PT_NAMELEN-2) && (PT.name[j]=name[j])) j++;
+                PT.name[j++] = '0' + unit;
+                PT.name[j] = 0;
+                if (DU[D_PRT]) pt_drive_count++;
+        }
+} 
+
+int pt_init (void)      /* preliminary initialisation */
+
+{       int unit;
+
+       if (disable) return -1;
+
+       pt_init_units();
+
+       if (pt_detect()) return -1;
+
+        if (register_chrdev(major,name,&pt_fops)) {
+                printk("pt_init: unable to get major number %d\n",
+                        major);
+               for (unit=0;unit<PT_UNITS;unit++)
+                 if (PT.present) pi_release(PI);
+                return -1;
+        }
+
+        return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err;
+
+        err = pt_init();
+
+        return err;
+}
+
+void    cleanup_module(void)
+
+{       int unit;
+
+        unregister_chrdev(major,name);
+
+       for (unit=0;unit<PT_UNITS;unit++)
+         if (PT.present) pi_release(PI);
+}
+
+#endif
+
+#define        WR(c,r,v)       pi_write_regr(PI,c,r,v)
+#define        RR(c,r)         (pi_read_regr(PI,c,r))
+
+#define DRIVE           (0xa0+0x10*PT.drive)
+
+static int pt_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{       int j, r, e, s, p;
+
+        j = 0;
+        while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PT_SPIN))
+                udelay(PT_SPIN_DEL);
+
+        if ((r&(STAT_ERR&stop))||(j>=PT_SPIN)) {
+           s = RR(0,7);
+           e = RR(0,1);
+           p = RR(0,2);
+           if (j >= PT_SPIN) e |= 0x100;
+           if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+                           " loop=%d phase=%d\n",
+                            PT.name,fun,msg,r,s,e,j,p);
+           return (e<<8)+s;
+        }
+        return 0;
+}
+
+static int pt_command( int unit, char * cmd, int dlen, char * fun )
+
+{       pi_connect(PI);
+
+        WR(0,6,DRIVE);
+
+        if (pt_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pt_wait(unit,STAT_BUSY,STAT_DRQ,fun,"command DRQ")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        if (RR(0,2) != 1) {
+           printk("%s: %s: command phase error\n",PT.name,fun);
+           pi_disconnect(PI);
+           return -1;
+        }
+
+        pi_write_block(PI,cmd,12);
+
+        return 0;
+}
+
+static int pt_completion( int unit, char * buf, char * fun )
+
+{       int r, s, n, p;
+
+        r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+                       fun,"completion");
+
+        if (RR(0,7)&STAT_DRQ) { 
+           n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc);
+          p = RR(0,2)&3;
+          if (p == 0) pi_write_block(PI,buf,n);
+          if (p == 2) pi_read_block(PI,buf,n);
+        }
+
+        s = pt_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+        pi_disconnect(PI); 
+
+        return (r?r:s);
+}
+
+static void pt_req_sense( int unit, int quiet )
+
+{       char    rs_cmd[12] = { ATAPI_REQ_SENSE,0,0,0,16,0,0,0,0,0,0,0 };
+        char    buf[16];
+        int     r;
+
+        r = pt_command(unit,rs_cmd,16,"Request sense");
+        udelay(1000);
+        if (!r) pt_completion(unit,buf,"Request sense");
+
+       PT.last_sense = -1;
+        if (!r) {
+           if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+                                    PT.name,buf[2]&0xf,buf[12],buf[13]);
+           PT.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
+                                        | ((buf[13]&0xff)<<16) ;
+       } 
+}
+
+static int pt_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{       int r;
+
+        r = pt_command(unit,cmd,dlen,fun);
+        udelay(1000);
+        if (!r) r = pt_completion(unit,buf,fun);
+        if (r) pt_req_sense(unit,!fun);
+        
+        return r;
+}
+
+static void pt_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+static int pt_poll_dsc( int unit, int pause, int tmo, char *msg )
+
+{      int     k, e, s;
+
+       k = 0;
+       while (k < tmo) {
+               pt_sleep(pause);
+               k++;
+               pi_connect(PI);
+               WR(0,6,DRIVE);
+               s = RR(0,7);
+               e = RR(0,1);
+               pi_disconnect(PI);
+               if (s & (STAT_ERR|STAT_SEEK)) break;
+       }
+       if ((k >= tmo) || (s & STAT_ERR)) {
+          if (k >= tmo) printk("%s: %s DSC timeout\n",PT.name,msg);
+            else printk("%s: %s stat=0x%x err=0x%x\n",PT.name,msg,s,e);
+          pt_req_sense(unit,0);
+          return 0;
+       }
+       return 1;
+}
+
+static void pt_media_access_cmd( int unit, int tmo, char *cmd, char *fun)
+
+{      if (pt_command(unit,cmd,0,fun)) {
+               pt_req_sense(unit,0);
+               return;
+       }
+       pi_disconnect(PI);
+       pt_poll_dsc(unit,100,tmo,fun);
+}
+
+static void pt_rewind( int unit )
+
+{      char    rw_cmd[12] = {ATAPI_REWIND,0,0,0,0,0,0,0,0,0,0,0};
+
+       pt_media_access_cmd(unit,PT_REWIND_TMO,rw_cmd,"rewind");
+}
+
+static void pt_write_fm( int unit )
+
+{      char    wm_cmd[12] = {ATAPI_WFM,0,0,0,1,0,0,0,0,0,0,0};
+
+        pt_media_access_cmd(unit,PT_TMO,wm_cmd,"write filemark");
+}
+
+#define DBMSG(msg)      NULL
+
+static int pt_reset( int unit )
+
+{      int     i, k, flg;
+       int     expect[5] = {1,1,1,0x14,0xeb};
+
+       pi_connect(PI);
+       WR(0,6,DRIVE);
+       WR(0,7,8);
+
+       pt_sleep(2);
+
+        k = 0;
+        while ((k++ < PT_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+                pt_sleep(10);
+
+       flg = 1;
+       for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+       if (verbose) {
+               printk("%s: Reset (%d) signature = ",PT.name,k);
+               for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+               if (!flg) printk(" (incorrect)");
+               printk("\n");
+       }
+       
+       pi_disconnect(PI);
+       return flg-1;   
+}
+
+static int pt_ready_wait( int unit, int tmo )
+
+{      char    tr_cmd[12] = {ATAPI_TEST_READY,0,0,0,0,0,0,0,0,0,0,0};
+       int     k, p;
+
+       k = 0;
+       while (k < tmo) {
+         PT.last_sense = 0;
+         pt_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready"));
+         p = PT.last_sense;
+         if (!p) return 0;
+         if (!(((p & 0xffff) == 0x0402)||((p & 0xff) == 6))) return p;
+         k++;
+          pt_sleep(100);
+       }
+       return 0x000020;        /* timeout */
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{      int     j,k,l;
+
+       j=0; l=0;
+       for (k=0;k<len;k++) 
+          if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+               l=targ[j++]=buf[k+offs];
+       if (l==0x20) j--; targ[j]=0;
+}
+
+static int xn( char *buf, int offs, int size )
+
+{      int     v,k;
+
+       v=0; 
+       for(k=0;k<size;k++) v=v*256+(buf[k+offs]&0xff);
+       return v;
+}
+
+static int pt_identify( int unit )
+
+{      int     dt, s;
+       char    *ms[2] = {"master","slave"};
+       char    mf[10], id[18];
+       char    id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0};
+        char    ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,128,0,0,0,0,0,0,0};
+       char    ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,128,0,0,0};
+       char    buf[36];
+
+        s = pt_atapi(unit,id_cmd,36,buf,"identify");
+       if (s) return -1;
+
+       dt = buf[0] & 0x1f;
+       if (dt != 1) {
+               if (verbose) 
+                  printk("%s: Drive %d, unsupported type %d\n",
+                               PT.name,PT.drive,dt);
+               return -1;
+               }
+
+       xs(buf,mf,8,8);
+       xs(buf,id,16,16);
+
+       PT.flags = 0;
+       PT.capacity = 0;
+       PT.bs = 0;
+
+       if (!pt_ready_wait(unit,PT_READY_TMO)) PT.flags |= PT_MEDIA;
+
+        if (!pt_atapi(unit,ms_cmd,36,buf,"mode sense")) {
+          if (!(buf[2] & 0x80)) PT.flags |= PT_WRITE_OK;
+         PT.bs = xn(buf,10,2);
+       }
+
+        if (!pt_atapi(unit,ls_cmd,36,buf,"log sense")) 
+               PT.capacity = xn(buf,24,4);
+
+        printk("%s: %s %s, %s",
+               PT.name,mf,id,ms[PT.drive]);
+        if (!(PT.flags & PT_MEDIA)) 
+                printk(", no media\n");
+        else {  if (!(PT.flags & PT_WRITE_OK)) printk(", RO");
+                printk(", blocksize %d, %d MB\n",
+                      PT.bs,PT.capacity/1024);
+        }
+
+       return 0;
+}
+
+static int pt_probe( int unit )
+
+/*     returns  0, with id set if drive is detected
+               -1, if drive detection failed
+*/
+
+{      if (PT.drive == -1) {
+          for (PT.drive=0;PT.drive<=1;PT.drive++)
+               if (!pt_reset(unit)) return pt_identify(unit);
+       } else {
+          if (!pt_reset(unit)) return pt_identify(unit);
+       }
+        return -1; 
+}
+
+static int pt_detect( void )
+
+{      int     k, unit;
+
+       printk("%s: %s version %s, major %d\n",
+               name,name,PT_VERSION,major);
+
+       k = 0;
+       if (pt_drive_count == 0) {
+           unit = 0;
+           if (pi_init(PI,1,-1,-1,-1,-1,-1,pt_scratch,
+                        PI_PT,verbose,PT.name)) {
+               if (!pt_probe(unit)) {
+                       PT.present = 1;
+                       k++;
+               } else pi_release(PI);
+           }
+
+       } else for (unit=0;unit<PT_UNITS;unit++) if (DU[D_PRT])
+           if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+                       DU[D_PRO],DU[D_DLY],pt_scratch,PI_PT,verbose,
+                       PT.name)) { 
+                if (!pt_probe(unit)) {
+                        PT.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+       if (k) return 0;
+
+       printk("%s: No ATAPI tape drive detected\n",name);
+       return -1;
+}
+
+#define DEVICE_NR(dev) (MINOR(dev) % 128)
+
+static int pt_open (struct inode *inode, struct file *file)
+
+{       int    unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PT_UNITS) || (!PT.present)) return -ENODEV;
+
+        PT.access++;
+
+       if (PT.access > 1) {
+               PT.access--;
+               return -EBUSY;
+       }
+
+        MOD_INC_USE_COUNT;
+
+       pt_identify(unit);
+
+       if (!PT.flags & PT_MEDIA) {
+               PT.access--;
+               MOD_DEC_USE_COUNT;
+               return -ENODEV;
+               }
+
+       if ((!PT.flags & PT_WRITE_OK) && (file ->f_mode & 2)) {
+               PT.access--;
+               MOD_DEC_USE_COUNT;
+               return -EROFS;
+               }
+
+       if (!(MINOR(inode->i_rdev) & 128))
+               PT.flags |= PT_REWIND;
+
+       PT.bufptr = kmalloc(PT_BUFSIZE,GFP_KERNEL);
+       if (PT.bufptr == NULL) {
+               PT.access--;
+               MOD_DEC_USE_COUNT;
+               printk("%s: buffer allocation failed\n",PT.name);
+               return -ENOMEM;
+       }
+
+        return 0;
+}
+
+static int pt_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg)
+{
+       int unit;
+       struct mtop mtop;
+
+        if (!inode || !inode->i_rdev)
+               return -EINVAL;
+        unit = DEVICE_NR(inode->i_rdev);
+        if (unit >= PT_UNITS)
+               return -EINVAL;
+        if (!PT.present)
+               return -ENODEV;
+
+        switch (cmd) {
+           case MTIOCTOP:      
+               memcpy_fromfs((char *)&mtop, (char *)arg, 
+                                  sizeof(struct mtop));
+
+               switch (mtop.mt_op) {
+
+                   case MTREW: 
+                       pt_rewind(unit);
+                       return 0;
+
+                   default:    
+                       printk("%s: Unimplemented mt_op %d\n",PT.name,
+                                       mtop.mt_op);
+                       return -EINVAL;
+               }
+
+            default:
+               printk("%s: Unimplemented ioctl 0x%x\n",PT.name,cmd);
+                return -EINVAL;
+
+        }
+}
+
+
+static void pt_release (struct inode *inode, struct file *file)
+{
+        int    unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PT_UNITS) || (PT.access <= 0)) 
+                return;
+
+       if (PT.flags & PT_WRITING) pt_write_fm(unit);
+
+       if (PT.flags & PT_REWIND) pt_rewind(unit);      
+
+       PT.access--;
+
+       kfree(PT.bufptr);
+       PT.bufptr = NULL;
+
+        MOD_DEC_USE_COUNT;
+
+}
+
+static int pt_read(struct inode *inode, struct file *filp, char *buf, int count)
+
+{      int     unit = DEVICE_NR(inode->i_rdev);
+       char    rd_cmd[12] = {ATAPI_READ_6,1,0,0,0,0,0,0,0,0,0,0};
+       int     k, n, r, p, s, t, b;
+
+       if (!(PT.flags & (PT_READING|PT_WRITING))) {
+           PT.flags |= PT_READING;
+           if (pt_atapi(unit,rd_cmd,0,NULL,"start read-ahead"))
+                       return -EIO;
+       } else if (PT.flags & PT_WRITING) return -EIO;
+
+       if (PT.flags & PT_EOF) return 0;
+
+       t = 0;
+
+       while (count > 0) {
+
+           if (!pt_poll_dsc(unit,1,PT_TMO,"read")) return -EIO;
+
+           n = count;
+           if (n > 32768) n = 32768;   /* max per command */
+           b = (n-1+PT.bs)/PT.bs;
+           n = b*PT.bs;                /* rounded up to even block */
+
+           rd_cmd[4] = b;
+
+           r = pt_command(unit,rd_cmd,n,"read");
+
+           udelay(1000);
+
+           if (r) {
+               pt_req_sense(unit,0);
+               return -EIO;
+           }
+
+           while (1) {
+
+               r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY,
+                                           DBMSG("read DRQ"),"");
+
+               if (r & STAT_SENSE) {
+                   pi_disconnect(PI);
+                   pt_req_sense(unit,0);
+                   return -EIO;
+               }
+
+               if (r) PT.flags |= PT_EOF; 
+
+               s = RR(0,7);
+
+               if (!(s & STAT_DRQ)) break;
+
+               n = (RR(0,4)+256*RR(0,5));
+               p = (RR(0,2)&3);
+               if (p != 2) {
+                   pi_disconnect(PI);
+                   printk("%s: Phase error on read: %d\n",PT.name,p);
+                   return -EIO;
+               }
+
+               while (n > 0) {
+                   k = n;
+                   if (k > PT_BUFSIZE) k = PT_BUFSIZE; 
+                   pi_read_block(PI,PT.bufptr,k);
+                   n -= k;
+                   b = k;
+                   if (b > count) b = count;
+                   memcpy_tofs(buf+t,PT.bufptr,b);
+                   t += b;
+                   count -= b;
+               }
+
+           }
+           pi_disconnect(PI);
+           if (PT.flags & PT_EOF) break;
+       }
+
+       return t;
+
+}
+
+static int pt_write(struct inode *inode, struct file *filp, 
+                       const char *buf, int count)
+
+{      int unit = DEVICE_NR(inode->i_rdev);
+        char    wr_cmd[12] = {ATAPI_WRITE_6,1,0,0,0,0,0,0,0,0,0,0};
+        int     k, n, r, p, s, t, b;
+
+       if (!(PT.flags & PT_WRITE_OK)) return -EROFS;
+
+        if (!(PT.flags & (PT_READING|PT_WRITING))) {
+            PT.flags |= PT_WRITING;
+            if (pt_atapi(unit,wr_cmd,0,NULL,"start buffer-available mode"))
+                        return -EIO;
+        } else if (PT.flags&PT_READING) return -EIO;
+
+       if (PT.flags & PT_EOF) return -ENOSPC;
+
+       t = 0;
+
+       while (count > 0) {
+
+           if (!pt_poll_dsc(unit,1,PT_TMO,"write")) return -EIO;
+
+            n = count;
+            if (n > 32768) n = 32768;  /* max per command */
+            b = (n-1+PT.bs)/PT.bs;
+            n = b*PT.bs;                /* rounded up to even block */
+
+            wr_cmd[4] = b;
+
+            r = pt_command(unit,wr_cmd,n,"write");
+
+            udelay(1000);
+
+            if (r) {                   /* error delivering command only */
+                pt_req_sense(unit,0);
+                return -EIO;
+            }
+
+           while (1) {
+
+                r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY,
+                                               DBMSG("write DRQ"),NULL);
+
+                if (r & STAT_SENSE) {
+                    pi_disconnect(PI);
+                    pt_req_sense(unit,0);
+                    return -EIO;
+                }
+
+                if (r) PT.flags |= PT_EOF;
+
+               s = RR(0,7);
+
+               if (!(s & STAT_DRQ)) break;
+
+                n = (RR(0,4)+256*RR(0,5));
+                p = (RR(0,2)&3);
+                if (p != 0) {
+                    pi_disconnect(PI);
+                    printk("%s: Phase error on write: %d \n",PT.name,p);
+                    return -EIO;
+                }
+
+                while (n > 0) {
+                   k = n;
+                   if (k > PT_BUFSIZE) k = PT_BUFSIZE;
+                   b = k;
+                   if (b > count) b = count;
+                   memcpy_fromfs(PT.bufptr,buf+t,b);
+                    pi_write_block(PI,PT.bufptr,k);
+                   t += b;
+                   count -= b;
+                   n -= k;
+                }
+
+           }
+           pi_disconnect(PI);
+           if (PT.flags & PT_EOF) break;
+       }
+
+       return t;
+}
+
+/* end of pt.c */
+
diff --git a/drivers/block/paride/setup.h b/drivers/block/paride/setup.h
new file mode 100644 (file)
index 0000000..9319241
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+       setup.h    (c) 1997-8   Grant R. Guenther <grant@torque.net>
+                               Under the terms of the GNU public license.
+
+        This is a table driven setup function for kernel modules
+        using the module.variable=val,... command line notation.
+
+*/
+
+/* Changes:
+
+       1.01    GRG 1998.05.05  Allow negative and defaulted values
+
+*/
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+struct setup_tab_t {
+
+       char    *tag;   /* variable name */
+       int     size;   /* number of elements in array */
+       int     *iv;    /* pointer to variable */
+};
+
+typedef struct setup_tab_t STT;
+
+/*  t    is a table that describes the variables that can be set
+         by gen_setup
+    n    is the number of entries in the table
+    ss   is a string of the form:
+
+               <tag>=[<val>,...]<val>
+*/
+
+static void generic_setup( STT t[], int n, char *ss )
+
+{      int     j,k, sgn;
+
+       k = 0;
+       for (j=0;j<n;j++) {
+               k = strlen(t[j].tag);
+               if (strncmp(ss,t[j].tag,k) == 0) break;
+       }
+       if (j == n) return;
+
+       if (ss[k] == 0) {
+               t[j].iv[0] = 1;
+               return;
+       }
+
+       if (ss[k] != '=') return;
+       ss += (k+1);
+
+       k = 0;
+       while (ss && (k < t[j].size)) {
+               if (!*ss) break;
+               sgn = 1;
+               if (*ss == '-') { ss++; sgn = -1; }
+               if (!*ss) break;
+               if (isdigit(*ss))
+                 t[j].iv[k] = sgn * simple_strtoul(ss,NULL,0);
+               k++; 
+               if ((ss = strchr(ss,',')) != NULL) ss++;
+       }
+}
+
+/* end of setup.h */
+
diff --git a/drivers/block/paride/spinlock.h b/drivers/block/paride/spinlock.h
new file mode 100644 (file)
index 0000000..2e2bf17
--- /dev/null
@@ -0,0 +1,6 @@
+/* spinlock.h -- dummy version for PARIDE-2.0.34 */
+
+#define spin_lock_irqsave(a,b)                 { save_flags(b); cli(); }
+#define spin_unlock_irqrestore(a,b)    restore_flags(b);
+
+
index 9981144f16229e82f51edcdbbb313ee3bd5a12bb..fdf700bd3848cfcdced777f800570f6b1b3f7e17 100644 (file)
@@ -243,6 +243,7 @@ static int raid0_status (char *page, int minor, struct md_dev *mddev)
                 data->strip_zone[j].size);
   }
 #endif
+  sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(mddev)));
   return sz;
 }
 
@@ -251,11 +252,14 @@ static struct md_personality raid0_personality=
 {
   "raid0",
   raid0_map,
+  NULL,                                /* no special make_request */
+  NULL,                                /* no special end_request */
   raid0_run,
   raid0_stop,
   raid0_status,
   NULL,                                /* no ioctls */
-  0
+  0,
+  NULL,                                /* no error_handler */
 };
 
 
diff --git a/drivers/block/raid1.c b/drivers/block/raid1.c
new file mode 100644 (file)
index 0000000..6187318
--- /dev/null
@@ -0,0 +1,766 @@
+/************************************************************************
+ * raid1.c : Multiple Devices driver for Linux
+ *           Copyright (C) 1996 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-1 management functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid1.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+/*
+ * The following can be used to debug the driver
+ */
+/*#define RAID1_DEBUG*/
+#ifdef RAID1_DEBUG
+#define PRINTK(x)   do { printk x; } while (0);
+#else
+#define PRINTK(x)   do { ; } while (0);
+#endif
+
+
+static struct md_personality raid1_personality;
+static struct md_thread *raid1_thread = NULL;
+struct buffer_head *raid1_retry_list = NULL;
+
+static int __raid1_map (struct md_dev *mddev, kdev_t *rdev,
+                       unsigned long *rsector, unsigned long size)
+{
+       struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+       int i, n = raid_conf->raid_disks;
+
+       /*
+        * Later we do read balancing on the read side 
+        * now we use the first available disk.
+        */
+
+       PRINTK(("raid1_map().\n"));
+
+       for (i=0; i<n; i++) {
+               if (raid_conf->mirrors[i].operational) {
+                       *rdev = raid_conf->mirrors[i].dev;
+                       return (0);
+               }
+       }
+
+       printk (KERN_ERR "raid1_map(): huh, no more operational devices?\n");
+       return (-1);
+}
+
+static int raid1_map (struct md_dev *mddev, kdev_t *rdev,
+                     unsigned long *rsector, unsigned long size)
+{
+       return 0;
+}
+
+void raid1_reschedule_retry (struct buffer_head *bh)
+{
+       struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->private_bh);
+
+       PRINTK(("raid1_reschedule_retry().\n"));
+
+       r1_bh->next_retry = raid1_retry_list;
+       raid1_retry_list = bh;
+       md_wakeup_thread(raid1_thread);
+}
+
+/*
+ * raid1_end_buffer_io() is called when we have finished servicing a mirrored
+ * operation and are ready to return a success/failture code to the buffer
+ * cache layer.
+ */
+static inline void raid1_end_buffer_io (struct buffer_head *bh, int uptodate)
+{
+       /*
+        * kfree() can sleep and we try to keep this bh operation atomic.
+        */
+       struct raid1_bh * tmp = (struct raid1_bh *) bh->private_bh;
+
+       clear_bit (BH_MD, &bh->b_state);
+       bh->private_bh = NULL;
+       bh->personality = NULL;
+       mark_buffer_uptodate(bh, uptodate);
+       unlock_buffer(bh);
+       kfree(tmp);
+}
+
+void raid1_end_request (struct buffer_head *bh, int uptodate)
+{
+       struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->private_bh);
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       PRINTK(("raid1_end_request().\n"));
+
+       /*
+        * this branch is our 'one mirror IO has finished' event handler:
+        */
+       if (!uptodate)
+               md_error (bh->b_dev, bh->b_rdev);
+       else {
+               /*
+                * Set BH_Uptodate in our master buffer_head, so that
+                * we will return a good error code for to the higher
+                * levels even if IO on some other mirrored buffer fails.
+                *
+                * The 'master' represents the complex operation to 
+                * user-side. So if something waits for IO, then it will
+                * wait for the 'master' buffer_head.
+                */
+               set_bit (BH_Uptodate, &r1_bh->state);
+       }
+
+       /*
+        * We split up the read and write side, imho they are 
+        * conceptually different.
+        */
+
+       if ( (r1_bh->cmd == READ) || (r1_bh->cmd == READA) ) {
+
+               PRINTK(("raid1_end_request(), read branch.\n"));
+
+               /*
+                * we have only one buffer_head on the read side
+                */
+               if (uptodate) {
+                       PRINTK(("raid1_end_request(), read branch, uptodate.\n"));
+                       raid1_end_buffer_io (bh, uptodate);
+                       restore_flags(flags);
+                       return;
+               }
+               /*
+                * oops, read error:
+                */
+               printk(KERN_ERR "raid1: %s: rescheduling block %lu\n", 
+                                kdevname(bh->b_dev), bh->b_blocknr);
+               raid1_reschedule_retry (bh);
+               restore_flags(flags);
+               return;
+       }
+
+       /*
+        * WRITE or WRITEA.
+        */
+       PRINTK(("raid1_end_request(), write branch.\n"));
+
+       /*
+        * lets see if all mirrored write operations have finished 
+        * already [we have irqs off, so we can decrease]:
+        */
+
+       if (!--r1_bh->remaining) {
+               struct md_dev *mddev = r1_bh->mddev;
+               struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+               int i, n = raid_conf->raid_disks;
+
+               PRINTK(("raid1_end_request(), remaining == 0.\n"));
+
+               /*
+                * kfree() can sleep? really? if yes then we are
+                * doomed here ...
+                */
+               for ( i=0; i<n; i++) {
+                       if (r1_bh->mirror_bh[i]) kfree(r1_bh->mirror_bh[i]);
+               }
+
+               /*
+                * the 'master' bh is the one that is used in page IO,
+                * perhaps someone is waiting on it. Lets erase all
+                * signs of mirroring, and lets finish the bh operation:
+                *
+                * In particular, the "uptodate" value which we return
+                * to the higher level represents the entire mirror set.
+                *
+                * yes, and this is why i want to use the 'master' bh as
+                * a 'representative'. Thats why i think it's not clean to
+                * use the master bh for real IO. We mix concepts, which
+                * isnt too good.
+                *
+                * a buffer_head is basically a user-side file buffer.
+                * Normally it has direct relationship with the physical
+                * device, but as in this case, we have an abstract mapping
+                * between the file buffer and the physical layout. So i've
+                * reverted all changes that do this mixing.
+                *
+                * we 'waste' about 76 bytes for the one more buffer_head,
+                * but note that we will do the mirror bh allocation at once
+                * in the future, so this isnt really a valid point, i think.
+                *
+                * Also i dont like the current way of mixing the user-side buffer
+                * concept with the 'real' physical layout like raid0.c does
+                * now: it increases the size of buffer_head even for nonstriped
+                * devices, etc.
+                *
+                * IMHO, in the future, we should have a lightweight buffer_head
+                * structure, which holds almost no physical device information.
+                
+                * Abstract relationship between buffers:
+                * ===================================== 
+                * 
+                *           [user] 
+                *              |
+                *              |
+                *    ['master' buffer_head] + [private_buffer_head]
+                *                                      |
+                *                                      |
+                *                                      |
+                *                        [additional 'sub'-buffer_heads]
+                *                           |          |           | 
+                *                         [dev1]     [dev2]      [dev3]
+                * 
+                * In this scheme it's not clean to use the 'master' as one of
+                * the 'sub' buffer_heads. If you think about it, currently we can 
+                * do this only because raid0 introduced it's own private_buffer_head
+                * structure in buffer_head: rdev,rsector. And raid0 has a 1:1
+                * relationship to the physical device. But this is really just a
+                * special case. Once we have our megafast bh pools running, we could
+                * clean up raid0.c too :))
+                *
+                * Not that it isnt clean, it is lethal if in the future we insert our 
+                * sub buffer_heads into the global block cache. The master request
+                * should be an IO operation label for the complex operation, nothing
+                * more.
+                *
+                * So we have almost no performance arguments, and alot of cleanness
+                * arguments.
+                *
+                * Comments? Gonna change it back to your way again if you can convince
+                * me :)) --mingo
+                *
+                */
+               raid1_end_buffer_io ( r1_bh->master_bh, 
+                               test_bit (BH_Uptodate, &r1_bh->state));
+       }
+       else PRINTK(("raid1_end_request(), remaining == %u.\n", r1_bh->remaining));
+       restore_flags(flags);
+}
+
+/* This routine checks if the undelying device is an md device and in that
+ * case it maps the blocks before putting the request on the queue
+ */
+static inline void
+map_and_make_request (int rw, struct buffer_head *bh)
+{
+       if (MAJOR (bh->b_rdev) == MD_MAJOR){
+               md_map (MINOR (bh->b_rdev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9);
+       }
+       make_request (MAJOR (bh->b_rdev), rw, bh);
+}
+       
+static int
+raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh)
+{
+
+       struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+       struct buffer_head *mirror_bh[MD_SB_DISKS];
+       struct raid1_bh * r1_bh;
+       int n = raid_conf->raid_disks, i, sum_bhs = 0, switch_disks = 0, sectors;
+       struct mirror_info *mirror;
+
+       PRINTK(("raid1_make_request().\n"));
+
+/*
+ * We put allocations at the beginning, to avoid sleeping while doing
+ * atomic operations of buffer heads. This might or might not make much
+ * difference, but lets rather be careful.
+ *
+ * but this has two side effects (probably non harmless):
+ *
+ *     1.      The buffer will not be locked while we sleep.
+ *     2.      The rest of the kernel will see BH_Req without
+ *             BH_Lock.
+ */
+       while (!( /* FIXME: now we are rather fault tolerant than nice */
+       r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL)
+       ) )
+               printk ("raid1_make_request(#1): out of memory\n");
+       memset (r1_bh, 0, sizeof (struct raid1_bh));
+/*
+ * make_request() can abort the operation when READA or WRITEA are being
+ * used and no empty request is available.
+ *
+ * Currently, just replace the command with READ/WRITE.
+ */
+       if (rw == READA) rw = READ;
+       if (rw == WRITEA) rw = WRITE;
+
+       if (rw == WRITE || rw == WRITEA)
+               mark_buffer_clean(bh);          /* Too early ? */
+
+/*
+ * i think the read and write branch should be separated completely, since we want
+ * to do read balancing on the read side for example. Comments? :) --mingo
+ */
+
+       r1_bh->master_bh=bh;
+       r1_bh->mddev=mddev;
+       r1_bh->cmd = rw;
+
+       set_bit (BH_MD, &bh->b_state);
+       bh->personality  = &raid1_personality;
+       bh->private_bh   = (void*)(r1_bh);
+
+       if (rw==READ || rw==READA) {
+               int last_used = raid_conf->last_used;
+               PRINTK(("raid1_make_request(), read branch.\n"));
+               mirror = raid_conf->mirrors + last_used;
+               bh->b_rdev = mirror->dev;
+               sectors = bh->b_size >> 9;
+               if (bh->b_blocknr * sectors == raid_conf->next_sect) {
+                       raid_conf->sect_count += sectors;
+                       if (raid_conf->sect_count >= mirror->sect_limit)
+                               switch_disks = 1;
+               } else
+                       switch_disks = 1;
+               raid_conf->next_sect = (bh->b_blocknr + 1) * sectors;
+               if (switch_disks) {
+                       PRINTK(("read-balancing: switching %d -> %d (%d sectors)\n", last_used, mirror->next, raid_conf->sect_count));
+                       raid_conf->sect_count = 0;
+                       raid_conf->last_used = mirror->next;
+               }
+               PRINTK (("raid1 read queue: %d %d\n", MAJOR (bh->b_rdev), MINOR (bh->b_rdev)));
+
+               clear_bit (BH_Lock, &bh->b_state);
+               map_and_make_request (rw, bh);
+               return 0;
+       }
+
+       /*
+        * WRITE or WRITEA.
+        */
+/*
+ * btw, we have no more master disk. 'slave' is gone too :) [i hate that word :))]
+ *
+ * We are now using the master bh for a real IO. It seems important that:
+ *
+ * 1.  lock_buffer() will be called when we start to handle the request,
+ *     before we do anything (done by ll_rw_blk.c).
+ *
+ * 2.  It seems that Linus took great care to set mark_buffer_clean()
+ *     atomically with cli() in effect just when the buffer was placed
+ *     into the queue. To be compatible with this behavior, it would be
+ *     best to lock the buffer *first*, but mark it clean *last*, and to
+ *     do this by passing through the exact logic in ll_rw_blk.c.
+ *
+ * Note: i've reverted this #3 thing, see the big comment in this file.
+ *
+ * 3.  We are now called from within make_request(), so the real bh
+ *     will be automatically handled last when we return, so we only need
+ *     to add the rest of the buffers (but remember to include the
+ *     master bh in the remaining count).
+ */
+       PRINTK(("raid1_make_request(n=%d), write branch.\n",n));
+
+       for (i = 0; i < n; i++) {
+
+               if (!raid_conf->mirrors [i].operational) {
+                       /*
+                        * the r1_bh->mirror_bh[i] pointer remains NULL
+                        */
+                       mirror_bh[i] = NULL;
+                       continue;
+               }
+
+       /*
+        * We should use a private pool (size depending on NR_REQUEST),
+        * to avoid writes filling up the memory with bhs
+        *
+        * Such pools are much faster than kmalloc anyways (so we waste almost 
+        * nothing by not using the master bh when writing and win alot of cleanness)
+        *
+        * but for now we are cool enough. --mingo
+        *
+        * It's safe to sleep here, buffer heads cannot be used in a shared
+        * manner in the write branch. Look how we lock the buffer at the beginning
+        * of this function to grok the difference ;)
+        */
+               while (!( /* FIXME: now we are rather fault tolerant than nice */
+               mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL)
+               ) )
+                       printk ("raid1_make_request(#2): out of memory\n");
+               memset (mirror_bh[i], 0, sizeof (struct buffer_head));
+
+       /*
+        * prepare mirrored bh (fields ordered for max mem throughput):
+        */
+               mirror_bh [i]->b_blocknr    = bh->b_blocknr;
+               mirror_bh [i]->b_dev        = bh->b_dev;
+               mirror_bh [i]->b_rdev       = raid_conf->mirrors [i].dev;
+               mirror_bh [i]->b_rsector    = bh->b_rsector;
+               mirror_bh [i]->b_state      =   (1<<BH_MD)      | (1<<BH_Req) | 
+                                               (1<<BH_Touched) | (1<<BH_Dirty);
+               mirror_bh [i]->b_count      = 1;
+               mirror_bh [i]->b_size       = bh->b_size;
+               mirror_bh [i]->b_data       = bh->b_data;
+               mirror_bh [i]->b_list       = BUF_LOCKED;
+               mirror_bh [i]->personality  = &raid1_personality;
+               mirror_bh [i]->private_bh   = (void*)(r1_bh);
+
+               r1_bh->mirror_bh[i] = mirror_bh[i];
+               sum_bhs++;
+       }
+
+       r1_bh->remaining = sum_bhs;
+
+       PRINTK(("raid1_make_request(), write branch, sum_bhs=%d.\n",sum_bhs));
+
+       /*
+        * We have to be a bit careful about the semaphore above, thats why we
+        * start the requests separately. Since kmalloc() could fail, sleep and
+        * make_request() can sleep too, this is the safer solution. Imagine,
+        * end_request decreasing the semaphore before we could have set it up ...
+        * We could play tricks with the semaphore (presetting it and correcting
+        * at the end if sum_bhs is not 'n' but we have to do end_request by hand
+        * if all requests finish until we had a chance to set up the semaphore
+        * correctly ... lots of races).
+        */
+       for (i = 0; i < n; i++)
+               if (mirror_bh [i] != NULL)
+                       map_and_make_request (rw, mirror_bh [i]);
+
+       return (0);
+}
+                          
+static int raid1_status (char *page, int minor, struct md_dev *mddev)
+{
+       struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+       int sz = 0, i;
+       
+       sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks);
+       for (i = 0; i < raid_conf->raid_disks; i++)
+               sz += sprintf (page+sz, "%s", raid_conf->mirrors [i].operational ? "U" : "_");
+       sz += sprintf (page+sz, "]");
+       return sz;
+}
+
+static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index)
+{
+       int disks = raid_conf->raid_disks;
+       int j;
+
+       for (j = 0; j < disks; j++)
+               if (raid_conf->mirrors [j].next == failed_index)
+                       raid_conf->mirrors [j].next = raid_conf->mirrors [failed_index].next;
+}
+
+static int raid1_error (struct md_dev *mddev, kdev_t dev)
+{
+       struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+       struct mirror_info *mirror;
+       md_superblock_t *sb = mddev->sb;
+       int disks = raid_conf->raid_disks;
+       int i;
+
+       PRINTK(("raid1_error called\n"));
+
+       if (raid_conf->working_disks == 1) {
+               /*
+                * Uh oh, we can do nothing if this is our last disk, but
+                * first check if this is a queued request for a device
+                * which has just failed.
+                */
+               for (i = 0, mirror = raid_conf->mirrors; i < disks; i++, mirror++)
+                       if (mirror->dev == dev && !mirror->operational)
+                               return 0;
+               printk (KERN_ALERT "RAID1: only one disk left and IO error.\n");
+               return 0;
+       }
+
+       /* Mark disk as unusable */
+       for (i = 0, mirror = raid_conf->mirrors; i < disks; i++, mirror++) {
+               if (mirror->dev == dev && mirror->operational){
+                       mirror->operational = 0;
+                       raid1_fix_links (raid_conf, i);
+                       sb->disks[mirror->number].state |= (1 << MD_FAULTY_DEVICE);
+                       sb->disks[mirror->number].state &= ~(1 << MD_SYNC_DEVICE);
+                       sb->disks[mirror->number].state &= ~(1 << MD_ACTIVE_DEVICE);
+                       sb->active_disks--;
+                       sb->working_disks--;
+                       sb->failed_disks++;
+                       mddev->sb_dirty = 1;
+                       md_wakeup_thread(raid1_thread);
+                       raid_conf->working_disks--;
+                       printk (KERN_ALERT
+                               "RAID1: Disk failure on %s, disabling device."
+                               "Operation continuing on %d devices\n",
+                               kdevname (dev), raid_conf->working_disks);
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * This is a kernel thread which:
+ *
+ *     1.      Retries failed read operations on working mirrors.
+ *     2.      Updates the raid superblock when problems are encountered.
+ */
+void raid1d (void *data)
+{
+       struct buffer_head *bh;
+       kdev_t dev;
+       unsigned long flags;
+       struct raid1_bh * r1_bh;
+       struct md_dev *mddev;
+
+       PRINTK(("raid1d() active\n"));
+       save_flags(flags);
+       cli();
+       while (raid1_retry_list) {
+               bh = raid1_retry_list;
+               r1_bh = (struct raid1_bh *)(bh->private_bh);
+               raid1_retry_list = r1_bh->next_retry;
+               restore_flags(flags);
+
+               mddev = md_dev + MINOR(bh->b_dev);
+               if (mddev->sb_dirty) {
+                       mddev->sb_dirty = 0;
+                       md_update_sb(MINOR(bh->b_dev));
+               }
+               dev = bh->b_rdev;
+               __raid1_map (md_dev + MINOR(bh->b_dev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9);
+               if (bh->b_rdev == dev) {
+                       printk (KERN_ALERT 
+                                       "raid1: %s: unrecoverable I/O read error for block %lu\n",
+                                               kdevname(bh->b_dev), bh->b_blocknr);
+                       raid1_end_buffer_io (bh, 0);
+               } else {
+                       printk (KERN_ERR "raid1: %s: redirecting sector %lu to another mirror\n", 
+                                         kdevname(bh->b_dev), bh->b_blocknr);
+                       clear_bit (BH_Lock, &bh->b_state);
+                       map_and_make_request (r1_bh->cmd, bh);
+               }
+               cli();
+       }
+       restore_flags(flags);
+       
+}
+
+/*
+ * This will catch the scenario in which one of the mirrors was
+ * mounted as a normal device rather than as a part of a raid set.
+ */
+static int check_consistenty (struct md_dev *mddev)
+{
+       struct raid1_data *raid_conf = mddev->private;
+       kdev_t dev;
+       struct buffer_head *bh = NULL;
+       int i, rc = 0;
+       char *buffer = NULL;
+
+       for (i = 0; i < raid_conf->raid_disks; i++) {
+               if (!raid_conf->mirrors[i].operational)
+                       continue;
+               dev = raid_conf->mirrors[i].dev;
+               set_blocksize(dev, 4096);
+               if ((bh = bread(dev, 0, 4096)) == NULL)
+                       break;
+               if (!buffer) {
+                       buffer = (char *) __get_free_page(GFP_KERNEL);
+                       if (!buffer)
+                               break;
+                       memcpy(buffer, bh->b_data, 4096);
+               } else if (memcmp(buffer, bh->b_data, 4096)) {
+                       rc = 1;
+                       break;
+               }
+               bforget(bh);
+               fsync_dev(dev);
+               invalidate_buffers(dev);
+               bh = NULL;
+       }
+       if (buffer)
+               free_page((unsigned long) buffer);
+       if (bh) {
+               dev = bh->b_dev;
+               bforget(bh);
+               fsync_dev(dev);
+               invalidate_buffers(dev);
+       }
+       return rc;
+}
+
+static int raid1_run (int minor, struct md_dev *mddev)
+{
+       struct raid1_data *raid_conf;
+       int i, j, raid_disk;
+       md_superblock_t *sb = mddev->sb;
+       md_descriptor_t *descriptor;
+       struct real_dev *realdev;
+
+       MOD_INC_USE_COUNT;
+
+       if (sb->level != 1) {
+               printk("raid1: %s: raid level not set to mirroring (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+               MOD_DEC_USE_COUNT;
+               return -EIO;
+       }
+       /****
+        * copy the now verified devices into our private RAID1 bookkeeping area:
+        *
+        * [whatever we allocate in raid1_run(), should be freed in raid1_stop()]
+        */
+
+       while (!( /* FIXME: now we are rather fault tolerant than nice */
+       mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL)
+       ) )
+               printk ("raid1_run(): out of memory\n");
+       raid_conf = mddev->private;
+       memset(raid_conf, 0, sizeof(*raid_conf));
+
+       PRINTK(("raid1_run(%d) called.\n", minor));
+
+       for (i = 0; i < mddev->nb_dev; i++) {
+               realdev = &mddev->devices[i];
+               if (!realdev->sb) {
+                       printk(KERN_ERR "raid1: disabled mirror %s (couldn't access raid superblock)\n", kdevname(realdev->dev));
+                       continue;
+               }
+
+               /*
+                * This is important -- we are using the descriptor on
+                * the disk only to get a pointer to the descriptor on
+                * the main superblock, which might be more recent.
+                */
+               descriptor = &sb->disks[realdev->sb->descriptor.number];
+               if (descriptor->state & (1 << MD_FAULTY_DEVICE)) {
+                       printk(KERN_ERR "raid1: disabled mirror %s (errors detected)\n", kdevname(realdev->dev));
+                       continue;
+               }
+               if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) {
+                       if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) {
+                               printk(KERN_ERR "raid1: disabled mirror %s (not in sync)\n", kdevname(realdev->dev));
+                               continue;
+                       }
+                       raid_disk = descriptor->raid_disk;
+                       if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) {
+                               printk(KERN_ERR "raid1: disabled mirror %s (inconsistent descriptor)\n", kdevname(realdev->dev));
+                               continue;
+                       }
+                       if (raid_conf->mirrors[raid_disk].operational) {
+                               printk(KERN_ERR "raid1: disabled mirror %s (mirror %d already operational)\n", kdevname(realdev->dev), raid_disk);
+                               continue;
+                       }
+                       printk(KERN_INFO "raid1: device %s operational as mirror %d\n", kdevname(realdev->dev), raid_disk);
+                       raid_conf->mirrors[raid_disk].number = descriptor->number;
+                       raid_conf->mirrors[raid_disk].raid_disk = raid_disk;
+                       raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev;
+                       raid_conf->mirrors[raid_disk].operational = 1;
+                       raid_conf->mirrors[raid_disk].sect_limit = 128;
+                       raid_conf->working_disks++;
+               }
+       }
+       if (!raid_conf->working_disks) {
+               printk(KERN_ERR "raid1: no operational mirrors for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+               kfree(raid_conf);
+               mddev->private = NULL;
+               MOD_DEC_USE_COUNT;
+               return -EIO;
+       }
+
+       raid_conf->raid_disks = sb->raid_disks;
+       raid_conf->mddev = mddev;
+
+       for (j = 0; !raid_conf->mirrors[j].operational; j++);
+       raid_conf->last_used = j;
+       for (i = raid_conf->raid_disks - 1; i >= 0; i--) {
+               if (raid_conf->mirrors[i].operational) {
+                       PRINTK(("raid_conf->mirrors[%d].next == %d\n", i, j));
+                       raid_conf->mirrors[i].next = j;
+                       j = i;
+               }
+       }
+
+       if (check_consistenty(mddev)) {
+               printk(KERN_ERR "raid1: detected mirror differences -- run ckraid\n");
+               sb->state |= 1 << MD_SB_ERRORS;
+               kfree(raid_conf);
+               mddev->private = NULL;
+               MOD_DEC_USE_COUNT;
+               return -EIO;
+       }
+
+       /*
+        * Regenerate the "device is in sync with the raid set" bit for
+        * each device.
+        */
+       for (i = 0; i < sb->nr_disks ; i++) {
+               sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE);
+               for (j = 0; j < sb->raid_disks; j++) {
+                       if (!raid_conf->mirrors[j].operational)
+                               continue;
+                       if (sb->disks[i].number == raid_conf->mirrors[j].number)
+                               sb->disks[i].state |= 1 << MD_SYNC_DEVICE;
+               }
+       }
+       sb->active_disks = raid_conf->working_disks;
+
+       printk("raid1: raid set %s active with %d out of %d mirrors\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks);
+       /* Ok, everything is just fine now */
+       return (0);
+}
+
+static int raid1_stop (int minor, struct md_dev *mddev)
+{
+       struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+
+       kfree (raid_conf);
+       mddev->private = NULL;
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static struct md_personality raid1_personality=
+{
+       "raid1",
+       raid1_map,
+       raid1_make_request,
+       raid1_end_request,
+       raid1_run,
+       raid1_stop,
+       raid1_status,
+       NULL,                   /* no ioctls */
+       0,
+       raid1_error
+};
+
+int raid1_init (void)
+{
+       if ((raid1_thread = md_register_thread(raid1d, NULL)) == NULL)
+               return -EBUSY;
+       return register_md_personality (RAID1, &raid1_personality);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+       return raid1_init();
+}
+
+void cleanup_module (void)
+{
+       md_unregister_thread (raid1_thread);
+       unregister_md_personality (RAID1);
+}
+#endif
diff --git a/drivers/block/raid5.c b/drivers/block/raid5.c
new file mode 100644 (file)
index 0000000..19a73db
--- /dev/null
@@ -0,0 +1,1504 @@
+/*****************************************************************************
+ * raid5.c : Multiple Devices driver for Linux
+ *           Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-5 management functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid5.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+struct buffer_head *efind_buffer(kdev_t dev, int block, int size);
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+static struct md_personality raid5_personality;
+
+struct stripe_head {
+       struct stripe_head      *hash_next, **hash_pprev; /* hash pointers */
+       struct stripe_head      *handle_next;           /* completed during hash scan pointers */
+       struct raid5_data       *raid_conf;
+       struct buffer_head      *bh_old[MD_SB_DISKS];   /* disk image */
+       struct buffer_head      *bh_new[MD_SB_DISKS];   /* buffers of the MD device (present in buffer cache) */
+       struct buffer_head      *bh_copy[MD_SB_DISKS];  /* copy on write of bh_new (bh_new can change from under us) */
+       int                     cmd_new[MD_SB_DISKS];   /* READ/WRITE for new */
+       int                     new[MD_SB_DISKS];       /* buffer added since the last handle_stripe() */
+       unsigned long           sector;                 /* sector of this row */
+       int                     size;                   /* buffers size */
+       int                     pd_idx;                 /* parity disk index */
+       int                     nr_pending;             /* nr of pending cmds */
+       __u32                   state;                  /* state flags */
+       int                     cmd;                    /* stripe cmd */
+       int                     count;                  /* nr of waiters */
+       int                     write_method;           /* reconstruct-write / read-modify-write */
+       int                     phase;                  /* PHASE_BEGIN, ..., PHASE_COMPLETE */
+       struct wait_queue       *wait;                  /* processes waiting for this stripe */
+};
+
+/*
+ * Phase
+ */
+#define PHASE_BEGIN            0
+#define PHASE_READ_OLD         1
+#define PHASE_WRITE            2
+#define PHASE_READ             3
+#define PHASE_COMPLETE         4
+
+/*
+ * Write method
+ */
+#define METHOD_NONE            0
+#define RECONSTRUCT_WRITE      1
+#define READ_MODIFY_WRITE      2
+
+/*
+ * Stripe state
+ */
+#define STRIPE_LOCKED          0
+#define STRIPE_ERROR           1
+
+/*
+ * Stripe commands
+ */
+#define STRIPE_NONE            0
+#define        STRIPE_WRITE            1
+#define STRIPE_READ            2
+
+/*
+ * Stripe cache
+ */
+#define RAID5_STRIPE_POOL_SIZE 128
+#define HASH_PAGES             1
+#define HASH_PAGES_ORDER       0
+#define NR_HASH                        (HASH_PAGES * PAGE_SIZE / sizeof(struct stripe_head *))
+#define HASH_MASK              (NR_HASH - 1)
+#define stripe_hash(sect, size)        (stripe_hashtbl[((sect) / (size >> 9)) & HASH_MASK])
+
+int nr_stripes = 0, nr_locked_stripes = 0, nr_pending_stripes = 0;
+struct stripe_head **stripe_hashtbl;
+static struct wait_queue *raid5_wait_for_stripe = NULL;
+struct stripe_head *stripe_handle_list = NULL, *stripe_handle_tail = NULL;
+
+/*
+ * Free buffers pool
+ */  
+#define RAID5_POOL_SIZE        3000
+static int nr_free_buffers = 0, nr_used_buffers = 0, max_nr_used_buffers = 0;
+static struct buffer_head *raid5_buffer_list = NULL;
+static struct wait_queue *raid5_wait_for_bh = NULL;
+
+/*
+ * The following can be used to debug the driver
+ */
+#define RAID5_DEBUG    0
+
+#if RAID5_DEBUG
+#define PRINTK(x)   do { printk x; } while (0);
+static int nr_pending = 0, free_1024 = 0, free_4096 = 0, used_1024 = 0, used_4096 = 0;
+#else
+#define PRINTK(x)   do { ; } while (0)
+#endif
+
+static inline int stripe_locked(struct stripe_head *sh)
+{
+       return test_bit(STRIPE_LOCKED, &sh->state);
+}
+
+static inline int stripe_error(struct stripe_head *sh)
+{
+       return test_bit(STRIPE_ERROR, &sh->state);
+}
+
+/*
+ * Stripes are locked whenever new buffers can't be added to them.
+ */
+static inline void lock_stripe(struct stripe_head *sh)
+{
+       if (!set_bit(STRIPE_LOCKED, &sh->state)) {
+               PRINTK(("locking stripe %lu\n", sh->sector));
+               nr_locked_stripes++;
+       }
+}
+
+static inline void unlock_stripe(struct stripe_head *sh)
+{
+       if (clear_bit(STRIPE_LOCKED, &sh->state)) {
+               PRINTK(("unlocking stripe %lu\n", sh->sector));
+               nr_locked_stripes--;
+               wake_up(&sh->wait);
+       }
+}
+
+static inline void finish_stripe(struct stripe_head *sh)
+{
+       unlock_stripe(sh);
+       sh->cmd = STRIPE_NONE;
+       sh->phase = PHASE_COMPLETE;
+       nr_pending_stripes--;
+       wake_up(&raid5_wait_for_stripe);
+}
+
+static void unplug_devices(struct stripe_head *sh)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+       int i;
+
+       for (i = 0; i < raid_conf->raid_disks; i++)
+               unplug_device(blk_dev + MAJOR(raid_conf->disks[i].dev));
+}
+
+static void raid5d (void *data);
+
+void __wait_on_stripe(struct stripe_head *sh)
+{
+       struct wait_queue wait = { current, NULL };
+
+       PRINTK(("wait_on_stripe %lu\n", sh->sector));
+       sh->count++;
+       add_wait_queue(&sh->wait, &wait);
+repeat:
+       current->state = TASK_UNINTERRUPTIBLE;
+       if (stripe_locked(sh)) {
+               schedule();
+               goto repeat;
+       }
+       PRINTK(("wait_on_stripe %lu done\n", sh->sector));
+       remove_wait_queue(&sh->wait, &wait);
+       sh->count--;
+       current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_stripe(struct stripe_head *sh)
+{
+       if (stripe_locked(sh))
+               __wait_on_stripe(sh);
+}
+
+static inline void remove_hash(struct stripe_head *sh)
+{
+       PRINTK(("remove_hash(), stripe %lu\n", sh->sector));
+
+       if (sh->hash_pprev) {
+               if (sh->hash_next)
+                       sh->hash_next->hash_pprev = sh->hash_pprev;
+               *sh->hash_pprev = sh->hash_next;
+               sh->hash_pprev = NULL;
+               nr_stripes--;
+       }
+}
+
+static inline void insert_hash(struct stripe_head *sh)
+{
+       struct stripe_head **shp = &stripe_hash(sh->sector, sh->size);
+
+       PRINTK(("insert_hash(), stripe %lu, nr_stripes %d\n", sh->sector, nr_stripes));
+
+       if ((sh->hash_next = *shp) != NULL)
+               (*shp)->hash_pprev = &sh->hash_next;
+       *shp = sh;
+       sh->hash_pprev = shp;
+       nr_stripes++;
+}
+
+static void add_bh (struct buffer_head *bh)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       bh->b_next = raid5_buffer_list;
+       raid5_buffer_list = bh;
+       nr_free_buffers++;
+#if RAID5_DEBUG
+       if (bh->b_size == 1024)
+               free_1024++;
+       if (bh->b_size == 4096)
+               free_4096++;
+#endif
+       restore_flags(flags);
+}
+
+static void raid5_kfree_bh (struct buffer_head *bh)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       nr_used_buffers--;
+#if RAID5_DEBUG
+       if (bh->b_size == 1024)
+               used_1024--;
+       if (bh->b_size == 4096)
+               used_4096--;
+#endif
+       if (nr_free_buffers < RAID5_POOL_SIZE) {
+#if 0 /* This can magically catch races :-) */
+               char *b_data = ((volatile struct buffer_head *) bh)->b_data;
+               int b_size = ((volatile struct buffer_head *) bh)->b_size;
+               memset (bh, 0, sizeof (struct buffer_head));
+               ((volatile struct buffer_head *) bh)->b_data = b_data;
+               ((volatile struct buffer_head *) bh)->b_size = b_size;
+#endif
+               add_bh (bh);
+               wake_up (&raid5_wait_for_bh);
+       } else {
+               if (bh->b_size == PAGE_SIZE)
+                       free_page ((unsigned long) bh->b_data);
+               else
+                       kfree (bh->b_data);
+#if 0
+               memset (bh, 0, sizeof (struct buffer_head));
+#endif
+               kfree (bh);
+       }
+#if RAID5_DEBUG
+       printk ("kfree_bh: nr_free == %d, nr_used == %d, max_nr_used == %d\n", nr_free_buffers, nr_used_buffers, max_nr_used_buffers);
+#endif
+       restore_flags(flags);
+}
+
+static void raid5_kfree_old_bh(struct stripe_head *sh, int i)
+{
+       if (!sh->bh_old[i]) {
+               printk("raid5_kfree_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i);
+               return;
+       }
+       raid5_kfree_bh(sh->bh_old[i]);
+       sh->bh_old[i] = NULL;
+}
+
+static void raid5_update_old_bh(struct stripe_head *sh, int i)
+{
+       PRINTK(("stripe %lu, idx %d, updating cache copy\n", sh->sector, i));
+       if (!sh->bh_copy[i]) {
+               printk("raid5_update_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i);
+               return;
+       }
+       if (sh->bh_old[i])
+               raid5_kfree_old_bh(sh, i);
+       sh->bh_old[i] = sh->bh_copy[i];
+       sh->bh_copy[i] = NULL;
+}
+
+static void kfree_stripe(struct stripe_head *sh)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+       int disks = raid_conf->raid_disks, j;
+
+       PRINTK(("kfree_stripe called, stripe %lu\n", sh->sector));
+       if (sh->phase != PHASE_COMPLETE || stripe_locked(sh) || sh->count) {
+               printk("raid5: kfree_stripe(), sector %lu, phase %d, locked %d, count %d\n", sh->sector, sh->phase, stripe_locked(sh), sh->count);
+               return;
+       }
+       for (j = 0; j < disks; j++) {
+               if (sh->bh_old[j])
+                       raid5_kfree_old_bh(sh, j);
+               if (sh->bh_new[j] || sh->bh_copy[j])
+                       printk("raid5: bug: sector %lu, new %p, copy %p\n", sh->sector, sh->bh_new[j], sh->bh_copy[j]);
+       }
+       remove_hash(sh);
+       kfree(sh);
+}
+
+static int shrink_stripe_cache(int nr)
+{
+       struct stripe_head *sh;
+       int i, count = 0;
+       static int clock = 0;
+
+       PRINTK(("shrink_stripe_cache called, %d/%d, clock %d\n", nr, nr_stripes, clock));
+       for (i = 0; i < NR_HASH; i++) {
+repeat:
+               sh = stripe_hashtbl[(i + clock) & HASH_MASK];
+               for (; sh; sh = sh->hash_next) {
+                       if (sh->phase != PHASE_COMPLETE)
+                               continue;
+                       if (stripe_locked(sh))
+                               continue;
+                       if (sh->count)
+                               continue;
+                       kfree_stripe(sh);
+                       if (++count == nr) {
+                               PRINTK(("shrink completed, nr_stripes %d\n", nr_stripes));
+                               clock = (i + clock) & HASH_MASK;
+                               return nr;
+                       }
+                       goto repeat;
+               }
+       }
+       PRINTK(("shrink completed, nr_stripes %d\n", nr_stripes));
+       return count;
+}
+
+static struct stripe_head *find_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+       struct stripe_head *sh;
+
+       if (raid_conf->buffer_size != size) {
+               PRINTK(("switching size, %d --> %d\n", raid_conf->buffer_size, size));
+               shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE);
+               raid_conf->buffer_size = size;
+       }
+
+       PRINTK(("find_stripe, sector %lu\n", sector));
+       for (sh = stripe_hash(sector, size); sh; sh = sh->hash_next)
+               if (sh->sector == sector && sh->raid_conf == raid_conf) {
+                       if (sh->size == size) {
+                               PRINTK(("found stripe %lu\n", sector));
+                               return sh;
+                       } else {
+                               PRINTK(("switching size for %lu, %d --> %d\n", sector, sh->size, size));
+                               kfree_stripe(sh);
+                               break;
+                       }
+               }
+       PRINTK(("stripe %lu not in cache\n", sector));
+       return NULL;
+}
+
+static struct stripe_head *kmalloc_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+       struct stripe_head *sh = NULL, *tmp;
+
+       PRINTK(("kmalloc_stripe called\n"));
+
+       while (nr_stripes > RAID5_STRIPE_POOL_SIZE) {
+               shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE / 8);
+               if (nr_stripes <= RAID5_STRIPE_POOL_SIZE)
+                       break;
+               md_wakeup_thread(raid_conf->thread);
+               PRINTK(("waiting for some stripes to complete\n"));
+               sleep_on(&raid5_wait_for_stripe);
+       }
+       md_wakeup_thread(raid_conf->thread);
+       sh = kmalloc(sizeof(*sh), GFP_KERNEL);
+
+       /*
+        * The above might have slept, so perhaps another process
+        * already created the stripe for us..
+        */
+       if ((tmp = find_stripe(raid_conf, sector, size)) != NULL) { 
+               kfree(sh);
+               wait_on_stripe(tmp);
+               return tmp;
+       }
+       if (sh) {
+               memset(sh, 0, sizeof(*sh));
+               sh->phase = PHASE_COMPLETE;
+               sh->cmd = STRIPE_NONE;
+               sh->raid_conf = raid_conf;
+               sh->sector = sector;
+               sh->size = size;
+               insert_hash(sh);
+       }
+       return sh;
+}
+
+static struct stripe_head *get_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+       struct stripe_head *sh;
+
+       PRINTK(("get_stripe, sector %lu\n", sector));
+       sh = find_stripe(raid_conf, sector, size);
+       if (sh)
+               wait_on_stripe(sh);
+       else
+               sh = kmalloc_stripe(raid_conf, sector, size);
+       return sh;
+}
+
+static struct buffer_head *remove_bh (int b_size)
+{
+       struct buffer_head *bh, *bhp = NULL;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       if ((bh = raid5_buffer_list) == NULL)
+               return NULL;
+       do {
+               if (bh->b_size == b_size || b_size == -1)
+                       break;
+               bhp = bh;
+               bh = bh->b_next;
+       } while (bh);
+       if (!bh)
+               return NULL;
+       if (bhp)
+               bhp->b_next = bh->b_next;
+       else
+               raid5_buffer_list = bh->b_next;
+#if RAID5_DEBUG
+       if (bh->b_size == 1024)
+               free_1024--;
+       if (bh->b_size == 4096)
+               free_4096--;
+#endif
+       nr_free_buffers--;
+       if (!nr_free_buffers && raid5_buffer_list)
+               printk ("raid5: bug: buffer_list != NULL, nr_free_buffers == 0\n");
+       restore_flags(flags);
+       return bh;
+}
+
+
+static void shrink_buffers (int num)
+{
+       struct buffer_head *bh;
+
+       while (num--) {
+               if ((bh = remove_bh(-1)) == NULL)
+                       return;
+               if (bh->b_size == PAGE_SIZE)
+                       free_page ((unsigned long) bh->b_data);
+               else
+                       kfree (bh->b_data);
+               kfree (bh);
+       }
+}
+
+static void grow_buffers (int num, int b_size, int priority)
+{
+       struct buffer_head *bh;
+
+       while (num--) {
+               bh = kmalloc (sizeof (struct buffer_head), priority);
+               if (!bh)
+                       break;
+               memset (bh, 0, sizeof (struct buffer_head));
+               if (b_size == PAGE_SIZE)
+                       bh->b_data = (char *) __get_free_page (priority);
+               else
+                       bh->b_data = kmalloc (b_size, priority);
+               if (!bh->b_data) {
+                       kfree (bh);
+                       break;
+               }
+               bh->b_size = b_size;
+               add_bh (bh);
+       }
+}
+
+static struct buffer_head *raid5_kmalloc_bh (struct stripe_head *sh, int b_size)
+{
+       struct buffer_head *bh;
+       struct raid5_data *raid_conf = sh->raid_conf;
+       unsigned long flags;
+
+       bh = remove_bh(b_size);
+       if (!bh && nr_free_buffers > RAID5_POOL_SIZE / 10)
+               shrink_buffers (RAID5_POOL_SIZE / 10);
+       if (!bh && nr_used_buffers < RAID5_POOL_SIZE) {
+#if 0
+               grow_buffers (200, b_size, GFP_BUFFER);
+#else
+               grow_buffers (200, b_size, GFP_KERNEL);
+#endif
+               bh = remove_bh(b_size);
+       }
+       if (bh == NULL && nr_used_buffers > RAID5_POOL_SIZE / 2) {
+               shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE / 2);
+               bh = remove_bh(b_size);
+       }
+
+       while (bh == NULL && nr_used_buffers > 3 * RAID5_POOL_SIZE / 4) {
+               md_wakeup_thread(raid_conf->thread);
+               run_task_queue (&tq_disk);
+               unplug_devices(sh);
+               PRINTK(("waiting for bh\n"));
+               sleep_on (&raid5_wait_for_bh);
+               bh = remove_bh(b_size);
+       }
+       if (bh == NULL) {
+               grow_buffers (200, b_size, GFP_KERNEL);
+               bh = remove_bh(b_size);
+       }
+       if (bh) {
+               save_flags(flags);
+               cli();
+               nr_used_buffers++;
+               if (nr_used_buffers > max_nr_used_buffers)
+                       max_nr_used_buffers = nr_used_buffers;
+#if RAID5_DEBUG
+               if (bh->b_size == 1024)
+                       used_1024++;
+               if (bh->b_size == 4096)
+                       used_4096++;
+               printk ("kmalloc_bh: free, used, pending, max = %d, %d, %d, %d\n", nr_free_buffers, nr_used_buffers, nr_pending, max_nr_used_buffers);
+               printk ("kmalloc_bh: free1, used1, free4, used4 = %d, %d, %d, %d\n", free_1024, used_1024, free_4096, used_4096);
+#endif
+               restore_flags(flags);
+       }
+       return bh;
+}
+
+static inline void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptodate)
+{
+       struct buffer_head *bh = sh->bh_new[i];
+
+       sh->bh_new[i] = NULL;
+       clear_bit (BH_MD, &bh->b_state);
+       bh->private_bh = NULL;
+       bh->personality = NULL;
+       mark_buffer_uptodate(bh, uptodate);
+       unlock_buffer(bh);
+       if (!uptodate)
+               printk(KERN_ALERT "raid5: %s: unrecoverable I/O error for "
+                      "block %lu\n", kdevname(bh->b_dev), bh->b_blocknr);
+}
+
+static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptodate)
+{
+       if (uptodate)
+               set_bit(BH_Uptodate, &bh->b_state);
+       else
+               clear_bit(BH_Uptodate, &bh->b_state);
+}
+
+static void raid5_end_request (struct buffer_head * bh, int uptodate)
+{
+       struct stripe_head *sh = bh->private_bh;
+       struct raid5_data *raid_conf = sh->raid_conf;
+       int disks = raid_conf->raid_disks, i;
+       unsigned long flags;
+
+       PRINTK(("end_request %lu, nr_pending %d\n", sh->sector, sh->nr_pending));
+       save_flags(flags);
+       cli();
+       raid5_mark_buffer_uptodate(bh, uptodate);
+       --sh->nr_pending;
+       if (!sh->nr_pending) {
+               md_wakeup_thread(raid_conf->thread);
+               atomic_inc(&raid_conf->nr_handle);
+               if (!stripe_handle_tail)
+                       stripe_handle_list = sh;
+               else
+                       stripe_handle_tail->handle_next = sh;
+               sh->handle_next = NULL;
+               stripe_handle_tail = sh;
+       }
+       if (!uptodate)
+               md_error(bh->b_dev, bh->b_rdev);
+       if (raid_conf->failed_disks) {
+               for (i = 0; i < disks; i++) {
+                       if (raid_conf->disks[i].operational)
+                               continue;
+                       if (bh != sh->bh_old[i] && bh != sh->bh_new[i] && bh != sh->bh_copy[i])
+                               continue;
+                       set_bit(STRIPE_ERROR, &sh->state);
+               }
+       }
+       restore_flags(flags);
+}
+
+static int raid5_map (struct md_dev *mddev, kdev_t *rdev,
+                     unsigned long *rsector, unsigned long size)
+{
+       /* No complex mapping used: the core of the work is done in the
+        * request routine
+        */
+       return 0;
+}
+
+static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, int i)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+       struct md_dev *mddev = raid_conf->mddev;
+       int minor = (int) (mddev - md_dev);
+       char *b_data;
+
+       b_data = ((volatile struct buffer_head *) bh)->b_data;
+       memset (bh, 0, sizeof (struct buffer_head));
+       ((volatile struct buffer_head *) bh)->b_data = b_data;
+
+       bh->personality = &raid5_personality;
+       bh->private_bh  = (void *) sh;
+
+       bh->b_rdev      = raid_conf->disks[i].dev;
+       bh->b_dev       = MKDEV(MD_MAJOR, minor);
+       bh->b_rsector   = sh->sector;
+       bh->b_blocknr   = sh->sector / (sh->size >> 9);
+
+       bh->b_state     = (1 << BH_MD) | (1 << BH_Req);
+       bh->b_count     = 1;
+       bh->b_size      = sh->size;
+       bh->b_list      = BUF_LOCKED;
+}
+
+static int raid5_error (struct md_dev *mddev, kdev_t dev)
+{
+       struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+       md_superblock_t *sb = mddev->sb;
+       struct disk_info *disk;
+       int i;
+
+       PRINTK(("raid5_error called\n"));
+       for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++)
+               if (disk->dev == dev && disk->operational) {
+                       disk->operational = 0;
+                       sb->disks[disk->number].state |= (1 << MD_FAULTY_DEVICE);
+                       sb->disks[disk->number].state &= ~(1 << MD_SYNC_DEVICE);
+                       sb->disks[disk->number].state &= ~(1 << MD_ACTIVE_DEVICE);
+                       sb->active_disks--;
+                       sb->working_disks--;
+                       sb->failed_disks++;
+                       mddev->sb_dirty = 1;
+                       raid_conf->working_disks--;
+                       raid_conf->failed_disks++;
+                       md_wakeup_thread(raid_conf->thread);
+                       printk (KERN_ALERT
+                               "RAID5: Disk failure on %s, disabling device."
+                               "Operation continuing on %d devices\n",
+                               kdevname (dev), raid_conf->working_disks);
+               }
+       return 0;
+}      
+
+/*
+ * Input: a 'big' sector number, 
+ * Output: index of the data and parity disk, and the sector # in them.
+ */
+static inline unsigned long 
+raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_disks,
+                       unsigned int * dd_idx, unsigned int * pd_idx, 
+                       struct raid5_data *raid_conf)
+{
+       unsigned int  stripe;
+       int chunk_number, chunk_offset;
+       unsigned long new_sector;
+       int sectors_per_chunk = raid_conf->chunk_size >> 9;
+
+       /* First compute the information on this sector */
+
+       /*
+        * Compute the chunk number and the sector offset inside the chunk
+        */
+       chunk_number = r_sector / sectors_per_chunk;
+       chunk_offset = r_sector % sectors_per_chunk;
+
+       /*
+        * Compute the stripe number
+        */
+       stripe = chunk_number / data_disks;
+
+       /*
+        * Compute the data disk and parity disk indexes inside the stripe
+        */
+       *dd_idx = chunk_number % data_disks;
+
+       /*
+        * Select the parity disk based on the user selected algorithm.
+        */
+       if (raid_conf->level == 4)
+               *pd_idx = data_disks;
+       else switch (raid_conf->algorithm) {
+               case ALGORITHM_LEFT_ASYMMETRIC:
+                       *pd_idx = data_disks - stripe % raid_disks;
+                       if (*dd_idx >= *pd_idx)
+                               (*dd_idx)++;
+                       break;
+               case ALGORITHM_RIGHT_ASYMMETRIC:
+                       *pd_idx = stripe % raid_disks;
+                       if (*dd_idx >= *pd_idx)
+                               (*dd_idx)++;
+                       break;
+               case ALGORITHM_LEFT_SYMMETRIC:
+                       *pd_idx = data_disks - stripe % raid_disks;
+                       *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
+                       break;
+               case ALGORITHM_RIGHT_SYMMETRIC:
+                       *pd_idx = stripe % raid_disks;
+                       *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
+                       break;
+               default:
+                       printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm);
+       }
+
+       /*
+        * Finally, compute the new sector number
+        */
+       new_sector = stripe * sectors_per_chunk + chunk_offset;
+
+#if 0
+       if (    *dd_idx > data_disks || *pd_idx > data_disks || 
+               chunk_offset + bh->b_size / 512 > sectors_per_chunk     )
+
+               printk ("raid5: bug: dd_idx == %d, pd_idx == %d, chunk_offset == %d\n", 
+                               *dd_idx, *pd_idx, chunk_offset);
+#endif
+
+       return new_sector;
+}
+
+static unsigned long compute_blocknr(struct stripe_head *sh, int i)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+       int raid_disks = raid_conf->raid_disks, data_disks = raid_disks - 1;
+       unsigned long new_sector = sh->sector, check;
+       int sectors_per_chunk = raid_conf->chunk_size >> 9;
+       unsigned long stripe = new_sector / sectors_per_chunk;
+       int chunk_offset = new_sector % sectors_per_chunk;
+       int chunk_number, dummy1, dummy2, dd_idx = i;
+       unsigned long r_sector, blocknr;
+
+       switch (raid_conf->algorithm) {
+               case ALGORITHM_LEFT_ASYMMETRIC:
+               case ALGORITHM_RIGHT_ASYMMETRIC:
+                       if (i > sh->pd_idx)
+                               i--;
+                       break;
+               case ALGORITHM_LEFT_SYMMETRIC:
+               case ALGORITHM_RIGHT_SYMMETRIC:
+                       if (i < sh->pd_idx)
+                               i += raid_disks;
+                       i -= (sh->pd_idx + 1);
+                       break;
+               default:
+                       printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm);
+       }
+
+       chunk_number = stripe * data_disks + i;
+       r_sector = chunk_number * sectors_per_chunk + chunk_offset;
+       blocknr = r_sector / (sh->size >> 9);
+
+       check = raid5_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, raid_conf);
+       if (check != sh->sector || dummy1 != dd_idx || dummy2 != sh->pd_idx) {
+               printk("compute_blocknr: map not correct\n");
+               return 0;
+       }
+       return blocknr;
+}
+
+static void xor_block(struct buffer_head *dest, struct buffer_head *source)
+{
+       int lines = dest->b_size / (sizeof (int)) / 8, i;
+       int *destp = (int *) dest->b_data, *sourcep = (int *) source->b_data;
+
+       for (i = lines; i > 0; i--) {
+               *(destp + 0) ^= *(sourcep + 0);
+               *(destp + 1) ^= *(sourcep + 1);
+               *(destp + 2) ^= *(sourcep + 2);
+               *(destp + 3) ^= *(sourcep + 3);
+               *(destp + 4) ^= *(sourcep + 4);
+               *(destp + 5) ^= *(sourcep + 5);
+               *(destp + 6) ^= *(sourcep + 6);
+               *(destp + 7) ^= *(sourcep + 7);
+               destp += 8;
+               sourcep += 8;
+       }
+}
+
+static void compute_block(struct stripe_head *sh, int dd_idx)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+       int i, disks = raid_conf->raid_disks;
+
+       PRINTK(("compute_block, stripe %lu, idx %d\n", sh->sector, dd_idx));
+
+       if (sh->bh_old[dd_idx] == NULL)
+               sh->bh_old[dd_idx] = raid5_kmalloc_bh(sh, sh->size);
+       raid5_build_block(sh, sh->bh_old[dd_idx], dd_idx);
+
+       memset(sh->bh_old[dd_idx]->b_data, 0, sh->size);
+       for (i = 0; i < disks; i++) {
+               if (i == dd_idx)
+                       continue;
+               if (sh->bh_old[i]) {
+                       xor_block(sh->bh_old[dd_idx], sh->bh_old[i]);
+                       continue;
+               } else
+                       printk("compute_block() %d, stripe %lu, %d not present\n", dd_idx, sh->sector, i);
+       }
+       raid5_mark_buffer_uptodate(sh->bh_old[dd_idx], 1);
+}
+
+static void compute_parity(struct stripe_head *sh, int method)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+       int i, pd_idx = sh->pd_idx, disks = raid_conf->raid_disks;
+
+       PRINTK(("compute_parity, stripe %lu, method %d\n", sh->sector, method));
+       for (i = 0; i < disks; i++) {
+               if (i == pd_idx || !sh->bh_new[i])
+                       continue;
+               if (!sh->bh_copy[i])
+                       sh->bh_copy[i] = raid5_kmalloc_bh(sh, sh->size);
+               raid5_build_block(sh, sh->bh_copy[i], i);
+               mark_buffer_clean(sh->bh_new[i]);
+               memcpy(sh->bh_copy[i]->b_data, sh->bh_new[i]->b_data, sh->size);
+       }
+       if (sh->bh_copy[pd_idx] == NULL)
+               sh->bh_copy[pd_idx] = raid5_kmalloc_bh(sh, sh->size);
+       raid5_build_block(sh, sh->bh_copy[pd_idx], sh->pd_idx);
+
+       if (method == RECONSTRUCT_WRITE) {
+               memset(sh->bh_copy[pd_idx]->b_data, 0, sh->size);
+               for (i = 0; i < disks; i++) {
+                       if (i == sh->pd_idx)
+                               continue;
+                       if (sh->bh_new[i]) {
+                               xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]);
+                               continue;
+                       }
+                       if (sh->bh_old[i]) {
+                               xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]);
+                               continue;
+                       }
+               }
+       } else if (method == READ_MODIFY_WRITE) {
+               memcpy(sh->bh_copy[pd_idx]->b_data, sh->bh_old[pd_idx]->b_data, sh->size);
+               for (i = 0; i < disks; i++) {
+                       if (i == sh->pd_idx)
+                               continue;
+                       if (sh->bh_new[i] && sh->bh_old[i]) {
+                               xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]);
+                               xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]);
+                               continue;
+                       }
+               }
+       }
+       raid5_mark_buffer_uptodate(sh->bh_copy[pd_idx], 1);
+}
+
+static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int dd_idx, int rw)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+
+       if (sh->bh_new[dd_idx])
+               printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector);
+
+       set_bit(BH_MD, &bh->b_state);
+       set_bit(BH_Lock, &bh->b_state);
+       bh->personality  = &raid5_personality;
+       bh->private_bh   = (void *) sh;
+       bh->b_rdev    = raid_conf->disks[dd_idx].dev;
+       bh->b_rsector = sh->sector;
+
+       if (sh->phase == PHASE_COMPLETE && sh->cmd == STRIPE_NONE) {
+               sh->phase = PHASE_BEGIN;
+               sh->cmd = (rw == READ) ? STRIPE_READ : STRIPE_WRITE;
+               nr_pending_stripes++;
+               atomic_inc(&raid_conf->nr_handle);
+       }
+       sh->bh_new[dd_idx] = bh;
+       sh->cmd_new[dd_idx] = rw;
+       sh->new[dd_idx] = 1;
+}
+
+static void complete_stripe(struct stripe_head *sh)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+       int disks = raid_conf->raid_disks;
+       int i, new = 0;
+       
+       PRINTK(("complete_stripe %lu\n", sh->sector));
+       for (i = 0; i < disks; i++) {
+               if (sh->cmd == STRIPE_WRITE && i == sh->pd_idx)
+                       raid5_update_old_bh(sh, i);
+               if (sh->bh_new[i]) {
+                       if (!sh->new[i]) {
+#if 0
+                               if (sh->cmd == STRIPE_WRITE) {
+                                       if (memcmp(sh->bh_new[i]->b_data, sh->bh_copy[i]->b_data, sh->size)) {
+                                               printk("copy differs, %s, sector %lu ",
+                                                       test_bit(BH_Dirty, &sh->bh_new[i]->b_state) ? "dirty" : "clean",
+                                                       sh->sector);
+                                       } else if (test_bit(BH_Dirty, &sh->bh_new[i]->b_state))
+                                               printk("sector %lu dirty\n", sh->sector);
+                               }
+#endif
+                               if (sh->cmd == STRIPE_WRITE)
+                                       raid5_update_old_bh(sh, i);
+                               raid5_end_buffer_io(sh, i, 1);
+                               continue;
+                       } else
+                               new++;
+               }
+               if (new && sh->cmd == STRIPE_WRITE)
+                       printk("raid5: bug, completed STRIPE_WRITE with new == %d\n", new);
+       }
+       if (!new)
+               finish_stripe(sh);
+       else {
+               PRINTK(("stripe %lu, new == %d\n", sh->sector, new));
+               sh->phase = PHASE_BEGIN;
+       }
+}
+
+/*
+ * handle_stripe() is our main logic routine. Note that:
+ *
+ * 1.  lock_stripe() should be used whenever we can't accept additonal
+ *     buffers, either during short sleeping in handle_stripe() or
+ *     during io operations.
+ *
+ * 2.  We should be careful to set sh->nr_pending whenever we sleep,
+ *     to prevent re-entry of handle_stripe() for the same sh.
+ *
+ * 3.  raid_conf->failed_disks and disk->operational can be changed
+ *     from an interrupt. This complicates things a bit, but it allows
+ *     us to stop issuing requests for a failed drive as soon as possible.
+ */
+static void handle_stripe(struct stripe_head *sh)
+{
+       struct raid5_data *raid_conf = sh->raid_conf;
+       struct md_dev *mddev = raid_conf->mddev;
+       int minor = (int) (mddev - md_dev);
+       struct buffer_head *bh;
+       int disks = raid_conf->raid_disks;
+       int i, nr = 0, nr_read = 0, nr_write = 0;
+       int nr_cache = 0, nr_cache_other = 0, nr_cache_overwrite = 0, parity = 0;
+       int nr_failed_other = 0, nr_failed_overwrite = 0, parity_failed = 0;
+       int reading = 0, nr_writing = 0;
+       int method1 = INT_MAX, method2 = INT_MAX;
+       int block;
+       unsigned long flags;
+       int operational[MD_SB_DISKS], failed_disks = raid_conf->failed_disks;
+
+       PRINTK(("handle_stripe(), stripe %lu\n", sh->sector));
+       if (sh->nr_pending) {
+               printk("handle_stripe(), stripe %lu, io still pending\n", sh->sector);
+               return;
+       }
+       if (sh->phase == PHASE_COMPLETE) {
+               printk("handle_stripe(), stripe %lu, already complete\n", sh->sector);
+               return;
+       }
+
+       atomic_dec(&raid_conf->nr_handle);
+
+       if (clear_bit(STRIPE_ERROR, &sh->state)) {
+               printk("raid5: restarting stripe %lu\n", sh->sector);
+               sh->phase = PHASE_BEGIN;
+       }
+
+       if ((sh->cmd == STRIPE_WRITE && sh->phase == PHASE_WRITE) ||
+           (sh->cmd == STRIPE_READ && sh->phase == PHASE_READ)) {
+               /*
+                * Completed
+                */
+               complete_stripe(sh);
+               if (sh->phase == PHASE_COMPLETE)
+                       return;
+       }
+
+       save_flags(flags);
+       cli();
+       for (i = 0; i < disks; i++)
+               operational[i] = raid_conf->disks[i].operational;
+       failed_disks = raid_conf->failed_disks;
+       restore_flags(flags);
+
+       if (failed_disks > 1) {
+               for (i = 0; i < disks; i++) {
+                       if (sh->bh_new[i]) {
+                               raid5_end_buffer_io(sh, i, 0);
+                               continue;
+                       }
+               }
+               finish_stripe(sh);
+               return;
+       }
+
+       for (i = 0; i < disks; i++) {
+               if (sh->bh_old[i])
+                       nr_cache++;
+               if (i == sh->pd_idx) {
+                       if (sh->bh_old[i])
+                               parity = 1;
+                       else if(!operational[i])
+                               parity_failed = 1;
+                       continue;
+               }
+               if (!sh->bh_new[i]) {
+                       if (sh->bh_old[i])
+                               nr_cache_other++;
+                       else if (!operational[i])
+                               nr_failed_other++;
+                       continue;
+               }
+               sh->new[i] = 0;
+               nr++;
+               if (sh->cmd_new[i] == READ)
+                       nr_read++;
+               if (sh->cmd_new[i] == WRITE)
+                       nr_write++;
+               if (sh->bh_old[i])
+                       nr_cache_overwrite++;
+               else if (!operational[i])
+                       nr_failed_overwrite++;
+       }
+
+       if (nr_write && nr_read)
+               printk("raid5: bug, nr_write == %d, nr_read == %d, sh->cmd == %d\n", nr_write, nr_read, sh->cmd);
+
+       if (nr_write) {
+               /*
+                * Attempt to add entries :-)
+                */
+               if (nr_write != disks - 1) {
+                       for (i = 0; i < disks; i++) {
+                               if (i == sh->pd_idx)
+                                       continue;
+                               if (sh->bh_new[i])
+                                       continue;
+                               block = (int) compute_blocknr(sh, i);
+                               bh = efind_buffer(MKDEV(MD_MAJOR, minor), block, sh->size);
+                               if (bh && bh->b_count == 0 && buffer_dirty(bh) && !buffer_locked(bh)) {
+                                       PRINTK(("Whee.. sector %lu, index %d (%d) found in the buffer cache!\n", sh->sector, i, block));
+                                       add_stripe_bh(sh, bh, i, WRITE);
+                                       sh->new[i] = 0;
+                                       nr++; nr_write++;
+                                       if (sh->bh_old[i]) {
+                                               nr_cache_overwrite++;
+                                               nr_cache_other--;
+                                       } else if (!operational[i]) {
+                                               nr_failed_overwrite++;
+                                               nr_failed_other--;
+                                       }
+                               }
+                       }
+               }
+               PRINTK(("handle_stripe() -- begin writing, stripe %lu\n", sh->sector));
+               /*
+                * Writing, need to update parity buffer.
+                *
+                * Compute the number of I/O requests in the "reconstruct
+                * write" and "read modify write" methods.
+                */
+               if (!nr_failed_other)
+                       method1 = (disks - 1) - (nr_write + nr_cache_other);
+               if (!nr_failed_overwrite && !parity_failed)
+                       method2 = nr_write - nr_cache_overwrite + (1 - parity);
+
+               if (method1 == INT_MAX && method2 == INT_MAX)
+                       printk("raid5: bug: method1 == method2 == INT_MAX\n");
+               PRINTK(("handle_stripe(), sector %lu, nr_write %d, method1 %d, method2 %d\n", sh->sector, nr_write, method1, method2));
+
+               if (!method1 || !method2) {
+                       lock_stripe(sh);
+                       sh->nr_pending++;
+                       sh->phase = PHASE_WRITE;
+                       compute_parity(sh, method1 <= method2 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE);
+                       for (i = 0; i < disks; i++) {
+                               if (!operational[i])
+                                       continue;
+                               if (i == sh->pd_idx || sh->bh_new[i])
+                                       nr_writing++;
+                       }
+
+                       sh->nr_pending = nr_writing;
+                       PRINTK(("handle_stripe() %lu, writing back %d\n", sh->sector, sh->nr_pending));
+
+                       for (i = 0; i < disks; i++) {
+                               if (!operational[i])
+                                       continue;
+                               bh = sh->bh_copy[i];
+                               if (i != sh->pd_idx && ((bh == NULL) ^ (sh->bh_new[i] == NULL)))
+                                       printk("raid5: bug: bh == %p, bh_new[%d] == %p\n", bh, i, sh->bh_new[i]);
+                               if (i == sh->pd_idx && !bh)
+                                       printk("raid5: bug: bh == NULL, i == pd_idx == %d\n", i);
+                               if (bh) {
+                                       bh->b_state |= (1<<BH_Dirty);
+                                       PRINTK(("making request for buffer %d\n", i));
+                                       clear_bit(BH_Lock, &bh->b_state);
+                                       make_request(MAJOR(raid_conf->disks[i].dev), WRITE, bh);
+                               }
+                       }
+                       return;
+               }
+
+               lock_stripe(sh);
+               sh->nr_pending++;
+               if (method1 < method2) {
+                       sh->write_method = RECONSTRUCT_WRITE;
+                       for (i = 0; i < disks; i++) {
+                               if (i == sh->pd_idx)
+                                       continue;
+                               if (sh->bh_new[i] || sh->bh_old[i])
+                                       continue;
+                               sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size);
+                               raid5_build_block(sh, sh->bh_old[i], i);
+                               reading++;
+                       }
+               } else {
+                       sh->write_method = READ_MODIFY_WRITE;
+                       for (i = 0; i < disks; i++) {
+                               if (sh->bh_old[i])
+                                       continue;
+                               if (!sh->bh_new[i] && i != sh->pd_idx)
+                                       continue;
+                               sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size);
+                               raid5_build_block(sh, sh->bh_old[i], i);
+                               reading++;
+                       }
+               }
+               sh->phase = PHASE_READ_OLD;
+               sh->nr_pending = reading;
+               PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, sh->nr_pending));
+               for (i = 0; i < disks; i++) {
+                       if (!sh->bh_old[i])
+                               continue;
+                       if (buffer_uptodate(sh->bh_old[i]))
+                               continue;
+                       clear_bit(BH_Lock, &sh->bh_old[i]->b_state);
+                       make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]);
+               }
+       } else {
+               /*
+                * Reading
+                */
+               method1 = nr_read - nr_cache_overwrite;
+               lock_stripe(sh);
+               sh->nr_pending++;
+
+               PRINTK(("handle_stripe(), sector %lu, nr_read %d, nr_cache %d, method1 %d\n", sh->sector, nr_read, nr_cache, method1));
+               if (!method1 || (method1 == 1 && nr_cache == disks - 1)) {
+                       PRINTK(("read %lu completed from cache\n", sh->sector));
+                       for (i = 0; i < disks; i++) {
+                               if (!sh->bh_new[i])
+                                       continue;
+                               if (!sh->bh_old[i])
+                                       compute_block(sh, i);
+                               memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size);
+                       }
+                       sh->nr_pending--;
+                       complete_stripe(sh);
+                       return;
+               }
+               if (nr_failed_overwrite) {
+                       sh->phase = PHASE_READ_OLD;
+                       sh->nr_pending = (disks - 1) - nr_cache;
+                       PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, sh->nr_pending));
+                       for (i = 0; i < disks; i++) {
+                               if (sh->bh_old[i])
+                                       continue;
+                               if (!operational[i])
+                                       continue;
+                               sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size);
+                               raid5_build_block(sh, sh->bh_old[i], i);
+                               clear_bit(BH_Lock, &sh->bh_old[i]->b_state);
+                               make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]);
+                       }
+               } else {
+                       sh->phase = PHASE_READ;
+                       sh->nr_pending = nr_read - nr_cache_overwrite;
+                       PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, sh->nr_pending));
+                       for (i = 0; i < disks; i++) {
+                               if (!sh->bh_new[i])
+                                       continue;
+                               if (sh->bh_old[i]) {
+                                       memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size);
+                                       continue;
+                               }
+                               clear_bit(BH_Lock, &sh->bh_new[i]->b_state);
+                               make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_new[i]);
+                       }
+               }
+       }
+}
+
+static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh)
+{
+       struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+       const unsigned int raid_disks = raid_conf->raid_disks;
+       const unsigned int data_disks = raid_disks - 1;
+       unsigned int  dd_idx, pd_idx;
+       unsigned long new_sector;
+
+       struct stripe_head *sh;
+
+       if (rw == READA) rw = READ;
+       if (rw == WRITEA) rw = WRITE;
+
+       new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks,
+                                               &dd_idx, &pd_idx, raid_conf);
+
+       PRINTK(("raid5_make_request, sector %lu\n", new_sector));
+       sh = get_stripe(raid_conf, new_sector, bh->b_size);
+       if ((rw == READ && sh->cmd == STRIPE_WRITE) || (rw == WRITE && sh->cmd == STRIPE_READ)) {
+               printk("raid5: lock contention, rw == %d, sh->cmd == %d\n", rw, sh->cmd);
+               lock_stripe(sh);
+               if (!sh->nr_pending)
+                       handle_stripe(sh);
+               wait_on_stripe(sh);
+       }
+       sh->pd_idx = pd_idx;
+       if (sh->phase != PHASE_COMPLETE && sh->phase != PHASE_BEGIN)
+               PRINTK(("stripe %lu catching the bus!\n", sh->sector));
+       add_stripe_bh(sh, bh, dd_idx, rw);
+
+       md_wakeup_thread(raid_conf->thread);
+       return 0;
+}
+
+/*
+ * This is our raid5 kernel thread.
+ *
+ * We scan the hash table for stripes which can be handled now.
+ * During the scan, completed stripes are saved for us by the interrupt
+ * handler, so that they will not have to wait for our next wakeup.
+ */
+static void raid5d (void *data)
+{
+       struct stripe_head *sh;
+       struct raid5_data *raid_conf = data;
+       struct md_dev *mddev = raid_conf->mddev;
+       int i, handled = 0, unplug = 0;
+       unsigned long flags;
+
+       PRINTK(("+++ raid5d active\n"));
+
+       if (mddev->sb_dirty) {
+               mddev->sb_dirty = 0;
+               md_update_sb((int) (mddev - md_dev));
+       }
+       save_flags(flags);
+       cli();
+       stripe_handle_list = stripe_handle_tail = NULL;
+       restore_flags(flags);
+
+       for (i = 0; i < NR_HASH; i++) {
+repeat:
+               sh = stripe_hashtbl[i];
+               for (; sh; sh = sh->hash_next) {
+                       if (sh->raid_conf != raid_conf)
+                               continue;
+                       if (sh->phase == PHASE_COMPLETE)
+                               continue;
+                       if (sh->nr_pending)
+                               continue;
+                       if (sh->sector == raid_conf->next_sector) {
+                               raid_conf->sector_count += (sh->size >> 9);
+                               if (raid_conf->sector_count >= 128)
+                                       unplug = 1;
+                       } else
+                               unplug = 1;
+                       if (unplug) {
+                               PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, raid_conf->sector_count));
+                               unplug_devices(sh);
+                               unplug = 0;
+                               raid_conf->sector_count = 0;
+                       }
+                       raid_conf->next_sector = sh->sector + (sh->size >> 9);
+                       handled++;
+                       handle_stripe(sh);
+                       goto repeat;
+               }
+       }
+       if (raid_conf) {
+               PRINTK(("%d stripes handled, nr_handle %d\n", handled, raid_conf->nr_handle));
+               save_flags(flags);
+               cli();
+               if (!raid_conf->nr_handle)
+                       clear_bit(THREAD_WAKEUP, &raid_conf->thread->flags);
+       }
+       PRINTK(("--- raid5d inactive\n"));
+}
+
+static int raid5_run (int minor, struct md_dev *mddev)
+{
+       struct raid5_data *raid_conf;
+       int i, j, raid_disk;
+       md_superblock_t *sb = mddev->sb;
+       md_descriptor_t *descriptor;
+       struct real_dev *realdev;
+
+       MOD_INC_USE_COUNT;
+
+       if (sb->level != 5 && sb->level != 4) {
+               printk("raid5: %s: raid level not set to 4/5 (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+               MOD_DEC_USE_COUNT;
+               return -EIO;
+       }
+
+       mddev->private = kmalloc (sizeof (struct raid5_data), GFP_KERNEL);
+       raid_conf = mddev->private;
+       memset (raid_conf, 0, sizeof (*raid_conf));
+       raid_conf->mddev = mddev;
+
+       PRINTK(("raid5_run(%d) called.\n", minor));
+
+       for (i = 0; i < mddev->nb_dev; i++) {
+               realdev = &mddev->devices[i];
+               if (!realdev->sb) {
+                       printk(KERN_ERR "raid5: disabled device %s (couldn't access raid superblock)\n", kdevname(realdev->dev));
+                       continue;
+               }
+
+               /*
+                * This is important -- we are using the descriptor on
+                * the disk only to get a pointer to the descriptor on
+                * the main superblock, which might be more recent.
+                */
+               descriptor = &sb->disks[realdev->sb->descriptor.number];
+               if (descriptor->state & (1 << MD_FAULTY_DEVICE)) {
+                       printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", kdevname(realdev->dev));
+                       continue;
+               }
+               if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) {
+                       if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) {
+                               printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", kdevname(realdev->dev));
+                               continue;
+                       }
+                       raid_disk = descriptor->raid_disk;
+                       if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) {
+                               printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", kdevname(realdev->dev));
+                               continue;
+                       }
+                       if (raid_conf->disks[raid_disk].operational) {
+                               printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", kdevname(realdev->dev), raid_disk);
+                               continue;
+                       }
+                       printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", kdevname(realdev->dev), raid_disk);
+       
+                       raid_conf->disks[raid_disk].number = descriptor->number;
+                       raid_conf->disks[raid_disk].raid_disk = raid_disk;
+                       raid_conf->disks[raid_disk].dev = mddev->devices[i].dev;
+                       raid_conf->disks[raid_disk].operational = 1;
+
+                       raid_conf->working_disks++;
+               }
+       }
+       raid_conf->raid_disks = sb->raid_disks;
+       raid_conf->failed_disks = raid_conf->raid_disks - raid_conf->working_disks;
+       raid_conf->mddev = mddev;
+       raid_conf->chunk_size = sb->chunk_size;
+       raid_conf->level = sb->level;
+       raid_conf->algorithm = sb->parity_algorithm;
+
+       if (!raid_conf->chunk_size || raid_conf->chunk_size % 4) {
+               printk(KERN_ERR "raid5: invalid chunk size %d for %s\n", raid_conf->chunk_size, kdevname(MKDEV(MD_MAJOR, minor)));
+               goto abort;
+       }
+       if (raid_conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) {
+               printk(KERN_ERR "raid5: unsupported parity algorithm %d for %s\n", raid_conf->algorithm, kdevname(MKDEV(MD_MAJOR, minor)));
+               goto abort;
+       }
+       if (raid_conf->failed_disks > 1) {
+               printk(KERN_ERR "raid5: not enough operational devices for %s (%d/%d failed)\n", kdevname(MKDEV(MD_MAJOR, minor)), raid_conf->failed_disks, raid_conf->raid_disks);
+               goto abort;
+       }
+
+#if 0
+       if (check_consistenty(mddev)) {
+               printk(KERN_ERR "raid5: detected raid-5 xor inconsistenty -- run ckraid\n");
+               sb->state |= 1 << MD_SB_ERRORS;
+               goto abort;
+       }
+#endif
+
+       if ((raid_conf->thread = md_register_thread(raid5d, raid_conf)) == NULL) {
+               printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+               goto abort;
+       }
+
+       /*
+        * Regenerate the "device is in sync with the raid set" bit for
+        * each device.
+        */
+       for (i = 0; i < sb->nr_disks ; i++) {
+               sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE);
+               for (j = 0; j < sb->raid_disks; j++) {
+                       if (!raid_conf->disks[j].operational)
+                               continue;
+                       if (sb->disks[i].number == raid_conf->disks[j].number)
+                               sb->disks[i].state |= 1 << MD_SYNC_DEVICE;
+               }
+       }
+       sb->active_disks = raid_conf->working_disks;
+
+       if (sb->active_disks == sb->raid_disks)
+               printk("raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm);
+       else
+               printk(KERN_ALERT "raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm);
+
+       /* Ok, everything is just fine now */
+       return (0);
+abort:
+       if (raid_conf)
+               kfree(raid_conf);
+       mddev->private = NULL;
+       printk(KERN_ALERT "raid5: failed to run raid set %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+       MOD_DEC_USE_COUNT;
+       return -EIO;
+}
+
+static int raid5_stop (int minor, struct md_dev *mddev)
+{
+       struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+
+       md_unregister_thread(raid_conf->thread);
+       kfree (raid_conf);
+       shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE);
+       shrink_buffers(RAID5_POOL_SIZE);
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static int raid5_status (char *page, int minor, struct md_dev *mddev)
+{
+       struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+       md_superblock_t *sb = mddev->sb;
+       int sz = 0, i;
+
+       sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->parity_algorithm);
+       sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks);
+       for (i = 0; i < raid_conf->raid_disks; i++)
+               sz += sprintf (page+sz, "%s", raid_conf->disks[i].operational ? "U" : "_");
+       sz += sprintf (page+sz, "]");
+       return sz;
+}
+
+static struct md_personality raid5_personality=
+{
+       "raid5",
+       raid5_map,
+       raid5_make_request,
+       raid5_end_request,
+       raid5_run,
+       raid5_stop,
+       raid5_status,
+       NULL,                   /* no ioctls */
+       0,
+       raid5_error
+};
+
+int raid5_init (void)
+{
+       if ((stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0)) == NULL)
+               return -ENOMEM;
+       memset(stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE);
+       return register_md_personality (RAID5, &raid5_personality);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+       return raid5_init();
+}
+
+void cleanup_module (void)
+{
+       free_pages((unsigned long) stripe_hashtbl, HASH_PAGES_ORDER);
+       shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE);
+       shrink_buffers(RAID5_POOL_SIZE);
+       unregister_md_personality (RAID5);
+}
+#endif
index 0376f64243d40dd41f47e9b187f8505dca9d82d1..4e629f17c886a45a2589ae4b91eecb2f28557aa3 100644 (file)
@@ -582,7 +582,7 @@ static uch *window;
 static unsigned insize = 0;  /* valid bytes in inbuf */
 static unsigned inptr = 0;   /* index of next byte to be processed in inbuf */
 static unsigned outcnt = 0;  /* bytes in output buffer */
-static exit_code = 0;
+static int exit_code = 0;
 static long bytes_out = 0;
 static struct file *crd_infp, *crd_outfp;
 
index 3196ac2120ced0c3db5694c02938c2a26339903a..a2f111513ade79d6633b7b22d2b1264845871bc8 100644 (file)
@@ -739,7 +739,7 @@ int get_toc_lba(uch track)
   uch * q = cd->q;
   uch ct;                      /* current track */
   int binary=0;
-  const skip = 3*60*75;
+  const int skip = 3*60*75;
 
   for (i=track; i>0; i--) if (cd->toc[i].track) {
     min = fsm2lba(cd->toc[i].fsm);
index 155ed92d54a99a7fbad0407a46d2e2b1fc285edf..fa3c90e97d1f3c15838bfb85102e95352e7622c4 100644 (file)
@@ -311,7 +311,10 @@ mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
        {
                i = updateToc();
                if (i < 0)
-                       return i;       /* error reading TOC */
+                       /* Hermann.Lauer@IWR.Uni-Heidelberg.De:
+                       We _can_ open the door even without a CD */
+                       if (cmd != CDROMEJECT)
+                               return i;       /* error reading TOC */
        }
 
        switch (cmd)
index 80151430a3231c03327cd040ecf15165c8e57a6e..32c411b4a2a9bde5e02d92bf7f513d4eb6e53589 100644 (file)
  *  More changes to support CDU-510/515 series
  *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
  *
+ * 1997-11-18
+ *  Blocksize awareness
+ *      Dong Liu <qian!dliu@arrow.njit.edu>
+ *
  * Things to do:
  *  - handle errors and status better, put everything into a single word
  *  - use interrupts (code mostly there, but a big hole still missing)
@@ -589,11 +593,13 @@ set_drive_mode(int mode, Byte status[2])
  *    The routine returns number of bytes read in if successful, otherwise
  *  it returns one of the standard error returns.
  ***************************************************************************/
+
+static int sonycd535_block_size = 2048;
+
 static int
 seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
                                           Byte **buff, int buf_size)
 {
-       const int block_size = 2048;
        Byte cmd_buff[7];
        int  i;
        int  read_status;
@@ -601,7 +607,7 @@ seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
        Byte *data_buff;
        int  sector_count = 0;
 
-       if (buf_size < ((long)block_size) * n_blocks)
+       if (buf_size < sonycd535_block_size * n_blocks)
                return NO_ROOM;
 
        set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
@@ -626,7 +632,7 @@ seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
                        if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
                                /* data is ready, read it */
                                data_buff = buff[sector_count++];
-                               for (i = 0; i < block_size; i++)
+                               for (i = 0; i < sonycd535_block_size; i++)
                                        *data_buff++ = inb(data_reg);   /* unrolling this loop does not seem to help */
                                break;                  /* exit the timeout loop */
                        }
@@ -639,7 +645,7 @@ seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
        /* read all the data, now read the status */
        if ((i = read_exec_status(status)) != 0)
                return i;
-       return block_size * sector_count;
+       return sonycd535_block_size * sector_count;
 }      /* seek_and_read_N_blocks() */
 
 /****************************************************************************
@@ -1594,6 +1600,7 @@ sony535_init(void)
                                        return -EIO;
                                }
                                blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+                               blksize_size[MAJOR_NR] = &sonycd535_block_size;
                                read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read-ahead */
 
                                sony_toc = (struct s535_sony_toc *)
index e84363d45dce0993ec72b5286ea15c0e884b3ebb..c7054877f189fccfec0d1ff9aa135f8c07bc4a21 100644 (file)
@@ -44,7 +44,7 @@
 /*
  *      ftape-io.c defined global vars.
  */
-extern bad_sector_map_changed;
+extern int bad_sector_map_changed;
 
 /*
  *      ftape-io.c defined global functions.
index 59fcfdc76bffbe46dbabbed8fad49221334dd50f..9a084a76192a7e327dc2a29e40b5875075db6c8c 100644 (file)
@@ -61,7 +61,7 @@ typedef struct {
  */
 extern int ftape_failure;
 extern int write_protected;
-extern ftape_offline;
+extern int ftape_offline;
 extern int formatted;
 extern int no_tape;
 extern history_record history;
index 9843390c78c8971aea394628be07c6e92c8de875..f835be5dc99147acc7d946c4cd76b92a84769715 100644 (file)
@@ -59,7 +59,7 @@ asmlinkage extern void cleanup_module(void);
 /*      kernel global functions not (yet) standard accessible
  *      (linked at load time by modules package).
  */
-asmlinkage extern sys_sgetmask(void);
-asmlinkage extern sys_ssetmask(int);
+asmlinkage extern int sys_sgetmask(void);
+asmlinkage extern int sys_ssetmask(int);
 
 #endif
index ad5c09689ce2573bed3c293d7e566591b1fbba86..c193a38ec588eb6befcc3c123b3d220a89e86284 100644 (file)
@@ -229,7 +229,7 @@ isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason)
 {
        printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n",
               dev->name, reason);
-       if(skb->proto==htons(ETH_P_IP))
+       if(skb->protocol==htons(ETH_P_IP))
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
 #if (LINUX_VERSION_CODE < 0x02010f)    /* 2.1.15 */
                  ,dev
index 18f6029da7b49de3cb78a2bb1e71c788465698b1..6b5b369e363b6d4bbbbb10fa6e4aa1fd175cc1ac 100644 (file)
@@ -32,7 +32,7 @@
 #include "message.h"
 #include "card.h"
 
-extern indicate_status(int, int, ulong, char *);
+extern int indicate_status(int, int, ulong, char *);
 extern void check_phystat(unsigned long);
 extern void dump_messages(int);
 extern int receivemessage(int, RspMessage *);
index 7c38191e6a56b74fec607af5c0c676574fa76542..a562db350c8fce6c7fae0358918a662d8c8259ab 100644 (file)
@@ -75,7 +75,7 @@ static int el2_close(struct device *dev);
 static void el2_reset_8390(struct device *dev);
 static void el2_init_card(struct device *dev);
 static void el2_block_output(struct device *dev, int count,
-                            const unsigned char *buf, const start_page);
+                            const unsigned char *buf, const int start_page);
 static void el2_block_input(struct device *dev, int count, struct sk_buff *skb,
                           int ring_offset);
 static void el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
@@ -432,7 +432,7 @@ el2_init_card(struct device *dev)
  */
 static void
 el2_block_output(struct device *dev, int count,
-                const unsigned char *buf, const start_page)
+                const unsigned char *buf, const int start_page)
 {
     unsigned short int *wrd;
     int boguscount;            /* timeout counter */
index f7fb292d069d377401ddeca5ed44113553f5fc39..634941c935b9527d48cdfbb999de7feec8435db2 100644 (file)
@@ -18,9 +18,9 @@ static char *version = "3c515.c:v0.99 4/7/98 becker@cesdis.gsfc.nasa.gov\n";
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1512 effectively disables this feature. */
-static const rx_copybreak = 200;
+static const int rx_copybreak = 200;
 /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const mtu = 1500;
+static const int mtu = 1500;
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
 
index a6d5fc03b2eeccdd877928254f9d33f08e32b0f7..b9331e60d431ea61fb76fe571b81bed1fea2d6be 100644 (file)
@@ -20,9 +20,9 @@ static char *version =
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1512 effectively disables this feature. */
-static const rx_copybreak = 200;
+static const int rx_copybreak = 200;
 /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const mtu = 1500;
+static const int mtu = 1500;
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
 
index d026ede2b0e0685c24aae569f4c6e58e2791a3cf..33e3b81f0c48432bccc95392940208bf6a9e9173 100644 (file)
@@ -1,5 +1,52 @@
 TLan Device Driver change log.
 
+0.43   - Changed the id strings of the Compaq devices to their real
+         names.
+       - Added 2 other Olicom devices with a patch provided by
+         Henrik Storner of Olicom.
+       - Changed AN so the driver won't try to autonegotiate with
+         a partner that doesn't autonegotiate.
+       - Added a section to verify whether in full or half duplex.
+       - Added value checking for speed and duplex inputs.
+       - Added B012 and B030 Compaq devices.
+         
+0.42   - Coverted tranceiver from misused per-tranceiver functions
+         to a timer-oriented path that covers four possible classes
+         of tranceivers:
+               1. Unmanaged
+               2. Manual configuration
+               3. Autonegotiation w/ manual configuration
+               4. Autonegotiation w/ auto configuration
+       - Added ability to force speed and duplex settings.
+       - Made speed, duplex, sa_int, etc, to be set per adapter with
+         ether= command.
+       - Added support for Olicom OC-2326
+
+0.41   - Added non-bounce buffer paths.  Added TLan_FreeLists to
+         dispose of unused sk_buff's at device close time.
+       - Discovered inlined functions aren't being inlined, or at
+         least take up more space than macros would.
+
+0.40   - Refined polarity checking to handle case when polarity
+         changes to normal from abnormal.
+       - Cleaned up TLan_Probe routine.
+       - Added an option for the SA_INTERRUPT flag to be set.
+       - Created FAQ.
+       - Removed all C++ style comments.
+       - Added error message if devices busmastering is inactive.
+         Also will now skip device.
+       - Put cli and sti back into TLan_HandleInterrupt.  It makes
+         me feel better.
+       - Moved the code that checks for boot parameter options to 
+         tlan_probe.
+
+0.39   - Minor cosmetic cleanups (especially variable declarations).
+       - Changes low level TLAN functions to use dev structures instead
+         individual data elements.
+        - Changed low level TLAN functions not to play with sti and cli
+         if in an interrupt routine.
+       - Removed cli and sti from TLan_HandleInterrupt.
+
 0.38   - Added code to isolate the external PHY if the internal PHY is
          being used for AUI/BNC connectivity.  Also set the aui and
          debug variables from mem_start and mem_end if the driver is
@@ -19,7 +66,7 @@ TLan Device Driver change log.
          100Mbs should work now on 0xAE32.
        - Fixed a small bug where heartbeat and PHY interrupts were
          always being enabled.
-       - Force the driver into Unmanaged PHY mode for 0xF130 devices,
+       - Force the the driver into Unmanaged PHY mode for 0xF130 devices,
          even if a managed (ie, the built-in one) PHY is detected.
        - Moved the PHY initialization to after the onboard PHY is enabled,
          if selected.
index 19896ff95fc751c93a75d3c159b825eeac967198..15c1bcac9388a199aedacccb539614a28ccd5a2c 100644 (file)
@@ -405,7 +405,7 @@ do_plx_dma(
                 */
                udelay(1);
 
-               csr = (volatile) priv->vplxdma[PLX_DMA_CSR/4];
+               csr = (volatile int) priv->vplxdma[PLX_DMA_CSR/4];
 
                 if (csr & PLX_DMA_CSR_0_DONE)
                         break;
@@ -876,7 +876,7 @@ dgrs_ioctl(struct device *devN, struct ifreq *ifr, int cmd)
                /* Wait for old command to finish */
                for (i = 0; i < 1000; ++i)
                {
-                       if ( (volatile) privN->bcomm->bc_filter_cmd <= 0 )
+                       if ( (volatile int) privN->bcomm->bc_filter_cmd <= 0 )
                                break;
                        udelay(1);
                }
index 141c444846d4209d1bdfd89aceafeec8251b9f5f..7ba12d311d07bc932ec776b76df28d6889f322a1 100644 (file)
@@ -101,7 +101,7 @@ static void e21_reset_8390(struct device *dev);
 static void e21_block_input(struct device *dev, int count,
                                                   struct sk_buff *skb, int ring_offset);
 static void e21_block_output(struct device *dev, int count,
-                                                        const unsigned char *buf, const start_page);
+                                                        const unsigned char *buf, const int start_page);
 static void e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
                                                        int ring_page);
 
@@ -330,7 +330,7 @@ e21_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_off
 
 static void
 e21_block_output(struct device *dev, int count, const unsigned char *buf,
-                                int start_page)
+                                const int start_page)
 {
        short ioaddr = dev->base_addr;
        volatile char *shared_mem = (char *)dev->mem_start;
index b887087fe8ba85a10b11c1a5788737ca80d7a2e8..39fd240c11d3f191d55457b46b663554c5377b9b 100644 (file)
@@ -714,7 +714,7 @@ static int eth16i_check_signature(short ioaddr)
   creg[0] &= 0x0F;      /* Mask collision cnr */
   creg[2] &= 0x7F;      /* Mask DCLEN bit */
 
-#ifdef 0
+#if 0
 /* 
        This was removed because the card was sometimes left to state
        from which it couldn't be find anymore. If there is need
index b2074435822b6c789accf463b848b585aa86719f..c8e361114a9935e109a3ae0d30a0e772e69bad74 100644 (file)
@@ -101,13 +101,13 @@ static int hpp_close(struct device *dev);
 static void hpp_mem_block_input(struct device *dev, int count,
                                                  struct sk_buff *skb, int ring_offset);
 static void hpp_mem_block_output(struct device *dev, int count,
-                                                       const unsigned char *buf, const start_page);
+                                                       const unsigned char *buf, const int start_page);
 static void hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
                                                  int ring_page);
 static void hpp_io_block_input(struct device *dev, int count,
                                                  struct sk_buff *skb, int ring_offset);
 static void hpp_io_block_output(struct device *dev, int count,
-                                                       const unsigned char *buf, const start_page);
+                                                       const unsigned char *buf, const int start_page);
 static void hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
                                                  int ring_page);
 
@@ -385,7 +385,7 @@ hpp_mem_block_input(struct device *dev, int count, struct sk_buff *skb, int ring
    It's always safe to round up, so we do. */
 static void
 hpp_io_block_output(struct device *dev, int count,
-                                       const unsigned char *buf, const start_page)
+                                       const unsigned char *buf, const int start_page)
 {
        int ioaddr = dev->base_addr - NIC_OFFSET;
        outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
@@ -395,7 +395,7 @@ hpp_io_block_output(struct device *dev, int count,
 
 static void
 hpp_mem_block_output(struct device *dev, int count,
-                               const unsigned char *buf, const start_page)
+                               const unsigned char *buf, const int start_page)
 {
        int ioaddr = dev->base_addr - NIC_OFFSET;
        int option_reg = inw(ioaddr + HPP_OPTION);
index fc965f77efe4440f621fa4e1e4c882251107f12c..741924f954ef404c3d3a11057990a6b46a02138f 100644 (file)
@@ -65,7 +65,7 @@ static void hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 static void hp_block_input(struct device *dev, int count,
                                        struct sk_buff *skb , int ring_offset);
 static void hp_block_output(struct device *dev, int count,
-                                                       const unsigned char *buf, const start_page);
+                                                       const unsigned char *buf, const int start_page);
 
 static void hp_init_card(struct device *dev);
 
@@ -309,7 +309,7 @@ hp_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offs
 
 static void
 hp_block_output(struct device *dev, int count,
-                               const unsigned char *buf, const start_page)
+                               const unsigned char *buf, const int start_page)
 {
        int nic_base = dev->base_addr;
        int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
index c3e7f6abc97ef60d8b8e93a3c9adb6546c56e6d0..226f077f33b66439735d09dd497aad0d76ac21f3 100644 (file)
@@ -45,7 +45,7 @@ static const char *version =
 #include "8390.h"
 
 /* Set statically or when loading the driver module. */
-static debug = 1;
+static int debug = 1;
 
 /* Some defines that people can play with if so inclined. */
 
index c72805a0bf930e72662a185e596289d34605b740..6d486e95d0525cdc299216e59bedf33bfef9a994 100644 (file)
@@ -984,7 +984,7 @@ static void ni52_rcv_int(struct device *dev)
   }
 #endif
 
-#ifdef 0
+#if 0
   if(!at_least_one)
   { 
     int i;
index 9bf545be47dca894e1f43fc42fa3f81230c495ef..074a235bfdda392576f8846a658439deea338b55 100644 (file)
@@ -75,13 +75,13 @@ static void ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 static void ultra_block_input(struct device *dev, int count,
                                                  struct sk_buff *skb, int ring_offset);
 static void ultra_block_output(struct device *dev, int count,
-                                                       const unsigned char *buf, const start_page);
+                                                       const unsigned char *buf, const int start_page);
 static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
                                                int ring_page);
 static void ultra_pio_input(struct device *dev, int count,
                                                  struct sk_buff *skb, int ring_offset);
 static void ultra_pio_output(struct device *dev, int count,
-                                                        const unsigned char *buf, const start_page);
+                                                        const unsigned char *buf, const int start_page);
 static int ultra_close_card(struct device *dev);
 
 \f
@@ -385,7 +385,7 @@ static void ultra_pio_input(struct device *dev, int count,
 }
 
 static void ultra_pio_output(struct device *dev, int count,
-                                                       const unsigned char *buf, const start_page)
+                                                       const unsigned char *buf, const int start_page)
 {
        int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
        outb(0x00, ioaddr + IOPA);      /* Set the address, LSB first. */
index 4043b8f24754638f092d762a30c3acbb2872103b..52e0cbd49ca8397f21f9eb93e0db784383cbfd2a 100644 (file)
@@ -68,7 +68,7 @@ static void ultra32_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 static void ultra32_block_input(struct device *dev, int count,
                                struct sk_buff *skb, int ring_offset);
 static void ultra32_block_output(struct device *dev, int count,
-                                const unsigned char *buf, const start_page);
+                                const unsigned char *buf, const int start_page);
 static int ultra32_close(struct device *dev);
 \f
 #define ULTRA32_CMDREG 0       /* Offset to ASIC command register. */
index 1e23421e6ed3cb3ffcd9c5d6f28f7237cd4c8f26..128f0683fee10710902aee32554a7c8a8178201a 100644 (file)
@@ -3,20 +3,22 @@
  *  Linux ThunderLAN Driver
  *
  *  tlan.c
- *  by James Banks, james.banks@caldera.com
+ *  by James Banks
  *
- *  (C) 1997 Caldera, Inc.
+ *  (C) 1997-1998 Caldera, Inc.
  *
  *  This software may be used and distributed according to the terms
  *  of the GNU Public License, incorporated herein by reference.
  *
- ** This file is best viewed/edited with tabstop=4 and colums>=132.
+ ** This file is best viewed/edited with columns>=132.
  *
  ** Useful (if not required) reading:
  *
  *             Texas Instruments, ThunderLAN Programmer's Guide,
  *                     TI Literature Number SPWU013A
  *                     available in PDF format from www.ti.com
+ *             Level One, LXT901 and LXT970 Data Sheets
+ *                     available in PDF format from www.level1.com
  *             National Semiconductor, DP83840A Data Sheet
  *                     available in PDF format from www.national.com
  *             Microchip Technology, 24C01A/02A/04A Data Sheet
@@ -50,48 +52,100 @@ static     int             TLanDevicesInstalled = 0;
 
 static  int            debug = 0;
 static int             aui = 0;
+static int             sa_int = 0;
+static int             bbuf = 0;
+static int             duplex = 0;
+static int             speed = 0;
 static u8              *TLanPadBuffer;
 static char            TLanSignature[] = "TLAN";
 static int             TLanVersionMajor = 0;
-static int             TLanVersionMinor = 38;
+static int             TLanVersionMinor = 43;
 
 
-static TLanPciId TLanDeviceList[] = {
+static TLanAdapterEntry TLanAdapterList[] = {
        { PCI_VENDOR_ID_COMPAQ, 
          PCI_DEVICE_ID_NETELLIGENT_10,
-         "Compaq Netelligent 10"
+         "Compaq Netelligent 10 T PCI UTP",
+         TLAN_ADAPTER_ACTIVITY_LED,
+         0x83
        },
        { PCI_VENDOR_ID_COMPAQ,
          PCI_DEVICE_ID_NETELLIGENT_10_100,
-         "Compaq Netelligent 10/100"
+         "Compaq Netelligent 10/100 TX PCI UTP",
+         TLAN_ADAPTER_ACTIVITY_LED,
+         0x83
        }, 
        { PCI_VENDOR_ID_COMPAQ,
          PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED,
-         "Compaq Integrated NetFlex-3/P"
+         "Compaq Integrated NetFlex-3/P",
+         TLAN_ADAPTER_NONE,
+         0x83
        }, 
        { PCI_VENDOR_ID_COMPAQ,
          PCI_DEVICE_ID_NETFLEX_3P,
-         "Compaq NetFlex-3/P"
+         "Compaq NetFlex-3/P",
+         TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY,
+         0x83
        },
        { PCI_VENDOR_ID_COMPAQ,
          PCI_DEVICE_ID_NETFLEX_3P_BNC,
-         "Compaq NetFlex-3/P"
+         "Compaq NetFlex-3/P",
+         TLAN_ADAPTER_NONE,
+         0x83
        },
        { PCI_VENDOR_ID_COMPAQ,
          PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT,
-         "Compaq ProLiant Netelligent 10/100"
+         "Compaq Netelligent Integrated 10/100 TX UTP",
+         TLAN_ADAPTER_NONE,
+         0x83
        },
        { PCI_VENDOR_ID_COMPAQ,
          PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL,
-         "Compaq Dual Port Netelligent 10/100"
+         "Compaq Netelligent Dual 10/100 TX PCI UTP",
+         TLAN_ADAPTER_NONE,
+         0x83
        },
        { PCI_VENDOR_ID_COMPAQ,
          PCI_DEVICE_ID_DESKPRO_4000_5233MMX,
-         "Compaq Deskpro 4000 5233MMX"
+         "Compaq Netelligent 10/100 TX Embedded UTP",
+         TLAN_ADAPTER_NONE,
+         0x83
+       },
+       { PCI_VENDOR_ID_OLICOM,
+         PCI_DEVICE_ID_OLICOM_OC2183,
+         "Olicom OC-2183/2185",
+         TLAN_ADAPTER_USE_INTERN_10,
+         0xF8
+       },
+       { PCI_VENDOR_ID_OLICOM,
+         PCI_DEVICE_ID_OLICOM_OC2325,
+         "Olicom OC-2325",
+         TLAN_ADAPTER_UNMANAGED_PHY,
+         0xF8
+       },
+       { PCI_VENDOR_ID_OLICOM,
+         PCI_DEVICE_ID_OLICOM_OC2326,
+         "Olicom OC-2326",
+         TLAN_ADAPTER_USE_INTERN_10,
+         0xF8
+       },
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100,
+         "Compaq Netelligent 10/100 TX UTP",
+         TLAN_ADAPTER_ACTIVITY_LED,
+         0x83
+       },
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_NETELLIGENT_10_T2,
+         "Compaq Netelligent 10 T/2 PCI UTP/Coax",
+         TLAN_ADAPTER_NONE,
+         0x83
        },
        { 0,
          0,
-         NULL
+         NULL,
+         0,
+         0
        } /* End of List */
 };
 
@@ -117,28 +171,37 @@ static u32        TLan_HandleRxEOC( struct device *, u16 );
 static void    TLan_Timer( unsigned long );
 
 static void    TLan_ResetLists( struct device * );
+static void    TLan_FreeLists( struct device * );
 static void    TLan_PrintDio( u16 );
 static void    TLan_PrintList( TLanList *, char *, int );
 static void    TLan_ReadAndClearStats( struct device *, int );
-static int     TLan_Reset( struct device * );
+static void    TLan_ResetAdapter( struct device * );
+static void    TLan_FinishReset( struct device * );
 static void    TLan_SetMac( struct device *, int areg, char *mac );
 
-static int     TLan_PhyNop( struct device * );
 static void    TLan_PhyPrint( struct device * );
-static void    TLan_PhySelect( struct device * );
+static void    TLan_PhyDetect( struct device * );
+static void    TLan_PhyPowerDown( struct device * );
+static void    TLan_PhyPowerUp( struct device * );
+static void    TLan_PhyReset( struct device * );
+static void    TLan_PhyStartLink( struct device * );
+static void    TLan_PhyFinishAutoNeg( struct device * );
+/*
+static int     TLan_PhyNop( struct device * );
 static int     TLan_PhyInternalCheck( struct device * );
 static int     TLan_PhyInternalService( struct device * );
 static int     TLan_PhyDp83840aCheck( struct device * );
+*/
 
-static int     TLan_MiiReadReg(u16, u16, u16, u16 *);
+static int     TLan_MiiReadReg( struct device *, u16, u16, u16 * );
 static void    TLan_MiiSendData( u16, u32, unsigned );
-static void    TLan_MiiSync(u16);
-static void    TLan_MiiWriteReg(u16, u16, u16, u16);
+static void    TLan_MiiSync( u16 );
+static void    TLan_MiiWriteReg( struct device *, u16, u16, u16 );
 
 static void    TLan_EeSendStart( u16 );
 static int     TLan_EeSendByte( u16, u8, int );
 static void    TLan_EeReceiveByte( u16, u8 *, int );
-static int     TLan_EeReadByte( u16, u8, u8 * );
+static int     TLan_EeReadByte( struct device *, u8, u8 * );
 
 
 static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
@@ -152,6 +215,25 @@ static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
        TLan_HandleRxEOC
 };
 
+static inline void
+TLan_SetTimer( struct device *dev, u32 ticks, u32 type )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+       cli();
+       if ( priv->timer.function != NULL ) {
+               return;
+       }
+       priv->timer.function = &TLan_Timer;
+       sti();
+
+       priv->timer.data = (unsigned long) dev;
+       priv->timer.expires = jiffies + ticks;
+       priv->timerSetAt = jiffies;
+       priv->timerType = type;
+       add_timer( &priv->timer );
+
+} /* TLan_SetTimer */
 
 
 /*****************************************************************************
@@ -177,7 +259,7 @@ static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
         *
         *      This function begins the setup of the driver creating a
         *      pad buffer, finding all TLAN devices (matching
-        *      TLanDeviceList entries), and creating and initializing a
+        *      TLanAdapterList entries), and creating and initializing a
         *      device structure for each adapter.
         *
         **************************************************************/
@@ -189,14 +271,14 @@ extern int init_module(void)
        struct device   *dev;
        size_t          dev_size;
        u8              dfn;
-       u32             dl_ix;
+       u32             index;
        int             failed;
        int             found;
        u32             io_base;
        u8              irq;
        u8              rev;
 
-       printk( "TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n",
+       printk( "TLAN driver, v%d.%d, (C) 1997-8 Caldera, Inc.\n",
                TLanVersionMajor,
                TLanVersionMinor
              );
@@ -212,7 +294,7 @@ extern int init_module(void)
 
        dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
 
-       while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) {
+       while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ) ) ) {
                dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
                if ( dev == NULL ) {
                        printk( "TLAN:  Could not allocate memory for device.\n" );
@@ -227,27 +309,41 @@ extern int init_module(void)
                dev->irq = irq;
                dev->init = TLan_Init;
 
-               priv->pciBus = bus;
-               priv->pciDeviceFn = dfn;
-               priv->pciRevision = rev;
-               priv->pciEntry = dl_ix;
+               priv->adapter =    &TLanAdapterList[index];
+               priv->adapterRev = rev;
+               priv->aui =        aui;
+               if ( ( duplex != 1 ) && ( duplex != 2 ) ) {
+                       duplex = 0;
+               }
+               priv->duplex =     duplex;
+               if ( ( speed != 10 ) && ( speed != 100 ) ) {
+                       speed = 0;
+               }
+               priv->speed =      speed;
+               priv->sa_int =     sa_int;
+               priv->debug =      debug;
 
                ether_setup( dev );
 
                failed = register_netdev( dev );
 
                if ( failed ) {
-                       printk( "TLAN:  Could not register network device.  Freeing struct.\n" );
+                       printk( "TLAN:  Could not register device.\n" );
                        kfree( dev );
                } else {
                        priv->nextDevice = TLanDevices;
                        TLanDevices = dev;
                        TLanDevicesInstalled++;
-                       printk("TLAN:  %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName );
+                       printk("TLAN:  %s irq=%2d io=%04x, %s, Rev. %d\n",
+                               dev->name,
+                               (int) dev->irq,
+                               (int) dev->base_addr,
+                               priv->adapter->deviceLabel,
+                               priv->adapterRev );
                }
        }
        
-       // printk( "TLAN:  Found %d device(s).\n", TLanDevicesInstalled );
+       /* printk( "TLAN:  Found %d device(s).\n", TLanDevicesInstalled ); */
 
     return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
 
@@ -276,11 +372,12 @@ extern void cleanup_module(void)
        struct device   *dev;
        TLanPrivateInfo *priv;
 
-       while (TLanDevicesInstalled) {
+       while ( TLanDevicesInstalled ) {
                dev = TLanDevices;
                priv = (TLanPrivateInfo *) dev->priv;
-               if ( priv->dmaStorage )
+               if ( priv->dmaStorage ) {
                        kfree( priv->dmaStorage );
+               }
                release_region( dev->base_addr, 0x10 );
                unregister_netdev( dev );
                TLanDevices = priv->nextDevice;
@@ -315,53 +412,79 @@ extern void cleanup_module(void)
         
 extern int tlan_probe( struct device *dev )
 {
+       TLanPrivateInfo *priv;
        static int      pad_allocated = 0;
        int             found;
-       TLanPrivateInfo *priv;
        u8              bus, dfn, irq, rev;
-       u32             io_base, dl_ix;
+       u32             io_base, index;
 
-       found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix );
-       if ( found ) {
-               dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
-               if ( dev->priv == NULL ) {
-                       printk( "TLAN:  Could not allocate memory for device.\n" );
+       found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index );
+
+       if ( ! found ) {
+               return -ENODEV;
+       }
+
+       dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
+       
+       if ( dev->priv == NULL ) {
+               printk( "TLAN:  Could not allocate memory for device.\n" );
+               return  -ENOMEM;
+       }
+
+       memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
+
+       if ( ! pad_allocated ) {
+               TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, 
+//                                             ( GFP_KERNEL | GFP_DMA )
+                                               ( GFP_KERNEL )
+                                             );
+               if ( TLanPadBuffer == NULL ) {
+                       printk( "TLAN:  Could not allocate memory for padding.\n" );
+                       kfree( dev->priv );
+                       return -ENOMEM;
+               } else {
+                       pad_allocated = 1;
+                       memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
                }
-               memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
-               priv = (TLanPrivateInfo *) dev->priv;
+       }
 
-               dev->name = priv->devName;
-               strcpy( priv->devName, "    " );
+       priv = (TLanPrivateInfo *) dev->priv;
 
-               dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
+       dev->name = priv->devName;
+       strcpy( priv->devName, "    " );
 
-               dev->base_addr = io_base;
-               dev->irq = irq;
+       dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
 
-               priv->pciBus = bus;
-               priv->pciDeviceFn = dfn;
-               priv->pciRevision = rev;
-               priv->pciEntry = dl_ix;
+       dev->base_addr = io_base;
+       dev->irq = irq;
 
-               if ( ! pad_allocated ) {
-                       TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
-                       if ( TLanPadBuffer == NULL ) {
-                               printk( "TLAN:  Could not allocate memory for pad buffer.\n" );
-                       } else {
-                               pad_allocated = 1;
-                               memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
-                       }
-               }
-               printk("TLAN %d.%d:  %s irq=%2d io=%04x, %s\n",TLanVersionMajor,
-                       TLanVersionMinor,
-                       dev->name, 
-                       (int) irq, 
-                       io_base, 
-                       TLanDeviceList[dl_ix].deviceName );
-               TLan_Init( dev );
+
+       priv->adapter =    &TLanAdapterList[index];
+       priv->adapterRev = rev;
+       priv->aui =        dev->mem_start & 0x01;
+       priv->duplex =     ( ( dev->mem_start & 0x0C ) == 0x0C ) ? 0 : ( dev->mem_start & 0x0C ) >> 2;
+       priv->speed =      ( ( dev->mem_start & 0x30 ) == 0x30 ) ? 0 : ( dev->mem_start & 0x30 ) >> 4;
+       if ( priv->speed == 0x1 ) {
+               priv->speed = TLAN_SPEED_10;
+       } else if ( priv->speed == 0x2 ) {
+               priv->speed = TLAN_SPEED_100;
        }
+       priv->sa_int =     dev->mem_start & 0x02;
+       priv->debug =      dev->mem_end;
+
+
+       printk("TLAN %d.%d:  %s irq=%2d io=%04x, %s, Rev. %d\n",
+               TLanVersionMajor,
+               TLanVersionMinor,
+               dev->name, 
+               (int) irq, 
+               io_base,
+               priv->adapter->deviceLabel,
+               priv->adapterRev );
+
+       TLan_Init( dev );
                        
-       return ( ( found ) ? 0 : -ENODEV );
+       return 0;
 
 } /* tlan_probe */
 
@@ -389,7 +512,7 @@ extern int tlan_probe( struct device *dev )
         *                              of the adapter.
         *
         *      This function searches for an adapter with PCI vendor
-        *      and device IDs matching those in the TLanDeviceList.
+        *      and device IDs matching those in the TLanAdapterList.
         *      The function 'remembers' the last device it found,
         *      and so finds a new device (if anymore are to be found)
         *      each time the function is called.  It then looks up
@@ -413,11 +536,11 @@ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_
                return 0;
        }
 
-       for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) {
+       for (; TLanAdapterList[dl_index].vendorId != 0; dl_index++) {
 
                not_found = pcibios_find_device(
-                       TLanDeviceList[dl_index].vendorId,
-                       TLanDeviceList[dl_index].deviceId,
+                       TLanAdapterList[dl_index].vendorId,
+                       TLanAdapterList[dl_index].deviceId,
                        pci_index,
                        pci_bus,
                        pci_dfn
@@ -428,8 +551,8 @@ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_
                        TLAN_DBG(
                                TLAN_DEBUG_GNRL,
                                "TLAN:  found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
-                               TLanDeviceList[dl_index].vendorId,
-                               TLanDeviceList[dl_index].deviceId
+                               TLanAdapterList[dl_index].vendorId,
+                               TLanAdapterList[dl_index].deviceId
                        );
 
                        pcibios_read_config_byte ( *pci_bus,  *pci_dfn, PCI_REVISION_ID, pci_rev);
@@ -457,8 +580,12 @@ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_
                        if ( *pci_io_base == 0 )
                                printk("TLAN:    IO mapping not available, ignoring device.\n");
 
-                       if (pci_command & PCI_COMMAND_MASTER) {
-                               TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:    Bus mastering is active.\n");
+                       if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) {
+                               pcibios_write_config_word ( *pci_bus,  *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER );
+                               printk( "TLAN:  Attempting to activate busmastering.\n" );
+                               printk( "TLAN:    You may need to set busmastering to on in the CMOS\n" );
+                               printk( "TLAN:    before this card will work.\n" );
+                               *pci_io_base = 0;
                        }
 
                        pci_index++;
@@ -499,9 +626,9 @@ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_
 
 int TLan_Init( struct device *dev )
 {
-       int                             dma_size;
-       int                             err;
-       int                             i;
+       int             dma_size;
+       int             err;
+       int             i;
        TLanPrivateInfo *priv;
 
        priv = (TLanPrivateInfo *) dev->priv;
@@ -516,8 +643,14 @@ int TLan_Init( struct device *dev )
        }
        request_region( dev->base_addr, 0x10, TLanSignature );
 
-       dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+       if ( bbuf ) {
+               dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
                   * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
+       } else {
+               dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+                  * ( sizeof(TLanList) );
+       }
+
        priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
        if ( priv->dmaStorage == NULL ) {
                printk( "TLAN:  Could not allocate lists and buffers for %s.\n",
@@ -528,19 +661,24 @@ int TLan_Init( struct device *dev )
        priv->rxList = (TLanList *) 
                       ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
        priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
-       priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
-       priv->txBuffer = priv->rxBuffer
-                        + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+
+       if ( bbuf ) {
+               priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+               priv->txBuffer = priv->rxBuffer
+                                + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+       }
 
        err = 0;
        for ( i = 0;  i < 6 ; i++ )
-               err |= TLan_EeReadByte( dev->base_addr,
-                                       (u8) 0x83 + i,
+               err |= TLan_EeReadByte( dev,
+                                       (u8) priv->adapter->addrOfs + i,
                                        (u8 *) &dev->dev_addr[i] );
-       if ( err )
+       if ( err ) {
                printk( "TLAN:  %s: Error reading MAC from eeprom: %d\n",
                        dev->name,
                        err );
+       }
+
        dev->addr_len = 6;
 
        dev->open = &TLan_Open;
@@ -549,12 +687,6 @@ int TLan_Init( struct device *dev )
        dev->get_stats = &TLan_GetStats;
        dev->set_multicast_list = &TLan_SetMulticastList;
 
-#ifndef MODULE
-
-       aui = dev->mem_start & 0x01;
-       debug = dev->mem_end;
-
-#endif /* MODULE */
 
        return 0;
 
@@ -582,16 +714,21 @@ int TLan_Init( struct device *dev )
 
 int TLan_Open( struct device *dev )
 {
-       int                             err;
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       int             err;
 
        priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
-       err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+       if ( priv->sa_int ) {
+               TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:   Using SA_INTERRUPT\n" ); 
+               err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev );
+       } else {
+               err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+       }
        if ( err ) {
                printk( "TLAN:  Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
                return -EAGAIN;
        }
-
+       
        MOD_INC_USE_COUNT;
 
        dev->tbusy = 0;
@@ -603,27 +740,9 @@ int TLan_Open( struct device *dev )
        */
        TLan_ResetLists( dev );
        TLan_ReadAndClearStats( dev, TLAN_IGNORE );
-       TLan_Reset( dev );
-       TLan_Reset( dev );
-       TLan_SetMac( dev, 0, dev->dev_addr );
-       outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
-       if ( debug >= 1 )
-               outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
-
-       init_timer( &priv->timer );
-       priv->timer.data = (unsigned long) dev;
-       priv->timer.function = &TLan_Timer;
-       if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
-               priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
-               priv->timerSetAt = jiffies;
-               priv->timerType = TLAN_TIMER_LINK;
-               add_timer( &priv->timer );
-       } else {
-               outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
-               outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
-       }
+       TLan_ResetAdapter( dev );
 
-       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  Device %s opened.  Revision = %x\n", dev->name, priv->tlanRev );
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Opened.  TLAN Chip Rev: %x\n", dev->name, priv->tlanRev );
 
        return 0;
 
@@ -656,9 +775,9 @@ int TLan_Open( struct device *dev )
 int TLan_StartTx( struct sk_buff *skb, struct device *dev )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-       TLanList                *tail_list;
-       u8                              *tail_buffer;
-       int                             pad;
+       TLanList        *tail_list;
+       u8              *tail_buffer;
+       int             pad;
 
        if ( ! priv->phyOnline ) {
                TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s PHY is not ready\n", dev->name );
@@ -667,16 +786,26 @@ int TLan_StartTx( struct sk_buff *skb, struct device *dev )
        }
 
        tail_list = priv->txList + priv->txTail;
+
        if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
                TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail );
                dev->tbusy = 1;
                priv->txBusyCount++;
                return 1;
        }
+
        tail_list->forward = 0;
-       tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
-       memcpy( tail_buffer, skb->data, skb->len );
+
+       if ( bbuf ) {
+               tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+               memcpy( tail_buffer, skb->data, skb->len );
+       } else {
+               tail_list->buffer[0].address = virt_to_bus( skb->data );
+               tail_list->buffer[9].address = (u32) skb;
+       }
+
        pad = TLAN_MIN_FRAME_SIZE - skb->len;
+
        if ( pad > 0 ) {
                tail_list->frameSize = (u16) skb->len + pad;
                tail_list->buffer[0].count = (u32) skb->len;
@@ -688,7 +817,7 @@ int TLan_StartTx( struct sk_buff *skb, struct device *dev )
                tail_list->buffer[1].count = 0;
                tail_list->buffer[1].address = 0;
        }
-       // are we transferring?
+
        cli();
        tail_list->cStat = TLAN_CSTAT_READY;
        if ( ! priv->txInProgress ) {
@@ -699,17 +828,19 @@ int TLan_StartTx( struct sk_buff *skb, struct device *dev )
                outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
        } else {
                TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Adding buffer %d to TX channel\n", priv->txTail );
-               if ( priv->txTail == 0 )
+               if ( priv->txTail == 0 ) {
                        ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
-               else
+               } else {
                        ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
+               }
        }
        sti();
-       priv->txTail++;
-       if ( priv->txTail >= TLAN_NUM_TX_LISTS )
-               priv->txTail = 0;
 
-       dev_kfree_skb( skb, FREE_WRITE );
+       CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS );
+
+       if ( bbuf ) {
+               dev_kfree_skb( skb, FREE_WRITE );
+       }
                
        dev->trans_start = jiffies;
        return 0;
@@ -742,35 +873,34 @@ int TLan_StartTx( struct sk_buff *skb, struct device *dev )
 
 void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-       u32                             ack;
+       u32             ack;
        struct device   *dev;
-       u32                             host_cmd;
-       u16                             host_int;
-       int                             type;
+       u32             host_cmd;
+       u16             host_int;
+       int             type;
 
        dev = (struct device *) dev_id;
 
-       if ( dev->interrupt )
+       cli();
+       if ( dev->interrupt ) {
                printk( "TLAN:   Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
+       }
        dev->interrupt++;
 
-       cli();
-
        host_int = inw( dev->base_addr + TLAN_HOST_INT );
-       outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints
+       outw( host_int, dev->base_addr + TLAN_HOST_INT );
 
        type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
 
        ack = TLanIntVector[type]( dev, host_int );
 
-       sti();
-
        if ( ack ) {
                host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
                outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
        }
 
        dev->interrupt--;
+       sti();
 
 } /* TLan_HandleInterrupts */
 
@@ -801,10 +931,11 @@ int TLan_Close(struct device *dev)
 
        TLan_ReadAndClearStats( dev, TLAN_RECORD );
        outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
-       if ( priv->timerSetAt != 0 )
+       if ( priv->timer.function != NULL )
                del_timer( &priv->timer );
        free_irq( dev->irq, dev );
-       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:   Device %s closed.\n", dev->name );
+       TLan_FreeLists( dev );
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  Device %s closed.\n", dev->name );
 
        MOD_DEC_USE_COUNT;
 
@@ -881,11 +1012,11 @@ struct net_device_stats *TLan_GetStats( struct device *dev )
 void TLan_SetMulticastList( struct device *dev )
 {      
        struct dev_mc_list      *dmi = dev->mc_list;
-       u32                                     hash1 = 0;
-       u32                                     hash2 = 0;
-       int                                     i;
-       u32                                     offset;
-       u8                                      tmp;
+       u32                     hash1 = 0;
+       u32                     hash2 = 0;
+       int                     i;
+       u32                     offset;
+       u8                      tmp;
 
        if ( dev->flags & IFF_PROMISC ) {
                tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
@@ -955,7 +1086,7 @@ void TLan_SetMulticastList( struct device *dev )
 u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
 {
        host_int = 0;
-       // printk( "TLAN:  Invalid interrupt on %s.\n", dev->name );
+       /* printk( "TLAN:  Invalid interrupt on %s.\n", dev->name ); */
        return 0;
 
 } /* TLan_HandleInvalid */
@@ -988,19 +1119,24 @@ u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
 u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-       int                             eoc = 0;
-       TLanList                *head_list;
-       u32                             ack = 1;
+       int             eoc = 0;
+       TLanList        *head_list;
+       u32             ack = 1;
 
        TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
        host_int = 0;
        head_list = priv->txList + priv->txHead;
+
+       if ( ! bbuf ) {
+               dev_kfree_skb( (struct sk_buff *) head_list->buffer[9].address, FREE_WRITE );
+               head_list->buffer[9].address = 0;
+       }
+
        if ( head_list->cStat & TLAN_CSTAT_EOC )
                eoc = 1;
        if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
                printk( "TLAN:  Received interrupt for uncompleted TX frame.\n" );
        }
-       // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat );
 
 #if LINUX_KERNEL_VERSION > 0x20100
        priv->stats->tx_bytes += head_list->frameSize;
@@ -1008,9 +1144,7 @@ u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
 
        head_list->cStat = TLAN_CSTAT_UNUSED;
        dev->tbusy = 0;
-       priv->txHead++;
-       if ( priv->txHead >= TLAN_NUM_TX_LISTS )
-               priv->txHead = 0;
+       CIRC_INC( priv->txHead, TLAN_NUM_TX_LISTS );
        if ( eoc ) {
                TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
                head_list = priv->txList + priv->txHead;
@@ -1021,17 +1155,13 @@ u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
                        priv->txInProgress = 0;
                }
        }
-       TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
-       if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
-               if ( priv->timerSetAt == 0 ) {
-                       // printk("TxEOF Starting timer...\n");
-                       priv->timerSetAt = jiffies;
-                       priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
-                       priv->timerType = TLAN_TIMER_ACT;
-                       add_timer( &priv->timer );
-               } else if ( priv->timerType == TLAN_TIMER_ACT ) {
+
+       if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
+               TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+               if ( priv->timer.function == NULL ) {
+                       TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+               } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
                        priv->timerSetAt = jiffies;
-                       // printk("TxEOF continuing timer...\n");
                }
        }
 
@@ -1092,7 +1222,7 @@ u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int )
         *      of the list.  If the frame was the last in the Rx
         *      channel (EOC), the function restarts the receive channel
         *      by sending an Rx Go command to the adapter.  Then it
-        *      activates/continues the activity LED.
+        *      activates/continues the the activity LED.
         *
         **************************************************************/
 
@@ -1111,11 +1241,14 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
        host_int = 0;
        head_list = priv->rxList + priv->rxHead;
        tail_list = priv->rxList + priv->rxTail;
-       if ( head_list->cStat & TLAN_CSTAT_EOC )
+
+       if ( head_list->cStat & TLAN_CSTAT_EOC ) {
                eoc = 1;
+       }
+
        if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
                printk( "TLAN:  Received interrupt for uncompleted RX frame.\n" );
-       } else {
+       } else if ( bbuf ) {
                skb = dev_alloc_skb( head_list->frameSize + 7 );
                if ( skb == NULL ) { 
                        printk( "TLAN:  Couldn't allocate memory for received data.\n" );
@@ -1124,7 +1257,6 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
                        skb->dev = dev;
                        skb_reserve( skb, 2 );
                        t = (void *) skb_put( skb, head_list->frameSize );
-                       // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t );
 
 #if LINUX_KERNEL_VERSION > 0x20100
                        priv->stats->rx_bytes += head_list->frameSize;
@@ -1134,17 +1266,39 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
                        skb->protocol = eth_type_trans( skb, dev );
                        netif_rx( skb );
                }
+       } else {
+               skb = (struct sk_buff *) head_list->buffer[9].address;
+               head_list->buffer[9].address = 0;
+               skb_trim( skb, head_list->frameSize );
+
+#if LINUX_KERNEL_VERSION > 0x20100
+                       priv->stats->rx_bytes += head_list->frameSize;
+#endif
+
+               skb->protocol = eth_type_trans( skb, dev );
+               netif_rx( skb );
+
+               skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
+               if ( skb == NULL ) {
+                       printk( "TLAN:  Couldn't allocate memory for received data.\n" );
+                       /* If this ever happened it would be a problem */
+               } else {
+                       skb->dev = dev;
+                       skb_reserve( skb, 2 );
+                       t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
+                       head_list->buffer[0].address = virt_to_bus( t );
+                       head_list->buffer[9].address = (u32) skb;
+               }
        }
+
        head_list->forward = 0;
        head_list->frameSize = TLAN_MAX_FRAME_SIZE;
        head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
        tail_list->forward = virt_to_bus( head_list );
-       priv->rxHead++;
-       if ( priv->rxHead >= TLAN_NUM_RX_LISTS )
-               priv->rxHead = 0;
-       priv->rxTail++;
-       if ( priv->rxTail >= TLAN_NUM_RX_LISTS )
-               priv->rxTail = 0;
+
+       CIRC_INC( priv->rxHead, TLAN_NUM_RX_LISTS );
+       CIRC_INC( priv->rxTail, TLAN_NUM_RX_LISTS );
+
        if ( eoc ) { 
                TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
                head_list = priv->rxList + priv->rxHead;
@@ -1152,19 +1306,16 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
                ack |= TLAN_HC_GO | TLAN_HC_RT;
                priv->rxEocCount++;
        }
-       TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
-       if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
-               if ( priv->timerSetAt == 0 )  {
-                       // printk("RxEOF Starting timer...\n");
-                       priv->timerSetAt = jiffies;
-                       priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
-                       priv->timerType = TLAN_TIMER_ACT;
-                       add_timer( &priv->timer );
-               } else if ( priv->timerType == TLAN_TIMER_ACT ) {
-                       // printk("RxEOF tarting continuing timer...\n");
+
+       if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
+               TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+               if ( priv->timer.function == NULL )  {
+                       TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+               } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
                        priv->timerSetAt = jiffies;
                }
        }
+
        dev->last_rx = jiffies;
 
        return ack;
@@ -1194,7 +1345,7 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
 u32 TLan_HandleDummy( struct device *dev, u16 host_int )
 {
        host_int = 0;
-       printk( "TLAN:  Dummy interrupt on %s.\n", dev->name );
+       printk( "TLAN:  Test interrupt on %s.\n", dev->name );
        return 1;
 
 } /* TLan_HandleDummy */
@@ -1269,45 +1420,49 @@ u32 TLan_HandleTxEOC( struct device *dev, u16 host_int )
 
 u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
 {      
-       u32                             ack;
-       u32                             error;
-       u8                              net_sts;
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u32             ack;
+       u32             error;
+       u8              net_sts;
+       u32             phy;
+       u16             tlphy_ctl;
+       u16             tlphy_sts;
        
        ack = 1;
        if ( host_int & TLAN_HI_IV_MASK ) {
                error = inl( dev->base_addr + TLAN_CH_PARM );
-               printk( "TLAN:   Adaptor Check on device %s err = 0x%x\n", dev->name, error );
+               printk( "TLAN:  %s: Adaptor Error = 0x%x\n", dev->name, error );
                TLan_ReadAndClearStats( dev, TLAN_RECORD );
                outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+               TLan_FreeLists( dev );
                TLan_ResetLists( dev );
-               TLan_Reset( dev );
+               TLan_ResetAdapter( dev );
                dev->tbusy = 0;
-               TLan_SetMac( dev, 0, dev->dev_addr );
-               if ( priv->timerType == 0 ) {
-                       if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
-                               priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
-                               priv->timerSetAt = jiffies;
-                               priv->timerType = TLAN_TIMER_LINK;
-                               add_timer( &priv->timer );
-                       } else {
-                               //printk( " RX GO---->\n" );
-                               outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
-                               outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
-                       }
-               }
                ack = 0;
        } else {
+               TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Status Check\n", dev->name );
+               phy = priv->phy[priv->phyNum];
+
                net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
-               if ( net_sts )
+               if ( net_sts ) {
                        TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
-               if ( net_sts & TLAN_NET_STS_MIRQ ) {
-                       (*priv->phyService)( dev );
+                       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s:    Net_Sts = %x\n", dev->name, (unsigned) net_sts );
+               }
+               if ( ( net_sts & TLAN_NET_STS_MIRQ ) &&  ( priv->phyNum == 0 ) ) {
+                       TLan_MiiReadReg( dev, phy, TLAN_TLPHY_STS, &tlphy_sts );
+                       TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
+                       if ( ! ( tlphy_sts & TLAN_TS_POLOK ) && ! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+                               tlphy_ctl |= TLAN_TC_SWAPOL;
+                               TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+                       } else if ( ( tlphy_sts & TLAN_TS_POLOK ) && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+                               tlphy_ctl &= ~TLAN_TC_SWAPOL;
+                               TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+                       }
+
                        if (debug) {
                                TLan_PhyPrint( dev );
                        }
                }
-               TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts );
        }
 
        return ack;
@@ -1340,8 +1495,8 @@ u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
 u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-       TLanList                *head_list;
-       u32                             ack = 1;
+       TLanList        *head_list;
+       u32             ack = 1;
 
        host_int = 0;
        if (  priv->tlanRev < 0x30 ) {
@@ -1401,34 +1556,44 @@ u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
 void TLan_Timer( unsigned long data )
 {
        struct device   *dev = (struct device *) data;
-       u16             gen_sts;
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u32             elapsed;
 
-       // printk( "TLAN:  %s Entered Timer, type = %d\n", dev->name, priv->timerType );
+       priv->timer.function = NULL;
 
        switch ( priv->timerType ) {
-               case TLAN_TIMER_LINK:
-                       TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts );
-                       if ( gen_sts & MII_GS_LINK ) {
-                               priv->phyOnline = 1;
-                               outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
-                               outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
-                               priv->timerSetAt = 0;
-                               priv->timerType = 0;
-                       } else {
-                               priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 );
-                               add_timer( &priv->timer );
-                       }
+               case TLAN_TIMER_PHY_PDOWN:
+                       TLan_PhyPowerDown( dev );
                        break;
-               case TLAN_TIMER_ACT:
-                       if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) {
-                               TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
-                               priv->timerSetAt = 0;
-                               priv->timerType = 0;
-                       } else {
-                               priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
-                               add_timer( &priv->timer );
+               case TLAN_TIMER_PHY_PUP:
+                       TLan_PhyPowerUp( dev );
+                       break;
+               case TLAN_TIMER_PHY_RESET:
+                       TLan_PhyReset( dev );
+                       break;
+               case TLAN_TIMER_PHY_START_LINK:
+                       TLan_PhyStartLink( dev );
+                       break;
+               case TLAN_TIMER_PHY_FINISH_AN:
+                       TLan_PhyFinishAutoNeg( dev );
+                       break;
+               case TLAN_TIMER_FINISH_RESET:
+                       TLan_FinishReset( dev );
+                       break;
+               case TLAN_TIMER_ACTIVITY:
+                       cli();
+                       if ( priv->timer.function == NULL ) {
+                               elapsed = jiffies - priv->timerSetAt;
+                               if ( elapsed >= TLAN_TIMER_ACT_DELAY ) {
+                                       TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+                               } else  {
+                                       priv->timer.function = &TLan_Timer;
+                                       priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
+                                       sti();
+                                       add_timer( &priv->timer );
+                               }
                        }
+                       sti();
                        break;
                default:
                        break;
@@ -1467,13 +1632,19 @@ void TLan_ResetLists( struct device *dev )
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
        int             i;
        TLanList        *list;
+       struct sk_buff  *skb;
+       void            *t = NULL;
 
        priv->txHead = 0;
        priv->txTail = 0;
        for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
                list = priv->txList + i;
                list->cStat = TLAN_CSTAT_UNUSED;
-               list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+               if ( bbuf ) {
+                       list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+               } else {
+                       list->buffer[0].address = 0;
+               }
                list->buffer[2].count = 0;
                list->buffer[2].address = 0;
        }
@@ -1485,7 +1656,21 @@ void TLan_ResetLists( struct device *dev )
                list->cStat = TLAN_CSTAT_READY;
                list->frameSize = TLAN_MAX_FRAME_SIZE;
                list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
-               list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+               if ( bbuf ) {
+                       list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+               } else {
+                       skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
+                       if ( skb == NULL ) {
+                               printk( "TLAN:  Couldn't allocate memory for received data.\n" );
+                               /* If this ever happened it would be a problem */
+                       } else {
+                               skb->dev = dev;
+                               skb_reserve( skb, 2 );
+                               t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
+                       }
+                       list->buffer[0].address = virt_to_bus( t );
+                       list->buffer[9].address = (u32) skb;
+               }
                list->buffer[1].count = 0;
                list->buffer[1].address = 0;
                if ( i < TLAN_NUM_RX_LISTS - 1 )
@@ -1497,6 +1682,36 @@ void TLan_ResetLists( struct device *dev )
 } /* TLan_ResetLists */
 
 
+void TLan_FreeLists( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       int             i;
+       TLanList        *list;
+       struct sk_buff  *skb;
+
+       if ( ! bbuf ) {
+               for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+                       list = priv->txList + i;
+                       skb = (struct sk_buff *) list->buffer[9].address;
+                       if ( skb ) {
+                               dev_kfree_skb( skb, FREE_WRITE );
+                               list->buffer[9].address = 0;
+                       }
+               }
+
+               for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+                       list = priv->rxList + i;
+                       skb = (struct sk_buff *) list->buffer[9].address;
+                       if ( skb ) {
+                               dev_kfree_skb( skb, FREE_READ );
+                               list->buffer[9].address = 0;
+                       }
+               }
+       }
+
+} /* TLan_FreeLists */
+
+
 
 
        /***************************************************************
@@ -1508,7 +1723,7 @@ void TLan_ResetLists( struct device *dev )
         *              io_base         Base IO port of the device of
         *                              which to print DIO registers.
         *
-        *      This function prints out all the internal (DIO)
+        *      This function prints out all the the internal (DIO)
         *      registers of a TLAN chip.
         *
         **************************************************************/
@@ -1556,7 +1771,7 @@ void TLan_PrintList( TLanList *list, char *type, int num)
        printk( "TLAN:      Forward    = 0x%08x\n",  list->forward );
        printk( "TLAN:      CSTAT      = 0x%04hx\n", list->cStat );
        printk( "TLAN:      Frame Size = 0x%04hx\n", list->frameSize );
-       // for ( i = 0; i < 10; i++ ) {
+       /* for ( i = 0; i < 10; i++ ) { */
        for ( i = 0; i < 2; i++ ) {
                printk( "TLAN:      Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
        }
@@ -1587,11 +1802,11 @@ void TLan_PrintList( TLanList *list, char *type, int num)
 void TLan_ReadAndClearStats( struct device *dev, int record )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-       u32                             tx_good, tx_under;
-       u32                             rx_good, rx_over;
-       u32                             def_tx, crc, code;
-       u32                             multi_col, single_col;
-       u32                             excess_col, late_col, loss;
+       u32             tx_good, tx_under;
+       u32             rx_good, rx_over;
+       u32             def_tx, crc, code;
+       u32             multi_col, single_col;
+       u32             excess_col, late_col, loss;
 
        outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
        tx_good  = inb( dev->base_addr + TLAN_DIO_DATA );
@@ -1659,7 +1874,8 @@ void TLan_ReadAndClearStats( struct device *dev, int record )
         *
         **************************************************************/
 
-int TLan_Reset( struct device *dev )
+void
+TLan_ResetAdapter( struct device *dev )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
        int             i;
@@ -1667,77 +1883,140 @@ int TLan_Reset( struct device *dev )
        u32             data;
        u8              data8;
 
-//  1. Assert reset bit.
+       priv->tlanFullDuplex = FALSE;
+/*  1. Assert reset bit. */
 
        data = inl(dev->base_addr + TLAN_HOST_CMD);
        data |= TLAN_HC_AD_RST;
        outl(data, dev->base_addr + TLAN_HOST_CMD);
+       
+       udelay(1000);
 
-//  2. Turn off interrupts. ( Probably isn't necessary )
+/*  2. Turn off interrupts. ( Probably isn't necessary ) */
 
        data = inl(dev->base_addr + TLAN_HOST_CMD);
        data |= TLAN_HC_INT_OFF;
        outl(data, dev->base_addr + TLAN_HOST_CMD);
 
-//  3. Clear AREGs and HASHs.
+/*  3. Clear AREGs and HASHs. */
 
        for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) {
                TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
        }
 
-//  4. Setup NetConfig register.
+/*  4. Setup NetConfig register. */
 
        data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
        TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
 
-//  5. Load Ld_Tmr and Ld_Thr in HOST_CMD.
+/*  5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */
 
        outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
        outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
 
-//  6. Unreset the MII by setting NMRST (in NetSio) to 1.
+/*  6. Unreset the MII by setting NMRST (in NetSio) to 1. */
 
        outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
        addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
        TLan_SetBit( TLAN_NET_SIO_NMRST, addr );
 
-//  7. Setup the remaining registers.
+/*  7. Setup the remaining registers. */
 
        if ( priv->tlanRev >= 0x30 ) {
                data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC;
                TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 );
        }
-       TLan_PhySelect( dev );
+       TLan_PhyDetect( dev );
        data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN;
-       if ( priv->phyFlags & TLAN_PHY_BIT_RATE ) {
+       if ( priv->adapter->flags & TLAN_ADAPTER_BIT_RATE_PHY ) {
                data |= TLAN_NET_CFG_BIT;
-               if ( aui == 1 ) {
+               if ( priv->aui == 1 ) {
                        TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a );
+               } else if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+                       TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x00 );
+                       priv->tlanFullDuplex = TRUE;
                } else {
                        TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 );
                }
        }
-       if ( priv->phyFlags & TLAN_PHY_INTERNAL ) {
+       if ( priv->phyNum == 0 ) {
                data |= TLAN_NET_CFG_PHY_EN;
        }
        TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
-       (*priv->phyCheck)( dev ); 
-       data8 = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
-       TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data8 );
-       data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; 
-       if ( priv->phyFlags & TLAN_PHY_INTS ) {
-               data8 |= TLAN_NET_MASK_MASK7; 
-       }
-       TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 );
+
+       if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+               TLan_FinishReset( dev );
+       } else {
+               TLan_PhyPowerDown( dev );
+       }
+
+} /* TLan_ResetAdapter */
+
+
+
+
+void
+TLan_FinishReset( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u8              data;
+       u32             phy;
+       u8              sio;
+       u16             status;
+       u16             tlphy_ctl;
+
+       phy = priv->phy[priv->phyNum];
+
+       data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
+       if ( priv->tlanFullDuplex ) {
+               data |= TLAN_NET_CMD_DUPLEX;
+       }
+       TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data );
+       data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; 
+       if ( priv->phyNum == 0 ) {
+               data |= TLAN_NET_MASK_MASK7; 
+       }
+       TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data );
        TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE );
 
-       if ( priv->phyFlags & TLAN_PHY_UNMANAGED ) {
-               priv->phyOnline = 1;
+       if ( ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) || ( priv->aui ) ) {
+               status = MII_GS_LINK;
+               printk( "TLAN:  %s: Link forced.\n", dev->name );
+       } else {
+               TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+               udelay( 1000 );
+               TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+               if ( status & MII_GS_LINK ) {
+                       printk( "TLAN:  %s: Link active.\n", dev->name );
+                       TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+               }
        }
 
-       return 0;
+       if ( priv->phyNum == 0 ) {
+               TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
+               tlphy_ctl |= TLAN_TC_INTEN;
+               TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl );
+               sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
+               sio |= TLAN_NET_SIO_MINTEN;
+               TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
+       }
+
+       if ( status & MII_GS_LINK ) {
+               TLan_SetMac( dev, 0, dev->dev_addr );
+               priv->phyOnline = 1;
+               outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+               if ( debug >= 1 ) {
+                       outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+               }
+               outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+               outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+       } else {
+               printk( "TLAN:  %s: Link inactive, will retry in 10 secs...\n", dev->name );
+               TLan_SetTimer( dev, 1000, TLAN_TIMER_FINISH_RESET );
+               return;
+       }
 
-} /* TLan_Reset */
+} /* TLan_FinishReset */
 
 
 
@@ -1787,56 +2066,10 @@ void TLan_SetMac( struct device *dev, int areg, char *mac )
 
        ThunderLAN Driver PHY Layer Routines
 
-       The TLAN chip can drive any number of PHYs (physical devices).  Rather
-       than having lots of 'if' or '#ifdef' statements, I have created a
-       second driver layer for the PHYs.  Each PHY can be identified from its
-       id in registers 2 and 3, and can be given a Check and Service routine
-       that will be called when the adapter is reset and when the adapter
-       receives a Network Status interrupt, respectively.
-       
 ******************************************************************************
 *****************************************************************************/
 
 
-static TLanPhyIdEntry  TLanPhyIdTable[] = {
-         { 0x4000,
-           0x5014,
-           &TLan_PhyInternalCheck,
-           &TLan_PhyInternalService,
-           TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
-         { 0x4000,
-           0x5015,
-           &TLan_PhyInternalCheck,
-           &TLan_PhyInternalService,
-           TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
-         { 0x4000,
-           0x5016,
-           &TLan_PhyInternalCheck,
-           &TLan_PhyInternalService,
-           TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
-         { 0x2000,
-           0x5C00,
-           &TLan_PhyDp83840aCheck,
-           &TLan_PhyNop,
-           TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
-         { 0x2000,
-           0x5C01,
-           &TLan_PhyDp83840aCheck,
-           &TLan_PhyNop,
-           TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
-         { 0x7810,
-           0x0000,
-           &TLan_PhyDp83840aCheck,
-           &TLan_PhyNop,
-           TLAN_PHY_AUTONEG },
-         { 0x0000,
-           0x0000,
-           NULL,
-           NULL,
-           0
-         }
-       };
-
 
        /*********************************************************************
         *      TLan_PhyPrint
@@ -1844,10 +2077,10 @@ static TLanPhyIdEntry   TLanPhyIdTable[] = {
         *      Returns:
         *              Nothing
         *      Parms:
-        *              dev     A pointer to the device structure of the adapter
-        *                      which the desired PHY is located.
+        *              dev     A pointer to the device structure of the
+        *                      TLAN device having the PHYs to be detailed.
         *                              
-        *      This function prints the registers a PHY.
+        *      This function prints the registers a PHY (aka tranceiver).
         *
         ********************************************************************/
 
@@ -1855,30 +2088,27 @@ void TLan_PhyPrint( struct device *dev )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
        u16 i, data0, data1, data2, data3, phy;
-       u32 io;
 
-       phy = priv->phyAddr;
-       io = dev->base_addr;
+       phy = priv->phy[priv->phyNum];
 
-       if ( ( phy > 0 ) && ( phy <= TLAN_PHY_MAX_ADDR ) ) {
+       if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+               printk( "TLAN:   Device %s, Unmanaged PHY.\n", dev->name );
+       } else if ( phy <= TLAN_PHY_MAX_ADDR ) {
                printk( "TLAN:   Device %s, PHY 0x%02x.\n", dev->name, phy );
                printk( "TLAN:      Off.  +0     +1     +2     +3 \n" );
                 for ( i = 0; i < 0x20; i+= 4 ) {
                        printk( "TLAN:      0x%02x", i );
-                       TLan_MiiReadReg( io, phy, i, &data0 );
+                       TLan_MiiReadReg( dev, phy, i, &data0 );
                        printk( " 0x%04hx", data0 );
-                       TLan_MiiReadReg( io, phy, i + 1, &data1 );
+                       TLan_MiiReadReg( dev, phy, i + 1, &data1 );
                        printk( " 0x%04hx", data1 );
-                       TLan_MiiReadReg( io, phy, i + 2, &data2 );
+                       TLan_MiiReadReg( dev, phy, i + 2, &data2 );
                        printk( " 0x%04hx", data2 );
-                       TLan_MiiReadReg( io, phy, i + 3, &data3 );
+                       TLan_MiiReadReg( dev, phy, i + 3, &data3 );
                        printk( " 0x%04hx\n", data3 );
                }
        } else {
-               printk( "TLAN:   Device %s, PHY 0x%02x (Unmanaged/Unknown).\n",
-                       dev->name,
-                       phy
-                     );
+               printk( "TLAN:   Device %s, Invalid PHY.\n", dev->name );
        }
 
 } /* TLan_PhyPrint */
@@ -1887,7 +2117,7 @@ void TLan_PhyPrint( struct device *dev )
 
 
        /*********************************************************************
-        *      TLan_PhySelect
+        *      TLan_PhyDetect
         *
         *      Returns:
         *              Nothing
@@ -1895,355 +2125,271 @@ void TLan_PhyPrint( struct device *dev )
         *              dev     A pointer to the device structure of the adapter
         *                      for which the PHY needs determined.
         *
-        *      This function decides which PHY amoung those attached to the
-        *      TLAN chip is to be used.  The TLAN chip can be attached to
-        *      multiple PHYs, and the driver needs to decide which one to
-        *      talk to.  Currently this routine picks the PHY with the lowest
-        *      address as the internal PHY address is 0x1F, the highest
-        *      possible.  This strategy assumes that there can be only one
-        *      other PHY, and, if it exists, it is the one to be used.  If
-        *      token ring PHYs are ever supported, this routine will become
-        *      a little more interesting...
+        *      So far I've found that adapters which have external PHYs
+        *      may also use the internal PHY for part of the functionality.
+        *      (eg, AUI/Thinnet).  This function finds out if this TLAN
+        *      chip has an internal PHY, and then finds the first external
+        *      PHY (starting from address 0) if it exists).
         *
         ********************************************************************/
 
-void TLan_PhySelect( struct device *dev )
+void TLan_PhyDetect( struct device *dev )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-       int                     phy;
-       int                     entry;
-       u16                     id_hi[TLAN_PHY_MAX_ADDR + 1];
-       u16                     id_lo[TLAN_PHY_MAX_ADDR + 1];
-       u16                     hi;
-       u16                     lo;
-       u16                     vendor;
-       u16                     device;
-
-       priv->phyCheck = &TLan_PhyNop;  // Make sure these aren't ever NULL
-       priv->phyService = &TLan_PhyNop;
-       
-       vendor = TLanDeviceList[priv->pciEntry].vendorId;
-       device = TLanDeviceList[priv->pciEntry].deviceId;
-
-       // This is a bit uglier than I'd like, but the 0xF130 device must
-       // NOT be assigned a valid PHY as it uses an unmanaged, bit-rate
-       // PHY.  It is simplest just to use another goto, rather than
-       // nesting the two for loops in the if statement.
+       u16             control;
+       u16             hi;
+       u16             lo;
+       u32             phy;
 
-       if ( ( vendor == PCI_VENDOR_ID_COMPAQ ) &&
-            ( device == PCI_DEVICE_ID_NETFLEX_3P ) ) {
-               entry = 0;
-               phy = 0;
-               goto FINISH;
+       if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+               priv->phyNum = 0xFFFF;
+               return;
        }
 
-       for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
-               hi = lo = 0;
-               TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &hi );
-               TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &lo );
-               id_hi[phy] = hi;
-               id_lo[phy] = lo;
-               TLAN_DBG( TLAN_DEBUG_GNRL,
-                         "TLAN: Phy %2x, hi = %hx, lo = %hx\n",
-                         phy,
-                         hi,
-                         lo
-                       );
+       TLan_MiiReadReg( dev, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi );
+
+       if ( hi != 0xFFFF ) {
+               priv->phy[0] = TLAN_PHY_MAX_ADDR;
+       } else {
+               priv->phy[0] = TLAN_PHY_NONE;
        }
 
+       priv->phy[1] = TLAN_PHY_NONE;
        for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
-               if ( ( aui == 1 ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
-                       if ( id_hi[phy] != 0xFFFF ) {
-                               TLan_MiiSync(dev->base_addr);
-                               TLan_MiiWriteReg(dev->base_addr,
-                                                phy,
-                                                MII_GEN_CTL,
-                                                MII_GC_PDOWN | 
-                                                MII_GC_LOOPBK | 
-                                                MII_GC_ISOLATE );
-
-                       }
-                       continue;
-               }
-               for ( entry = 0; TLanPhyIdTable[entry].check; entry++) {
-                       if ( ( id_hi[phy] == TLanPhyIdTable[entry].idHi ) &&
-                            ( id_lo[phy] == TLanPhyIdTable[entry].idLo ) ) {
-                               TLAN_DBG( TLAN_DEBUG_GNRL,
-                                         "TLAN: Selected Phy %hx\n",
-                                         phy
-                                       );
-                               goto FINISH;
+               TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &control );
+               TLan_MiiReadReg( dev, phy, MII_GEN_ID_HI, &hi );
+               TLan_MiiReadReg( dev, phy, MII_GEN_ID_LO, &lo );
+               if ( ( control != 0xFFFF ) || ( hi != 0xFFFF ) || ( lo != 0xFFFF ) ) {
+                       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: PHY found at %02x %04x %04x %04x\n", phy, control, hi, lo );
+                       if ( ( priv->phy[1] == TLAN_PHY_NONE ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
+                               priv->phy[1] = phy;
                        }
                }
        }
 
-       entry = 0;
-       phy = 0;
-
-FINISH:
-       
-       if ( ( entry == 0 ) && ( phy == 0 ) ) {
-               priv->phyAddr = phy;
-               priv->phyEntry = entry;
-               priv->phyCheck = TLan_PhyNop;
-               priv->phyService = TLan_PhyNop;
-               priv->phyFlags = TLAN_PHY_BIT_RATE | 
-                                 TLAN_PHY_UNMANAGED |
-                                TLAN_PHY_ACTIVITY;
+       if ( priv->phy[1] != TLAN_PHY_NONE ) {
+               priv->phyNum = 1;
+       } else if ( priv->phy[0] != TLAN_PHY_NONE ) {
+               priv->phyNum = 0;
        } else {
-               priv->phyAddr = phy;
-               priv->phyEntry = entry;
-               priv->phyCheck = TLanPhyIdTable[entry].check;
-               priv->phyService = TLanPhyIdTable[entry].service;
-               priv->phyFlags = TLanPhyIdTable[entry].flags;
+               printk( "TLAN:  Cannot initialize device, no PHY was found!\n" );
        }
 
-} /* TLan_PhySelect */
+} /* TLan_PhyDetect */
 
 
 
 
-       /***************************************************************
-        *      TLan_PhyNop
-        *
-        *      Returns:
-        *              Nothing
-        *      Parms:
-        *              dev     A pointer to a device structure.
-        *
-        *      This function does nothing and is meant as a stand-in
-        *      for when a Check or Service function would be
-        *      meaningless.
-        *
-        **************************************************************/
-
-int TLan_PhyNop( struct device *dev )
+void TLan_PhyPowerDown( struct device *dev )
 {
-       dev = NULL;
-       return 0;
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16             value;
+
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Powering down PHY(s).\n", dev->name );
+       value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE;
+       TLan_MiiSync( dev->base_addr );
+       TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
+       if ( ( priv->phyNum == 0 ) && ( priv->phy[1] != TLAN_PHY_NONE ) && ( ! ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) ) ) {
+               TLan_MiiSync( dev->base_addr );
+               TLan_MiiWriteReg( dev, priv->phy[1], MII_GEN_CTL, value );
+       }
 
-} /* TLan_PhyNop */
+       /* Wait for 5 jiffies (50 ms) and powerup
+        * This is abitrary.  It is intended to make sure the
+        * tranceiver settles.
+        */
+       TLan_SetTimer( dev, 5, TLAN_TIMER_PHY_PUP );
 
+} /* TLan_PhyPowerDown */
 
 
 
-       /***************************************************************
-        *  TLan_PhyInternalCheck
-        *
-        *      Returns:
-        *              Nothing
-        *      Parms:
-        *              dev     A pointer to a device structure of the
-        *                      adapter holding the PHY to be checked.
-        *
-        *      This function resets the internal PHY on a TLAN chip.
-        *      See Chap. 7, "Physical Interface (PHY)" of "ThunderLAN
-        *      Programmer's Guide"
-        *
-        **************************************************************/
 
-int TLan_PhyInternalCheck( struct device *dev )
+void TLan_PhyPowerUp( struct device *dev )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-       u16                             gen_ctl;
-       u32                             io;
-       u16                             phy;
-       u16                             value;
-       u8                              sio;
-
-       io = dev->base_addr;
-       phy = priv->phyAddr;
-
-       TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
-       if ( gen_ctl & MII_GC_PDOWN ) {
-               TLan_MiiSync( io );
-               TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
-               TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
-               udelay(50000);
-               TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
-               TLan_MiiSync( io );
-       }
-
-       TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-       while ( value & MII_GC_RESET )
-               TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-
-       // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
-       // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
-       TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
-
-       udelay(500000);
-
-       TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
-       if ( aui ) 
-               value |= TLAN_TC_AUISEL;
-       else
-               value &= ~TLAN_TC_AUISEL;
-       TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-
-       // Read Possible Latched Link Status
-       TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); 
-       // Read Real Link Status
-               TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); 
-       if ( ( value & MII_GS_LINK ) || aui ) {
-               priv->phyOnline = 1;
-               TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
-       } else {
-               priv->phyOnline = 0;
-               TLan_DioWrite8( io, TLAN_LED_REG, 0 );
-       }
+       u16             value;
 
-       // Enable Interrupts
-               TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
-       value |= TLAN_TC_INTEN;
-       TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Powering up PHY.\n", dev->name );
+       TLan_MiiSync( dev->base_addr );
+       value = MII_GC_LOOPBK;
+       TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
 
-       sio = TLan_DioRead8( io, TLAN_NET_SIO );
-       sio |= TLAN_NET_SIO_MINTEN;
-       TLan_DioWrite8( io, TLAN_NET_SIO, sio );
-                       
-       return 0;
+       /* Wait for 50 jiffies (500 ms) and reset the
+        * tranceiver.  The TLAN docs say both 50 ms and
+        * 500 ms, so do the longer, just in case
+        */
+       TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_RESET );
 
-} /* TLanPhyInternalCheck */
+} /* TLan_PhyPowerUp */
 
 
 
 
-       /***************************************************************
-        *      TLan_PhyInternalService
-        *
-        *      Returns:
-        *              Nothing
-        *      Parms:
-        *              dev     A pointer to a device structure of the
-        *                      adapter holding the PHY to be serviced.
-        *
-        *      This function services an interrupt generated by the
-        *      internal PHY.  It can turn on/off the link LED.  See
-        *      Chap. 7, "Physical Interface (PHY)" of "ThunderLAN
-        *      Programmer's Guide".
-        *
-        **************************************************************/
-
-int TLan_PhyInternalService( struct device *dev )
+void TLan_PhyReset( struct device *dev )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-       u16                     tlphy_sts;
-       u16                     gen_sts;
-       u16                     an_exp;
-       u32                     io;
-       u16                     phy;
-
-       io = dev->base_addr;
-       phy = priv->phyAddr;
-
-       TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts );
-       TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts ); 
-       TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp ); 
-       if ( ( gen_sts & MII_GS_LINK ) || aui ) {
-               priv->phyOnline = 1;
-               TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
-       } else {
-               priv->phyOnline = 0;
-               TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+       u16             phy;
+       u16             value;
+
+       phy = priv->phy[priv->phyNum];
+
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Reseting PHY.\n", dev->name );
+       TLan_MiiSync( dev->base_addr );
+       value = MII_GC_LOOPBK | MII_GC_RESET;
+       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, value );
+       TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
+       while ( value & MII_GC_RESET ) {
+               TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
        }
+       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 );
 
-        if ( ( tlphy_sts & TLAN_TS_POLOK ) == 0) {
-                u16     value;
-                TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value);
-                value |= TLAN_TC_SWAPOL;
-                TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value);
-        }
+       /* Wait for 50 jiffies (500 ms) and initialize.
+        * I don't remember why I wait this long.
+        */
+       TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_START_LINK );
 
-       return 0;
+} /* TLan_PhyReset */
 
-} /* TLan_PhyInternalService */
 
 
 
+void TLan_PhyStartLink( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16             ability;
+       u16             control;
+       u16             data;
+       u16             phy;
+       u16             status;
+       u16             tctl;
+
+       phy = priv->phy[priv->phyNum];
+
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Trying to activate link.\n", dev->name );
+       TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+       if ( ( status & MII_GS_AUTONEG ) && 
+            ( priv->duplex == TLAN_DUPLEX_DEFAULT ) && 
+            ( priv->speed == TLAN_SPEED_DEFAULT ) &&
+            ( ! priv->aui ) ) {
+               ability = status >> 11;
+
+               if ( priv->speed == TLAN_SPEED_10 ) {
+                       ability &= 0x0003;
+               } else if ( priv->speed == TLAN_SPEED_100 ) {
+                       ability &= 0x001C;
+               }
+
+               if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+                       ability &= 0x000A;
+               } else if ( priv->duplex == TLAN_DUPLEX_HALF ) {
+                       ability &= 0x0005;
+               }
+
+               TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 );
+                       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
+               TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
+
+               /* Wait for 400 jiffies (4 sec) for autonegotiation
+                * to complete.  The max spec time is less than this
+                * but the card need additional time to start AN.
+                * .5 sec should be plenty extra.
+                */
+               printk( "TLAN:  %s: Starting autonegotiation.\n", dev->name );
+               TLan_SetTimer( dev, 400, TLAN_TIMER_PHY_FINISH_AN );
+               return;
+       }
+
+       if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) {
+               priv->phyNum = 0;
+               data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+               TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
+               TLan_SetTimer( dev, 4, TLAN_TIMER_PHY_PDOWN );
+               return;
+       } else if ( priv->phyNum == 0 ) {
+               TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl );
+               if ( priv->aui ) {
+                       tctl |= TLAN_TC_AUISEL;
+               } else {
+                       tctl &= ~TLAN_TC_AUISEL;
+                       control = 0;
+                       if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+                               control |= MII_GC_DUPLEX;
+                               priv->tlanFullDuplex = TRUE;
+                       }
+                       if ( priv->speed == TLAN_SPEED_100 ) {
+                               control |= MII_GC_SPEEDSEL;
+                       }
+                               TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, control );
+               }
+               TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl );
+       }
+
+       /* Wait for 100 jiffies (1 sec) to give the tranceiver time
+        * to establish link.
+        */
+       TLan_SetTimer( dev, 100, TLAN_TIMER_FINISH_RESET );
+
+} /* TLan_PhyStartLink */
 
-       /***************************************************************
-        *      TLan_PhyDp83840aCheck
-        *
-        *      Returns:
-        *              Nothing
-        *      Parms:
-        *              dev     A pointer to a device structure of the
-        *                      adapter holding the PHY to be reset.
-        *
-        *      This function resets a National Semiconductor DP83840A
-        *      10/100 Mb/s PHY device.  See National Semiconductor's
-        *      data sheet for more info.  This PHY is used on Compaq
-        *      Netelligent 10/100 cards.
-        *
-        **************************************************************/
 
-static int TLan_PhyDp83840aCheck( struct device *dev )
+
+
+void TLan_PhyFinishAutoNeg( struct device *dev )
 {
        TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-       u16                     gen_ctl;
-       int                     i;
-       u32                     io;
-       u16                     phy;
-       u16                     value;
-       u8                      sio;
-
-       io = dev->base_addr;
-       phy = priv->phyAddr;
-
-       TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
-       if ( gen_ctl & MII_GC_PDOWN ) {
-               TLan_MiiSync( io );
-               TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
-               TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
-               for ( i = 0; i < 500000; i++ )
-                       SLOW_DOWN_IO;
-               TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
-               TLan_MiiSync( io );
-       }
-
-       TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-       while ( value & MII_GC_RESET )
-               TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-
-       // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
-       // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
-       TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
-       TLan_MiiReadReg( io, phy, MII_AN_ADV, &value );
-       value &= ~0x0140;
-       TLan_MiiWriteReg( io, phy, MII_AN_ADV, value );
-               TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 );
-       TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 );
-
-       for ( i = 0; i < 50000; i++ )
-               SLOW_DOWN_IO;
+       u16             an_adv;
+       u16             an_lpa;
+       u16             data;
+       u16             mode;
+       u16             phy;
+       u16             status;
+       
+       phy = priv->phy[priv->phyNum];
+
+       TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+       if ( ! ( status & MII_GS_AUTOCMPLT ) ) {
+               /* Wait for 800 jiffies (8 sec) to give the process
+                * more time.  Perhaps we should fail after a while.
+                */
+               printk( "TLAN:  Giving autonegotiation more time.\n" );
+               TLan_SetTimer( dev, 800, TLAN_TIMER_PHY_FINISH_AN );
+               return;
+       }
 
-/*
-       // Read Possible Latched Link Status
-       TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); 
-       // Read Real Link Status
-       TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); 
-       if ( value & MII_GS_LINK ) {
-               priv->phyOnline = 1;
-               TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
-       } else {
-               priv->phyOnline = 0;
-               TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+       printk( "TLAN:  %s: Autonegotiation complete.\n", dev->name );
+       TLan_MiiReadReg( dev, phy, MII_AN_ADV, &an_adv );
+       TLan_MiiReadReg( dev, phy, MII_AN_LPA, &an_lpa );
+       mode = an_adv & an_lpa & 0x03E0;
+       if ( mode & 0x0100 ) {
+               priv->tlanFullDuplex = TRUE;
+       } else if ( ! ( mode & 0x0080 ) && ( mode & 0x0040 ) ) {
+               priv->tlanFullDuplex = TRUE;
        }
 
-       // Enable Interrupts
-       TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
-       value |= TLAN_TC_INTEN;
-       TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-*/
-       sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
-       sio &= ~TLAN_NET_SIO_MINTEN;
-       TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
-//     priv->phyOnline = 1;
-                       
-       return 0;
+       if ( ( ! ( mode & 0x0180 ) ) && ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) && ( priv->phyNum != 0 ) ) {
+               priv->phyNum = 0;
+               data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+               TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
+               TLan_SetTimer( dev, 40, TLAN_TIMER_PHY_PDOWN );
+               return;
+       }
+
+       if ( priv->phyNum == 0 ) {
+               if ( ( priv->duplex == TLAN_DUPLEX_FULL ) || ( an_adv & an_lpa & 0x0040 ) ) {
+                       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX );
+                       printk( "TLAN:  Starting internal PHY with DUPLEX\n" );
+               } else {
+                       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB );
+                       printk( "TLAN:  Starting internal PHY with HALF-DUPLEX\n" );
+               }
+       }
 
-} /* TLan_PhyDp83840aCheck */
+       /* Wait for 10 jiffies (100 ms).  No reason in partiticular.
+        */
+       TLan_SetTimer( dev, 10, TLAN_TIMER_FINISH_RESET );
+               
+} /* TLan_PhyFinishAutoNeg */
 
 
 
@@ -2268,9 +2414,10 @@ static int TLan_PhyDp83840aCheck( struct device *dev )
         *              1       otherwise.
         *
         *      Parms:
-        *              base_port       The base IO port of the adapter in
-        *                              question.
-        *              dev             The address of the PHY to be queried.
+        *              dev             The device structure containing
+        *                              The io address and interrupt count
+        *                              for this device.
+        *              phy             The address of the PHY to be queried.
         *              reg             The register whose contents are to be
         *                              retreived.
         *              val             A pointer to a variable to store the
@@ -2283,30 +2430,32 @@ static int TLan_PhyDp83840aCheck( struct device *dev )
         *
         **************************************************************/
 
-int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val)
+int TLan_MiiReadReg( struct device *dev, u16 phy, u16 reg, u16 *val )
 {
        u8      nack;
-       u16 sio, tmp;
-       u32 i;
+       u16     sio, tmp;
+       u32     i;
        int     err;
-       int minten;
+       int     minten;
 
        err = FALSE;
-       outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
-       sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+       outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
+       sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
 
-       cli();
+       if ( dev->interrupt == 0 )
+               cli();
+       dev->interrupt++;
 
-       TLan_MiiSync(base_port);
+       TLan_MiiSync(dev->base_addr);
 
        minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
        if ( minten )
                TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
 
-       TLan_MiiSendData( base_port, 0x1, 2 );          /* Start ( 01b ) */
-       TLan_MiiSendData( base_port, 0x2, 2 );          /* Read  ( 10b ) */
-       TLan_MiiSendData( base_port, dev, 5 );          /* Device #      */
-       TLan_MiiSendData( base_port, reg, 5 );          /* Register #    */
+       TLan_MiiSendData( dev->base_addr, 0x1, 2 );     /* Start ( 01b ) */
+       TLan_MiiSendData( dev->base_addr, 0x2, 2 );     /* Read  ( 10b ) */
+       TLan_MiiSendData( dev->base_addr, phy, 5 );     /* Device #      */
+       TLan_MiiSendData( dev->base_addr, reg, 5 );     /* Register #    */
 
 
        TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio);         /* Change direction */
@@ -2342,7 +2491,9 @@ int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val)
 
        *val = tmp;
 
-       sti();
+       dev->interrupt--;
+       if ( dev->interrupt == 0 )
+               sti();
 
        return err;
 
@@ -2436,9 +2587,9 @@ void TLan_MiiSync( u16 base_port )
         *      Returns:
         *              Nothing
         *      Parms:
-        *              base_port       The base IO port of the adapter in
-        *                              question.
-        *              dev             The address of the PHY to be written to.
+        *              dev             The device structure for the device
+        *                              to write to.
+        *              phy             The address of the PHY to be written to.
         *              reg             The register whose contents are to be
         *                              written.
         *              val             The value to be written to the register.
@@ -2450,29 +2601,31 @@ void TLan_MiiSync( u16 base_port )
         *
         **************************************************************/
 
-void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
+void TLan_MiiWriteReg( struct device *dev, u16 phy, u16 reg, u16 val )
 {
-       u16 sio;
+       u16     sio;
        int     minten;
 
-       outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
-       sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+       outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
+       sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
 
-       cli();
+       if ( dev->interrupt == 0 )
+               cli();
+       dev->interrupt++;
 
-       TLan_MiiSync( base_port );
+       TLan_MiiSync( dev->base_addr );
 
        minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
        if ( minten )
                TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
 
-       TLan_MiiSendData( base_port, 0x1, 2 );          /* Start ( 01b ) */
-       TLan_MiiSendData( base_port, 0x1, 2 );          /* Write ( 01b ) */
-       TLan_MiiSendData( base_port, dev, 5 );          /* Device #      */
-       TLan_MiiSendData( base_port, reg, 5 );          /* Register #    */
+       TLan_MiiSendData( dev->base_addr, 0x1, 2 );     /* Start ( 01b ) */
+       TLan_MiiSendData( dev->base_addr, 0x1, 2 );     /* Write ( 01b ) */
+       TLan_MiiSendData( dev->base_addr, phy, 5 );     /* Device #      */
+       TLan_MiiSendData( dev->base_addr, reg, 5 );     /* Register #    */
 
-       TLan_MiiSendData( base_port, 0x2, 2 );          /* Send ACK */
-       TLan_MiiSendData( base_port, val, 16 );         /* Send Data */
+       TLan_MiiSendData( dev->base_addr, 0x2, 2 );     /* Send ACK */
+       TLan_MiiSendData( dev->base_addr, val, 16 );    /* Send Data */
 
        TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );        /* Idle cycle */
        TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
@@ -2480,10 +2633,15 @@ void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
        if ( minten )
                TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
 
-       sti();
+       dev->interrupt--;
+       if ( dev->interrupt == 0 )
+               sti();
 
 } /* TLan_MiiWriteReg */
 
+
+
+
 /*****************************************************************************
 ******************************************************************************
 
@@ -2562,7 +2720,7 @@ int TLan_EeSendByte( u16 io_base, u8 data, int stop )
        outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
        sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
 
-       // Assume clock is low, tx is enabled;
+       /* Assume clock is low, tx is enabled; */
        for ( place = 0x80; place != 0; place >>= 1 ) {
                if ( place & data )
                        TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
@@ -2578,7 +2736,7 @@ int TLan_EeSendByte( u16 io_base, u8 data, int stop )
        TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
 
        if ( ( ! err ) && stop ) {
-               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       // STOP, raise data while clock is high
+               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       /* STOP, raise data while clock is high */
                TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
                TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
        }
@@ -2623,7 +2781,7 @@ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
        sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
        *data = 0;
 
-       // Assume clock is low, tx is enabled;
+       /* Assume clock is low, tx is enabled; */
        TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
        for ( place = 0x80; place; place >>= 1 ) {
                TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
@@ -2634,14 +2792,14 @@ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
 
        TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
        if ( ! stop ) {
-               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       // Ack = 0
+               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       /* Ack = 0 */
                TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
                TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
        } else {
-               TLan_SetBit( TLAN_NET_SIO_EDATA, sio );         // No ack = 1 (?)
+               TLan_SetBit( TLAN_NET_SIO_EDATA, sio );         /* No ack = 1 (?) */
                TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
                TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
-               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       // STOP, raise data while clock is high
+               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       /* STOP, raise data while clock is high */
                TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
                TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
        }
@@ -2656,7 +2814,7 @@ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
         *
         *      Returns:
         *              No error = 0, else, the stage at which the error
-        *              occurred.
+        *              occured.
         *      Parms:
         *              io_base         The IO port base address for the
         *                              TLAN device with the EEPROM to
@@ -2672,26 +2830,30 @@ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
         *
         **************************************************************/
 
-int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data )
+int TLan_EeReadByte( struct device *dev, u8 ee_addr, u8 *data )
 {
        int err;
 
-       cli();
+       if ( dev->interrupt == 0 )
+               cli();
+       dev->interrupt++;
 
-       TLan_EeSendStart( io_base );
-       err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK );
+       TLan_EeSendStart( dev->base_addr );
+       err = TLan_EeSendByte( dev->base_addr, 0xA0, TLAN_EEPROM_ACK );
        if (err)
                return 1;
-       err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK );
+       err = TLan_EeSendByte( dev->base_addr, ee_addr, TLAN_EEPROM_ACK );
        if (err)
                return 2;
-       TLan_EeSendStart( io_base );
-       err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK );
+       TLan_EeSendStart( dev->base_addr );
+       err = TLan_EeSendByte( dev->base_addr, 0xA1, TLAN_EEPROM_ACK );
        if (err)
                return 3;
-       TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP );
+       TLan_EeReceiveByte( dev->base_addr, data, TLAN_EEPROM_STOP );
 
-       sti();
+       dev->interrupt--;
+       if ( dev->interrupt == 0 )
+               sti();
 
        return 0;
 
index 94613d3a649348f6eb1813df5e21a93a6952fb32..a66e26c2873cdb7f627cf3b1040c6712b085be0b 100644 (file)
@@ -5,9 +5,9 @@
  *  Linux ThunderLAN Driver
  *
  *  tlan.h
- *  by James Banks, james.banks@caldera.com
+ *  by James Banks
  *
- *  (C) 1997 Caldera, Inc.
+ *  (C) 1997-1998 Caldera, Inc.
  *
  *  This software may be used and distributed according to the terms
  *  of the GNU Public License, incorporated herein by reference.
         *
         ****************************************************************/
 
-#define FALSE                          0
-#define TRUE                           1
+#define FALSE                  0
+#define TRUE                   1
 
 #define TLAN_MIN_FRAME_SIZE    64
 #define TLAN_MAX_FRAME_SIZE    1600
 
-#define TLAN_NUM_RX_LISTS 4
-#define TLAN_NUM_TX_LISTS 8
+#define TLAN_NUM_RX_LISTS      4
+#define TLAN_NUM_TX_LISTS      8
 
-#define TLAN_IGNORE    0
-#define TLAN_RECORD    1
+#define TLAN_IGNORE            0
+#define TLAN_RECORD            1
 
 #define TLAN_DBG(lvl, format, args...) if (debug&lvl) printk( format, ##args );
-#define TLAN_DEBUG_GNRL        0x0001
-#define TLAN_DEBUG_TX  0x0002
-#define TLAN_DEBUG_RX  0x0004 
-#define TLAN_DEBUG_LIST        0x0008
+#define TLAN_DEBUG_GNRL                0x0001
+#define TLAN_DEBUG_TX          0x0002
+#define TLAN_DEBUG_RX          0x0004 
+#define TLAN_DEBUG_LIST                0x0008
 
 
 
         *
         ****************************************************************/
                
-       /* NOTE: These have been moved to pci.h, will use them
-          eventually */
-#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
-#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
-#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35
-#define PCI_DEVICE_ID_NETFLEX_3P 0xF130
-#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150
-#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43
-#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40
-#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011
-
-
-typedef struct tlan_pci_id {
+#define PCI_DEVICE_ID_NETELLIGENT_10                   0xAE34
+#define PCI_DEVICE_ID_NETELLIGENT_10_100               0xAE32
+#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED            0xAE35
+#define PCI_DEVICE_ID_NETFLEX_3P                       0xF130
+#define PCI_DEVICE_ID_NETFLEX_3P_BNC                   0xF150
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT      0xAE43
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL          0xAE40
+#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX             0xB011
+#define PCI_DEVICE_ID_NETELLIGENT_10_T2                        0xB012
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100       0xB030
+#ifndef PCI_DEVICE_ID_OLICOM_OC2183
+#define PCI_DEVICE_ID_OLICOM_OC2183                    0x0013
+#endif
+#ifndef PCI_DEVICE_ID_OLICOM_OC2325
+#define PCI_DEVICE_ID_OLICOM_OC2325                    0x0012
+#endif
+#ifndef PCI_DEVICE_ID_OLICOM_OC2326
+#define PCI_DEVICE_ID_OLICOM_OC2326                    0x0014
+#endif
+
+typedef struct tlan_adapter_entry {
        u16     vendorId;
        u16     deviceId;
-       char *deviceName;
-} TLanPciId;
+       char    *deviceLabel;
+       u32     flags;
+       u16     addrOfs;
+} TLanAdapterEntry;
+
+#define TLAN_ADAPTER_NONE              0x00000000
+#define TLAN_ADAPTER_UNMANAGED_PHY     0x00000001
+#define TLAN_ADAPTER_BIT_RATE_PHY      0x00000002
+#define TLAN_ADAPTER_USE_INTERN_10     0x00000004
+#define TLAN_ADAPTER_ACTIVITY_LED      0x00000008
+
+#define TLAN_SPEED_DEFAULT     0
+#define TLAN_SPEED_10          10
+#define TLAN_SPEED_100         100
+
+#define TLAN_DUPLEX_DEFAULT    0
+#define TLAN_DUPLEX_HALF       1
+#define TLAN_DUPLEX_FULL       2
 
 
 
@@ -121,25 +145,7 @@ typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
         ****************************************************************/
 
 #define TLAN_PHY_MAX_ADDR      0x1F
-
-#define TLAN_PHY_ACTIVITY      0x00000001
-#define TLAN_PHY_AUTONEG       0x00000002
-#define TLAN_PHY_INTS          0x00000004
-#define TLAN_PHY_BIT_RATE      0x00000008
-#define TLAN_PHY_UNMANAGED     0x00000010
-#define TLAN_PHY_INTERNAL      0x00000020
-
-
-typedef int (TLanPhyFunc)( struct device * );
-
-
-typedef struct tlan_phy_id_entry_tag {
-       u16             idHi;
-       u16             idLo;
-       TLanPhyFunc     *check;
-       TLanPhyFunc     *service;
-       u32             flags;
-} TLanPhyIdEntry;
+#define TLAN_PHY_NONE          0x20
 
 
 
@@ -164,21 +170,22 @@ typedef struct tlan_private_tag {
        u32                     txInProgress;
        u32                     txTail;
        u32                     txBusyCount;
-       u32                     phyAddr;
-       u32                     phyEntry;
        u32                     phyOnline;
-       u32                     phyFlags;
-       TLanPhyFunc             *phyCheck;
-       TLanPhyFunc             *phyService;
        u32                     timerSetAt;
        u32                     timerType;
        struct timer_list       timer;
        struct net_device_stats stats;
-       u32                     pciEntry;
-       u8                      pciRevision;
-       u8                      pciBus;
-       u8                      pciDeviceFn;
+       TLanAdapterEntry        *adapter;
+       u32                     adapterRev;
+       u32                     aui;
+       u32                     debug;
+       u32                     duplex;
+       u32                     phy[2];
+       u32                     phyNum;
+       u32                     sa_int;
+       u32                     speed;
        u8                      tlanRev;
+       u8                      tlanFullDuplex;
        char                    devName[8];
 } TLanPrivateInfo;
 
@@ -191,10 +198,15 @@ typedef struct tlan_private_tag {
         ****************************************************************/
 
 #define TLAN_TIMER_LINK                        1
-#define TLAN_TIMER_ACT                 2
+#define TLAN_TIMER_ACTIVITY            2
+#define TLAN_TIMER_PHY_PDOWN           3
+#define TLAN_TIMER_PHY_PUP             4
+#define TLAN_TIMER_PHY_RESET           5
+#define TLAN_TIMER_PHY_START_LINK      6
+#define TLAN_TIMER_PHY_FINISH_AN       7
+#define TLAN_TIMER_FINISH_RESET                8
 
-#define TLAN_TIMER_LINK_DELAY  230
-#define TLAN_TIMER_ACT_DELAY   10
+#define TLAN_TIMER_ACT_DELAY           10
 
 
 
@@ -215,29 +227,29 @@ typedef struct tlan_private_tag {
         *
         ****************************************************************/
 
-#define TLAN_HOST_CMD          0x00
+#define TLAN_HOST_CMD                  0x00
 #define        TLAN_HC_GO              0x80000000
-#define                TLAN_HC_STOP    0x40000000
+#define                TLAN_HC_STOP            0x40000000
 #define                TLAN_HC_ACK             0x20000000
-#define                TLAN_HC_CS_MASK 0x1FE00000
+#define                TLAN_HC_CS_MASK         0x1FE00000
 #define                TLAN_HC_EOC             0x00100000
 #define                TLAN_HC_RT              0x00080000
 #define                TLAN_HC_NES             0x00040000
-#define                TLAN_HC_AD_RST  0x00008000
-#define                TLAN_HC_LD_TMR  0x00004000
-#define                TLAN_HC_LD_THR  0x00002000
-#define                TLAN_HC_REQ_INT 0x00001000
-#define                TLAN_HC_INT_OFF 0x00000800
-#define                TLAN_HC_INT_ON  0x00000400
-#define                TLAN_HC_AC_MASK 0x000000FF
-#define TLAN_CH_PARM           0x04
-#define TLAN_DIO_ADR           0x08
-#define                TLAN_DA_ADR_INC 0x8000
-#define                TLAN_DA_RAM_ADR 0x4000
-#define TLAN_HOST_INT          0x0A
-#define                TLAN_HI_IV_MASK 0x1FE0
-#define                TLAN_HI_IT_MASK 0x001C
-#define TLAN_DIO_DATA          0x0C
+#define                TLAN_HC_AD_RST          0x00008000
+#define                TLAN_HC_LD_TMR          0x00004000
+#define                TLAN_HC_LD_THR          0x00002000
+#define                TLAN_HC_REQ_INT         0x00001000
+#define                TLAN_HC_INT_OFF         0x00000800
+#define                TLAN_HC_INT_ON          0x00000400
+#define                TLAN_HC_AC_MASK         0x000000FF
+#define TLAN_CH_PARM                   0x04
+#define TLAN_DIO_ADR                   0x08
+#define                TLAN_DA_ADR_INC         0x8000
+#define                TLAN_DA_RAM_ADR         0x4000
+#define TLAN_HOST_INT                  0x0A
+#define                TLAN_HI_IV_MASK         0x1FE0
+#define                TLAN_HI_IT_MASK         0x001C
+#define TLAN_DIO_DATA                  0x0C
 
 
 /* ThunderLAN Internal Register DIO Offsets */
@@ -264,7 +276,7 @@ typedef struct tlan_private_tag {
 #define                TLAN_NET_STS_MIRQ       0x80
 #define                TLAN_NET_STS_HBEAT      0x40
 #define                TLAN_NET_STS_TXSTOP     0x20
-#define                TLAN_NET_STS_RXSTOP 0x10
+#define                TLAN_NET_STS_RXSTOP     0x10
 #define                TLAN_NET_STS_RSRVD      0x0F
 #define TLAN_NET_MASK                  0x03
 #define                TLAN_NET_MASK_MASK7     0x80
@@ -283,36 +295,36 @@ typedef struct tlan_private_tag {
 #define                TLAN_NET_CFG_MTEST      0x0100
 #define                TLAN_NET_CFG_PHY_EN     0x0080
 #define                TLAN_NET_CFG_MSMASK     0x007F
-#define TLAN_MAN_TEST          0x06
-#define TLAN_DEF_VENDOR_ID     0x08
-#define TLAN_DEF_DEVICE_ID     0x0A
-#define TLAN_DEF_REVISION      0x0C
-#define TLAN_DEF_SUBCLASS      0x0D
-#define TLAN_DEF_MIN_LAT       0x0E
-#define TLAN_DEF_MAX_LAT       0x0F
+#define TLAN_MAN_TEST                  0x06
+#define TLAN_DEF_VENDOR_ID             0x08
+#define TLAN_DEF_DEVICE_ID             0x0A
+#define TLAN_DEF_REVISION              0x0C
+#define TLAN_DEF_SUBCLASS              0x0D
+#define TLAN_DEF_MIN_LAT               0x0E
+#define TLAN_DEF_MAX_LAT               0x0F
 #define TLAN_AREG_0                    0x10
 #define TLAN_AREG_1                    0x16
 #define TLAN_AREG_2                    0x1C
 #define TLAN_AREG_3                    0x22
 #define TLAN_HASH_1                    0x28
 #define TLAN_HASH_2                    0x2C
-#define TLAN_GOOD_TX_FRMS      0x30
-#define TLAN_TX_UNDERUNS       0x33
-#define TLAN_GOOD_RX_FRMS      0x34
-#define TLAN_RX_OVERRUNS       0x37
-#define TLAN_DEFERRED_TX       0x38
-#define TLAN_CRC_ERRORS                0x3A
-#define TLAN_CODE_ERRORS       0x3B
-#define TLAN_MULTICOL_FRMS     0x3C
-#define TLAN_SINGLECOL_FRMS    0x3E
-#define TLAN_EXCESSCOL_FRMS    0x40
-#define TLAN_LATE_COLS         0x41
-#define TLAN_CARRIER_LOSS      0x42
-#define TLAN_ACOMMIT           0x43
-#define TLAN_LED_REG           0x44
-#define                TLAN_LED_ACT    0x10
-#define                TLAN_LED_LINK   0x01
-#define TLAN_BSIZE_REG         0x45
+#define TLAN_GOOD_TX_FRMS              0x30
+#define TLAN_TX_UNDERUNS               0x33
+#define TLAN_GOOD_RX_FRMS              0x34
+#define TLAN_RX_OVERRUNS               0x37
+#define TLAN_DEFERRED_TX               0x38
+#define TLAN_CRC_ERRORS                        0x3A
+#define TLAN_CODE_ERRORS               0x3B
+#define TLAN_MULTICOL_FRMS             0x3C
+#define TLAN_SINGLECOL_FRMS            0x3E
+#define TLAN_EXCESSCOL_FRMS            0x40
+#define TLAN_LATE_COLS                 0x41
+#define TLAN_CARRIER_LOSS              0x42
+#define TLAN_ACOMMIT                   0x43
+#define TLAN_LED_REG                   0x44
+#define                TLAN_LED_ACT            0x10
+#define                TLAN_LED_LINK           0x01
+#define TLAN_BSIZE_REG                 0x45
 #define TLAN_MAX_RX                    0x46
 #define TLAN_INT_DIS                   0x48
 #define                TLAN_ID_TX_EOC          0x04
@@ -327,11 +339,11 @@ typedef struct tlan_private_tag {
 
 #define TLAN_INT_NONE                  0x0000
 #define TLAN_INT_TX_EOF                        0x0001
-#define TLAN_INT_STAT_OVERFLOW 0x0002
+#define TLAN_INT_STAT_OVERFLOW         0x0002
 #define TLAN_INT_RX_EOF                        0x0003
 #define TLAN_INT_DUMMY                 0x0004
 #define TLAN_INT_TX_EOC                        0x0005
-#define TLAN_INT_STATUS_CHECK  0x0006
+#define TLAN_INT_STATUS_CHECK          0x0006
 #define TLAN_INT_RX_EOC                        0x0007
 
 
@@ -340,7 +352,7 @@ typedef struct tlan_private_tag {
 
 /* Generic MII/PHY Registers */
 
-#define MII_GEN_CTL                            0x00
+#define MII_GEN_CTL                    0x00
 #define        MII_GC_RESET            0x8000
 #define                MII_GC_LOOPBK           0x4000
 #define                MII_GC_SPEEDSEL         0x2000
@@ -351,7 +363,7 @@ typedef struct tlan_private_tag {
 #define                MII_GC_DUPLEX           0x0100
 #define                MII_GC_COLTEST          0x0080
 #define                MII_GC_RESERVED         0x007F
-#define MII_GEN_STS                            0x01
+#define MII_GEN_STS                    0x01
 #define                MII_GS_100BT4           0x8000
 #define                MII_GS_100BTXFD         0x4000
 #define                MII_GS_100BTXHD         0x2000
@@ -359,19 +371,19 @@ typedef struct tlan_private_tag {
 #define                MII_GS_10BTHD           0x0800
 #define                MII_GS_RESERVED         0x07C0
 #define                MII_GS_AUTOCMPLT        0x0020
-#define                MII_GS_RFLT                     0x0010
+#define                MII_GS_RFLT             0x0010
 #define                MII_GS_AUTONEG          0x0008
-#define                MII_GS_LINK                     0x0004
+#define                MII_GS_LINK             0x0004
 #define                MII_GS_JABBER           0x0002
 #define                MII_GS_EXTCAP           0x0001
 #define MII_GEN_ID_HI                  0x02
 #define MII_GEN_ID_LO                  0x03
-#define        MII_GIL_OUI                     0xFC00
+#define        MII_GIL_OUI             0xFC00
 #define        MII_GIL_MODEL           0x03F0
 #define        MII_GIL_REVISION        0x000F
-#define MII_AN_ADV                             0x04
-#define MII_AN_LPA                             0x05
-#define MII_AN_EXP                             0x06
+#define MII_AN_ADV                     0x04
+#define MII_AN_LPA                     0x05
+#define MII_AN_EXP                     0x06
 
 /* ThunderLAN Specific MII/PHY Registers */
 
@@ -394,6 +406,7 @@ typedef struct tlan_private_tag {
 #define                TLAN_TS_RESERVED        0x0FFF
 
 
+#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
 
 /* Routines to access internal registers. */
 
@@ -456,7 +469,7 @@ inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data)
 
 
 
-
+#if 0
 inline void TLan_ClearBit(u8 bit, u16 port)
 {
        outb_p(inb_p(port) & ~bit, port);
@@ -477,6 +490,11 @@ inline void TLan_SetBit(u8 bit, u16 port)
 {
        outb_p(inb_p(port) | bit, port);
 }
+#endif
+
+#define TLan_ClearBit( bit, port )     outb_p(inb_p(port) & ~bit, port)
+#define TLan_GetBit( bit, port )       ((int) (inb_p(port) & bit))
+#define TLan_SetBit( bit, port )       outb_p(inb_p(port) | bit, port)
 
 
 inline u32     xor( u32 a, u32 b )
index 7af3be77a47c0be05c00c93c17a3c75f4f597981..a737a01da1c56a66869b223db575d14ff7e7a34d 100644 (file)
@@ -54,7 +54,7 @@ static void wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 static void wd_block_input(struct device *dev, int count,
                                                  struct sk_buff *skb, int ring_offset);
 static void wd_block_output(struct device *dev, int count,
-                                                       const unsigned char *buf, const start_page);
+                                                       const unsigned char *buf, const int start_page);
 static int wd_close_card(struct device *dev);
 
 \f
index 211dd6ae803fa439d29a790e0c769ef31665c73f..c8e5314dfc74bd5f58ee571c0fb5880f7c00749a 100644 (file)
@@ -38,7 +38,7 @@ static int fifo_cfg = 0x0020;                         /* Bypass external Tx FIFO. */
 
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1518 effectively disables this feature. */
-static const rx_copybreak = 100;
+static const int rx_copybreak = 100;
 
 /* Keep the ring sizes a power of two for efficiency.
    Making the Tx ring too large decreases the effectiveness of channel
index fc779ada4a7d88f65baf3a8d99fefc89288e95a9..9ff5a2e41bdab0ead4d15843b42915b6f40a7aac 100644 (file)
@@ -2204,7 +2204,7 @@ return(SCSI_ABORT_NOT_RUNNING);
 * 
 * Returns : status (SCSI_ABORT_SUCCESS)
 **************************************************************************/
-int AM53C974_reset(Scsi_Cmnd *cmd)
+int AM53C974_reset(Scsi_Cmnd *cmd, unsigned int flags)
 {
 AM53C974_local_declare();
 int                      i;
index 8481e5e06468ec780a4c22fbc71417ffa81b2611..ab51d2941e36b6f6b65e35cdf4cf23f35e16c74d 100644 (file)
@@ -290,7 +290,7 @@ const char *AM53C974_info(struct Scsi_Host *);
 int AM53C974_command(Scsi_Cmnd *SCpnt);
 int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
 int AM53C974_abort(Scsi_Cmnd *cmd);
-int AM53C974_reset (Scsi_Cmnd *cmd);
+int AM53C974_reset (Scsi_Cmnd *cmd, unsigned int flags);
 
 #define AM53C974_local_declare()       unsigned long io_port
 #define AM53C974_setio(instance)       io_port = instance->io_port
index a8515921ef806da8b40e512094282117a5957bc5..d94631a416e9e2ebc1549808e690c44b20c29563 100644 (file)
@@ -4944,7 +4944,7 @@ int SccbMgr_isr(ULONG pCurrCard)
  *
  * Function: Sccb_bad_isr
  *
- * Description: Some type of interrupt has occurred which is slightly
+ * Description: Some type of interrupt has occured which is slightly
  *              out of the ordinary.  We will now decode it fully, in
  *              this routine.  This is broken up in an attempt to save
  *              processing time.
index f37de04e46815873a15544f12865b34fb1ffe01c..9f2de9a0c58938346abf966a1b5ae56de1953eef 100644 (file)
@@ -729,7 +729,7 @@ NCR53c406a_abort(Scsi_Cmnd *SCpnt){
 }
 
 int 
-NCR53c406a_reset(Scsi_Cmnd *SCpnt){
+NCR53c406a_reset(Scsi_Cmnd *SCpnt, unsigned int flags){
     DEB(printk("NCR53c406a_reset called\n"));
     outb(C4_IMG, CONFIG4);      /* Select reg set 0 */
     outb(CHIP_RESET, CMD_REG);
index dcb48870bfdc0493a1abd09a9383533bccf47ff6..88e45e5e6a9201e9d6546f2699b53ba55bbf0c63 100644 (file)
@@ -57,7 +57,7 @@ const char* NCR53c406a_info(struct Scsi_Host *);
 int NCR53c406a_command(Scsi_Cmnd *);
 int NCR53c406a_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int NCR53c406a_abort(Scsi_Cmnd *);
-int NCR53c406a_reset(Scsi_Cmnd *);
+int NCR53c406a_reset(Scsi_Cmnd *, unsigned int);
 int NCR53c406a_biosparm(Disk *, kdev_t, int []);
 
 #endif /* _NCR53C406A_H */
index ff07954d8c352af5acfa91d254ee075dbc992078..76fd05cb714e3c93e50a980f90cb3dd09ac17e65 100644 (file)
@@ -1,5 +1,5 @@
-/* $Id: advansys.c,v 1.49 1998/01/22 20:19:25 bobf Exp bobf $ */
-#define ASC_VERSION "3.1D"    /* AdvanSys Driver Version */
+/* $Id: advansys.c,v 1.50 1998/05/08 23:39:15 bobf Exp bobf $ */
+#define ASC_VERSION "3.1E"    /* AdvanSys Driver Version */
 
 /*
  * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
             be page aligned.
 
      3.1C (1/10/98):
-         1. Update latest BIOS version to 3.1E.
+         1. Update latest BIOS version checked for from the /proc file.
          2. Don't set microcode SDTR variable at initialization. Instead
             wait until device capabilities have been detected from an Inquiry
             command.
          1. Improve performance when the driver is compiled as module by
             allowing up to 64 scatter-gather elements instead of 8.
 
+     3.1E (5/1/98):
+         1. Set time delay in AscWaitTixISRDone() to 1000 ms.
+         2. Include SMP locking changes.
+         3. For v2.1.93 and newer kernels use CONFIG_PCI and new PCI BIOS
+            access functions.
+         4. Update board serial number printing.
+         5. Try allocating an IRQ both with and without the SA_INTERRUPT
+            flag set to allow IRQ sharing with drivers that do not set
+            the SA_INTERRUPT flag. Also display a more descriptive error
+            message if request_irq() fails.
+         5. Update to latest Asc and Adv Libraries.
+
   J. Known Problems or Issues
 
          1. Remove conditional constants (ASC_QUEUE_FLOW_CONTROL) around
  * --- Linux Include Files 
  */
 
+#include <linux/config.h>
 #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
 #ifdef MODULE
 #include <linux/module.h>
 #include <linux/blk.h>
 #include <linux/stat.h>
 #endif /* version >= v1.3.0 */
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,95)
+#include <asm/spinlock.h>
+#endif /* version >= 2.1.95 */
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
 #include "advansys.h"
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,93)
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif /* CONFIG_PCI */
+#else /* version < v2.1.93 */
+/*
+ * For earlier than v2.1.93 the driver has its own PCI configuration.
+ * If PCI is not needed in a kernel before v2.1.93 this define can be
+ * turned-off to make the driver object smaller.
+ */
+#define ASC_CONFIG_PCI
+#endif /* version < v2.1.93 */
 
 /*
  * If Linux eventually defines a DID_UNDERRUN, the constant here can be
 #define ADVANSYS_ASSERT
 
 /* Enable driver tracing. */
-/*#define ADVANSYS_DEBUG*/
+/* #define ADVANSYS_DEBUG */
 
 /*
  * Because of no /proc to display them, statistics are disabled
 
 #define ASC_LIB_VERSION_MAJOR  1
 #define ASC_LIB_VERSION_MINOR  22
-#define ASC_LIB_SERIAL_NUMBER  111
+#define ASC_LIB_SERIAL_NUMBER  113
 
 typedef unsigned char uchar;
 
@@ -1484,7 +1512,7 @@ typedef struct asc_dvc_cfg {
     ASC_SCSI_BIT_ID_TYPE can_tagged_qng;
     ASC_SCSI_BIT_ID_TYPE cmd_qng_enabled;
     ASC_SCSI_BIT_ID_TYPE disc_enable;
-    uchar               res;
+    ASC_SCSI_BIT_ID_TYPE sdtr_enable;
     uchar               chip_scsi_id:4;
     uchar               isa_dma_speed:4;
     uchar               isa_dma_channel;
@@ -1947,8 +1975,6 @@ STATIC ushort    AscInitAscDvcVar(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitFromEEP(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitFromAscDvcVar(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitMicroCodeVar(ASC_DVC_VAR asc_ptr_type * asc_dvc);
-STATIC void      AscInitPollIsrCallBack(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_QDONE_INFO *);
 STATIC int       AscTestExternalLram(ASC_DVC_VAR asc_ptr_type *);
 STATIC uchar     AscMsgOutSDTR(ASC_DVC_VAR asc_ptr_type *, uchar, uchar);
 STATIC uchar     AscCalSDTRData(ASC_DVC_VAR asc_ptr_type *, uchar, uchar);
@@ -1991,31 +2017,6 @@ STATIC int       AscIsrChipHalted(ASC_DVC_VAR asc_ptr_type *);
 STATIC uchar     _AscCopyLramScsiDoneQ(PortAddr, ushort,
                     ASC_QDONE_INFO *, ulong);
 STATIC int       AscIsrQDone(ASC_DVC_VAR asc_ptr_type *);
-STATIC int       AscScsiSetupCmdQ(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar *, ulong);
-STATIC int       AscScsiInquiry(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar *, int);
-STATIC int       AscScsiTestUnitReady(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *);
-STATIC int       AscScsiStartStopUnit(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar);
-STATIC int       AscScsiReadCapacity(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar *);
-STATIC ulong     *swapfarbuf4(uchar *);
-STATIC int       PollQueueDone(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, int);
-STATIC int       PollScsiReadCapacity(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, ASC_CAP_INFO *);
-STATIC int       PollScsiInquiry(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar *, int);
-STATIC int       PollScsiTestUnitReady(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *);
-STATIC int       PollScsiStartUnit(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *);
-STATIC int       InitTestUnitReady(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *);
-STATIC int       AscPollQDone(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, int);
 STATIC int       AscCompareString(uchar *, uchar *, int);
 STATIC ushort    AscGetEisaChipCfg(PortAddr);
 STATIC ulong     AscGetEisaProductID(PortAddr);
@@ -2051,10 +2052,11 @@ STATIC PortAddr  AscSearchIOPortAddr(PortAddr, ushort);
 STATIC ushort    AscInitGetConfig(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitSetConfig(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitAsc1000Driver(ASC_DVC_VAR asc_ptr_type *);
-STATIC int       AscInitPollBegin(ASC_DVC_VAR asc_ptr_type *);
-STATIC int       AscInitPollEnd(ASC_DVC_VAR asc_ptr_type *);
-STATIC int       AscInitPollTarget(ASC_DVC_VAR asc_ptr_type *,
-                     ASC_SCSI_REQ_Q *, ASC_SCSI_INQUIRY *, ASC_CAP_INFO *);
+STATIC void      AscAsyncFix(ASC_DVC_VAR asc_ptr_type *, uchar,
+                    ASC_SCSI_INQUIRY *);
+STATIC int       AscTagQueuingSafe(ASC_SCSI_INQUIRY *);
+STATIC void      AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *,
+                    uchar, ASC_SCSI_INQUIRY *);
 STATIC int       AscExeScsiQueue(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q *);
 STATIC int       AscISR(ASC_DVC_VAR asc_ptr_type *);
 STATIC uint      AscGetNumOfFreeQueue(ASC_DVC_VAR asc_ptr_type *, uchar,
@@ -2074,7 +2076,7 @@ STATIC ulong     AscGetMaxDmaCount(ushort);
  */
 
 #define ADV_LIB_VERSION_MAJOR  3
-#define ADV_LIB_VERSION_MINOR  34
+#define ADV_LIB_VERSION_MINOR  45
 
 /* d_os_dep.h */
 #define ADV_OS_LINUX
@@ -3145,10 +3147,6 @@ extern ADVEEP_CONFIG Default_EEPROM_Config;
 #define ADV_IS_DATA_FLAG        0x08 /* 'addr' is data virtual pointer */
 #define ADV_IS_SGLIST_FLAG      0x10 /* 'addr' is sglist virtual pointer */
 
-/* 'IS_SCSIQ_FLAG is now obsolete; Instead use ADV_IS_SCSIQ_FLAG. */
-#define IS_SCSIQ_FLAG           ADV_IS_SCSIQ_FLAQ
-
-
 /* Return the address that is aligned at the next doubleword >= to 'addr'. */
 #define ADV_DWALIGN(addr)       (((ulong) (addr) + 0x3) & ~0x3)
 
@@ -3355,6 +3353,7 @@ typedef Scsi_Cmnd            REQ, *REQP;
 #define PCI_MAX_BUS             0xFF
 #define PCI_IOADDRESS_MASK      0xFFFE
 #define ASC_PCI_VENDORID        0x10CD
+#define ASC_PCI_DEVICE_ID_CNT   4       /* PCI Device ID count. */
 #define ASC_PCI_DEVICE_ID_1100  0x1100
 #define ASC_PCI_DEVICE_ID_1200  0x1200
 #define ASC_PCI_DEVICE_ID_1300  0x1300
@@ -3792,7 +3791,11 @@ STATIC ushort asc_bus[ASC_NUM_BUS] ASC_INITDATA = {
     ASC_IS_PCI,
 };
 
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
 STATIC int pci_scan_method ASC_INITDATA = -1;
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
 
 /*
  * Used with the LILO 'advansys' option to eliminate or
@@ -3854,7 +3857,8 @@ STATIC int        adv_build_req(asc_board_t *, Scsi_Cmnd *, ADV_SCSI_REQ_Q **);
 STATIC int        adv_get_sglist(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *, Scsi_Cmnd *);
 STATIC void       asc_isr_callback(ASC_DVC_VAR *, ASC_QDONE_INFO *);
 STATIC void       adv_isr_callback(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
-STATIC int        asc_init_dev(ASC_DVC_VAR *, Scsi_Cmnd *);
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
 STATIC int        asc_srch_pci_dev(PCI_DEVICE *);
 STATIC uchar      asc_scan_method(void);
 STATIC int        asc_pci_find_dev(PCI_DEVICE *);
@@ -3862,6 +3866,8 @@ STATIC void       asc_get_pci_cfg(PCI_DEVICE *, PCI_CONFIG_SPACE *);
 STATIC ushort     asc_get_cfg_word(PCI_DATA *);
 STATIC uchar      asc_get_cfg_byte(PCI_DATA *);
 STATIC void       asc_put_cfg_byte(PCI_DATA *, uchar);
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
 STATIC void       asc_enqueue(asc_queue_t *, REQP, int);
 STATIC REQP       asc_dequeue(asc_queue_t *, int);
 STATIC REQP       asc_dequeue_list(asc_queue_t *, REQP *, int);
@@ -4172,8 +4178,27 @@ advansys_detect(Scsi_Host_Template *tpnt)
     ADV_DVC_VAR         *adv_dvc_varp = NULL;
     int                 ioport = 0;
     int                 share_irq = FALSE;
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DEVICE          pciDevice;
     PCI_CONFIG_SPACE    pciConfig;
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+    unsigned long       pci_memory_address;
+#endif /* version >= v1,3,0 */
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    struct pci_dev      *pci_devp = NULL;
+    int                 pci_device_id_cnt = 0;
+    unsigned int        pci_device_id[ASC_PCI_DEVICE_ID_CNT] = {
+                                    ASC_PCI_DEVICE_ID_1100,
+                                    ASC_PCI_DEVICE_ID_1200,
+                                    ASC_PCI_DEVICE_ID_1300,
+                                    ASC_PCI_DEVICE_ID_2300
+                        };
+    unsigned long       pci_memory_address;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
     int                 warn_code, err_code;
     int                 ret;
 
@@ -4217,10 +4242,14 @@ advansys_detect(Scsi_Host_Template *tpnt)
         ioport = 0;
     }
 
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     memset(&pciDevice, 0, sizeof(PCI_DEVICE));
     memset(&pciConfig, 0, sizeof(PCI_CONFIG_SPACE));
     pciDevice.maxBusNumber = PCI_MAX_BUS;
     pciDevice.endSlot = PCI_MAX_SLOT;
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */ 
 
     for (bus = 0; bus < ASC_NUM_BUS; bus++) {
 
@@ -4301,22 +4330,52 @@ advansys_detect(Scsi_Host_Template *tpnt)
                 iop = AscSearchIOPortAddr(iop, asc_bus[bus]);
                 break;
 
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+            case ASC_IS_PCI:
+                if (asc_srch_pci_dev(&pciDevice) != PCI_DEVICE_FOUND) {
+                    iop = 0;
+                } else {
+                    ASC_DBG2(2,
+                        "advansys_detect: slotFound %d, busNumber %d\n",
+                        pciDevice.slotFound, pciDevice.busNumber);
+                    asc_get_pci_cfg(&pciDevice, &pciConfig);
+                    iop = pciConfig.baseAddress[0] & PCI_IOADDRESS_MASK;
+                    ASC_DBG2(1,
+                        "advansys_detect: vendorID %X, deviceID %X\n",
+                        pciConfig.vendorID, pciConfig.deviceID);
+                    ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
+                        iop, pciConfig.irqLine);
+                }
+                break;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
             case ASC_IS_PCI:
-                    if (asc_srch_pci_dev(&pciDevice) != PCI_DEVICE_FOUND) {
-                        iop = 0;
+                while (pci_device_id_cnt < ASC_PCI_DEVICE_ID_CNT) {
+                    if ((pci_devp = pci_find_device(ASC_PCI_VENDORID,
+                         pci_device_id[pci_device_id_cnt], pci_devp)) == NULL) {
+                        pci_device_id_cnt++;
                     } else {
-                        ASC_DBG2(2,
-                            "advansys_detect: slotFound %d, busNumber %d\n",
-                            pciDevice.slotFound, pciDevice.busNumber);
-                        asc_get_pci_cfg(&pciDevice, &pciConfig);
-                        iop = pciConfig.baseAddress[0] & PCI_IOADDRESS_MASK;
-                        ASC_DBG2(1,
-                            "advansys_detect: vendorID %X, deviceID %X\n",
-                            pciConfig.vendorID, pciConfig.deviceID);
-                        ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
-                            iop, pciConfig.irqLine);
+                        break;
                     }
+                }
+                if (pci_devp == NULL) {
+                    iop = 0;
+                } else {
+                    ASC_DBG2(2,
+                        "advansys_detect: devfn %d, bus number %d\n",
+                        pci_devp->devfn, pci_devp->bus->number);
+                    iop = pci_devp->base_address[0] & PCI_IOADDRESS_MASK;
+                    ASC_DBG2(1,
+                        "advansys_detect: vendorID %X, deviceID %X\n",
+                        pci_devp->vendor, pci_devp->device);
+                    ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
+                        iop, pci_devp->irq);
+                }
                 break;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 
             default:
                 ASC_PRINT1("advansys_detect: unknown bus type: %d\n",
@@ -4350,14 +4409,29 @@ advansys_detect(Scsi_Host_Template *tpnt)
             boardp->id = asc_board_count - 1;
 
             /*
-             * Handle both narrow and wide PCI boards.
+             * Handle both narrow and wide boards.
              *
              * If a Wide board was detected, set the board structure
              * wide board flag. Set-up the board structure based on
              * the board type.
              */
-            if ((asc_bus[bus] == ASC_IS_PCI &&
-                 pciConfig.deviceID == ASC_PCI_DEVICE_ID_2300) == 0) {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+            if (asc_bus[bus] == ASC_IS_PCI &&
+                 pciConfig.deviceID == ASC_PCI_DEVICE_ID_2300) {
+                boardp->flags |= ASC_IS_WIDE_BOARD;
+            }
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+            if (asc_bus[bus] == ASC_IS_PCI &&
+                 pci_devp->device == ASC_PCI_DEVICE_ID_2300) {
+                boardp->flags |= ASC_IS_WIDE_BOARD;
+            }
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
+
+            if (ASC_NARROW_BOARD(boardp)) {
                 ASC_DBG(1, "advansys_detect: narrow board\n");
                 asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
                 asc_dvc_varp->bus_type = asc_bus[bus];
@@ -4368,7 +4442,6 @@ advansys_detect(Scsi_Host_Template *tpnt)
                 asc_dvc_varp->isr_callback = (Ptr2Func) asc_isr_callback;
             } else {
                 ASC_DBG(1, "advansys_detect: wide board\n");
-                boardp->flags |= ASC_IS_WIDE_BOARD;
                 adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
                 adv_dvc_varp->drv_ptr = (ulong) boardp;
                 adv_dvc_varp->cfg = &boardp->dvc_cfg.adv_dvc_cfg;
@@ -4388,20 +4461,41 @@ advansys_detect(Scsi_Host_Template *tpnt)
                  * PCI register base address will not cross a page
                  * boundary.
                  */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+                pci_memory_address = pciConfig.baseAddress[1];
+                if ((boardp->ioremap_addr =
+                    ioremap(pci_memory_address & PAGE_MASK,
+                         PAGE_SIZE)) == 0) {
+                   ASC_PRINT3(
+"advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n",
+                   boardp->id, pci_memory_address, ADV_CONDOR_IOLEN);
+                   scsi_unregister(shp);
+                   asc_board_count--;
+                   continue;
+                }
+                adv_dvc_varp->iop_base = (AdvPortAddr)
+                    (boardp->ioremap_addr +
+                     (pci_memory_address - (pci_memory_address & PAGE_MASK)));
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+                pci_memory_address = pci_devp->base_address[1];
                 if ((boardp->ioremap_addr =
-                    ioremap(pciConfig.baseAddress[1] & PAGE_MASK,
+                    ioremap(pci_memory_address & PAGE_MASK,
                          PAGE_SIZE)) == 0) {
                    ASC_PRINT3(
 "advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n",
-                   boardp->id, pciConfig.baseAddress[1], ADV_CONDOR_IOLEN);
+                   boardp->id, pci_memory_address, ADV_CONDOR_IOLEN);
                    scsi_unregister(shp);
                    asc_board_count--;
                    continue;
                 }
                 adv_dvc_varp->iop_base = (AdvPortAddr)
                     (boardp->ioremap_addr +
-                     (pciConfig.baseAddress[1] -
-                      (pciConfig.baseAddress[1] & PAGE_MASK)));
+                     (pci_memory_address - (pci_memory_address & PAGE_MASK)));
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 #endif /* version >= v1,3,0 */
 
                 /*
@@ -4447,16 +4541,33 @@ advansys_detect(Scsi_Host_Template *tpnt)
                     shp->unchecked_isa_dma = FALSE;
                     share_irq = TRUE;
                     break;
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
                 case ASC_IS_PCI:
                     shp->irq = asc_dvc_varp->irq_no = pciConfig.irqLine;
-                    shp->unchecked_isa_dma = FALSE;
-                    share_irq = TRUE;
                     asc_dvc_varp->cfg->pci_device_id = pciConfig.deviceID;
                     asc_dvc_varp->cfg->pci_slot_info =
                         ASC_PCI_MKID(pciDevice.busNumber,
                             pciDevice.slotFound,
                             pciDevice.devFunc);
+                    shp->unchecked_isa_dma = FALSE;
+                    share_irq = TRUE;
                     break;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+                case ASC_IS_PCI:
+                    shp->irq = asc_dvc_varp->irq_no = pci_devp->irq;
+                    asc_dvc_varp->cfg->pci_device_id = pci_devp->device;
+                    asc_dvc_varp->cfg->pci_slot_info =
+                        ASC_PCI_MKID(pci_devp->bus->number,
+                            PCI_SLOT(pci_devp->devfn),
+                            PCI_FUNC(pci_devp->devfn));
+                    shp->unchecked_isa_dma = FALSE;
+                    share_irq = TRUE;
+                    break;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
                 default:
                     ASC_PRINT2(
 "advansys_detect: board %d: unknown adapter type: %d\n",
@@ -4470,14 +4581,29 @@ advansys_detect(Scsi_Host_Template *tpnt)
                  * For Wide boards set PCI information before calling
                  * AdvInitGetConfig().
                  */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
                 shp->irq = adv_dvc_varp->irq_no = pciConfig.irqLine;
+                adv_dvc_varp->cfg->pci_device_id = pciConfig.deviceID;
+                adv_dvc_varp->cfg->pci_slot_info =
+                ASC_PCI_MKID(pciDevice.busNumber,
+                    pciDevice.slotFound,
+                    pciDevice.devFunc);
                 shp->unchecked_isa_dma = FALSE;
                 share_irq = TRUE;
-                adv_dvc_varp->cfg->pci_device_id = pciConfig.deviceID;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+                shp->irq = adv_dvc_varp->irq_no = pci_devp->irq;
+                adv_dvc_varp->cfg->pci_device_id = pci_devp->device;
                 adv_dvc_varp->cfg->pci_slot_info =
-                    ASC_PCI_MKID(pciDevice.busNumber,
-                        pciDevice.slotFound,
-                        pciDevice.devFunc);
+                ASC_PCI_MKID(pci_devp->bus->number,
+                    PCI_SLOT(pci_devp->devfn),
+                    PCI_FUNC(pci_devp->devfn));
+                shp->unchecked_isa_dma = FALSE;
+                share_irq = TRUE;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
             }
 
             /*
@@ -4572,7 +4698,7 @@ advansys_detect(Scsi_Host_Template *tpnt)
                  */
                 ep = &boardp->eep_config.asc_eep;
 
-                ep->init_sdtr = asc_dvc_varp->init_sdtr;
+                ep->init_sdtr = asc_dvc_varp->cfg->sdtr_enable;
                 ep->disc_enable = asc_dvc_varp->cfg->disc_enable;
                 ep->use_cmd_qng = asc_dvc_varp->cfg->cmd_qng_enabled;
                 ep->isa_dma_speed = asc_dvc_varp->cfg->isa_dma_speed;
@@ -4879,14 +5005,36 @@ advansys_detect(Scsi_Host_Template *tpnt)
             if ((ret = request_irq(shp->irq, advansys_interrupt,
                             SA_INTERRUPT, "advansys")) != 0)
 #else /* version >= v1.3.70 */
-            if ((ret = request_irq(shp->irq, advansys_interrupt,
+           /*
+            * If request_irq() fails with the SA_INTERRUPT flag set,
+            * then try again without the SA_INTERRUPT flag set. This
+            * allows IRQ sharing to work even with other drivers that
+            * do not set the SA_INTERRUPT flag.
+            *
+            * If SA_INTERRUPT is not set, then interrupts are enabled
+            * before the driver interrupt function is called.
+            */
+            if (((ret = request_irq(shp->irq, advansys_interrupt,
                             SA_INTERRUPT | (share_irq == TRUE ? SA_SHIRQ : 0),
-                            "advansys", boardp)) != 0)
+                            "advansys", boardp)) != 0) &&
+                ((ret = request_irq(shp->irq, advansys_interrupt,
+                            (share_irq == TRUE ? SA_SHIRQ : 0),
+                            "advansys", boardp)) != 0))
 #endif /* version >= v1.3.70 */
             {
-                ASC_PRINT2(
-"advansys_detect: board %d: request_irq() failed %d\n",
-                    boardp->id, ret);
+                if (ret == -EBUSY) {
+                    ASC_PRINT2(
+"advansys_detect: board %d: request_irq(): IRQ %d already in use.\n",
+                        boardp->id, shp->irq);
+                } else if (ret == -EINVAL) {
+                    ASC_PRINT2(
+"advansys_detect: board %d: request_irq(): IRQ %d not valid.\n",
+                        boardp->id, shp->irq);
+                } else {
+                    ASC_PRINT3(
+"advansys_detect: board %d: request_irq(): IRQ %d failed with %d\n",
+                        boardp->id, shp->irq, ret);
+                }
                 release_region(shp->io_port, shp->n_io_port);
 #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
                 iounmap(boardp->ioremap_addr);
@@ -5140,7 +5288,11 @@ advansys_info(struct Scsi_Host *shp)
                 busname = "ISA";
             }
             sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
 "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u, DMA %u",
+#else /* version >= v2.1.92 */ 
+"AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u, DMA %u",
+#endif /* version >= v2.1.92 */ 
                 ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
                 (unsigned) shp->base,
                 shp->io_port, shp->n_io_port - 1,
@@ -5153,7 +5305,11 @@ advansys_info(struct Scsi_Host *shp)
                 busname = "PCI";
             }
             sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
                 "AdvanSys SCSI %s: %s %u CDB: IO %X/%X, IRQ %u",
+#else /* version >= v2.1.92 */ 
+                "AdvanSys SCSI %s: %s %u CDB: IO %lX/%X, IRQ %u",
+#endif /* version >= v2.1.92 */ 
                 ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
                 shp->io_port, shp->n_io_port - 1, shp->irq);
         } else {
@@ -5168,7 +5324,11 @@ advansys_info(struct Scsi_Host *shp)
                     boardp->id, asc_dvc_varp->bus_type);
             }
             sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
                 "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u",
+#else /* version >= v2.1.92 */ 
+                "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u",
+#endif /* version >= v2.1.92 */ 
                 ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
                 (unsigned) shp->base, shp->io_port - 1,
                 shp->n_io_port, shp->irq);
@@ -5718,13 +5878,6 @@ advansys_reset(Scsi_Cmnd *scp, unsigned int reset_flags)
                 status = AscResetDevice(asc_dvc_varp, scp->target);
                 cli();
 
-                /*
-                 * If the device has been reset, try to initialize it.
-                 */
-                if (status == ASC_TRUE) {
-                    status = asc_init_dev(asc_dvc_varp, scp);
-                }
-
                 switch (status) {
                 case ASC_TRUE:
                     ASC_DBG(1, "advansys_reset: AscResetDevice() success\n");
@@ -5945,7 +6098,7 @@ advansys_reset(Scsi_Cmnd *scp, unsigned int reset_flags)
         asc_scsi_done_list(done_scp);
     }
 
-    ASC_DBG1(1, "advansys_reset: ret %d", ret);
+    ASC_DBG1(1, "advansys_reset: ret %d\n", ret);
 
     /* Re-enable interrupts, if they were enabled on entry. */
     restore_flags(flags);
@@ -6109,15 +6262,27 @@ advansys_interrupt(int irq, struct pt_regs *regs)
 advansys_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 #endif /* version >= v1.3.70 */
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
     int             flags;
+#else /* version >= v2.1.95 */
+    unsigned long   flags;
+#endif /* version >= v2.1.95 */
     int             i;
     asc_board_t     *boardp;
     Scsi_Cmnd       *done_scp = NULL, *last_scp = NULL;
     Scsi_Cmnd       *new_last_scp;
 
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
     /* Disable interrupts, if they aren't already disabled. */
     save_flags(flags);
     cli();
+#else /* version >= v2.1.95 */
+    /*
+     * Disable interrupts, if they aren't already disabled and acquire
+     * the I/O spinlock.
+     */
+    spin_lock_irqsave(&io_request_lock, flags);
+#endif /* version >= v2.1.95 */
 
     ASC_DBG(1, "advansys_interrupt: begin\n");
 
@@ -6191,13 +6356,26 @@ advansys_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
     /*
      * It is possible for the request done function to re-enable
-     * interrupts without confusing the driver. But here interrupts
-     * aren't enabled until all requests have been completed.
+     * interrupts without confusing the driver. But here the
+     * original flags aren't restored until all requests have been
+     * completed.
      */
     asc_scsi_done_list(done_scp);
 
-    /* Re-enable interrupts, if they were enabled on entry. */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
+    /*
+     * Restore the original flags which will enable interrupts
+     * if and only if they were enabled on entry.
+     */
     restore_flags(flags);
+#else /* version >= v2.1.95 */
+    /*
+     * Release the I/O spinlock and restore the original flags
+     * which will enable interrupts if and only if they were
+     * enabled on entry.
+     */
+    spin_unlock_irqrestore(&io_request_lock, flags);
+#endif /* version >= v2.1.95 */
 
     ASC_DBG(1, "advansys_interrupt: end\n");
     return;
@@ -6341,21 +6519,6 @@ asc_execute_scsi_cmnd(Scsi_Cmnd *scp)
 
         asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
 
-        /*
-         * Narrow Board - Asc Library requires special device initialization.
-         *
-         * If this is the first command, then initialize the device. If
-         * no device is found set 'DID_BAD_TARGET' and return.
-         */
-        if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0) {
-            if (asc_init_dev(asc_dvc_varp, scp) == ASC_FALSE) {
-                scp->result = HOST_BYTE(DID_BAD_TARGET);
-                asc_enqueue(&boardp->done, scp, ASC_BACK);
-                return ASC_ERROR;
-            }
-            boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->target);
-        }
-
         /*
          * Build Asc Library request structure using the
          * global structures 'asc_scsi_req' and 'asc_sg_head'.
@@ -6901,7 +7064,7 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
     asc_board_t         *boardp;
     Scsi_Cmnd           *scp;
     struct Scsi_Host    *shp;
-    int            underrun = ASC_FALSE;
+    int                 underrun = ASC_FALSE;
     int                 i;
 
     ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
@@ -6947,6 +7110,7 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
      * Display a message and return.
      */
     boardp = ASC_BOARDP(shp);
+    ASC_ASSERT(asc_dvc_varp == &boardp->dvc_var.asc_dvc_var);
     if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) {
         ASC_PRINT2("asc_isr_callback: board %d: scp %x not on active queue\n",
             boardp->id, (unsigned) scp);
@@ -6957,7 +7121,7 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
      * Check for an underrun condition.
      */
     if (scp->request_bufflen != 0 && qdonep->remain_bytes != 0 && 
-    qdonep->remain_bytes <= scp->request_bufflen != 0) {
+        qdonep->remain_bytes <= scp->request_bufflen != 0) {
         ASC_DBG1(1, "asc_isr_callback: underrun condition %u bytes\n",
         (unsigned) qdonep->remain_bytes);
         underrun = ASC_TRUE;
@@ -6978,13 +7142,28 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
             scp->result = HOST_BYTE(DID_ERROR);
             break;
         }
-    /*
-     * If there was an underrun without any other error,
-     * set DID_ERROR to indicate the underrun error.
-     */
-    if (scp->result == 0 && underrun == ASC_TRUE) {
+
+        /*
+         * If an INQUIRY command completed successfully, then call
+         * the AscInquiryHandling() function to set-up the device.
+         */
+        if (scp->cmnd[0] == SCSICMD_Inquiry && scp->lun == 0 &&
+            (scp->request_bufflen - qdonep->remain_bytes) >= 8)
+        {
+            AscInquiryHandling(asc_dvc_varp, scp->target & 0x7,
+                (ASC_SCSI_INQUIRY *) scp->request_buffer);
+        }
+
+        /*
+         * If there was an underrun without any other error,
+         * set DID_ERROR to indicate the underrun error.
+         *
+         * Note: There is no way yet to indicate the number
+         * of underrun bytes.
+         */
+        if (scp->result == 0 && underrun == ASC_TRUE) {
             scp->result = HOST_BYTE(DID_UNDERRUN);
-    }
+        }
         break;
 
     case QD_WITH_ERROR:
@@ -7014,10 +7193,9 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
 
         default:
             /* QHSTA error occurred */
-            ASC_DBG1(2, "asc_isr_callback: host_stat %x\n",
+            ASC_DBG1(1, "asc_isr_callback: host_stat %x\n",
                 qdonep->d3.host_stat);
-            scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) |
-                STATUS_BYTE(qdonep->d3.scsi_stat);
+            scp->result = HOST_BYTE(DID_BAD_TARGET);
             break;
         }
         break;
@@ -7029,12 +7207,23 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
         break;
 
     default:
-        ASC_PRINT1("asc_isr_callback: done_stat %x\n", qdonep->d3.done_stat);
+        ASC_DBG1(1, "asc_isr_callback: done_stat %x\n", qdonep->d3.done_stat);
         scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) |
                 STATUS_BYTE(qdonep->d3.scsi_stat);
         break;
     }
 
+    /*
+     * If the 'init_tidmask' bit isn't already set for the target and the
+     * current request finished normally, then set the bit for the target
+     * to indicate that a device is present.
+     */
+    if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0 &&
+        qdonep->d3.done_stat == QD_NO_ERROR &&
+        qdonep->d3.host_stat == QHSTA_NO_ERROR) {
+        boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->target);
+    }
+
     /* 
      * Because interrupts may be enabled by the 'Scsi_Cmnd' done
      * function, add the command to the end of the board's done queue.
@@ -7058,7 +7247,7 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
     adv_req_t           *reqp;
     Scsi_Cmnd           *scp;
     struct Scsi_Host    *shp;
-    int            underrun = ASC_FALSE;
+    int                 underrun = ASC_FALSE;
     int                 i;
 
     ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
@@ -7126,6 +7315,7 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
      * to free the adv_req_t and adv_sgblk_t, if any, structures.
      */
     boardp = ASC_BOARDP(shp);
+    ASC_ASSERT(adv_dvc_varp == &boardp->dvc_var.adv_dvc_var);
     if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) {
         ASC_PRINT2("adv_isr_callback: board %d: scp %x not on active queue\n",
             boardp->id, (unsigned) scp);
@@ -7158,13 +7348,16 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
             scp->result = HOST_BYTE(DID_ERROR);
             break;
         }
-    /*
-     * If there was an underrun without any other error,
-     * set DID_ERROR to indicate the underrun error.
-     */
-    if (scp->result == 0 && underrun == ASC_TRUE) {
-            scp->result = HOST_BYTE(DID_UNDERRUN);
-    }
+        /*
+         * If there was an underrun without any other error,
+         * set DID_ERROR to indicate the underrun error.
+         *
+         * Note: There is no way yet to indicate the number
+         * of underrun bytes.
+         */
+        if (scp->result == 0 && underrun == ASC_TRUE) {
+                scp->result = HOST_BYTE(DID_UNDERRUN);
+        }
         break;
 
     case QD_WITH_ERROR:
@@ -7194,7 +7387,7 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
 
         default:
             /* Some other QHSTA error occurred. */
-            ASC_DBG1(2, "adv_isr_callback: host_status %x\n",
+            ASC_DBG1(1, "adv_isr_callback: host_status %x\n",
                 scsiqp->host_status);
             scp->result = HOST_BYTE(DID_BAD_TARGET);
             break;
@@ -7207,15 +7400,15 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
         break;
 
     default:
-        ASC_PRINT1("adv_isr_callback: done_status %x\n", scsiqp->done_status);
+        ASC_DBG1(1, "adv_isr_callback: done_status %x\n", scsiqp->done_status);
         scp->result = HOST_BYTE(DID_ERROR) | STATUS_BYTE(scsiqp->scsi_status);
         break;
     }
 
     /*
      * If the 'init_tidmask' bit isn't already set for the target and the
-     * current request did not finish with a Selection Timeout, then set
-     * the bit for the target to indicate that a device is present.
+     * current request finished normally, then set the bit for the target
+     * to indicate that a device is present.
      */
     if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0 &&
         scsiqp->done_status == QD_NO_ERROR &&
@@ -7252,119 +7445,8 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
     return;
 }
 
-/*
- * asc_init_dev() - Narrow Board initialization function. 
- *
- * Perform one-time initialization of a device for Asc Library
- */
-STATIC int
-asc_init_dev(ASC_DVC_VAR *asc_dvc_varp, Scsi_Cmnd *scp)
-{
-    asc_board_t             *boardp;
-    ASC_SCSI_REQ_Q          *scsireqq;
-    ASC_CAP_INFO            *cap_info;
-    ASC_SCSI_INQUIRY        *inquiry;
-    int                     found;
-    ASC_SCSI_BIT_ID_TYPE    save_use_tagged_qng;
-    ASC_SCSI_BIT_ID_TYPE    save_can_tagged_qng;
-    int                     ret;
-#ifdef ADVANSYS_DEBUG
-    ASC_SCSI_BIT_ID_TYPE    tidmask; /* target id bit mask: 1 - 128 */
-#endif /* ADVANSYS_DEBUG */
-
-    ASC_DBG1(1, "asc_init_dev: target %d\n", (unsigned) scp->target);
-
-    /* The host's target id is set in init_tidmask during initialization. */
-    ASC_ASSERT(asc_dvc_varp->cfg->chip_scsi_id != scp->target);
-
-    boardp = ASC_BOARDP(scp->host);
-
-    /* Set-up AscInitPollTarget() arguments. */
-    scsireqq = &boardp->scsireqq;
-    memset(scsireqq, 0, sizeof(ASC_SCSI_REQ_Q));
-    cap_info = &boardp->cap_info;
-    memset(cap_info, 0, sizeof(ASC_CAP_INFO));
-    inquiry = &boardp->inquiry;
-    memset(inquiry, 0, sizeof(ASC_SCSI_INQUIRY));
-
-    /*
-     * AscInitPollBegin() re-initializes these bitmask fields to zero.
-     * Save the current bitmask value and 'or' them back in after calling
-     * AscInitPollEnd() below..
-     */
-    save_use_tagged_qng = asc_dvc_varp->use_tagged_qng;
-    save_can_tagged_qng = asc_dvc_varp->cfg->can_tagged_qng;
-
-    ASC_DBG(2, "asc_init_dev: AscInitPollBegin()\n");
-    if (AscInitPollBegin(asc_dvc_varp)) {
-        ASC_PRINT1("asc_init_dev: board %d: AscInitPollBegin() failed\n",
-            boardp->id);
-        return ASC_FALSE;
-    }
-
-    scsireqq->sense_ptr = &scsireqq->sense[0];
-    scsireqq->r1.sense_len = ASC_MIN_SENSE_LEN;
-    scsireqq->r1.target_id = ASC_TID_TO_TARGET_ID(scp->target);
-    scsireqq->r1.target_lun = 0;
-    scsireqq->r2.target_ix = ASC_TIDLUN_TO_IX(scp->target, 0);
-
-    found = ASC_FALSE;
-    ASC_DBG(2, "asc_init_dev: AscInitPollTarget()\n");
-    switch (ret = AscInitPollTarget(asc_dvc_varp, scsireqq, inquiry,
-        cap_info)) {
-    case ASC_TRUE:
-        found = ASC_TRUE;
-#ifdef ADVANSYS_DEBUG
-        tidmask = ADV_TID_TO_TIDMASK(scp->target);
-        ASC_DBG2(1, "asc_init_dev: lba %lu, blk_size %lu\n",
-            cap_info->lba, cap_info->blk_size);
-        ASC_DBG1(1, "asc_init_dev: peri_dvc_type %x\n",
-            inquiry->byte0.peri_dvc_type);
-        if (asc_dvc_varp->use_tagged_qng & tidmask) {
-            ASC_DBG1(1, "asc_init_dev: command queuing enabled: %d\n",
-                asc_dvc_varp->max_dvc_qng[scp->target]);
-        } else {
-            ASC_DBG(1, "asc_init_dev: command queuing disabled\n");
-        }
-        if (asc_dvc_varp->init_sdtr & tidmask) {
-            ASC_DBG(1, "asc_init_dev: synchronous transfers enabled\n");
-        } else {
-            ASC_DBG(1, "asc_init_dev: synchronous transfers disabled\n");
-        }
-        /* Set bit means fix disabled. */
-        if (asc_dvc_varp->pci_fix_asyn_xfer & tidmask) {
-            ASC_DBG(1, "asc_init_dev: synchronous transfer fix disabled\n");
-        } else {
-            ASC_DBG(1, "asc_init_dev: synchronous transfer fix enabled\n");
-        }
-#endif /* ADVANSYS_DEBUG */
-        break;
-    case ASC_FALSE:
-        ASC_DBG(1, "asc_init_dev: no device found\n");
-        break;
-    case ASC_ERROR:
-        ASC_PRINT1("asc_init_dev: board %d: AscInitPollTarget() ASC_ERROR\n",
-                boardp->id);
-        break;
-    default:
-        ASC_PRINT2(
-"asc_init_dev: board %d: AscInitPollTarget() unknown ret %d\n",
-                boardp->id, ret);
-        break;
-    }
-
-    /* Restore previously set bits in the bitmask fields. */
-    asc_dvc_varp->use_tagged_qng |= save_use_tagged_qng;
-    asc_dvc_varp->cfg->can_tagged_qng |= save_can_tagged_qng;
-
-    ASC_DBG(2, "asc_init_dev: AscInitPollEnd()\n");
-    AscInitPollEnd(asc_dvc_varp);
-
-    ASC_DBG1(1, "asc_init_dev: found %d\n", found); 
-
-    return found;
-}
-
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
 /*
  * Search for an AdvanSys PCI device in the PCI configuration space.
  */
@@ -7373,7 +7455,7 @@ STATIC int
 asc_srch_pci_dev(PCI_DEVICE *pciDevice)
 )
 {
-    int ret;
+    int                        ret = PCI_DEVICE_NOT_FOUND;
 
     ASC_DBG(2, "asc_srch_pci_dev: begin\n");
 
@@ -7400,6 +7482,7 @@ asc_srch_pci_dev(PCI_DEVICE *pciDevice)
             ASC_DBG1(2, "asc_srch_pci_dev: recursive call return %d\n", ret);
         }
     }
+
     ASC_DBG1(2, "asc_srch_pci_dev: return %d\n", ret);
     return ret;
 }
@@ -7500,7 +7583,7 @@ asc_get_pci_cfg(PCI_DEVICE *pciDevice, PCI_CONFIG_SPACE *pciConfig)
     uchar       counter;
     uchar       *localConfig;
 
-    ASC_DBG1(4, "asc_get_pci_cfg: slot found - %d\n ",
+    ASC_DBG1(4, "asc_get_pci_cfg: slotFound %d\n ",
         pciDevice->slotFound);
 
     pciData.type = pciDevice->type;
@@ -7767,6 +7850,8 @@ asc_put_cfg_byte(PCI_DATA *pciData, uchar byte_data)
     }
     ASC_DBG(4, "asc_put_cfg_byte: end\n");
 }
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
 
 /*
  * Add a 'REQP' to the end of specified queue. Set 'tidmask'
@@ -8176,9 +8261,9 @@ asc_prt_adv_bios(struct Scsi_Host *shp, char *cp, int cplen)
             major, minor, letter >= 26 ? '?' : letter + 'A');
         ASC_PRT_NEXT();
 
-        /* Current available ROM BIOS release is 3.1E. */
+        /* Current available ROM BIOS release is 3.1C. */
         if (major < 3 || (major <= 3 && minor < 1) ||
-            (major <= 3 && minor <= 1 && letter < ('E'- 'A'))) {
+            (major <= 3 && minor <= 1 && letter < ('C'- 'A'))) {
             upgrade = ASC_TRUE;
         }
     }
@@ -8195,18 +8280,18 @@ asc_prt_adv_bios(struct Scsi_Host *shp, char *cp, int cplen)
  * Add serial number to information bar if signature AAh
  * is found in at bit 15-9 (7 bits) of word 1.
  *
- * Serial Number consists 12 alpha-numeric digits.
+ * Serial Number consists fo 12 alpha-numeric digits.
  *
- *       1 - Product type (A,B,C,D..) Word0: 15-13 (3 bits)
- *       2 - MFG Location (A,B,C,D..) Word0: 12-10 (3 bits)
- *     3-4 - Product ID (0-99)        Word0: 10-0 (11 bits)
- *       5 - Product revision         Word0:  "         "
+ *       1 - Product type (A,B,C,D..)  Word0: 15-13 (3 bits)
+ *       2 - MFG Location (A,B,C,D..)  Word0: 12-10 (3 bits)
+ *     3-4 - Product ID (0-99)         Word0: 9-0 (10 bits)
+ *       5 - Product revision (A-J)    Word0:  "         "
  *
- *         Signature                Word1: 15-9 (7 bits)
- *       6 - Year (4-9)               Word1: 8-6 (3 bits)
- *     7-8 - Week of the year         Word1: 5-0 (6 bits)
+ *           Signature                 Word1: 15-9 (7 bits)
+ *       6 - Year (0-9)                Word1: 8-6 (3 bits) & Word2: 15 (1 bit)
+ *     7-8 - Week of the year (1-52)   Word1: 5-0 (6 bits)
  *
- *    9-12 - Serial Number            Word2: 15-0 (16 bits)
+ *    9-12 - Serial Number (A001-Z999) Word2: 14-0 (15 bits)
  *
  * Note 1: Only production cards will have a serial number.
  *
@@ -8228,7 +8313,11 @@ asc_get_eeprom_string(ushort *serialnum, uchar *cp)
         w = serialnum[0];
 
         /* Product type - 1st digit. */
-        *cp++ = 'A' + ((w & 0xE000) >> 13);
+        if ((*cp = 'A' + ((w & 0xE000) >> 13)) == 'H') {
+            /* Product type is P=Prototype */
+            *cp += 0x8;
+        }
+        cp++;
 
         /* Manufacturing location - 2nd digit. */
         *cp++ = 'A' + ((w & 0x1C00) >> 10);
@@ -8247,8 +8336,17 @@ asc_get_eeprom_string(ushort *serialnum, uchar *cp)
          */
         w = serialnum[1];
 
-        /* Year - 6th digit. */
-        *cp++ = '0' + ((w & 0x1C0) >> 6);
+        /*
+         * Year - 6th digit.
+         *
+         * If bit 15 of third word is set, then the
+         * last digit of the year is greater than 7.
+         */
+        if (serialnum[2] & 0x8000) {
+            *cp++ = '8' + ((w & 0x1C0) >> 6);
+        } else {
+            *cp++ = '0' + ((w & 0x1C0) >> 6);
+        }
 
         /* Week of year - 7th, 8th digits. */
         num = w & 0x003F;
@@ -8259,7 +8357,7 @@ asc_get_eeprom_string(ushort *serialnum, uchar *cp)
         /*
          * Third word
          */
-        w = serialnum[2];
+        w = serialnum[2] & 0x7FFF;
 
         /* Serial number - 9th digit. */
         *cp++ = 'A' + (w / 1000);
@@ -9275,6 +9373,8 @@ DvcReadPCIConfigByte(
         ushort offset)
 )
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DATA    pciData;
 
     pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
@@ -9283,6 +9383,21 @@ DvcReadPCIConfigByte(
     pciData.offset = offset;
     pciData.type = pci_scan_method;
     return asc_get_cfg_byte(&pciData);
+#else /* ASC_CONFIG_PCI */
+    return 0;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    uchar byte_data;
+    pcibios_read_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+        PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+            ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+        offset, &byte_data);
+    return byte_data;
+#else /* CONFIG_PCI */
+    return 0;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 }
 
 /*
@@ -9296,6 +9411,8 @@ DvcWritePCIConfigByte(
         uchar  byte_data)
 )
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DATA    pciData;
 
     pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
@@ -9304,6 +9421,15 @@ DvcWritePCIConfigByte(
     pciData.offset = offset;
     pciData.type = pci_scan_method;
     asc_put_cfg_byte(&pciData, byte_data);
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    pcibios_write_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+        PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+            ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+        offset, byte_data);
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 }
 
 /*
@@ -9400,6 +9526,8 @@ DvcAdvReadPCIConfigByte(
         ushort offset)
 )
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DATA    pciData;
 
     pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
@@ -9408,6 +9536,21 @@ DvcAdvReadPCIConfigByte(
     pciData.offset = offset;
     pciData.type = pci_scan_method;
     return asc_get_cfg_byte(&pciData);
+#else /* ASC_CONFIG_PCI */
+    return 0;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    uchar byte_data;
+    pcibios_read_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+        PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+            ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+        offset, &byte_data);
+    return byte_data;
+#else /* CONFIG_PCI */
+    return 0;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 }
 
 /*
@@ -9421,6 +9564,8 @@ DvcAdvWritePCIConfigByte(
         uchar  byte_data)
 )
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DATA    pciData;
 
     pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
@@ -9429,6 +9574,15 @@ DvcAdvWritePCIConfigByte(
     pciData.offset = offset;
     pciData.type = pci_scan_method;
     asc_put_cfg_byte(&pciData, byte_data);
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    pcibios_write_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+        PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+            ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+        offset, byte_data);
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 }
 
 /*
@@ -9745,8 +9899,9 @@ asc_prt_asc_dvc_cfg(ASC_DVC_CFG *h)
     printk("ASC_DVC_CFG at addr %x\n", (unsigned) h);
 
     printk(
-" can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, res %x,\n",
-            h->can_tagged_qng, h->cmd_qng_enabled, h->disc_enable, h->res);
+" can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, sdtr_enable %x,\n",
+            h->can_tagged_qng, h->cmd_qng_enabled, h->disc_enable,
+            h->sdtr_enable);
 
     printk(
 " chip_scsi_id %d, isa_dma_speed %d, isa_dma_channel %d, chip_version %d,\n",
@@ -10931,200 +11086,156 @@ AscISR(
     return (int_pending);
 }
 
-STATIC int
-AscScsiSetupCmdQ(
-                    REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                    REG ASC_SCSI_REQ_Q * scsiq,
-                    uchar * buf_addr,
-                    ulong buf_len
-)
-{
-    ulong               phy_addr;
-
-    scsiq->r1.cntl = 0;
-    scsiq->r1.sg_queue_cnt = 0;
-    scsiq->r1.q_no = 0;
-    scsiq->r1.extra_bytes = 0;
-    scsiq->r3.scsi_stat = 0;
-    scsiq->r3.scsi_msg = 0;
-    scsiq->r3.host_stat = 0;
-    scsiq->r3.done_stat = 0;
-    scsiq->r2.vm_id = 0;
-    scsiq->r1.data_cnt = buf_len;
-    scsiq->cdbptr = (uchar *) scsiq->cdb;
-    scsiq->sense_ptr = (uchar *) scsiq->sense ;
-    scsiq->r1.sense_len = ASC_MIN_SENSE_LEN ;
-    scsiq->r2.tag_code = (uchar) M2_QTAG_MSG_SIMPLE;
-    scsiq->r2.flag = (uchar) ASC_FLAG_SCSIQ_REQ;
-    scsiq->r2.srb_ptr = (ulong) scsiq;
-    scsiq->r1.status = (uchar) QS_READY;
-    scsiq->r1.data_addr = 0L;
-    if (buf_len != 0L) {
-        if ((phy_addr = AscGetOnePhyAddr(asc_dvc,
-                    (uchar *) buf_addr, scsiq->r1.data_cnt)) == 0L) {
-            return (ERR);
-        }
-        scsiq->r1.data_addr = phy_addr;
-    }
-    if ((phy_addr = AscGetOnePhyAddr(asc_dvc,
-                       (uchar *) scsiq->sense_ptr,
-                       (ulong) scsiq->r1.sense_len)) == 0L) {
-        return (ERR);
-    }
-    scsiq->r1.sense_addr = phy_addr ;
-    return (0);
-}
-
 STATIC uchar _asc_mcode_buf[] ASC_INITDATA =
 {
   0x01,  0x03,  0x01,  0x19,  0x0F,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x0F,  0x0F,  0x0F,  0x0F,  0x0F,  0x0F,  0x0F,  0x0F,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-  0x00,  0x00,  0x00,  0x00,  0xDB,  0x0C,  0x0A,  0x05,  0x01,  0x00,  0x00,  0x00,  0x00,  0xFF,  0x00,  0x00,
+  0x00,  0x00,  0x00,  0x00,  0x91,  0x10,  0x0A,  0x05,  0x01,  0x00,  0x00,  0x00,  0x00,  0xFF,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0xFF,  0x80,  0xFF,  0xFF,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-  0x00,  0x00,  0x00,  0x23,  0x00,  0x23,  0x00,  0x00,  0x00,  0x07,  0x00,  0xFF,  0x00,  0x00,  0x00,  0x00,
-  0xFF,  0xFF,  0xFF,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0xD4,  0x88,  0x00,  0x00,  0x00,  0x00,
+  0x00,  0x00,  0x00,  0x23,  0x00,  0x24,  0x00,  0x00,  0x00,  0x07,  0x00,  0xFF,  0x00,  0x00,  0x00,  0x00,
+  0xFF,  0xFF,  0xFF,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0xE2,  0x88,  0x00,  0x00,  0x00,  0x00,
   0x80,  0x73,  0x48,  0x04,  0x36,  0x00,  0x00,  0xA2,  0xC2,  0x00,  0x80,  0x73,  0x03,  0x23,  0x36,  0x40,
   0xB6,  0x00,  0x36,  0x00,  0x05,  0xD6,  0x0C,  0xD2,  0x12,  0xDA,  0x00,  0xA2,  0xC2,  0x00,  0x92,  0x80,
-  0x10,  0x98,  0x50,  0x00,  0xF5,  0x00,  0x3A,  0x98,  0xDF,  0x23,  0x36,  0x60,  0xB6,  0x00,  0x92,  0x80,
-  0x4F,  0x00,  0xF5,  0x00,  0x3A,  0x98,  0xEF,  0x23,  0x36,  0x60,  0xB6,  0x00,  0x92,  0x80,  0x80,  0x62,
+  0x1E,  0x98,  0x50,  0x00,  0xF5,  0x00,  0x48,  0x98,  0xDF,  0x23,  0x36,  0x60,  0xB6,  0x00,  0x92,  0x80,
+  0x4F,  0x00,  0xF5,  0x00,  0x48,  0x98,  0xEF,  0x23,  0x36,  0x60,  0xB6,  0x00,  0x92,  0x80,  0x80,  0x62,
   0x92,  0x80,  0x00,  0x46,  0x17,  0xEE,  0x13,  0xEA,  0x02,  0x01,  0x09,  0xD8,  0xCD,  0x04,  0x4D,  0x00,
-  0x00,  0xA3,  0xD6,  0x00,  0x98,  0x97,  0x7F,  0x23,  0x04,  0x61,  0x84,  0x01,  0xE0,  0x84,  0xD2,  0xC1,
-  0x80,  0x73,  0xCD,  0x04,  0x4D,  0x00,  0x00,  0xA3,  0xE2,  0x01,  0x98,  0x97,  0xCE,  0x81,  0x00,  0x33,
-  0x02,  0x00,  0xB2,  0x88,  0x80,  0x73,  0x80,  0x77,  0x00,  0x01,  0x01,  0xA1,  0x02,  0x01,  0x4F,  0x00,
-  0x76,  0x97,  0x07,  0xA6,  0x0C,  0x01,  0x00,  0x33,  0x03,  0x00,  0xB2,  0x88,  0x03,  0x03,  0x03,  0xDE,
-  0x00,  0x33,  0x05,  0x00,  0xB2,  0x88,  0xCE,  0x00,  0x69,  0x60,  0xCE,  0x00,  0x02,  0x03,  0x4A,  0x60,
+  0x00,  0xA3,  0xD6,  0x00,  0xA6,  0x97,  0x7F,  0x23,  0x04,  0x61,  0x84,  0x01,  0xE6,  0x84,  0xD2,  0xC1,
+  0x80,  0x73,  0xCD,  0x04,  0x4D,  0x00,  0x00,  0xA3,  0xE2,  0x01,  0xA6,  0x97,  0xCE,  0x81,  0x00,  0x33,
+  0x02,  0x00,  0xC0,  0x88,  0x80,  0x73,  0x80,  0x77,  0x00,  0x01,  0x01,  0xA1,  0x02,  0x01,  0x4F,  0x00,
+  0x84,  0x97,  0x07,  0xA6,  0x0C,  0x01,  0x00,  0x33,  0x03,  0x00,  0xC0,  0x88,  0x03,  0x03,  0x03,  0xDE,
+  0x00,  0x33,  0x05,  0x00,  0xC0,  0x88,  0xCE,  0x00,  0x69,  0x60,  0xCE,  0x00,  0x02,  0x03,  0x4A,  0x60,
   0x00,  0xA2,  0x80,  0x01,  0x80,  0x63,  0x07,  0xA6,  0x2C,  0x01,  0x80,  0x81,  0x03,  0x03,  0x80,  0x63,
-  0xE2,  0x00,  0x07,  0xA6,  0x3C,  0x01,  0x00,  0x33,  0x04,  0x00,  0xB2,  0x88,  0x03,  0x07,  0x02,  0x01,
-  0x04,  0xCA,  0x0D,  0x23,  0x5A,  0x98,  0x4D,  0x04,  0xFE,  0x84,  0x05,  0xD8,  0x0D,  0x23,  0x5A,  0x98,
-  0xCD,  0x04,  0x15,  0x23,  0xE8,  0x88,  0xFB,  0x23,  0x02,  0x61,  0x82,  0x01,  0x80,  0x63,  0x02,  0x03,
-  0x06,  0xA3,  0x6A,  0x01,  0x00,  0x33,  0x0A,  0x00,  0xB2,  0x88,  0x4E,  0x00,  0x07,  0xA3,  0x76,  0x01,
-  0x00,  0x33,  0x0B,  0x00,  0xB2,  0x88,  0xCD,  0x04,  0x36,  0x2D,  0x00,  0x33,  0x1A,  0x00,  0xB2,  0x88,
+  0xE2,  0x00,  0x07,  0xA6,  0x3C,  0x01,  0x00,  0x33,  0x04,  0x00,  0xC0,  0x88,  0x03,  0x07,  0x02,  0x01,
+  0x04,  0xCA,  0x0D,  0x23,  0x68,  0x98,  0x4D,  0x04,  0x04,  0x85,  0x05,  0xD8,  0x0D,  0x23,  0x68,  0x98,
+  0xCD,  0x04,  0x15,  0x23,  0xF6,  0x88,  0xFB,  0x23,  0x02,  0x61,  0x82,  0x01,  0x80,  0x63,  0x02,  0x03,
+  0x06,  0xA3,  0x6A,  0x01,  0x00,  0x33,  0x0A,  0x00,  0xC0,  0x88,  0x4E,  0x00,  0x07,  0xA3,  0x76,  0x01,
+  0x00,  0x33,  0x0B,  0x00,  0xC0,  0x88,  0xCD,  0x04,  0x36,  0x2D,  0x00,  0x33,  0x1A,  0x00,  0xC0,  0x88,
   0x50,  0x04,  0x90,  0x81,  0x06,  0xAB,  0x8A,  0x01,  0x90,  0x81,  0x4E,  0x00,  0x07,  0xA3,  0x9A,  0x01,
-  0x50,  0x00,  0x00,  0xA3,  0x44,  0x01,  0x00,  0x05,  0x84,  0x81,  0x38,  0x97,  0x02,  0x01,  0x05,  0xC6,
+  0x50,  0x00,  0x00,  0xA3,  0x44,  0x01,  0x00,  0x05,  0x84,  0x81,  0x46,  0x97,  0x02,  0x01,  0x05,  0xC6,
   0x04,  0x23,  0xA0,  0x01,  0x15,  0x23,  0xA1,  0x01,  0xC6,  0x81,  0xFD,  0x23,  0x02,  0x61,  0x82,  0x01,
   0x0A,  0xDA,  0x4A,  0x00,  0x06,  0x61,  0x00,  0xA0,  0xBC,  0x01,  0x80,  0x63,  0xCD,  0x04,  0x36,  0x2D,
-  0x00,  0x33,  0x1B,  0x00,  0xB2,  0x88,  0x06,  0x23,  0x5A,  0x98,  0xCD,  0x04,  0xE0,  0x84,  0x06,  0x01,
-  0x00,  0xA2,  0xDC,  0x01,  0x57,  0x60,  0x00,  0xA0,  0xE2,  0x01,  0xE0,  0x84,  0x80,  0x23,  0xA0,  0x01,
-  0xE0,  0x84,  0x80,  0x73,  0x4B,  0x00,  0x06,  0x61,  0x00,  0xA2,  0x08,  0x02,  0x04,  0x01,  0x0C,  0xDE,
-  0x02,  0x01,  0x03,  0xCC,  0x4F,  0x00,  0x76,  0x97,  0x04,  0x82,  0x08,  0x23,  0x02,  0x41,  0x82,  0x01,
-  0x4F,  0x00,  0x54,  0x97,  0x48,  0x04,  0x84,  0x80,  0xE2,  0x97,  0x00,  0x46,  0x56,  0x00,  0x03,  0xC0,
+  0x00,  0x33,  0x1B,  0x00,  0xC0,  0x88,  0x06,  0x23,  0x68,  0x98,  0xCD,  0x04,  0xE6,  0x84,  0x06,  0x01,
+  0x00,  0xA2,  0xDC,  0x01,  0x57,  0x60,  0x00,  0xA0,  0xE2,  0x01,  0xE6,  0x84,  0x80,  0x23,  0xA0,  0x01,
+  0xE6,  0x84,  0x80,  0x73,  0x4B,  0x00,  0x06,  0x61,  0x00,  0xA2,  0x08,  0x02,  0x04,  0x01,  0x0C,  0xDE,
+  0x02,  0x01,  0x03,  0xCC,  0x4F,  0x00,  0x84,  0x97,  0x04,  0x82,  0x08,  0x23,  0x02,  0x41,  0x82,  0x01,
+  0x4F,  0x00,  0x62,  0x97,  0x48,  0x04,  0x84,  0x80,  0xF0,  0x97,  0x00,  0x46,  0x56,  0x00,  0x03,  0xC0,
   0x01,  0x23,  0xE8,  0x00,  0x81,  0x73,  0x06,  0x29,  0x03,  0x42,  0x06,  0xE2,  0x03,  0xEE,  0x67,  0xEB,
-  0x11,  0x23,  0xE8,  0x88,  0xF6,  0x97,  0xF4,  0x80,  0x80,  0x73,  0x80,  0x77,  0x06,  0xA6,  0x36,  0x02,
-  0x00,  0x33,  0x31,  0x00,  0xB2,  0x88,  0x04,  0x01,  0x03,  0xD8,  0xA4,  0x98,  0x4C,  0x96,  0x48,  0x82,
-  0xDC,  0x95,  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0xB6,  0x2D,  0x02,  0xA6,  0x72,  0x02,  0x07,  0xA6,
-  0x60,  0x02,  0x06,  0xA6,  0x64,  0x02,  0x03,  0xA6,  0x68,  0x02,  0x00,  0x33,  0x10,  0x00,  0xB2,  0x88,
-  0x76,  0x95,  0x4A,  0x82,  0x42,  0x96,  0x4A,  0x82,  0x04,  0x23,  0xA0,  0x01,  0x14,  0x23,  0xA1,  0x01,
-  0x36,  0x84,  0x04,  0x01,  0x0C,  0xDC,  0xE0,  0x23,  0x25,  0x61,  0xEF,  0x00,  0x14,  0x01,  0x4F,  0x04,
-  0xA8,  0x01,  0x6F,  0x00,  0xA5,  0x01,  0x03,  0x23,  0xA4,  0x01,  0x06,  0x23,  0x9C,  0x01,  0x24,  0x2B,
-  0x1C,  0x01,  0x02,  0xA6,  0xB0,  0x02,  0x07,  0xA6,  0x60,  0x02,  0x06,  0xA6,  0x64,  0x02,  0x03,  0xA6,
-  0x1A,  0x04,  0x01,  0xA6,  0xBA,  0x02,  0x00,  0xA6,  0xBA,  0x02,  0x00,  0x33,  0x12,  0x00,  0xB2,  0x88,
-  0x00,  0x0E,  0x80,  0x63,  0x00,  0x43,  0x00,  0xA0,  0x92,  0x02,  0x4D,  0x04,  0x04,  0x01,  0x0B,  0xDC,
-  0xE7,  0x23,  0x04,  0x61,  0x84,  0x01,  0x10,  0x31,  0x12,  0x35,  0x14,  0x01,  0xEC,  0x00,  0x6C,  0x38,
-  0x00,  0x3F,  0x00,  0x00,  0xF0,  0x82,  0x18,  0x23,  0x04,  0x61,  0x18,  0xA0,  0xE8,  0x02,  0x04,  0x01,
-  0x9C,  0xC8,  0x00,  0x33,  0x1F,  0x00,  0xB2,  0x88,  0x08,  0x31,  0x0A,  0x35,  0x0C,  0x39,  0x0E,  0x3D,
-  0x70,  0x98,  0xB6,  0x2D,  0x01,  0xA6,  0x1A,  0x03,  0x00,  0xA6,  0x1A,  0x03,  0x07,  0xA6,  0x12,  0x03,
-  0x06,  0xA6,  0x16,  0x03,  0x03,  0xA6,  0x1A,  0x04,  0x02,  0xA6,  0x72,  0x02,  0x00,  0x33,  0x33,  0x00,
-  0xB2,  0x88,  0x76,  0x95,  0xF4,  0x82,  0x42,  0x96,  0xF4,  0x82,  0x74,  0x98,  0x80,  0x42,  0x70,  0x98,
-  0x60,  0xE4,  0x04,  0x01,  0x29,  0xC8,  0x31,  0x05,  0x07,  0x01,  0x00,  0xA2,  0x5A,  0x03,  0x00,  0x43,
-  0x87,  0x01,  0x05,  0x05,  0x78,  0x98,  0x70,  0x98,  0x00,  0xA6,  0x1C,  0x03,  0x07,  0xA6,  0x52,  0x03,
-  0x03,  0xA6,  0x36,  0x04,  0x06,  0xA6,  0x56,  0x03,  0x01,  0xA6,  0x1C,  0x03,  0x00,  0x33,  0x25,  0x00,
-  0xB2,  0x88,  0x76,  0x95,  0x38,  0x83,  0x42,  0x96,  0x38,  0x83,  0x04,  0x01,  0x0C,  0xCE,  0x03,  0xC8,
-  0x00,  0x33,  0x42,  0x00,  0xB2,  0x88,  0x00,  0x01,  0x05,  0x05,  0xFF,  0xA2,  0x78,  0x03,  0xB1,  0x01,
-  0x08,  0x23,  0xB2,  0x01,  0x34,  0x83,  0x05,  0x05,  0x15,  0x01,  0x00,  0xA2,  0x98,  0x03,  0xEC,  0x00,
-  0x6E,  0x00,  0x95,  0x01,  0x6C,  0x38,  0x00,  0x3F,  0x00,  0x00,  0x01,  0xA6,  0x94,  0x03,  0x00,  0xA6,
-  0x94,  0x03,  0x0C,  0x84,  0x80,  0x42,  0x70,  0x98,  0x01,  0xA6,  0xA2,  0x03,  0x00,  0xA6,  0xBA,  0x03,
-  0x0C,  0x84,  0x98,  0x98,  0x80,  0x42,  0x01,  0xA6,  0xA2,  0x03,  0x07,  0xA6,  0xB0,  0x03,  0xD2,  0x83,
-  0x76,  0x95,  0xA6,  0x83,  0x00,  0x33,  0x2F,  0x00,  0xB2,  0x88,  0x98,  0x98,  0x80,  0x42,  0x00,  0xA6,
-  0xBA,  0x03,  0x07,  0xA6,  0xC8,  0x03,  0xD2,  0x83,  0x76,  0x95,  0xBE,  0x83,  0x00,  0x33,  0x26,  0x00,
-  0xB2,  0x88,  0x38,  0x2B,  0x80,  0x32,  0x80,  0x36,  0x04,  0x23,  0xA0,  0x01,  0x12,  0x23,  0xA1,  0x01,
-  0x0C,  0x84,  0x06,  0xF0,  0x06,  0xA4,  0xF0,  0x03,  0x80,  0x6B,  0x05,  0x23,  0x83,  0x03,  0x80,  0x63,
-  0x03,  0xA6,  0x0A,  0x04,  0x07,  0xA6,  0x02,  0x04,  0x06,  0xA6,  0x06,  0x04,  0x00,  0x33,  0x17,  0x00,
-  0xB2,  0x88,  0x76,  0x95,  0xF0,  0x83,  0x42,  0x96,  0xF0,  0x83,  0x1A,  0x84,  0x06,  0xF0,  0x06,  0xA4,
-  0x1A,  0x04,  0x80,  0x6B,  0x05,  0x23,  0x83,  0x03,  0x80,  0x63,  0xB6,  0x2D,  0x03,  0xA6,  0x36,  0x04,
-  0x07,  0xA6,  0x2E,  0x04,  0x06,  0xA6,  0x32,  0x04,  0x00,  0x33,  0x30,  0x00,  0xB2,  0x88,  0x76,  0x95,
-  0x1A,  0x84,  0x42,  0x96,  0x1A,  0x84,  0x1D,  0x01,  0x06,  0xCC,  0x00,  0x33,  0x00,  0x84,  0xC0,  0x20,
-  0x00,  0x23,  0xEA,  0x00,  0x81,  0x62,  0xA2,  0x0D,  0x80,  0x63,  0x07,  0xA6,  0x54,  0x04,  0x00,  0x33,
-  0x18,  0x00,  0xB2,  0x88,  0x03,  0x03,  0x80,  0x63,  0xA3,  0x01,  0x07,  0xA4,  0x5E,  0x04,  0x23,  0x01,
-  0x00,  0xA2,  0x80,  0x04,  0x0A,  0xA0,  0x70,  0x04,  0xE0,  0x00,  0x00,  0x33,  0x1D,  0x00,  0xB2,  0x88,
-  0x0B,  0xA0,  0x7C,  0x04,  0xE0,  0x00,  0x00,  0x33,  0x1E,  0x00,  0xB2,  0x88,  0x42,  0x23,  0xE8,  0x88,
-  0x00,  0x23,  0x22,  0xA3,  0xE0,  0x04,  0x08,  0x23,  0x22,  0xA3,  0x9C,  0x04,  0x28,  0x23,  0x22,  0xA3,
-  0xA8,  0x04,  0x02,  0x23,  0x22,  0xA3,  0xBE,  0x04,  0x42,  0x23,  0xE8,  0x88,  0x4A,  0x00,  0x06,  0x61,
-  0x00,  0xA0,  0xA8,  0x04,  0x45,  0x23,  0xE8,  0x88,  0xF6,  0x97,  0x00,  0xA2,  0xBA,  0x04,  0xA4,  0x98,
-  0x00,  0x33,  0x00,  0x82,  0xC0,  0x20,  0x81,  0x62,  0xF0,  0x81,  0x47,  0x23,  0xE8,  0x88,  0x04,  0x01,
-  0x0B,  0xDE,  0xF6,  0x97,  0xA4,  0x98,  0x00,  0x33,  0x00,  0x81,  0xC0,  0x20,  0x81,  0x62,  0x14,  0x01,
-  0x00,  0xA0,  0x08,  0x02,  0x43,  0x23,  0xE8,  0x88,  0x04,  0x23,  0xA0,  0x01,  0x44,  0x23,  0xA1,  0x01,
-  0x80,  0x73,  0x4D,  0x00,  0x03,  0xA3,  0xEE,  0x04,  0x00,  0x33,  0x27,  0x00,  0xB2,  0x88,  0x04,  0x01,
-  0x04,  0xDC,  0x02,  0x23,  0xA2,  0x01,  0x04,  0x23,  0xA0,  0x01,  0xF6,  0x97,  0x20,  0x95,  0x4B,  0x00,
-  0xF6,  0x00,  0x4F,  0x04,  0x4F,  0x00,  0x00,  0xA3,  0x1C,  0x05,  0x00,  0x05,  0x76,  0x00,  0x06,  0x61,
-  0x00,  0xA2,  0x16,  0x05,  0x04,  0x85,  0x38,  0x97,  0xCD,  0x04,  0x1E,  0x85,  0x48,  0x04,  0x84,  0x80,
-  0x02,  0x01,  0x03,  0xDA,  0x80,  0x23,  0x82,  0x01,  0x2E,  0x85,  0x02,  0x23,  0xA0,  0x01,  0x4A,  0x00,
-  0x06,  0x61,  0x00,  0xA2,  0x3A,  0x05,  0x1D,  0x01,  0x04,  0xD6,  0xFF,  0x23,  0x86,  0x41,  0x4B,  0x60,
-  0xCB,  0x00,  0xFF,  0x23,  0x80,  0x01,  0x49,  0x00,  0x81,  0x01,  0x04,  0x01,  0x02,  0xC8,  0x30,  0x01,
-  0x80,  0x01,  0xF7,  0x04,  0x03,  0x01,  0x49,  0x04,  0x80,  0x01,  0xC9,  0x00,  0x00,  0x05,  0x00,  0x01,
-  0xFF,  0xA0,  0x5A,  0x05,  0x77,  0x04,  0x01,  0x23,  0xEA,  0x00,  0x5D,  0x00,  0xFE,  0xC7,  0x00,  0x62,
-  0x00,  0x23,  0xEA,  0x00,  0x00,  0x63,  0x07,  0xA4,  0xDA,  0x05,  0x03,  0x03,  0x02,  0xA0,  0x88,  0x05,
-  0xD6,  0x85,  0x00,  0x33,  0x2D,  0x00,  0xB2,  0x88,  0x04,  0xA0,  0xAE,  0x05,  0x80,  0x63,  0x4A,  0x00,
-  0x06,  0x61,  0x00,  0xA2,  0x9A,  0x05,  0x1D,  0x01,  0x06,  0xD6,  0x02,  0x23,  0x02,  0x41,  0x82,  0x01,
-  0x50,  0x00,  0x54,  0x97,  0xFE,  0x84,  0x04,  0x23,  0x02,  0x41,  0x82,  0x01,  0xFE,  0x84,  0x08,  0xA0,
-  0xB4,  0x05,  0xD6,  0x85,  0x03,  0xA0,  0xBA,  0x05,  0xD6,  0x85,  0x01,  0xA0,  0xC4,  0x05,  0x88,  0x00,
-  0x80,  0x63,  0xB2,  0x86,  0x07,  0xA0,  0xD0,  0x05,  0x06,  0x23,  0x5A,  0x98,  0x48,  0x23,  0xE8,  0x88,
-  0x07,  0x23,  0x80,  0x00,  0xF8,  0x86,  0x80,  0x63,  0x76,  0x85,  0x00,  0x63,  0x4A,  0x00,  0x06,  0x61,
-  0x00,  0xA2,  0x18,  0x06,  0x1D,  0x01,  0x18,  0xD4,  0xC0,  0x23,  0x07,  0x41,  0x83,  0x03,  0x80,  0x63,
-  0x06,  0xA6,  0xFA,  0x05,  0x00,  0x33,  0x37,  0x00,  0xB2,  0x88,  0x1D,  0x01,  0x02,  0xD6,  0x46,  0x23,
-  0xE8,  0x88,  0x63,  0x60,  0x83,  0x03,  0x80,  0x63,  0x06,  0xA6,  0x12,  0x06,  0x00,  0x33,  0x38,  0x00,
-  0xB2,  0x88,  0xEF,  0x04,  0x6F,  0x00,  0x00,  0x63,  0x4B,  0x00,  0x06,  0x41,  0xCB,  0x00,  0x52,  0x00,
-  0x06,  0x61,  0x00,  0xA2,  0x30,  0x06,  0x1D,  0x01,  0x03,  0xCA,  0xC0,  0x23,  0x07,  0x41,  0x00,  0x63,
-  0x1D,  0x01,  0x04,  0xCC,  0x00,  0x33,  0x00,  0x83,  0xC0,  0x20,  0x81,  0x62,  0x80,  0x23,  0x07,  0x41,
-  0x00,  0x63,  0x80,  0x67,  0x08,  0x23,  0x83,  0x03,  0x80,  0x63,  0x00,  0x63,  0x06,  0xA6,  0x5E,  0x06,
-  0x07,  0xA6,  0x76,  0x05,  0x02,  0xA6,  0xEC,  0x06,  0x00,  0x33,  0x39,  0x00,  0xB2,  0x88,  0x00,  0x00,
-  0x01,  0xA0,  0x06,  0x07,  0xDC,  0x95,  0x83,  0x03,  0x80,  0x63,  0x06,  0xA6,  0x72,  0x06,  0x07,  0xA6,
-  0x76,  0x05,  0x00,  0x00,  0x01,  0xA0,  0x06,  0x07,  0x00,  0x2B,  0x40,  0x0E,  0x80,  0x63,  0x01,  0x00,
-  0x06,  0xA6,  0x8E,  0x06,  0x07,  0xA6,  0x76,  0x05,  0x00,  0x33,  0x3A,  0x00,  0xB2,  0x88,  0x40,  0x0E,
-  0x80,  0x63,  0x00,  0x43,  0x00,  0xA0,  0x80,  0x06,  0x06,  0xA6,  0xA6,  0x06,  0x07,  0xA6,  0x76,  0x05,
-  0x00,  0x33,  0x3B,  0x00,  0xB2,  0x88,  0x80,  0x67,  0x40,  0x0E,  0x80,  0x63,  0x07,  0xA6,  0x76,  0x05,
-  0x00,  0x63,  0x07,  0xA6,  0xBC,  0x06,  0x00,  0x33,  0x2A,  0x00,  0xB2,  0x88,  0x03,  0x03,  0x80,  0x63,
-  0x89,  0x00,  0x0A,  0x2B,  0x07,  0xA6,  0xCE,  0x06,  0x00,  0x33,  0x29,  0x00,  0xB2,  0x88,  0x00,  0x43,
-  0x00,  0xA2,  0xDA,  0x06,  0xC0,  0x0E,  0x80,  0x63,  0xC4,  0x86,  0xC0,  0x0E,  0x00,  0x33,  0x00,  0x80,
-  0xC0,  0x20,  0x81,  0x62,  0x04,  0x01,  0x08,  0xDA,  0x80,  0x63,  0x76,  0x85,  0x80,  0x67,  0x00,  0x33,
-  0x00,  0x40,  0xC0,  0x20,  0x81,  0x62,  0x00,  0x63,  0x80,  0x7B,  0x80,  0x63,  0x06,  0xA6,  0x6A,  0x06,
-  0x00,  0x33,  0x2C,  0x00,  0xB2,  0x88,  0x0C,  0xA2,  0x20,  0x07,  0xDC,  0x95,  0x83,  0x03,  0x80,  0x63,
-  0x06,  0xA6,  0x1E,  0x07,  0x07,  0xA6,  0x76,  0x05,  0x00,  0x33,  0x3D,  0x00,  0xB2,  0x88,  0x00,  0x00,
-  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0x0C,  0xA0,  0x36,  0x07,  0x07,  0xA6,  0x76,  0x05,  0xBF,  0x23,
-  0x04,  0x61,  0x84,  0x01,  0xE0,  0x84,  0x00,  0x63,  0xF0,  0x04,  0x01,  0x01,  0xF1,  0x00,  0x00,  0x01,
-  0xF2,  0x00,  0x01,  0x05,  0x80,  0x01,  0x72,  0x04,  0x71,  0x00,  0x81,  0x01,  0x70,  0x04,  0x80,  0x05,
-  0x81,  0x05,  0x00,  0x63,  0xF0,  0x04,  0xF2,  0x00,  0x72,  0x04,  0x01,  0x01,  0xF1,  0x00,  0x70,  0x00,
-  0x81,  0x01,  0x70,  0x04,  0x71,  0x00,  0x81,  0x01,  0x72,  0x00,  0x80,  0x01,  0x71,  0x04,  0x70,  0x00,
-  0x80,  0x01,  0x70,  0x04,  0x00,  0x63,  0xF0,  0x04,  0xF2,  0x00,  0x72,  0x04,  0x00,  0x01,  0xF1,  0x00,
-  0x70,  0x00,  0x80,  0x01,  0x70,  0x04,  0x71,  0x00,  0x80,  0x01,  0x72,  0x00,  0x81,  0x01,  0x71,  0x04,
-  0x70,  0x00,  0x81,  0x01,  0x70,  0x04,  0x00,  0x63,  0x00,  0x23,  0xB3,  0x01,  0x83,  0x05,  0xA3,  0x01,
-  0xA2,  0x01,  0xA1,  0x01,  0x01,  0x23,  0xA0,  0x01,  0x00,  0x01,  0xC8,  0x00,  0x03,  0xA1,  0xB6,  0x07,
-  0x00,  0x33,  0x07,  0x00,  0xB2,  0x88,  0x80,  0x05,  0x81,  0x05,  0x04,  0x01,  0x11,  0xC8,  0x48,  0x00,
-  0xB0,  0x01,  0xB1,  0x01,  0x08,  0x23,  0xB2,  0x01,  0x05,  0x01,  0x48,  0x04,  0x00,  0x43,  0x00,  0xA2,
-  0xD6,  0x07,  0x00,  0x05,  0xCC,  0x87,  0x00,  0x01,  0xC8,  0x00,  0xFF,  0x23,  0x80,  0x01,  0x05,  0x05,
-  0x00,  0x63,  0xF7,  0x04,  0x1A,  0x09,  0xF6,  0x08,  0x6E,  0x04,  0x00,  0x02,  0x80,  0x43,  0x76,  0x08,
-  0x80,  0x02,  0x77,  0x04,  0x00,  0x63,  0xF7,  0x04,  0x1A,  0x09,  0xF6,  0x08,  0x6E,  0x04,  0x00,  0x02,
-  0x00,  0xA0,  0x06,  0x08,  0x08,  0x88,  0x00,  0x43,  0x76,  0x08,  0x80,  0x02,  0x77,  0x04,  0x00,  0x63,
-  0xF3,  0x04,  0x00,  0x23,  0xF4,  0x00,  0x74,  0x00,  0x80,  0x43,  0xF4,  0x00,  0xCF,  0x40,  0x00,  0xA2,
-  0x36,  0x08,  0x74,  0x04,  0x02,  0x01,  0xF7,  0xC9,  0xF6,  0xD9,  0x00,  0x01,  0x01,  0xA1,  0x16,  0x08,
-  0xF6,  0x97,  0x20,  0x95,  0x16,  0x88,  0x73,  0x04,  0x00,  0x63,  0xF3,  0x04,  0x75,  0x04,  0x4C,  0x88,
-  0x02,  0x01,  0x04,  0xD8,  0x38,  0x97,  0xF6,  0x97,  0x20,  0x95,  0x3C,  0x88,  0x75,  0x00,  0x00,  0xA3,
-  0x56,  0x08,  0x00,  0x05,  0x40,  0x88,  0x73,  0x04,  0x00,  0x63,  0x80,  0x7B,  0x80,  0x63,  0x06,  0xA6,
-  0x68,  0x08,  0x00,  0x33,  0x3E,  0x00,  0xB2,  0x88,  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0x00,  0x63,
-  0x38,  0x2B,  0x8E,  0x88,  0x38,  0x2B,  0x84,  0x88,  0x32,  0x09,  0x31,  0x05,  0x84,  0x98,  0x05,  0x05,
-  0xB2,  0x09,  0x00,  0x63,  0x00,  0x32,  0x00,  0x36,  0x00,  0x3A,  0x00,  0x3E,  0x00,  0x63,  0x80,  0x32,
-  0x80,  0x36,  0x80,  0x3A,  0x80,  0x3E,  0x00,  0x63,  0x38,  0x2B,  0x40,  0x32,  0x40,  0x36,  0x40,  0x3A,
-  0x40,  0x3E,  0x00,  0x63,  0x5A,  0x20,  0xC9,  0x40,  0x00,  0xA0,  0xA4,  0x08,  0x5D,  0x00,  0xFE,  0xC3,
-  0x00,  0x63,  0x80,  0x73,  0xE6,  0x20,  0x02,  0x23,  0xE8,  0x00,  0x82,  0x73,  0xFF,  0xFD,  0x80,  0x73,
-  0x13,  0x23,  0xE8,  0x88,  0x66,  0x20,  0xC0,  0x20,  0x04,  0x23,  0xA0,  0x01,  0xA1,  0x23,  0xA1,  0x01,
-  0x81,  0x62,  0xD2,  0x88,  0x80,  0x73,  0x80,  0x77,  0x68,  0x00,  0x00,  0xA2,  0x80,  0x00,  0x03,  0xC2,
-  0xF1,  0xC7,  0x41,  0x23,  0xE8,  0x88,  0x11,  0x23,  0xA1,  0x01,  0x04,  0x23,  0xA0,  0x01,  0xE0,  0x84,
-
+  0x11,  0x23,  0xF6,  0x88,  0x04,  0x98,  0xF4,  0x80,  0x80,  0x73,  0x80,  0x77,  0x07,  0xA4,  0x32,  0x02,
+  0x7C,  0x95,  0x06,  0xA6,  0x3C,  0x02,  0x03,  0xA6,  0x4C,  0x04,  0xC0,  0x88,  0x04,  0x01,  0x03,  0xD8,
+  0xB2,  0x98,  0x6A,  0x96,  0x4E,  0x82,  0xFE,  0x95,  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0xB6,  0x2D,
+  0x02,  0xA6,  0x78,  0x02,  0x07,  0xA6,  0x66,  0x02,  0x06,  0xA6,  0x6A,  0x02,  0x03,  0xA6,  0x6E,  0x02,
+  0x00,  0x33,  0x10,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0x50,  0x82,  0x60,  0x96,  0x50,  0x82,  0x04,  0x23,
+  0xA0,  0x01,  0x14,  0x23,  0xA1,  0x01,  0x3C,  0x84,  0x04,  0x01,  0x0C,  0xDC,  0xE0,  0x23,  0x25,  0x61,
+  0xEF,  0x00,  0x14,  0x01,  0x4F,  0x04,  0xA8,  0x01,  0x6F,  0x00,  0xA5,  0x01,  0x03,  0x23,  0xA4,  0x01,
+  0x06,  0x23,  0x9C,  0x01,  0x24,  0x2B,  0x1C,  0x01,  0x02,  0xA6,  0xB6,  0x02,  0x07,  0xA6,  0x66,  0x02,
+  0x06,  0xA6,  0x6A,  0x02,  0x03,  0xA6,  0x20,  0x04,  0x01,  0xA6,  0xC0,  0x02,  0x00,  0xA6,  0xC0,  0x02,
+  0x00,  0x33,  0x12,  0x00,  0xC0,  0x88,  0x00,  0x0E,  0x80,  0x63,  0x00,  0x43,  0x00,  0xA0,  0x98,  0x02,
+  0x4D,  0x04,  0x04,  0x01,  0x0B,  0xDC,  0xE7,  0x23,  0x04,  0x61,  0x84,  0x01,  0x10,  0x31,  0x12,  0x35,
+  0x14,  0x01,  0xEC,  0x00,  0x6C,  0x38,  0x00,  0x3F,  0x00,  0x00,  0xF6,  0x82,  0x18,  0x23,  0x04,  0x61,
+  0x18,  0xA0,  0xEE,  0x02,  0x04,  0x01,  0x9C,  0xC8,  0x00,  0x33,  0x1F,  0x00,  0xC0,  0x88,  0x08,  0x31,
+  0x0A,  0x35,  0x0C,  0x39,  0x0E,  0x3D,  0x7E,  0x98,  0xB6,  0x2D,  0x01,  0xA6,  0x20,  0x03,  0x00,  0xA6,
+  0x20,  0x03,  0x07,  0xA6,  0x18,  0x03,  0x06,  0xA6,  0x1C,  0x03,  0x03,  0xA6,  0x20,  0x04,  0x02,  0xA6,
+  0x78,  0x02,  0x00,  0x33,  0x33,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0xFA,  0x82,  0x60,  0x96,  0xFA,  0x82,
+  0x82,  0x98,  0x80,  0x42,  0x7E,  0x98,  0x60,  0xE4,  0x04,  0x01,  0x29,  0xC8,  0x31,  0x05,  0x07,  0x01,
+  0x00,  0xA2,  0x60,  0x03,  0x00,  0x43,  0x87,  0x01,  0x05,  0x05,  0x86,  0x98,  0x7E,  0x98,  0x00,  0xA6,
+  0x22,  0x03,  0x07,  0xA6,  0x58,  0x03,  0x03,  0xA6,  0x3C,  0x04,  0x06,  0xA6,  0x5C,  0x03,  0x01,  0xA6,
+  0x22,  0x03,  0x00,  0x33,  0x25,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0x3E,  0x83,  0x60,  0x96,  0x3E,  0x83,
+  0x04,  0x01,  0x0C,  0xCE,  0x03,  0xC8,  0x00,  0x33,  0x42,  0x00,  0xC0,  0x88,  0x00,  0x01,  0x05,  0x05,
+  0xFF,  0xA2,  0x7E,  0x03,  0xB1,  0x01,  0x08,  0x23,  0xB2,  0x01,  0x3A,  0x83,  0x05,  0x05,  0x15,  0x01,
+  0x00,  0xA2,  0x9E,  0x03,  0xEC,  0x00,  0x6E,  0x00,  0x95,  0x01,  0x6C,  0x38,  0x00,  0x3F,  0x00,  0x00,
+  0x01,  0xA6,  0x9A,  0x03,  0x00,  0xA6,  0x9A,  0x03,  0x12,  0x84,  0x80,  0x42,  0x7E,  0x98,  0x01,  0xA6,
+  0xA8,  0x03,  0x00,  0xA6,  0xC0,  0x03,  0x12,  0x84,  0xA6,  0x98,  0x80,  0x42,  0x01,  0xA6,  0xA8,  0x03,
+  0x07,  0xA6,  0xB6,  0x03,  0xD8,  0x83,  0x7C,  0x95,  0xAC,  0x83,  0x00,  0x33,  0x2F,  0x00,  0xC0,  0x88,
+  0xA6,  0x98,  0x80,  0x42,  0x00,  0xA6,  0xC0,  0x03,  0x07,  0xA6,  0xCE,  0x03,  0xD8,  0x83,  0x7C,  0x95,
+  0xC4,  0x83,  0x00,  0x33,  0x26,  0x00,  0xC0,  0x88,  0x38,  0x2B,  0x80,  0x32,  0x80,  0x36,  0x04,  0x23,
+  0xA0,  0x01,  0x12,  0x23,  0xA1,  0x01,  0x12,  0x84,  0x06,  0xF0,  0x06,  0xA4,  0xF6,  0x03,  0x80,  0x6B,
+  0x05,  0x23,  0x83,  0x03,  0x80,  0x63,  0x03,  0xA6,  0x10,  0x04,  0x07,  0xA6,  0x08,  0x04,  0x06,  0xA6,
+  0x0C,  0x04,  0x00,  0x33,  0x17,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0xF6,  0x83,  0x60,  0x96,  0xF6,  0x83,
+  0x20,  0x84,  0x06,  0xF0,  0x06,  0xA4,  0x20,  0x04,  0x80,  0x6B,  0x05,  0x23,  0x83,  0x03,  0x80,  0x63,
+  0xB6,  0x2D,  0x03,  0xA6,  0x3C,  0x04,  0x07,  0xA6,  0x34,  0x04,  0x06,  0xA6,  0x38,  0x04,  0x00,  0x33,
+  0x30,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0x20,  0x84,  0x60,  0x96,  0x20,  0x84,  0x1D,  0x01,  0x06,  0xCC,
+  0x00,  0x33,  0x00,  0x84,  0xC0,  0x20,  0x00,  0x23,  0xEA,  0x00,  0x81,  0x62,  0xA2,  0x0D,  0x80,  0x63,
+  0x07,  0xA6,  0x5A,  0x04,  0x00,  0x33,  0x18,  0x00,  0xC0,  0x88,  0x03,  0x03,  0x80,  0x63,  0xA3,  0x01,
+  0x07,  0xA4,  0x64,  0x04,  0x23,  0x01,  0x00,  0xA2,  0x86,  0x04,  0x0A,  0xA0,  0x76,  0x04,  0xE0,  0x00,
+  0x00,  0x33,  0x1D,  0x00,  0xC0,  0x88,  0x0B,  0xA0,  0x82,  0x04,  0xE0,  0x00,  0x00,  0x33,  0x1E,  0x00,
+  0xC0,  0x88,  0x42,  0x23,  0xF6,  0x88,  0x00,  0x23,  0x22,  0xA3,  0xE6,  0x04,  0x08,  0x23,  0x22,  0xA3,
+  0xA2,  0x04,  0x28,  0x23,  0x22,  0xA3,  0xAE,  0x04,  0x02,  0x23,  0x22,  0xA3,  0xC4,  0x04,  0x42,  0x23,
+  0xF6,  0x88,  0x4A,  0x00,  0x06,  0x61,  0x00,  0xA0,  0xAE,  0x04,  0x45,  0x23,  0xF6,  0x88,  0x04,  0x98,
+  0x00,  0xA2,  0xC0,  0x04,  0xB2,  0x98,  0x00,  0x33,  0x00,  0x82,  0xC0,  0x20,  0x81,  0x62,  0xF0,  0x81,
+  0x47,  0x23,  0xF6,  0x88,  0x04,  0x01,  0x0B,  0xDE,  0x04,  0x98,  0xB2,  0x98,  0x00,  0x33,  0x00,  0x81,
+  0xC0,  0x20,  0x81,  0x62,  0x14,  0x01,  0x00,  0xA0,  0x08,  0x02,  0x43,  0x23,  0xF6,  0x88,  0x04,  0x23,
+  0xA0,  0x01,  0x44,  0x23,  0xA1,  0x01,  0x80,  0x73,  0x4D,  0x00,  0x03,  0xA3,  0xF4,  0x04,  0x00,  0x33,
+  0x27,  0x00,  0xC0,  0x88,  0x04,  0x01,  0x04,  0xDC,  0x02,  0x23,  0xA2,  0x01,  0x04,  0x23,  0xA0,  0x01,
+  0x04,  0x98,  0x26,  0x95,  0x4B,  0x00,  0xF6,  0x00,  0x4F,  0x04,  0x4F,  0x00,  0x00,  0xA3,  0x22,  0x05,
+  0x00,  0x05,  0x76,  0x00,  0x06,  0x61,  0x00,  0xA2,  0x1C,  0x05,  0x0A,  0x85,  0x46,  0x97,  0xCD,  0x04,
+  0x24,  0x85,  0x48,  0x04,  0x84,  0x80,  0x02,  0x01,  0x03,  0xDA,  0x80,  0x23,  0x82,  0x01,  0x34,  0x85,
+  0x02,  0x23,  0xA0,  0x01,  0x4A,  0x00,  0x06,  0x61,  0x00,  0xA2,  0x40,  0x05,  0x1D,  0x01,  0x04,  0xD6,
+  0xFF,  0x23,  0x86,  0x41,  0x4B,  0x60,  0xCB,  0x00,  0xFF,  0x23,  0x80,  0x01,  0x49,  0x00,  0x81,  0x01,
+  0x04,  0x01,  0x02,  0xC8,  0x30,  0x01,  0x80,  0x01,  0xF7,  0x04,  0x03,  0x01,  0x49,  0x04,  0x80,  0x01,
+  0xC9,  0x00,  0x00,  0x05,  0x00,  0x01,  0xFF,  0xA0,  0x60,  0x05,  0x77,  0x04,  0x01,  0x23,  0xEA,  0x00,
+  0x5D,  0x00,  0xFE,  0xC7,  0x00,  0x62,  0x00,  0x23,  0xEA,  0x00,  0x00,  0x63,  0x07,  0xA4,  0xF8,  0x05,
+  0x03,  0x03,  0x02,  0xA0,  0x8E,  0x05,  0xF4,  0x85,  0x00,  0x33,  0x2D,  0x00,  0xC0,  0x88,  0x04,  0xA0,
+  0xB8,  0x05,  0x80,  0x63,  0x00,  0x23,  0xDF,  0x00,  0x4A,  0x00,  0x06,  0x61,  0x00,  0xA2,  0xA4,  0x05,
+  0x1D,  0x01,  0x06,  0xD6,  0x02,  0x23,  0x02,  0x41,  0x82,  0x01,  0x50,  0x00,  0x62,  0x97,  0x04,  0x85,
+  0x04,  0x23,  0x02,  0x41,  0x82,  0x01,  0x04,  0x85,  0x08,  0xA0,  0xBE,  0x05,  0xF4,  0x85,  0x03,  0xA0,
+  0xC4,  0x05,  0xF4,  0x85,  0x01,  0xA0,  0xCE,  0x05,  0x88,  0x00,  0x80,  0x63,  0xCC,  0x86,  0x07,  0xA0,
+  0xEE,  0x05,  0x5F,  0x00,  0x00,  0x2B,  0xDF,  0x08,  0x00,  0xA2,  0xE6,  0x05,  0x80,  0x67,  0x80,  0x63,
+  0x01,  0xA2,  0x7A,  0x06,  0x7C,  0x85,  0x06,  0x23,  0x68,  0x98,  0x48,  0x23,  0xF6,  0x88,  0x07,  0x23,
+  0x80,  0x00,  0x06,  0x87,  0x80,  0x63,  0x7C,  0x85,  0x00,  0x23,  0xDF,  0x00,  0x00,  0x63,  0x4A,  0x00,
+  0x06,  0x61,  0x00,  0xA2,  0x36,  0x06,  0x1D,  0x01,  0x16,  0xD4,  0xC0,  0x23,  0x07,  0x41,  0x83,  0x03,
+  0x80,  0x63,  0x06,  0xA6,  0x1C,  0x06,  0x00,  0x33,  0x37,  0x00,  0xC0,  0x88,  0x1D,  0x01,  0x01,  0xD6,
+  0x20,  0x23,  0x63,  0x60,  0x83,  0x03,  0x80,  0x63,  0x02,  0x23,  0xDF,  0x00,  0x07,  0xA6,  0x7C,  0x05,
+  0xEF,  0x04,  0x6F,  0x00,  0x00,  0x63,  0x4B,  0x00,  0x06,  0x41,  0xCB,  0x00,  0x52,  0x00,  0x06,  0x61,
+  0x00,  0xA2,  0x4E,  0x06,  0x1D,  0x01,  0x03,  0xCA,  0xC0,  0x23,  0x07,  0x41,  0x00,  0x63,  0x1D,  0x01,
+  0x04,  0xCC,  0x00,  0x33,  0x00,  0x83,  0xC0,  0x20,  0x81,  0x62,  0x80,  0x23,  0x07,  0x41,  0x00,  0x63,
+  0x80,  0x67,  0x08,  0x23,  0x83,  0x03,  0x80,  0x63,  0x00,  0x63,  0x01,  0x23,  0xDF,  0x00,  0x06,  0xA6,
+  0x84,  0x06,  0x07,  0xA6,  0x7C,  0x05,  0x80,  0x67,  0x80,  0x63,  0x00,  0x33,  0x00,  0x40,  0xC0,  0x20,
+  0x81,  0x62,  0x00,  0x63,  0x00,  0x00,  0xFE,  0x95,  0x83,  0x03,  0x80,  0x63,  0x06,  0xA6,  0x94,  0x06,
+  0x07,  0xA6,  0x7C,  0x05,  0x00,  0x00,  0x01,  0xA0,  0x14,  0x07,  0x00,  0x2B,  0x40,  0x0E,  0x80,  0x63,
+  0x01,  0x00,  0x06,  0xA6,  0xAA,  0x06,  0x07,  0xA6,  0x7C,  0x05,  0x40,  0x0E,  0x80,  0x63,  0x00,  0x43,
+  0x00,  0xA0,  0xA2,  0x06,  0x06,  0xA6,  0xBC,  0x06,  0x07,  0xA6,  0x7C,  0x05,  0x80,  0x67,  0x40,  0x0E,
+  0x80,  0x63,  0x07,  0xA6,  0x7C,  0x05,  0x00,  0x23,  0xDF,  0x00,  0x00,  0x63,  0x07,  0xA6,  0xD6,  0x06,
+  0x00,  0x33,  0x2A,  0x00,  0xC0,  0x88,  0x03,  0x03,  0x80,  0x63,  0x89,  0x00,  0x0A,  0x2B,  0x07,  0xA6,
+  0xE8,  0x06,  0x00,  0x33,  0x29,  0x00,  0xC0,  0x88,  0x00,  0x43,  0x00,  0xA2,  0xF4,  0x06,  0xC0,  0x0E,
+  0x80,  0x63,  0xDE,  0x86,  0xC0,  0x0E,  0x00,  0x33,  0x00,  0x80,  0xC0,  0x20,  0x81,  0x62,  0x04,  0x01,
+  0x02,  0xDA,  0x80,  0x63,  0x7C,  0x85,  0x80,  0x7B,  0x80,  0x63,  0x06,  0xA6,  0x8C,  0x06,  0x00,  0x33,
+  0x2C,  0x00,  0xC0,  0x88,  0x0C,  0xA2,  0x2E,  0x07,  0xFE,  0x95,  0x83,  0x03,  0x80,  0x63,  0x06,  0xA6,
+  0x2C,  0x07,  0x07,  0xA6,  0x7C,  0x05,  0x00,  0x33,  0x3D,  0x00,  0xC0,  0x88,  0x00,  0x00,  0x80,  0x67,
+  0x83,  0x03,  0x80,  0x63,  0x0C,  0xA0,  0x44,  0x07,  0x07,  0xA6,  0x7C,  0x05,  0xBF,  0x23,  0x04,  0x61,
+  0x84,  0x01,  0xE6,  0x84,  0x00,  0x63,  0xF0,  0x04,  0x01,  0x01,  0xF1,  0x00,  0x00,  0x01,  0xF2,  0x00,
+  0x01,  0x05,  0x80,  0x01,  0x72,  0x04,  0x71,  0x00,  0x81,  0x01,  0x70,  0x04,  0x80,  0x05,  0x81,  0x05,
+  0x00,  0x63,  0xF0,  0x04,  0xF2,  0x00,  0x72,  0x04,  0x01,  0x01,  0xF1,  0x00,  0x70,  0x00,  0x81,  0x01,
+  0x70,  0x04,  0x71,  0x00,  0x81,  0x01,  0x72,  0x00,  0x80,  0x01,  0x71,  0x04,  0x70,  0x00,  0x80,  0x01,
+  0x70,  0x04,  0x00,  0x63,  0xF0,  0x04,  0xF2,  0x00,  0x72,  0x04,  0x00,  0x01,  0xF1,  0x00,  0x70,  0x00,
+  0x80,  0x01,  0x70,  0x04,  0x71,  0x00,  0x80,  0x01,  0x72,  0x00,  0x81,  0x01,  0x71,  0x04,  0x70,  0x00,
+  0x81,  0x01,  0x70,  0x04,  0x00,  0x63,  0x00,  0x23,  0xB3,  0x01,  0x83,  0x05,  0xA3,  0x01,  0xA2,  0x01,
+  0xA1,  0x01,  0x01,  0x23,  0xA0,  0x01,  0x00,  0x01,  0xC8,  0x00,  0x03,  0xA1,  0xC4,  0x07,  0x00,  0x33,
+  0x07,  0x00,  0xC0,  0x88,  0x80,  0x05,  0x81,  0x05,  0x04,  0x01,  0x11,  0xC8,  0x48,  0x00,  0xB0,  0x01,
+  0xB1,  0x01,  0x08,  0x23,  0xB2,  0x01,  0x05,  0x01,  0x48,  0x04,  0x00,  0x43,  0x00,  0xA2,  0xE4,  0x07,
+  0x00,  0x05,  0xDA,  0x87,  0x00,  0x01,  0xC8,  0x00,  0xFF,  0x23,  0x80,  0x01,  0x05,  0x05,  0x00,  0x63,
+  0xF7,  0x04,  0x1A,  0x09,  0xF6,  0x08,  0x6E,  0x04,  0x00,  0x02,  0x80,  0x43,  0x76,  0x08,  0x80,  0x02,
+  0x77,  0x04,  0x00,  0x63,  0xF7,  0x04,  0x1A,  0x09,  0xF6,  0x08,  0x6E,  0x04,  0x00,  0x02,  0x00,  0xA0,
+  0x14,  0x08,  0x16,  0x88,  0x00,  0x43,  0x76,  0x08,  0x80,  0x02,  0x77,  0x04,  0x00,  0x63,  0xF3,  0x04,
+  0x00,  0x23,  0xF4,  0x00,  0x74,  0x00,  0x80,  0x43,  0xF4,  0x00,  0xCF,  0x40,  0x00,  0xA2,  0x44,  0x08,
+  0x74,  0x04,  0x02,  0x01,  0xF7,  0xC9,  0xF6,  0xD9,  0x00,  0x01,  0x01,  0xA1,  0x24,  0x08,  0x04,  0x98,
+  0x26,  0x95,  0x24,  0x88,  0x73,  0x04,  0x00,  0x63,  0xF3,  0x04,  0x75,  0x04,  0x5A,  0x88,  0x02,  0x01,
+  0x04,  0xD8,  0x46,  0x97,  0x04,  0x98,  0x26,  0x95,  0x4A,  0x88,  0x75,  0x00,  0x00,  0xA3,  0x64,  0x08,
+  0x00,  0x05,  0x4E,  0x88,  0x73,  0x04,  0x00,  0x63,  0x80,  0x7B,  0x80,  0x63,  0x06,  0xA6,  0x76,  0x08,
+  0x00,  0x33,  0x3E,  0x00,  0xC0,  0x88,  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0x00,  0x63,  0x38,  0x2B,
+  0x9C,  0x88,  0x38,  0x2B,  0x92,  0x88,  0x32,  0x09,  0x31,  0x05,  0x92,  0x98,  0x05,  0x05,  0xB2,  0x09,
+  0x00,  0x63,  0x00,  0x32,  0x00,  0x36,  0x00,  0x3A,  0x00,  0x3E,  0x00,  0x63,  0x80,  0x32,  0x80,  0x36,
+  0x80,  0x3A,  0x80,  0x3E,  0x00,  0x63,  0x38,  0x2B,  0x40,  0x32,  0x40,  0x36,  0x40,  0x3A,  0x40,  0x3E,
+  0x00,  0x63,  0x5A,  0x20,  0xC9,  0x40,  0x00,  0xA0,  0xB2,  0x08,  0x5D,  0x00,  0xFE,  0xC3,  0x00,  0x63,
+  0x80,  0x73,  0xE6,  0x20,  0x02,  0x23,  0xE8,  0x00,  0x82,  0x73,  0xFF,  0xFD,  0x80,  0x73,  0x13,  0x23,
+  0xF6,  0x88,  0x66,  0x20,  0xC0,  0x20,  0x04,  0x23,  0xA0,  0x01,  0xA1,  0x23,  0xA1,  0x01,  0x81,  0x62,
+  0xE0,  0x88,  0x80,  0x73,  0x80,  0x77,  0x68,  0x00,  0x00,  0xA2,  0x80,  0x00,  0x03,  0xC2,  0xF1,  0xC7,
+  0x41,  0x23,  0xF6,  0x88,  0x11,  0x23,  0xA1,  0x01,  0x04,  0x23,  0xA0,  0x01,  0xE6,  0x84,
 };
 
 STATIC ushort _asc_mcode_size ASC_INITDATA = sizeof(_asc_mcode_buf);
-STATIC ulong _asc_mcode_chksum ASC_INITDATA = 0x012A727FUL ;
+STATIC ulong _asc_mcode_chksum ASC_INITDATA = 0x012B5442UL;
 
 #define ASC_SYN_OFFSET_ONE_DISABLE_LIST  16
 STATIC uchar _syn_offset_one_disable_cmd[ASC_SYN_OFFSET_ONE_DISABLE_LIST] =
@@ -12232,7 +12343,7 @@ AscWaitTixISRDone(
         if ((cur_req = asc_dvc->cur_dvc_qng[tid_no]) == 0) {
             break;
         }
-        DvcSleepMilliSecond(100L);
+        DvcSleepMilliSecond(1000L);
         if (asc_dvc->cur_dvc_qng[tid_no] == cur_req) {
             break;
         }
@@ -12850,11 +12961,12 @@ AscInitAscDvcVar(
     asc_dvc->cfg->can_tagged_qng = 0 ;
     asc_dvc->cfg->cmd_qng_enabled = 0;
     asc_dvc->dvc_cntl = ASC_DEF_DVC_CNTL;
-    asc_dvc->init_sdtr = ASC_SCSI_WIDTH_BIT_SET;
+    asc_dvc->init_sdtr = 0;
     asc_dvc->max_total_qng = ASC_DEF_MAX_TOTAL_QNG;
     asc_dvc->scsi_reset_wait = 3;
     asc_dvc->start_motor = ASC_SCSI_WIDTH_BIT_SET;
     asc_dvc->max_dma_count = AscGetMaxDmaCount(asc_dvc->bus_type);
+    asc_dvc->cfg->sdtr_enable = ASC_SCSI_WIDTH_BIT_SET;
     asc_dvc->cfg->disc_enable = ASC_SCSI_WIDTH_BIT_SET;
     asc_dvc->cfg->chip_scsi_id = ASC_DEF_CHIP_SCSI_ID;
     asc_dvc->cfg->lib_serial_no = ASC_LIB_SERIAL_NUMBER;
@@ -13009,7 +13121,7 @@ AscInitFromEEP(
                 warn_code |= ASC_WARN_EEPROM_CHKSUM ;
             }
     }
-    asc_dvc->init_sdtr = eep_config->init_sdtr;
+    asc_dvc->cfg->sdtr_enable = eep_config->init_sdtr ;
     asc_dvc->cfg->disc_enable = eep_config->disc_enable;
     asc_dvc->cfg->cmd_qng_enabled = eep_config->use_cmd_qng;
     asc_dvc->cfg->isa_dma_speed = eep_config->isa_dma_speed;
@@ -13127,47 +13239,6 @@ AscInitMicroCodeVar(
     return (warn_code);
 }
 
-STATIC void
-AscInitPollIsrCallBack(
-                          ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                          ASC_QDONE_INFO * scsi_done_q
-)
-{
-    ASC_SCSI_REQ_Q *scsiq_req;
-    ASC_ISR_CALLBACK    asc_isr_callback;
-    uchar               cp_sen_len;
-    uchar               i;
-
-    ASC_DBG(1, "AscInitPollIsrCallBack: begin\n");
-    if ((scsi_done_q->d2.flag & ASC_FLAG_SCSIQ_REQ) != 0) {
-        scsiq_req = (ASC_SCSI_REQ_Q *) scsi_done_q->d2.srb_ptr;
-        scsiq_req->r3.done_stat = scsi_done_q->d3.done_stat;
-        scsiq_req->r3.host_stat = scsi_done_q->d3.host_stat;
-        scsiq_req->r3.scsi_stat = scsi_done_q->d3.scsi_stat;
-        scsiq_req->r3.scsi_msg = scsi_done_q->d3.scsi_msg;
-        ASC_DBG4(1, "AscInitPollIsrCallBack: done_stat %x, host_stat %x, scsi_stat %x, scsi_msg %x\n",
-            scsi_done_q->d3.done_stat, scsi_done_q->d3.host_stat,
-            scsi_done_q->d3.scsi_stat, scsi_done_q->d3.scsi_msg);
-        if ((scsi_done_q->d3.scsi_stat == SS_CHK_CONDITION) &&
-            (scsi_done_q->d3.host_stat == 0)) {
-            cp_sen_len = (uchar) ASC_MIN_SENSE_LEN;
-            if (scsiq_req->r1.sense_len < ASC_MIN_SENSE_LEN) {
-                cp_sen_len = (uchar) scsiq_req->r1.sense_len;
-            }
-            for (i = 0; i < cp_sen_len; i++) {
-                scsiq_req->sense[i] = scsiq_req->sense_ptr[i];
-            }
-        }
-    } else {
-        if (asc_dvc->isr_callback != 0) {
-            asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
-            (*asc_isr_callback) (asc_dvc, scsi_done_q);
-        }
-    }
-    ASC_DBG(1, "AscInitPollIsrCallBack: end\n");
-    return;
-}
-
 ASC_INITFUNC(
 STATIC int
 AscTestExternalLram(
@@ -13437,413 +13508,124 @@ AscSetEEPConfig(
     return (n_error);
 }
 
-STATIC int
-AscInitPollBegin(
-                    REG ASC_DVC_VAR asc_ptr_type * asc_dvc
-)
-{
-    PortAddr            iop_base;
-
-    iop_base = asc_dvc->iop_base;
-    AscDisableInterrupt(iop_base);
-    asc_dvc->init_state |= ASC_INIT_STATE_BEG_INQUIRY;
-    AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B, 0x00);
-    asc_dvc->use_tagged_qng = 0;
-    asc_dvc->cfg->can_tagged_qng = 0;
-    asc_dvc->saved_ptr2func = (ulong) asc_dvc->isr_callback;
-    asc_dvc->isr_callback = ASC_GET_PTR2FUNC(AscInitPollIsrCallBack);
-    return (0);
-}
-
-STATIC int
-AscInitPollEnd(
-                  REG ASC_DVC_VAR asc_ptr_type * asc_dvc
-)
-{
-    PortAddr            iop_base;
-    rint                i;
-
-    iop_base = asc_dvc->iop_base;
-    asc_dvc->isr_callback = (Ptr2Func) asc_dvc->saved_ptr2func;
-    AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B,
-                     asc_dvc->cfg->disc_enable);
-    AscWriteLramByte(iop_base, ASCV_USE_TAGGED_QNG_B,
-                     asc_dvc->use_tagged_qng);
-    AscWriteLramByte(iop_base, ASCV_CAN_TAGGED_QNG_B,
-                     asc_dvc->cfg->can_tagged_qng);
-    for (i = 0; i <= ASC_MAX_TID; i++) {
-        AscWriteLramByte(iop_base,
-                      (ushort) ((ushort) ASCV_MAX_DVC_QNG_BEG + (ushort) i),
-                         asc_dvc->max_dvc_qng[i]);
-    }
-    AscAckInterrupt(iop_base);
-    AscEnableInterrupt(iop_base);
-    asc_dvc->init_state |= ASC_INIT_STATE_END_INQUIRY;
-    return (0);
-}
-
-STATIC int
-AscInitPollTarget(
-                     REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                     REG ASC_SCSI_REQ_Q * scsiq,
-                     REG ASC_SCSI_INQUIRY * inq,
-                     REG ASC_CAP_INFO * cap_info
-)
+STATIC void
+AscAsyncFix(
+               ASC_DVC_VAR asc_ptr_type *asc_dvc,
+               uchar tid_no,
+               ASC_SCSI_INQUIRY *inq)
 {
-    uchar               tid_no, lun;
-    uchar               dvc_type;
+    uchar                dvc_type;
     ASC_SCSI_BIT_ID_TYPE tid_bits;
-    int                 dvc_found;
-    int                 support_read_cap;
-    int                 tmp_disable_init_sdtr;
-    int                 sta;
 
-    dvc_found = 0;
-    tmp_disable_init_sdtr = FALSE;
-    tid_bits = scsiq->r1.target_id;
-    lun = scsiq->r1.target_lun;
-    tid_no = ASC_TIX_TO_TID(scsiq->r2.target_ix);
-    if (((asc_dvc->init_sdtr & tid_bits) != 0) &&
-        ((asc_dvc->sdtr_done & tid_bits) == 0)) {
-        asc_dvc->init_sdtr &= ~tid_bits;
-        tmp_disable_init_sdtr = TRUE;
-    }
-    ASC_DBG(1, "AscInitPollTarget: before PollScsiInquiry\n");
-    if (PollScsiInquiry(asc_dvc, scsiq, (uchar *) inq,
-        sizeof (ASC_SCSI_INQUIRY)) == 1) {
-        dvc_found = 1;
-        dvc_type = inq->byte0.peri_dvc_type;
-        if (dvc_type != SCSI_TYPE_UNKNOWN) {
-            support_read_cap = TRUE;
-            if ((dvc_type != SCSI_TYPE_DASD)
-                && (dvc_type != SCSI_TYPE_WORM)
-                && (dvc_type != SCSI_TYPE_CDROM)
-                && (dvc_type != SCSI_TYPE_OPTMEM)) {
-                asc_dvc->start_motor &= ~tid_bits;
-                support_read_cap = FALSE;
+    dvc_type = inq->byte0.peri_dvc_type;
+    tid_bits = ASC_TIX_TO_TARGET_ID(tid_no);
+
+    if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ASYN_USE_SYN) {
+        if (!(asc_dvc->init_sdtr & tid_bits)) {
+            if ((dvc_type == SCSI_TYPE_CDROM) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                    (uchar *) "HP ", 3) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer_always |= tid_bits;
             }
-            if (lun == 0) {
-                if ((inq->byte3.rsp_data_fmt >= 2) ||
-                    (inq->byte2.ansi_apr_ver >= 2)) {
-                    if (inq->byte7.CmdQue) {
-                        asc_dvc->cfg->can_tagged_qng |= tid_bits;
-                        if (asc_dvc->cfg->cmd_qng_enabled & tid_bits) {
-                                asc_dvc->use_tagged_qng |= tid_bits;
-                                asc_dvc->max_dvc_qng[tid_no] = asc_dvc->cfg->max_tag_qng[tid_no];
-                        }
-                    }
-                    if (!inq->byte7.Sync) {
-                        asc_dvc->init_sdtr &= ~tid_bits;
-                        asc_dvc->sdtr_done &= ~tid_bits;
-                    } else if (tmp_disable_init_sdtr) {
-                        asc_dvc->init_sdtr |= tid_bits;
-                    }
-                } else {
-                    asc_dvc->init_sdtr &= ~tid_bits;
-                    asc_dvc->sdtr_done &= ~tid_bits;
-                    asc_dvc->use_tagged_qng &= ~tid_bits;
-                }
+            asc_dvc->pci_fix_asyn_xfer |= tid_bits;
+            if ((dvc_type == SCSI_TYPE_PROC) ||
+                (dvc_type == SCSI_TYPE_SCANNER)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
             }
-            if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ASYN_USE_SYN) {
-                if (!(asc_dvc->init_sdtr & tid_bits)) {
-                    if ((dvc_type == SCSI_TYPE_CDROM) &&
-                        (AscCompareString((uchar *) inq->vendor_id,
-                            (uchar *) "HP ", 3) == 0)) {
-                        asc_dvc->pci_fix_asyn_xfer_always |= tid_bits;
-                    }
-                    asc_dvc->pci_fix_asyn_xfer |= tid_bits;
-                    if ((dvc_type == SCSI_TYPE_PROC) || (dvc_type == SCSI_TYPE_SCANNER)) {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
-                    }
-                    if ((dvc_type == SCSI_TYPE_SASD) &&
-                        (AscCompareString((uchar *) inq->vendor_id,
-                            (uchar *) "TANDBERG", 8) == 0) &&
-                        (AscCompareString((uchar *) inq->product_id,
-                         (uchar *) " TDC 36", 7) == 0)) {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
-                    }
-                    if ((dvc_type == SCSI_TYPE_SASD) &&
-                        (AscCompareString((uchar *) inq->vendor_id,
-                         (uchar *) "WANGTEK ", 8) == 0)) {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
-                    }
-
-                    if ((dvc_type == SCSI_TYPE_CDROM) &&
-                       (AscCompareString((uchar *)inq->vendor_id,
-                        (uchar *)"NEC     ", 8) == 0) &&
-                        (AscCompareString((uchar *)inq->product_id,
-                         (uchar *)"CD-ROM DRIVE    ", 16) == 0)) {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits ;
-                    }
-                    if ((dvc_type == SCSI_TYPE_CDROM) &&
-                        (AscCompareString((uchar *) inq->vendor_id,
-                         (uchar *) "YAMAHA", 6) == 0)  &&
-                        (AscCompareString((uchar *) inq->product_id,
-                         (uchar *) "CDR400", 6) == 0))
-                    {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits ;
-                    }
-                    if (asc_dvc->pci_fix_asyn_xfer & tid_bits) {
-                        AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no,
-                                                ASYN_SDTR_DATA_FIX_PCI_REV_AB);
-                    }
-                }
+            if ((dvc_type == SCSI_TYPE_SASD) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                 (uchar *) "TANDBERG", 8) == 0) &&
+                (AscCompareString((uchar *) inq->product_id,
+                 (uchar *) " TDC 36", 7) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
             }
-            sta = 1;
-            ASC_DBG(1, "AscInitPollTarget: before InitTestUnitReady\n");
-            sta = InitTestUnitReady(asc_dvc, scsiq);
-            if (sta == 1) {
-                if ((cap_info != 0L) && support_read_cap) {
-                    ASC_DBG(1,
-                        "AscInitPollTarget: before PollScsiReadCapacity\n");
-                    if (PollScsiReadCapacity(asc_dvc, scsiq,
-                                             cap_info) != 1) {
-                        cap_info->lba = 0L;
-                        cap_info->blk_size = 0x0000;
-                    } else {
-                    }
-                }
+            if ((dvc_type == SCSI_TYPE_SASD) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                 (uchar *) "WANGTEK ", 8) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
             }
-        } else {
-            asc_dvc->start_motor &= ~tid_bits;
-        }
-    } else if (tmp_disable_init_sdtr) {
-        asc_dvc->init_sdtr |= tid_bits;
-    }
-    ASC_DBG1(1, "AscInitPollTarget: dvc_found %d\n", dvc_found);
-    return (dvc_found);
-}
 
-STATIC int
-PollQueueDone(
-                 REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                 REG ASC_SCSI_REQ_Q * scsiq,
-                 int timeout_sec
-)
-{
-    int                 status;
-    int                 retry = 0;
+            if ((dvc_type == SCSI_TYPE_CDROM) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                 (uchar *) "NEC     ", 8) == 0) &&
+                (AscCompareString((uchar *) inq->product_id,
+                 (uchar *) "CD-ROM DRIVE    ", 16) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+            }
 
-    ASC_DBG1(1, "PollQueueDone: timeout_sec %d\n", timeout_sec);
-    do {
-        ASC_DBG(1, "PollQueueDone: before AscExeScsiQueue\n");
-        if ((status = AscExeScsiQueue(asc_dvc,
-                (ASC_SCSI_Q *) scsiq)) == 1) {
-            ASC_DBG(1, "PollQueueDone: before AscPollQDone\n");
-            if ((status = AscPollQDone(asc_dvc, scsiq,
-                                       timeout_sec)) != 1) {
-                ASC_DBG1(1, "PollQueueDone: status %x\n", status);
-                if (status == 0x80) {
-                    if (retry++ > ASC_MAX_INIT_BUSY_RETRY) {
-                        break;
-                    }
-                    scsiq->r3.done_stat = 0;
-                    scsiq->r3.host_stat = 0;
-                    scsiq->r3.scsi_stat = 0;
-                    scsiq->r3.scsi_msg = 0;
-                    DvcSleepMilliSecond(1000);
-                    continue;
-                }
-                scsiq->r3.done_stat = 0;
-                scsiq->r3.host_stat = 0;
-                scsiq->r3.scsi_stat = 0;
-                scsiq->r3.scsi_msg = 0;
-                ASC_DBG(1, "PollQueueDone: before AscAbortSRB()\n");
-                AscAbortSRB(asc_dvc, (ulong) scsiq);
+            if ((dvc_type == SCSI_TYPE_CDROM) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                 (uchar *) "YAMAHA", 6) == 0) &&
+                (AscCompareString((uchar *) inq->product_id,
+                 (uchar *) "CDR400", 6) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+            }
+            if (asc_dvc->pci_fix_asyn_xfer & tid_bits) {
+                AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no,
+                                        ASYN_SDTR_DATA_FIX_PCI_REV_AB);
             }
-            ASC_DBG1(1, "PollQueueDone: status %x\n", status);
-            ASC_DBG1(1, "PollQueueDone: done_stat %x\n", scsiq->r3.done_stat);
-            return (scsiq->r3.done_stat);
         }
-        ASC_DBG1(1, "PollQueueDone: status %x\n", status);
-        DvcSleepMilliSecond(5);
-    } while (((status == 0) || (status == 0x80)) &&
-              retry++ < ASC_MAX_INIT_BUSY_RETRY);
-    ASC_DBG1(1, "PollQueueDone: status %x\n", status);
-    ASC_DBG(1, "PollQueueDone: done_stat QD_WITH_ERROR\n");
-    return (scsiq->r3.done_stat = QD_WITH_ERROR);
-}
-
-STATIC int
-PollScsiInquiry(
-                   REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                   REG ASC_SCSI_REQ_Q * scsiq,
-                   uchar * buf,
-                   int buf_len
-)
-{
-    if (AscScsiInquiry(asc_dvc, scsiq, buf, buf_len) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
     }
-    return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 4));
-}
-
-STATIC int
-PollScsiStartUnit(
-                     REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                     REG ASC_SCSI_REQ_Q * scsiq
-)
-{
-    if (AscScsiStartStopUnit(asc_dvc, scsiq, 1) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 40));
+    return;
 }
 
 STATIC int
-PollScsiReadCapacity(
-                        REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                        REG ASC_SCSI_REQ_Q * scsiq,
-                        REG ASC_CAP_INFO * cap_info
-)
+AscTagQueuingSafe(ASC_SCSI_INQUIRY *inq)
 {
-    ASC_CAP_INFO        scsi_cap_info;
-    int                 status;
-
-    if (AscScsiReadCapacity(asc_dvc, scsiq,
-                            (uchar *) & scsi_cap_info) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    status = PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 8);
-    if (status == 1) {
-        cap_info->lba = (ulong) * swapfarbuf4((uchar *) & scsi_cap_info.lba);
-        cap_info->blk_size = (ulong) * swapfarbuf4((uchar *) & scsi_cap_info.blk_size);
-        return (scsiq->r3.done_stat);
+    if ((inq->add_len >= 32) &&
+        (AscCompareString((uchar *) inq->vendor_id,
+            (uchar *) "QUANTUM XP34301", 15) == 0) &&
+        (AscCompareString((uchar *) inq->product_rev_level,
+            (uchar *) "1071", 4) == 0))
+    {
+        return 0;
     }
-    return (scsiq->r3.done_stat = QD_WITH_ERROR);
+    return 1;
 }
 
-STATIC ulong *
-swapfarbuf4(
-    uchar *buf
-)
+STATIC void
+AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *asc_dvc,
+                   uchar tid_no, ASC_SCSI_INQUIRY *inq)
 {
-    uchar      tmp;
-
-    tmp = buf[3];
-    buf[3] = buf[0];
-    buf[0] = tmp;
+    ASC_SCSI_BIT_ID_TYPE tid_bit = ASC_TIX_TO_TARGET_ID(tid_no);
+    ASC_SCSI_BIT_ID_TYPE orig_init_sdtr, orig_use_tagged_qng;
 
-    tmp = buf[1];
-    buf[1] = buf[2];
-    buf[2] = tmp;
+    orig_init_sdtr = asc_dvc->init_sdtr;
+    orig_use_tagged_qng = asc_dvc->use_tagged_qng;
 
-    return ((ulong *) buf);
-}
+    asc_dvc->init_sdtr &= ~tid_bit;
+    asc_dvc->cfg->can_tagged_qng &= ~tid_bit;
+    asc_dvc->use_tagged_qng &= ~tid_bit;
 
-STATIC int
-PollScsiTestUnitReady(
-                         REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                         REG ASC_SCSI_REQ_Q * scsiq
-)
-{
-    if (AscScsiTestUnitReady(asc_dvc, scsiq) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 12));
-}
-
-STATIC int
-InitTestUnitReady(
-                     REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                     REG ASC_SCSI_REQ_Q * scsiq
-)
-{
-    ASC_SCSI_BIT_ID_TYPE tid_bits;
-    int                 retry;
-    ASC_REQ_SENSE *sen;
-
-    retry = 0;
-    tid_bits = scsiq->r1.target_id;
-    while (retry++ < 4) {
-        PollScsiTestUnitReady(asc_dvc, scsiq);
-        if (scsiq->r3.done_stat == 0x01) {
-            return (1);
-        } else if (scsiq->r3.done_stat == QD_WITH_ERROR) {
-            sen = (ASC_REQ_SENSE *) scsiq->sense_ptr;
-            if ((scsiq->r3.scsi_stat == SS_CHK_CONDITION) &&
-                ((sen->err_code & 0x70) != 0)) {
-                if (sen->sense_key == SCSI_SENKEY_NOT_READY) {
-                    if (sen->asc == SCSI_ASC_NOMEDIA)
-                    {
-                        break;
-                    }
-                    if (asc_dvc->start_motor & tid_bits) {
-                        if (PollScsiStartUnit(asc_dvc, scsiq) == 1) {
-                            DvcSleepMilliSecond(250);
-                            continue;
-                        } else {
-                            asc_dvc->start_motor &= ~tid_bits;
-                            break;
-                        }
-                    } else {
-                        DvcSleepMilliSecond(250);
-                    }
-                } else if (sen->sense_key == SCSI_SENKEY_ATTENTION) {
-                    DvcSleepMilliSecond(250);
-                } else {
-                    break;
-                }
-            } else {
-                break;
+    if (inq->byte3.rsp_data_fmt >= 2 || inq->byte2.ansi_apr_ver >= 2) {
+        if ((asc_dvc->cfg->sdtr_enable & tid_bit) && inq->byte7.Sync) {
+            asc_dvc->init_sdtr |= tid_bit;
+        }
+        if ((asc_dvc->cfg->cmd_qng_enabled & tid_bit) && inq->byte7.CmdQue) {
+            if (AscTagQueuingSafe(inq)) {
+                asc_dvc->use_tagged_qng |= tid_bit;
+                asc_dvc->cfg->can_tagged_qng |= tid_bit;
             }
-        } else if (scsiq->r3.done_stat == QD_ABORTED_BY_HOST) {
-            break;
-        } else {
-            break;
         }
     }
-    return (0);
-}
-
-STATIC int
-AscPollQDone(
-                REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                REG ASC_SCSI_REQ_Q * scsiq,
-                int timeout_sec
-)
-{
-    int                 loop, loop_end;
-    int                 sta;
-    PortAddr            iop_base;
+    if (orig_use_tagged_qng != asc_dvc->use_tagged_qng) {
+        AscWriteLramByte(asc_dvc->iop_base, ASCV_DISC_ENABLE_B,
+                         asc_dvc->cfg->disc_enable);
+        AscWriteLramByte(asc_dvc->iop_base, ASCV_USE_TAGGED_QNG_B,
+                         asc_dvc->use_tagged_qng);
+        AscWriteLramByte(asc_dvc->iop_base, ASCV_CAN_TAGGED_QNG_B,
+                         asc_dvc->cfg->can_tagged_qng);
 
-    iop_base = asc_dvc->iop_base;
-    loop = 0;
-    loop_end = timeout_sec * 100;
-    sta = 1;
-    while (TRUE) {
-        if (asc_dvc->err_code != 0) {
-            scsiq->r3.done_stat = QD_WITH_ERROR;
-            ASC_DBG1(1, "AscPollQDone: err_code %x\n", asc_dvc->err_code);
-            sta = ERR;
-            break;
-        }
-        if (scsiq->r3.done_stat != QD_IN_PROGRESS) {
-            if ((scsiq->r3.done_stat == QD_WITH_ERROR) &&
-                (scsiq->r3.scsi_stat == SS_TARGET_BUSY)) {
-                sta = 0x80;
-            }
-            break;
-        }
-        DvcSleepMilliSecond(10);
-        if (loop++ > loop_end) {
-            ASC_DBG(1, "AscPollQDone: loop finished\n");
-            sta = 0;
-            break;
-        }
-        if (AscIsChipHalted(iop_base)) {
-            ASC_DBG(1, "AscPollQDone: AscIsChipHalted()\n");
-            AscISR(asc_dvc);
-            loop = 0;
-        } else {
-            if (AscIsIntPending(iop_base)) {
-                ASC_DBG(1, "AscPollQDone: AscIsIntPending()\n");
-                AscISR(asc_dvc);
-            }
-        }
+        asc_dvc->max_dvc_qng[tid_no] =
+          asc_dvc->cfg->max_tag_qng[tid_no];
+        AscWriteLramByte(asc_dvc->iop_base,
+                         (ushort) (ASCV_MAX_DVC_QNG_BEG + tid_no),
+                         asc_dvc->max_dvc_qng[tid_no]);
     }
-    return (sta);
+    if (orig_init_sdtr != asc_dvc->init_sdtr) {
+        AscAsyncFix(asc_dvc, tid_no, inq);
+    }
+    return;
 }
 
 STATIC int
@@ -14040,105 +13822,16 @@ AscMemWordSetLram(
 }
 
 
-STATIC int
-AscScsiInquiry(
-                  REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                  REG ASC_SCSI_REQ_Q * scsiq,
-                  uchar * buf,
-                  int buf_len
-)
-{
-    if (AscScsiSetupCmdQ(asc_dvc, scsiq, buf,
-                         (ulong) buf_len) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    scsiq->cdb[0] = (uchar) SCSICMD_Inquiry;
-    scsiq->cdb[1] = scsiq->r1.target_lun << 5;
-    scsiq->cdb[2] = 0;
-    scsiq->cdb[3] = 0;
-    scsiq->cdb[4] = buf_len;
-    scsiq->cdb[5] = 0;
-    scsiq->r2.cdb_len = 6;
-    return (0);
-}
-
-STATIC int
-AscScsiReadCapacity(
-                       REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                       REG ASC_SCSI_REQ_Q * scsiq,
-                       uchar * info
-)
-{
-    if (AscScsiSetupCmdQ(asc_dvc, scsiq, info, 8L) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    scsiq->cdb[0] = (uchar) SCSICMD_ReadCapacity;
-    scsiq->cdb[1] = scsiq->r1.target_lun << 5;
-    scsiq->cdb[2] = 0;
-    scsiq->cdb[3] = 0;
-    scsiq->cdb[4] = 0;
-    scsiq->cdb[5] = 0;
-    scsiq->cdb[6] = 0;
-    scsiq->cdb[7] = 0;
-    scsiq->cdb[8] = 0;
-    scsiq->cdb[9] = 0;
-    scsiq->r2.cdb_len = 10;
-    return (0);
-}
-
-STATIC int
-AscScsiTestUnitReady(
-                        REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                        REG ASC_SCSI_REQ_Q * scsiq
-)
-{
-    if (AscScsiSetupCmdQ(asc_dvc, scsiq, FNULLPTR,
-                         (ulong) 0L) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    scsiq->r1.cntl = (uchar) ASC_SCSIDIR_NODATA;
-    scsiq->cdb[0] = (uchar) SCSICMD_TestUnitReady;
-    scsiq->cdb[1] = scsiq->r1.target_lun << 5;
-    scsiq->cdb[2] = 0;
-    scsiq->cdb[3] = 0;
-    scsiq->cdb[4] = 0;
-    scsiq->cdb[5] = 0;
-    scsiq->r2.cdb_len = 6;
-    return (0);
-}
-
-STATIC int
-AscScsiStartStopUnit(
-                        REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                        REG ASC_SCSI_REQ_Q * scsiq,
-                        uchar op_mode
-)
-{
-    if (AscScsiSetupCmdQ(asc_dvc, scsiq, FNULLPTR, (ulong) 0L) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    scsiq->r1.cntl = (uchar) ASC_SCSIDIR_NODATA;
-    scsiq->cdb[0] = (uchar) SCSICMD_StartStopUnit;
-    scsiq->cdb[1] = scsiq->r1.target_lun << 5;
-    scsiq->cdb[2] = 0;
-    scsiq->cdb[3] = 0;
-    scsiq->cdb[4] = op_mode;
-    scsiq->cdb[5] = 0;
-    scsiq->r2.cdb_len = 6;
-    return (0);
-}
-
-
 /*
  * --- Adv Library Functions
  */
 
 /* a_qswap.h */
 STATIC unsigned char _adv_mcode_buf[] ASC_INITDATA = {
-  0x9C,  0xF0,  0x80,  0x01,  0x00,  0xF0,  0x40,  0x0A,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+  0x9C,  0xF0,  0x80,  0x01,  0x00,  0xF0,  0x44,  0x0A,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x72,  0x01,  0xD2,  0x11,  0x00,  0x00,  0x70,  0x01,
-  0x30,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x88,  0x0F,  0x22,  0x03,  0x00,  0x00,  0x00,  0x00,
+  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x72,  0x01,  0xD6,  0x11,  0x00,  0x00,  0x70,  0x01,
+  0x30,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x90,  0x10,  0x2D,  0x03,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
@@ -14158,273 +13851,274 @@ STATIC unsigned char _adv_mcode_buf[] ASC_INITDATA = {
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-  0x00,  0x00,  0x04,  0xF7,  0x70,  0x01,  0x0C,  0x1C,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF2,  0xD2,  0x0A,
+  0x00,  0x00,  0x04,  0xF7,  0x70,  0x01,  0x0C,  0x1C,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF2,  0xD6,  0x0A,
   0x04,  0xF7,  0x70,  0x01,  0x06,  0xF7,  0x02,  0x00,  0x3E,  0x57,  0x3C,  0x56,  0x0C,  0x1C,  0x00,  0xFC,
-  0xA6,  0x00,  0x01,  0x58,  0xAA,  0x13,  0x20,  0xF0,  0x9A,  0x03,  0x06,  0xEC,  0xB9,  0x00,  0x0E,  0x47,
+  0xA6,  0x00,  0x01,  0x58,  0xAA,  0x13,  0x20,  0xF0,  0xA6,  0x03,  0x06,  0xEC,  0xB9,  0x00,  0x0E,  0x47,
   0x03,  0xE6,  0x10,  0x00,  0xCE,  0x45,  0x02,  0x13,  0x3E,  0x57,  0x06,  0xEA,  0xB9,  0x00,  0x47,  0x4B,
-  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x01,  0x48,  0x4E,  0x12,  0x03,  0xF6,  0xC0,  0x00,
-  0x00,  0xF2,  0x64,  0x0A,  0x41,  0x58,  0x03,  0xF6,  0xD0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x49,  0x44,
-  0x59,  0xF0,  0x0A,  0x02,  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x44,  0x58,  0x00,  0xF2,
-  0xDE,  0x0D,  0x02,  0xCC,  0x4A,  0xE4,  0x01,  0x00,  0x55,  0xF0,  0x08,  0x03,  0x45,  0xF4,  0x02,  0x00,
-  0x83,  0x5A,  0x04,  0xCC,  0x01,  0x4A,  0x12,  0x12,  0x00,  0xF2,  0xDE,  0x0D,  0x00,  0xCD,  0x48,  0xE4,
-  0x01,  0x00,  0xE9,  0x13,  0x00,  0xF2,  0xC2,  0x0F,  0xFA,  0x10,  0x0E,  0x47,  0x03,  0xE6,  0x10,  0x00,
+  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x01,  0x48,  0x4E,  0x12,  0x03,  0xF6,  0xC0,  0x00,
+  0x00,  0xF2,  0x68,  0x0A,  0x41,  0x58,  0x03,  0xF6,  0xD0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x49,  0x44,
+  0x59,  0xF0,  0x0A,  0x02,  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x44,  0x58,  0x00,  0xF2,
+  0xE2,  0x0D,  0x02,  0xCC,  0x4A,  0xE4,  0x01,  0x00,  0x55,  0xF0,  0x08,  0x03,  0x45,  0xF4,  0x02,  0x00,
+  0x83,  0x5A,  0x04,  0xCC,  0x01,  0x4A,  0x12,  0x12,  0x00,  0xF2,  0xE2,  0x0D,  0x00,  0xCD,  0x48,  0xE4,
+  0x01,  0x00,  0xE9,  0x13,  0x00,  0xF2,  0xC6,  0x0F,  0xFA,  0x10,  0x0E,  0x47,  0x03,  0xE6,  0x10,  0x00,
   0xCE,  0x45,  0x02,  0x13,  0x3E,  0x57,  0xCE,  0x47,  0x97,  0x13,  0x04,  0xEC,  0xB4,  0x00,  0x00,  0xF2,
-  0xDE,  0x0D,  0x00,  0xCD,  0x48,  0xE4,  0x00,  0x00,  0x12,  0x12,  0x3E,  0x57,  0x06,  0xCC,  0x45,  0xF4,
+  0xE2,  0x0D,  0x00,  0xCD,  0x48,  0xE4,  0x00,  0x00,  0x12,  0x12,  0x3E,  0x57,  0x06,  0xCC,  0x45,  0xF4,
   0x02,  0x00,  0x83,  0x5A,  0x00,  0xCC,  0x00,  0xEA,  0xB4,  0x00,  0x92,  0x10,  0x00,  0xF0,  0x8C,  0x01,
   0x43,  0xF0,  0x5C,  0x02,  0x44,  0xF0,  0x60,  0x02,  0x45,  0xF0,  0x64,  0x02,  0x46,  0xF0,  0x68,  0x02,
   0x47,  0xF0,  0x6E,  0x02,  0x48,  0xF0,  0x9E,  0x02,  0xB9,  0x54,  0x62,  0x10,  0x00,  0x1C,  0x5A,  0x10,
-  0x02,  0x1C,  0x56,  0x10,  0x1E,  0x1C,  0x52,  0x10,  0x00,  0xF2,  0x1A,  0x11,  0x50,  0x10,  0x06,  0xFC,
-  0xA8,  0x00,  0x03,  0xF6,  0xBE,  0x00,  0x00,  0xF2,  0x4A,  0x0A,  0x8C,  0x10,  0x01,  0xF6,  0x01,  0x00,
-  0x01,  0xFA,  0xA8,  0x00,  0x00,  0xF2,  0x28,  0x0B,  0x06,  0x10,  0xB9,  0x54,  0x01,  0xFA,  0xA8,  0x00,
-  0x03,  0xF6,  0xBE,  0x00,  0x00,  0xF2,  0x54,  0x0A,  0x01,  0xFC,  0xA8,  0x00,  0x20,  0x10,  0x58,  0x1C,
-  0x00,  0xF2,  0x18,  0x0B,  0x5A,  0x1C,  0x01,  0xF6,  0x01,  0x00,  0x38,  0x54,  0x00,  0xFA,  0xA6,  0x00,
+  0x02,  0x1C,  0x56,  0x10,  0x1E,  0x1C,  0x52,  0x10,  0x00,  0xF2,  0x1E,  0x11,  0x50,  0x10,  0x06,  0xFC,
+  0xA8,  0x00,  0x03,  0xF6,  0xBE,  0x00,  0x00,  0xF2,  0x4E,  0x0A,  0x8C,  0x10,  0x01,  0xF6,  0x01,  0x00,
+  0x01,  0xFA,  0xA8,  0x00,  0x00,  0xF2,  0x2C,  0x0B,  0x06,  0x10,  0xB9,  0x54,  0x01,  0xFA,  0xA8,  0x00,
+  0x03,  0xF6,  0xBE,  0x00,  0x00,  0xF2,  0x58,  0x0A,  0x01,  0xFC,  0xA8,  0x00,  0x20,  0x10,  0x58,  0x1C,
+  0x00,  0xF2,  0x1C,  0x0B,  0x5A,  0x1C,  0x01,  0xF6,  0x01,  0x00,  0x38,  0x54,  0x00,  0xFA,  0xA6,  0x00,
   0x01,  0xFA,  0xA8,  0x00,  0x20,  0x1C,  0x00,  0xF0,  0x72,  0x01,  0x01,  0xF6,  0x01,  0x00,  0x38,  0x54,
   0x00,  0xFA,  0xA6,  0x00,  0x01,  0xFA,  0xA8,  0x00,  0x20,  0x1C,  0x00,  0xF0,  0x80,  0x01,  0x03,  0xF6,
-  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x01,  0x48,  0x0A,  0x13,  0x00,  0xF2,  0x34,  0x10,  0x00,  0xF2,
-  0x50,  0x0F,  0x24,  0x10,  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x02,  0xF6,  0xD0,  0x00,
-  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x49,  0x44,  0x5B,  0xF0,  0x04,  0x03,  0x00,  0xF2,  0x98,  0x0F,
-  0x00,  0xF0,  0x80,  0x01,  0x00,  0xF2,  0x10,  0x10,  0x0C,  0x1C,  0x02,  0x4B,  0xBF,  0x57,  0x9E,  0x43,
-  0x77,  0x57,  0x07,  0x4B,  0x20,  0xF0,  0x9A,  0x03,  0x40,  0x1C,  0x1E,  0xF0,  0x30,  0x03,  0x26,  0xF0,
-  0x2C,  0x03,  0xA0,  0xF0,  0x1A,  0x03,  0x11,  0xF0,  0x9A,  0x03,  0x12,  0x10,  0x9F,  0xF0,  0x3E,  0x03,
-  0x46,  0x1C,  0x82,  0xE7,  0x05,  0x00,  0x9E,  0xE7,  0x11,  0x00,  0x00,  0xF0,  0x02,  0x0A,  0x0C,  0x1C,
+  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x01,  0x48,  0x0A,  0x13,  0x00,  0xF2,  0x38,  0x10,  0x00,  0xF2,
+  0x54,  0x0F,  0x24,  0x10,  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x02,  0xF6,  0xD0,  0x00,
+  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x49,  0x44,  0x5B,  0xF0,  0x04,  0x03,  0x00,  0xF2,  0x9C,  0x0F,
+  0x00,  0xF0,  0x80,  0x01,  0x00,  0xF2,  0x14,  0x10,  0x0C,  0x1C,  0x02,  0x4B,  0xBF,  0x57,  0x9E,  0x43,
+  0x77,  0x57,  0x07,  0x4B,  0x20,  0xF0,  0xA6,  0x03,  0x40,  0x1C,  0x1E,  0xF0,  0x30,  0x03,  0x26,  0xF0,
+  0x2C,  0x03,  0xA0,  0xF0,  0x1A,  0x03,  0x11,  0xF0,  0xA6,  0x03,  0x12,  0x10,  0x9F,  0xF0,  0x3E,  0x03,
+  0x46,  0x1C,  0x82,  0xE7,  0x05,  0x00,  0x9E,  0xE7,  0x11,  0x00,  0x00,  0xF0,  0x06,  0x0A,  0x0C,  0x1C,
   0x48,  0x1C,  0x46,  0x1C,  0x38,  0x54,  0x00,  0xEC,  0xBA,  0x00,  0x08,  0x44,  0x00,  0xEA,  0xBA,  0x00,
-  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x08,  0x44,  0x00,  0x4C,  0x82,  0xE7,  0x02,  0x00,
-  0x00,  0xF2,  0x0E,  0x11,  0x00,  0xF2,  0x0E,  0x11,  0x06,  0xF0,  0x74,  0x03,  0x1E,  0xF0,  0xF8,  0x09,
-  0x00,  0xF0,  0xFE,  0x09,  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0x55,  0xF0,  0xA0,  0x04,  0x01,  0xE6,
-  0x0C,  0x00,  0x00,  0xF2,  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,
-  0xC4,  0x11,  0x01,  0xF0,  0x7C,  0x02,  0x00,  0xF0,  0x8A,  0x02,  0x46,  0x1C,  0x0C,  0x1C,  0x67,  0x1B,
-  0xBF,  0x57,  0x77,  0x57,  0x02,  0x4B,  0x48,  0x1C,  0x32,  0x1C,  0x00,  0xF2,  0x8E,  0x0D,  0x30,  0x1C,
-  0x96,  0xF0,  0xB0,  0x03,  0xB1,  0xF0,  0xB4,  0x03,  0x1E,  0xF0,  0xF8,  0x09,  0x85,  0xF0,  0xFE,  0x09,
-  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0x14,  0x12,  0x01,  0xE6,  0x0C,  0x00,  0x00,  0xF2,  0x4A,  0x0D,
-  0x00,  0xF2,  0x0E,  0x11,  0x01,  0xF0,  0x7C,  0x02,  0x00,  0xF0,  0x8A,  0x02,  0x03,  0xF6,  0xE0,  0x00,
-  0x00,  0xF2,  0x64,  0x0A,  0x01,  0x48,  0x55,  0xF0,  0x8C,  0x04,  0x03,  0x82,  0x03,  0xFC,  0xA0,  0x00,
-  0x9B,  0x57,  0x40,  0x12,  0x69,  0x18,  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,  0x36,  0x04,  0x69,  0x08,
-  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,  0xFE,  0x09,  0x68,  0x08,  0x4C,  0x44,  0x28,  0x12,  0x44,  0x48,
-  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x45,  0x58,  0x00,  0xF2,  0xF2,  0x0D,  0x00,  0xCC,
-  0x01,  0x48,  0x55,  0xF0,  0x8C,  0x04,  0x4C,  0x44,  0xEF,  0x13,  0x00,  0xF2,  0xC2,  0x0F,  0x00,  0xF2,
-  0x10,  0x10,  0x08,  0x10,  0x68,  0x18,  0x45,  0x5A,  0x00,  0xF2,  0xF2,  0x0D,  0x04,  0x80,  0x18,  0xE4,
-  0x10,  0x00,  0x28,  0x12,  0x01,  0xE6,  0x06,  0x00,  0x04,  0x80,  0x18,  0xE4,  0x01,  0x00,  0x04,  0x12,
-  0x01,  0xE6,  0x0D,  0x00,  0x00,  0xF2,  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,  0x04,  0xE6,  0x02,  0x00,
-  0x9E,  0xE7,  0x15,  0x00,  0x01,  0xF0,  0x18,  0x0A,  0x00,  0xF0,  0xFE,  0x09,  0x69,  0x08,  0x05,  0x80,
-  0x48,  0xE4,  0x00,  0x00,  0x0C,  0x12,  0x00,  0xE6,  0x11,  0x00,  0x00,  0xEA,  0xB8,  0x00,  0x00,  0xF2,
-  0xB2,  0x10,  0x82,  0xE7,  0x02,  0x00,  0x1C,  0x90,  0x40,  0x5C,  0x00,  0x16,  0x01,  0xE6,  0x06,  0x00,
-  0x00,  0xF2,  0x4A,  0x0D,  0x01,  0xF0,  0x80,  0x01,  0x1E,  0xF0,  0x80,  0x01,  0x00,  0xF0,  0x94,  0x04,
-  0x42,  0x5B,  0x06,  0xF7,  0x03,  0x00,  0x46,  0x59,  0xBF,  0x57,  0x77,  0x57,  0x01,  0xE6,  0x80,  0x00,
-  0x07,  0x80,  0x31,  0x44,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x5E,  0x13,  0x20,  0x80,  0x48,  0xE4,
-  0x03,  0x00,  0x56,  0x12,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x4C,  0x13,  0x00,  0xFC,  0xA2,  0x00,
-  0x98,  0x57,  0x55,  0xF0,  0x18,  0x05,  0x31,  0xE4,  0x40,  0x00,  0x00,  0xFC,  0xA0,  0x00,  0x98,  0x57,
-  0x36,  0x12,  0x4C,  0x1C,  0x00,  0xF2,  0x0E,  0x11,  0x89,  0x48,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,
-  0x2A,  0x05,  0x82,  0xE7,  0x06,  0x00,  0x1B,  0x80,  0x48,  0xE4,  0x22,  0x00,  0x5B,  0xF0,  0x08,  0x05,
-  0x48,  0xE4,  0x20,  0x00,  0x59,  0xF0,  0x0C,  0x05,  0x00,  0xE6,  0x20,  0x00,  0x09,  0x48,  0x00,  0xF2,
-  0x0E,  0x11,  0x86,  0xF0,  0x2A,  0x05,  0x83,  0x80,  0x04,  0x10,  0x00,  0xF2,  0x9E,  0x0D,  0x00,  0xE6,
-  0x01,  0x00,  0x00,  0xEA,  0x26,  0x01,  0x01,  0xEA,  0x27,  0x01,  0x04,  0x80,  0x18,  0xE4,  0x10,  0x00,
-  0x36,  0x12,  0xB9,  0x54,  0x00,  0xF2,  0xF2,  0x0E,  0x01,  0xE6,  0x06,  0x00,  0x04,  0x80,  0x18,  0xE4,
-  0x01,  0x00,  0x04,  0x12,  0x01,  0xE6,  0x0D,  0x00,  0x00,  0xF2,  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,
-  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,  0xC4,  0x11,  0x04,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,
-  0x01,  0xF0,  0x18,  0x0A,  0x00,  0xF0,  0xFE,  0x09,  0x00,  0xFC,  0x20,  0x01,  0x98,  0x57,  0x34,  0x12,
-  0x00,  0xFC,  0x24,  0x01,  0x98,  0x57,  0x2C,  0x13,  0xB9,  0x54,  0x00,  0xF2,  0xF2,  0x0E,  0x86,  0xF0,
-  0xA4,  0x05,  0x03,  0xF6,  0x01,  0x00,  0x00,  0xF2,  0x88,  0x0E,  0x85,  0xF0,  0x9A,  0x05,  0x82,  0xE7,
-  0x03,  0x00,  0x00,  0xF2,  0x5C,  0x0B,  0x82,  0xE7,  0x02,  0x00,  0x00,  0xFC,  0x24,  0x01,  0xB0,  0x57,
-  0x00,  0xFA,  0x24,  0x01,  0x00,  0xFC,  0x9E,  0x00,  0x98,  0x57,  0x5A,  0x12,  0x00,  0xFC,  0xB6,  0x00,
-  0x98,  0x57,  0x52,  0x13,  0x03,  0xE6,  0x0C,  0x00,  0x00,  0xFC,  0x9C,  0x00,  0x98,  0x57,  0x04,  0x13,
-  0x03,  0xE6,  0x19,  0x00,  0x05,  0xE6,  0x08,  0x00,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,
-  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,  0x00,  0x80,  0x04,  0x13,  0x05,  0xE6,  0x0F,  0x00,  0xB9,  0x54,
-  0x00,  0xF2,  0xF2,  0x0E,  0x86,  0xF0,  0x06,  0x06,  0x00,  0xF2,  0xB6,  0x0E,  0x85,  0xF0,  0xFC,  0x05,
-  0x82,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x5C,  0x0B,  0x82,  0xE7,  0x02,  0x00,  0x00,  0xFC,  0xB6,  0x00,
-  0xB0,  0x57,  0x00,  0xFA,  0xB6,  0x00,  0x01,  0xF6,  0x01,  0x00,  0x00,  0xF2,  0xF2,  0x0E,  0x9C,  0x32,
-  0x4E,  0x1C,  0x32,  0x1C,  0x00,  0xF2,  0x8E,  0x0D,  0x30,  0x1C,  0x82,  0xE7,  0x04,  0x00,  0xB1,  0xF0,
-  0x1E,  0x06,  0x0A,  0xF0,  0x3A,  0x06,  0x05,  0xF0,  0xD2,  0x06,  0x06,  0xF0,  0xD8,  0x06,  0x09,  0xF0,
-  0x20,  0x09,  0x1E,  0xF0,  0xF8,  0x09,  0x00,  0xF0,  0xFE,  0x09,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,
-  0x30,  0x12,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x21,  0x80,  0x18,  0xE4,  0xE0,  0x00,
-  0x09,  0x48,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,
-  0x00,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x99,  0xA4,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,  0x00,  0x00,
-  0x9A,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x34,  0x12,  0x09,  0xE7,  0x1B,  0x00,  0x00,  0xF2,
-  0x0E,  0x11,  0x21,  0x80,  0x18,  0xE4,  0xE0,  0x00,  0x09,  0x48,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,
-  0x00,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,
-  0x01,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF0,  0x08,  0x09,  0xBB,  0x55,
-  0x9A,  0x81,  0x03,  0xF7,  0x20,  0x00,  0x09,  0x6F,  0x93,  0x45,  0x55,  0xF0,  0xDE,  0x06,  0xB1,  0xF0,
-  0xBE,  0x06,  0x0A,  0xF0,  0xB6,  0x06,  0x09,  0xF0,  0x20,  0x09,  0x1E,  0xF0,  0xF8,  0x09,  0x00,  0xF0,
-  0xFE,  0x09,  0x00,  0xF2,  0x5C,  0x0B,  0x47,  0x10,  0x09,  0xE7,  0x08,  0x00,  0x41,  0x10,  0x05,  0x80,
-  0x48,  0xE4,  0x00,  0x00,  0x1E,  0x12,  0x00,  0xE6,  0x11,  0x00,  0x00,  0xEA,  0xB8,  0x00,  0x00,  0xF2,
-  0xB2,  0x10,  0x2C,  0x90,  0xAE,  0x90,  0x08,  0x50,  0x8A,  0x50,  0x38,  0x54,  0x1F,  0x40,  0x00,  0xF2,
-  0xB0,  0x0D,  0x08,  0x10,  0x08,  0x90,  0x8A,  0x90,  0x30,  0x50,  0xB2,  0x50,  0x9C,  0x32,  0x0C,  0x92,
-  0x8E,  0x92,  0x38,  0x54,  0x04,  0x80,  0x30,  0xE4,  0x08,  0x00,  0x04,  0x40,  0x0C,  0x1C,  0x00,  0xF6,
-  0x03,  0x00,  0xB1,  0xF0,  0x22,  0x07,  0x9E,  0xF0,  0x36,  0x07,  0x01,  0x48,  0x55,  0xF0,  0xF8,  0x09,
-  0x0C,  0x1C,  0x10,  0x44,  0xED,  0x10,  0x0B,  0xF0,  0x5A,  0x07,  0x0C,  0xF0,  0x5E,  0x07,  0x05,  0xF0,
-  0x4E,  0x07,  0x06,  0xF0,  0x54,  0x07,  0x09,  0xF0,  0x20,  0x09,  0x00,  0xF0,  0xFE,  0x09,  0x00,  0xF2,
-  0x5C,  0x0B,  0xCF,  0x10,  0x09,  0xE7,  0x08,  0x00,  0xC9,  0x10,  0x2E,  0x1C,  0x02,  0x10,  0x2C,  0x1C,
-  0xAA,  0xF0,  0x60,  0x07,  0xAC,  0xF0,  0x6E,  0x07,  0x40,  0x10,  0x34,  0x1C,  0xF3,  0x10,  0xAD,  0xF0,
-  0x78,  0x07,  0xC8,  0x10,  0x36,  0x1C,  0xE9,  0x10,  0x2B,  0xF0,  0x7E,  0x08,  0x6B,  0x18,  0x18,  0xF4,
-  0x00,  0xFE,  0x20,  0x12,  0x01,  0x58,  0xD2,  0xF0,  0x7E,  0x08,  0x76,  0x18,  0x18,  0xF4,  0x03,  0x00,
-  0xEC,  0x12,  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0xE2,  0x12,  0x0B,  0xF0,  0x60,  0x07,
-  0x0C,  0xF0,  0x60,  0x07,  0x36,  0x1C,  0x34,  0x1C,  0xB7,  0x10,  0x38,  0x54,  0xB9,  0x54,  0x84,  0x80,
-  0x19,  0xE4,  0x20,  0x00,  0xB2,  0x13,  0x85,  0x80,  0x81,  0x48,  0x66,  0x12,  0x04,  0x80,  0x18,  0xE4,
-  0x08,  0x00,  0x58,  0x13,  0x1F,  0x80,  0x08,  0x44,  0xC8,  0x44,  0x9F,  0x12,  0x1F,  0x40,  0x34,  0x91,
-  0xB6,  0x91,  0x44,  0x55,  0xE5,  0x55,  0x02,  0xEC,  0xB8,  0x00,  0x02,  0x49,  0xBB,  0x55,  0x82,  0x81,
-  0xC0,  0x55,  0x48,  0xF4,  0x0F,  0x00,  0x5A,  0xF0,  0x16,  0x08,  0x4A,  0xE4,  0x17,  0x00,  0xD5,  0xF0,
-  0xF6,  0x07,  0x02,  0xF6,  0x0F,  0x00,  0x02,  0xF4,  0x02,  0x00,  0x02,  0xEA,  0xB8,  0x00,  0x04,  0x91,
-  0x86,  0x91,  0x02,  0x4B,  0x2C,  0x90,  0x08,  0x50,  0x2E,  0x90,  0x0A,  0x50,  0x2C,  0x51,  0xAE,  0x51,
-  0x00,  0xF2,  0xB2,  0x10,  0x38,  0x54,  0x00,  0xF2,  0xB0,  0x0D,  0x56,  0x10,  0x34,  0x91,  0xB6,  0x91,
-  0x0C,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x08,  0x00,  0x41,  0x12,  0x0C,  0x91,  0x8E,  0x91,  0x04,  0x80,
-  0x18,  0xE4,  0xF7,  0x00,  0x04,  0x40,  0x30,  0x90,  0xB2,  0x90,  0x36,  0x10,  0x02,  0x80,  0x48,  0xE4,
-  0x10,  0x00,  0x31,  0x12,  0x82,  0xE7,  0x10,  0x00,  0x84,  0x80,  0x19,  0xE4,  0x20,  0x00,  0x10,  0x13,
-  0x0C,  0x90,  0x8E,  0x90,  0x5D,  0xF0,  0x74,  0x07,  0x0C,  0x58,  0x8D,  0x58,  0x00,  0xF0,  0x60,  0x07,
-  0x38,  0x54,  0xB9,  0x54,  0x19,  0x80,  0xF1,  0x10,  0x3A,  0x55,  0x19,  0x81,  0xBB,  0x55,  0x10,  0x90,
-  0x92,  0x90,  0x10,  0x58,  0x91,  0x58,  0x14,  0x59,  0x95,  0x59,  0x00,  0xF0,  0x60,  0x07,  0x04,  0x80,
-  0x18,  0xE4,  0x20,  0x00,  0x06,  0x12,  0x6C,  0x19,  0x19,  0x41,  0x7C,  0x10,  0x6C,  0x19,  0x0C,  0x51,
-  0xED,  0x19,  0x8E,  0x51,  0x6B,  0x18,  0x18,  0xF4,  0x00,  0xFF,  0x02,  0x13,  0x6A,  0x10,  0x01,  0x58,
-  0xD2,  0xF0,  0xBC,  0x08,  0x76,  0x18,  0x18,  0xF4,  0x03,  0x00,  0x0A,  0x12,  0x00,  0xFC,  0x22,  0x01,
-  0x18,  0xF4,  0x01,  0x00,  0x06,  0x13,  0x9E,  0xE7,  0x16,  0x00,  0x4C,  0x10,  0xD1,  0xF0,  0xC6,  0x08,
-  0x9E,  0xE7,  0x17,  0x00,  0x42,  0x10,  0xD0,  0xF0,  0xD0,  0x08,  0x9E,  0xE7,  0x19,  0x00,  0x38,  0x10,
-  0xCF,  0xF0,  0xDA,  0x08,  0x9E,  0xE7,  0x20,  0x00,  0x2E,  0x10,  0xCE,  0xF0,  0xE4,  0x08,  0x9E,  0xE7,
-  0x21,  0x00,  0x24,  0x10,  0xCD,  0xF0,  0xEE,  0x08,  0x9E,  0xE7,  0x22,  0x00,  0x1A,  0x10,  0xCC,  0xF0,
-  0x00,  0x09,  0x84,  0x80,  0x19,  0xE4,  0x04,  0x00,  0x06,  0x12,  0x9E,  0xE7,  0x12,  0x00,  0x08,  0x10,
-  0xCB,  0xF0,  0x08,  0x09,  0x9E,  0xE7,  0x24,  0x00,  0xB1,  0xF0,  0x08,  0x09,  0x05,  0xF0,  0x1A,  0x09,
-  0x09,  0xF0,  0x20,  0x09,  0x1E,  0xF0,  0xF8,  0x09,  0xE4,  0x10,  0x00,  0xF2,  0x5C,  0x0B,  0xE9,  0x10,
-  0x9C,  0x32,  0x82,  0xE7,  0x20,  0x00,  0x32,  0x1C,  0xE9,  0x09,  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,
-  0xFE,  0x09,  0x69,  0x08,  0x01,  0xF0,  0x40,  0x09,  0x1E,  0xF0,  0xF8,  0x09,  0x00,  0xF0,  0x34,  0x09,
-  0x30,  0x44,  0x06,  0x12,  0x9E,  0xE7,  0x42,  0x00,  0xB8,  0x10,  0x04,  0xF6,  0x01,  0x00,  0xB3,  0x45,
-  0x74,  0x12,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x22,  0x13,  0x4B,  0xE4,  0x02,  0x00,  0x36,  0x12,
-  0x4B,  0xE4,  0x28,  0x00,  0xAC,  0x13,  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,  0xC4,  0x11,  0x03,  0xF6,
-  0xD0,  0x00,  0xFA,  0x14,  0x82,  0xE7,  0x01,  0x00,  0x00,  0xF0,  0x80,  0x01,  0x9E,  0xE7,  0x44,  0x00,
-  0x4B,  0xE4,  0x02,  0x00,  0x06,  0x12,  0x03,  0xE6,  0x02,  0x00,  0x76,  0x10,  0x00,  0xF2,  0x9E,  0x0D,
-  0x03,  0xE6,  0x02,  0x00,  0x6C,  0x10,  0x00,  0xF2,  0x9E,  0x0D,  0x19,  0x82,  0x34,  0x46,  0x0A,  0x13,
-  0x03,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x43,  0x00,  0x68,  0x10,  0x04,  0x80,  0x30,  0xE4,  0x20,  0x00,
-  0x04,  0x40,  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,  0xC4,  0x11,  0x82,  0xE7,  0x01,  0x00,  0x06,  0xF7,
-  0x02,  0x00,  0x00,  0xF0,  0x08,  0x03,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x06,  0x12,  0x03,  0xE6,
-  0x02,  0x00,  0x3E,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x3A,  0x12,  0x04,  0x80,  0x18,  0xE4,
-  0xFD,  0x00,  0x04,  0x40,  0x1C,  0x1C,  0x9D,  0xF0,  0xE6,  0x09,  0x1C,  0x1C,  0x9D,  0xF0,  0xEC,  0x09,
-  0xC1,  0x10,  0x9E,  0xE7,  0x13,  0x00,  0x0A,  0x10,  0x9E,  0xE7,  0x41,  0x00,  0x04,  0x10,  0x9E,  0xE7,
-  0x24,  0x00,  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0xD5,  0xF0,  0x8A,  0x02,  0x04,  0xE6,  0x04,  0x00,
-  0x06,  0x10,  0x04,  0xE6,  0x04,  0x00,  0x9D,  0x41,  0x1C,  0x42,  0x9F,  0xE7,  0x00,  0x00,  0x06,  0xF7,
-  0x02,  0x00,  0x03,  0xF6,  0xE0,  0x00,  0x3C,  0x14,  0x44,  0x58,  0x45,  0x58,  0x00,  0xF2,  0xF2,  0x0D,
-  0x00,  0xF2,  0x7A,  0x10,  0x00,  0xF2,  0xC2,  0x0F,  0x3C,  0x14,  0x1E,  0x1C,  0x00,  0xF0,  0x80,  0x01,
-  0x12,  0x1C,  0x22,  0x1C,  0xD2,  0x14,  0x00,  0xF0,  0x72,  0x01,  0x83,  0x59,  0x03,  0xDC,  0x73,  0x57,
-  0x80,  0x5D,  0x00,  0x16,  0x83,  0x59,  0x03,  0xDC,  0x38,  0x54,  0x70,  0x57,  0x33,  0x54,  0x3B,  0x54,
-  0x80,  0x5D,  0x00,  0x16,  0x03,  0x57,  0x83,  0x59,  0x38,  0x54,  0x00,  0xCC,  0x00,  0x16,  0x03,  0x57,
-  0x83,  0x59,  0x00,  0x4C,  0x00,  0x16,  0x02,  0x80,  0x48,  0xE4,  0x01,  0x00,  0x0E,  0x12,  0x48,  0xE4,
-  0x05,  0x00,  0x08,  0x12,  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,  0xC4,  0x11,  0xC1,  0x5A,  0x3A,  0x55,
-  0x02,  0xEC,  0xB5,  0x00,  0x45,  0x59,  0x00,  0xF2,  0xF2,  0x0D,  0x83,  0x58,  0x30,  0xE7,  0x00,  0x00,
-  0x10,  0x4D,  0x30,  0xE7,  0x40,  0x00,  0x10,  0x4F,  0x38,  0x90,  0xBA,  0x90,  0x10,  0x5C,  0x80,  0x5C,
-  0x83,  0x5A,  0x10,  0x4E,  0x04,  0xEA,  0xB5,  0x00,  0x43,  0x5B,  0x03,  0xF4,  0xE0,  0x00,  0x83,  0x59,
-  0x04,  0xCC,  0x01,  0x4A,  0x0A,  0x12,  0x45,  0x5A,  0x00,  0xF2,  0xF2,  0x0D,  0x00,  0xF2,  0x34,  0x10,
-  0x00,  0x16,  0x08,  0x1C,  0x00,  0xFC,  0xAC,  0x00,  0x06,  0x58,  0x67,  0x18,  0x18,  0xF4,  0x8F,  0xE1,
-  0x01,  0xFC,  0xAE,  0x00,  0x19,  0xF4,  0x70,  0x1E,  0xB0,  0x54,  0x07,  0x58,  0x00,  0xFC,  0xB0,  0x00,
-  0x08,  0x58,  0x00,  0xFC,  0xB2,  0x00,  0x09,  0x58,  0x0A,  0x1C,  0x00,  0xE6,  0x0F,  0x00,  0x00,  0xEA,
-  0xB9,  0x00,  0x38,  0x54,  0x00,  0xFA,  0x24,  0x01,  0x00,  0xFA,  0xB6,  0x00,  0x18,  0x1C,  0x14,  0x1C,
-  0x10,  0x1C,  0x32,  0x1C,  0x12,  0x1C,  0x00,  0x16,  0x3E,  0x57,  0x0C,  0x14,  0x0E,  0x47,  0x07,  0xE6,
-  0x10,  0x00,  0xCE,  0x47,  0xF5,  0x13,  0x00,  0x16,  0x00,  0xF2,  0x9E,  0x0D,  0x02,  0x4B,  0x03,  0xF6,
-  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x01,  0x48,  0x20,  0x12,  0x44,  0x58,  0x45,  0x58,  0x9E,  0xE7,
-  0x15,  0x00,  0x9C,  0xE7,  0x04,  0x00,  0x00,  0xF2,  0xF2,  0x0D,  0x00,  0xF2,  0x7A,  0x10,  0x00,  0xF2,
-  0xC2,  0x0F,  0x00,  0xF2,  0x76,  0x0A,  0x1E,  0x1C,  0xD5,  0x10,  0x00,  0x16,  0x69,  0x08,  0x48,  0xE4,
-  0x04,  0x00,  0x64,  0x12,  0x48,  0xE4,  0x02,  0x00,  0x20,  0x12,  0x48,  0xE4,  0x03,  0x00,  0x1A,  0x12,
-  0x48,  0xE4,  0x08,  0x00,  0x14,  0x12,  0x48,  0xE4,  0x01,  0x00,  0xF0,  0x12,  0x48,  0xE4,  0x07,  0x00,
-  0x12,  0x12,  0x01,  0xE6,  0x07,  0x00,  0x00,  0xF2,  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,  0x05,  0xF0,
-  0x5C,  0x0B,  0x00,  0x16,  0x00,  0xE6,  0x01,  0x00,  0x00,  0xEA,  0x99,  0x00,  0x02,  0x80,  0x48,  0xE4,
-  0x03,  0x00,  0xB9,  0x12,  0x48,  0xE4,  0x06,  0x00,  0xB3,  0x12,  0x01,  0xE6,  0x06,  0x00,  0x00,  0xF2,
-  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,  0x04,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,  0x01,  0xF0,
-  0x18,  0x0A,  0x00,  0xF0,  0xFE,  0x09,  0x00,  0x16,  0x02,  0x80,  0x48,  0xE4,  0x10,  0x00,  0x1C,  0x12,
-  0x82,  0xE7,  0x08,  0x00,  0x3C,  0x56,  0x03,  0x82,  0x00,  0xF2,  0xDE,  0x0D,  0x30,  0xE7,  0x08,  0x00,
-  0x04,  0xF7,  0x70,  0x01,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF0,  0x80,  0x01,  0x6C,  0x19,  0xED,  0x19,
-  0x5D,  0xF0,  0xD0,  0x0B,  0x44,  0x55,  0xE5,  0x55,  0x59,  0xF0,  0x4E,  0x0C,  0x04,  0x55,  0xA5,  0x55,
-  0x1F,  0x80,  0x01,  0xEC,  0xB8,  0x00,  0x82,  0x48,  0x82,  0x80,  0x49,  0x44,  0x2E,  0x13,  0x01,  0xEC,
-  0xB8,  0x00,  0x41,  0xE4,  0x02,  0x00,  0x01,  0xEA,  0xB8,  0x00,  0x49,  0xE4,  0x11,  0x00,  0x59,  0xF0,
-  0x2A,  0x0C,  0x01,  0xE6,  0x17,  0x00,  0x01,  0xEA,  0xB8,  0x00,  0x02,  0x4B,  0x88,  0x90,  0xAC,  0x50,
-  0x8A,  0x90,  0xAE,  0x50,  0x01,  0xEC,  0xB8,  0x00,  0x82,  0x48,  0x82,  0x80,  0x10,  0x44,  0x02,  0x4B,
-  0x1F,  0x40,  0xC0,  0x44,  0x00,  0xF2,  0xB0,  0x0D,  0x04,  0x55,  0xA5,  0x55,  0x9F,  0x10,  0x0C,  0x51,
-  0x8E,  0x51,  0x30,  0x90,  0xB2,  0x90,  0x00,  0x56,  0xA1,  0x56,  0x30,  0x50,  0xB2,  0x50,  0x34,  0x90,
-  0xB6,  0x90,  0x40,  0x56,  0xE1,  0x56,  0x34,  0x50,  0xB6,  0x50,  0x65,  0x10,  0xB1,  0xF0,  0x6C,  0x0C,
-  0x85,  0xF0,  0xC6,  0x0B,  0xE9,  0x09,  0x4B,  0xE4,  0x03,  0x00,  0x78,  0x12,  0x4B,  0xE4,  0x02,  0x00,
-  0x01,  0x13,  0xB1,  0xF0,  0x82,  0x0C,  0x85,  0xF0,  0xC6,  0x0B,  0x69,  0x08,  0x48,  0xE4,  0x03,  0x00,
-  0xD5,  0xF0,  0x82,  0x0B,  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,  0xC6,  0x0B,  0xE8,  0x09,  0x3C,  0x56,
-  0x00,  0xFC,  0x20,  0x01,  0x98,  0x57,  0x02,  0x13,  0xBB,  0x45,  0x4B,  0xE4,  0x00,  0x00,  0x08,  0x12,
-  0x03,  0xE6,  0x01,  0x00,  0x04,  0xF6,  0x00,  0x80,  0xA8,  0x14,  0xD2,  0x14,  0x30,  0x1C,  0x02,  0x80,
-  0x48,  0xE4,  0x03,  0x00,  0x10,  0x13,  0x00,  0xFC,  0xB6,  0x00,  0x98,  0x57,  0x02,  0x13,  0x4C,  0x1C,
-  0x3E,  0x1C,  0x00,  0xF0,  0x8A,  0x0B,  0x00,  0xFC,  0x24,  0x01,  0xB0,  0x57,  0x00,  0xFA,  0x24,  0x01,
-  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0x8A,  0x0B,  0x00,  0xF2,  0x88,  0x0E,
-  0x00,  0xF0,  0x8A,  0x0B,  0xB1,  0xF0,  0xF4,  0x0C,  0x85,  0xF0,  0x82,  0x0B,  0x69,  0x08,  0x48,  0xE4,
-  0x01,  0x00,  0xD5,  0xF0,  0x82,  0x0B,  0xFC,  0x14,  0x42,  0x58,  0x6C,  0x14,  0x80,  0x14,  0x30,  0x1C,
-  0x4A,  0xF4,  0x02,  0x00,  0x55,  0xF0,  0x82,  0x0B,  0x4A,  0xF4,  0x01,  0x00,  0x0E,  0x12,  0x02,  0x80,
-  0x48,  0xE4,  0x03,  0x00,  0x06,  0x13,  0x3E,  0x1C,  0x00,  0xF0,  0x8A,  0x0B,  0x00,  0xFC,  0xB6,  0x00,
-  0xB0,  0x57,  0x00,  0xFA,  0xB6,  0x00,  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,
-  0x8A,  0x0B,  0x00,  0xF2,  0xB6,  0x0E,  0x00,  0xF0,  0x8A,  0x0B,  0x4C,  0x1C,  0xB1,  0xF0,  0x4C,  0x0D,
-  0x85,  0xF0,  0x58,  0x0D,  0x69,  0x08,  0xF3,  0x10,  0x86,  0xF0,  0x60,  0x0D,  0x4E,  0x1C,  0x89,  0x48,
-  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,
-  0xFF,  0x7F,  0x30,  0x56,  0x00,  0x5C,  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,
-  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,  0x00,  0x80,  0x30,  0x56,  0x00,  0x5C,  0x00,  0x16,  0x00,  0xF6,
-  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x0B,  0x58,  0x00,  0x16,  0x03,  0xF6,
-  0x24,  0x01,  0x00,  0xF2,  0x54,  0x0A,  0x03,  0xF6,  0xB6,  0x00,  0x00,  0xF2,  0x54,  0x0A,  0x00,  0x16,
-  0x02,  0xEC,  0xB8,  0x00,  0x02,  0x49,  0x18,  0xF4,  0xFF,  0x00,  0x00,  0x54,  0x00,  0x54,  0x00,  0x54,
-  0x00,  0xF4,  0x08,  0x00,  0xE1,  0x18,  0x80,  0x54,  0x03,  0x58,  0x00,  0xDD,  0x01,  0xDD,  0x02,  0xDD,
-  0x03,  0xDC,  0x02,  0x4B,  0x30,  0x50,  0xB2,  0x50,  0x34,  0x51,  0xB6,  0x51,  0x00,  0x16,  0x45,  0x5A,
-  0x1D,  0xF4,  0xFF,  0x00,  0x85,  0x56,  0x85,  0x56,  0x85,  0x56,  0x05,  0xF4,  0x02,  0x12,  0x83,  0x5A,
-  0x00,  0x16,  0x1D,  0xF4,  0xFF,  0x00,  0x85,  0x56,  0x85,  0x56,  0x85,  0x56,  0x05,  0xF4,  0x00,  0x12,
-  0x83,  0x5A,  0x00,  0x16,  0x38,  0x54,  0xBB,  0x55,  0x3C,  0x56,  0xBD,  0x56,  0x00,  0xF2,  0x0E,  0x11,
-  0x85,  0xF0,  0x7E,  0x0E,  0xE9,  0x09,  0xC1,  0x59,  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,  0x7E,  0x0E,
-  0xE8,  0x0A,  0x83,  0x55,  0x83,  0x55,  0x4B,  0xF4,  0x90,  0x01,  0x5C,  0xF0,  0x32,  0x0E,  0xBD,  0x56,
-  0x40,  0x10,  0x4B,  0xF4,  0x30,  0x00,  0x59,  0xF0,  0x44,  0x0E,  0x01,  0xF6,  0x0C,  0x00,  0x00,  0xF6,
-  0x01,  0x00,  0x2E,  0x10,  0x02,  0xFC,  0x9C,  0x00,  0x9A,  0x57,  0x14,  0x13,  0x4B,  0xF4,  0x64,  0x00,
-  0x59,  0xF0,  0x60,  0x0E,  0x03,  0xF6,  0x64,  0x00,  0x01,  0xF6,  0x19,  0x00,  0x00,  0xF6,  0x01,  0x00,
-  0x43,  0xF4,  0x33,  0x00,  0x56,  0xF0,  0x72,  0x0E,  0x04,  0xF4,  0x00,  0x01,  0x43,  0xF4,  0x19,  0x00,
-  0xF3,  0x10,  0xB4,  0x56,  0xC3,  0x58,  0x02,  0xFC,  0x9E,  0x00,  0x9A,  0x57,  0x08,  0x13,  0x3C,  0x56,
-  0x00,  0xF6,  0x02,  0x00,  0x00,  0x16,  0x00,  0x16,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,  0x0E,  0x11,
-  0x86,  0xF0,  0xB4,  0x0E,  0x09,  0xE7,  0x02,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0xB4,  0x0E,
-  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0xB4,  0x0E,  0x4E,  0x1C,  0x89,  0x49,
-  0x00,  0xF2,  0x0E,  0x11,  0x00,  0x16,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,
-  0xEE,  0x0E,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0xEE,  0x0E,  0x09,  0xE7,
-  0x01,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0xEE,  0x0E,  0x89,  0x49,  0x00,  0xF2,  0x0E,  0x11,
-  0x86,  0xF0,  0xEE,  0x0E,  0x4E,  0x1C,  0x89,  0x4A,  0x00,  0xF2,  0x0E,  0x11,  0x00,  0x16,  0x3C,  0x56,
-  0x00,  0x16,  0x00,  0xEC,  0x26,  0x01,  0x48,  0xE4,  0x01,  0x00,  0x1E,  0x13,  0x38,  0x44,  0x00,  0xEA,
-  0x26,  0x01,  0x49,  0xF4,  0x00,  0x00,  0x04,  0x12,  0x4E,  0x1C,  0x02,  0x10,  0x4C,  0x1C,  0x01,  0xEC,
-  0x27,  0x01,  0x89,  0x48,  0x00,  0xF2,  0x0E,  0x11,  0x02,  0x14,  0x00,  0x16,  0x85,  0xF0,  0x4E,  0x0F,
-  0x38,  0x54,  0x00,  0xEA,  0x99,  0x00,  0x00,  0xF2,  0x5C,  0x0B,  0x02,  0x80,  0x48,  0xE4,  0x06,  0x00,
-  0x1C,  0x13,  0x00,  0xEC,  0x99,  0x00,  0x48,  0xE4,  0x01,  0x00,  0x0A,  0x12,  0x04,  0x80,  0x30,  0xE4,
-  0x01,  0x00,  0x04,  0x40,  0x08,  0x10,  0x04,  0x80,  0x18,  0xE4,  0xFE,  0x00,  0x04,  0x40,  0x00,  0x16,
-  0x02,  0xF6,  0xE0,  0x00,  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x81,  0x48,  0x22,  0x12,  0x00,  0x4E,
-  0x83,  0x5A,  0x90,  0x4C,  0x20,  0xE7,  0x00,  0x00,  0xC3,  0x58,  0x1B,  0xF4,  0xFF,  0x00,  0x83,  0x55,
-  0x83,  0x55,  0x83,  0x55,  0x03,  0xF4,  0x00,  0x12,  0x8B,  0x55,  0x83,  0x59,  0x00,  0x4E,  0x00,  0x16,
-  0x00,  0x4E,  0x02,  0xF6,  0xF0,  0x00,  0x02,  0x57,  0x03,  0x59,  0x00,  0x4E,  0x83,  0x5A,  0x30,  0xE7,
-  0x00,  0x00,  0x20,  0xE7,  0x00,  0x00,  0x00,  0x16,  0x02,  0xF6,  0xF0,  0x00,  0x02,  0x57,  0x03,  0x59,
-  0x01,  0xCC,  0x00,  0x4E,  0x83,  0x5A,  0x30,  0xE7,  0x00,  0x00,  0x80,  0x4C,  0xC3,  0x58,  0x1B,  0xF4,
-  0xFF,  0x00,  0x83,  0x55,  0x83,  0x55,  0x83,  0x55,  0x03,  0xF4,  0x00,  0x12,  0x83,  0x59,  0x00,  0x4E,
-  0x00,  0x16,  0x03,  0xF6,  0xE0,  0x00,  0x03,  0x57,  0x83,  0x59,  0x3A,  0x55,  0x02,  0xCC,  0x45,  0x5A,
-  0x00,  0xF2,  0xF2,  0x0D,  0xC0,  0x5A,  0x40,  0x5C,  0x38,  0x54,  0x00,  0xCD,  0x01,  0xCC,  0x4A,  0x46,
-  0x0A,  0x13,  0x83,  0x59,  0x00,  0x4C,  0x01,  0x48,  0x16,  0x13,  0x0C,  0x10,  0xC5,  0x58,  0x00,  0xF2,
-  0xF2,  0x0D,  0x00,  0x4C,  0x01,  0x48,  0x08,  0x13,  0x05,  0xF6,  0xF0,  0x00,  0x05,  0x57,  0x08,  0x10,
-  0x45,  0x58,  0x00,  0xF2,  0xF2,  0x0D,  0x8D,  0x56,  0x83,  0x5A,  0x80,  0x4C,  0x05,  0x17,  0x00,  0x16,
-  0x02,  0x4B,  0x06,  0xF7,  0x04,  0x00,  0x62,  0x0B,  0x03,  0x82,  0x00,  0xF2,  0xDE,  0x0D,  0x02,  0x80,
-  0x00,  0x4C,  0x45,  0xF4,  0x02,  0x00,  0x52,  0x14,  0x06,  0xF7,  0x02,  0x00,  0x06,  0x14,  0x00,  0xF2,
-  0x50,  0x0F,  0x00,  0x16,  0x02,  0x4B,  0x01,  0xF6,  0xFF,  0x00,  0x38,  0x1C,  0x05,  0xF4,  0x04,  0x00,
-  0x83,  0x5A,  0x18,  0xDF,  0x19,  0xDF,  0x1D,  0xF7,  0x3C,  0x00,  0xB8,  0xF0,  0x4A,  0x10,  0x9C,  0x14,
-  0x01,  0x48,  0x1C,  0x13,  0x0E,  0xF7,  0x3C,  0x00,  0x03,  0xF7,  0x04,  0x00,  0xAF,  0x19,  0x03,  0x42,
-  0x45,  0xF4,  0x02,  0x00,  0x83,  0x5A,  0x02,  0xCC,  0x02,  0x41,  0x45,  0xF4,  0x02,  0x00,  0x00,  0x16,
-  0x91,  0x44,  0xD5,  0xF0,  0x3A,  0x10,  0x00,  0xF0,  0x9E,  0x02,  0x01,  0xF6,  0xFF,  0x00,  0x38,  0x1C,
-  0x05,  0xF4,  0x04,  0x00,  0x83,  0x5A,  0x18,  0xDF,  0x19,  0xDF,  0x0E,  0xF7,  0x3C,  0x00,  0x03,  0xF7,
-  0x04,  0x00,  0x0F,  0x79,  0x1C,  0xF7,  0x3C,  0x00,  0xB8,  0xF0,  0x98,  0x10,  0x4E,  0x14,  0x01,  0x48,
-  0x06,  0x13,  0x45,  0xF4,  0x04,  0x00,  0x00,  0x16,  0x91,  0x44,  0xD5,  0xF0,  0x7E,  0x10,  0x00,  0xF0,
-  0x9E,  0x02,  0x02,  0xF6,  0xFF,  0x00,  0x38,  0x1C,  0x2C,  0xBC,  0xAE,  0xBC,  0xE2,  0x08,  0x00,  0xEC,
-  0xB8,  0x00,  0x02,  0x48,  0x1D,  0xF7,  0x80,  0x00,  0xB8,  0xF0,  0xC8,  0x10,  0x1E,  0x14,  0x01,  0x48,
-  0x0E,  0x13,  0x0E,  0xF7,  0x80,  0x00,  0x38,  0x54,  0x03,  0x58,  0xAF,  0x19,  0x82,  0x48,  0x00,  0x16,
-  0x82,  0x48,  0x12,  0x45,  0xD5,  0xF0,  0xB6,  0x10,  0x00,  0xF0,  0x9E,  0x02,  0x39,  0xF0,  0xF4,  0x10,
-  0x38,  0x44,  0x00,  0x16,  0x7E,  0x18,  0x18,  0xF4,  0x03,  0x00,  0x04,  0x13,  0x61,  0x18,  0x00,  0x16,
-  0x38,  0x1C,  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0xF1,  0x12,  0xE3,  0x10,  0x30,  0x44,
-  0x30,  0x44,  0x30,  0x44,  0xB1,  0xF0,  0x14,  0x11,  0x00,  0x16,  0x3E,  0x57,  0x03,  0xF6,  0xE0,  0x00,
-  0x03,  0x57,  0x83,  0x59,  0x04,  0xCC,  0x01,  0x4A,  0x6A,  0x12,  0x45,  0x5A,  0x00,  0xF2,  0xF2,  0x0D,
-  0x02,  0x4B,  0x70,  0x14,  0x34,  0x13,  0x02,  0x80,  0x48,  0xE4,  0x08,  0x00,  0x18,  0x12,  0x9C,  0xE7,
-  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,  0x00,  0xF2,  0xC2,  0x0F,  0x00,  0xF2,  0x76,  0x0A,  0x1E,  0x1C,
-  0x01,  0xF6,  0x01,  0x00,  0x00,  0x16,  0x30,  0xE4,  0x10,  0x00,  0x04,  0x40,  0x00,  0xF2,  0xDE,  0x0D,
-  0x20,  0xE7,  0x01,  0x00,  0x01,  0xF6,  0x01,  0x00,  0x00,  0x16,  0x04,  0xDC,  0x01,  0x4A,  0x24,  0x12,
-  0x45,  0x5A,  0x00,  0xF2,  0xF2,  0x0D,  0x43,  0x5B,  0x06,  0xEC,  0x98,  0x00,  0x00,  0xF2,  0x34,  0x10,
-  0xC6,  0x59,  0x20,  0x14,  0x0A,  0x13,  0x00,  0xF2,  0xC2,  0x0F,  0x00,  0xF2,  0x10,  0x10,  0xA7,  0x10,
-  0x83,  0x5A,  0xD7,  0x10,  0x0E,  0x47,  0x07,  0xE6,  0x10,  0x00,  0xCE,  0x47,  0x5A,  0xF0,  0x1C,  0x11,
-  0xB9,  0x54,  0x00,  0x16,  0x14,  0x90,  0x96,  0x90,  0x02,  0xFC,  0xA8,  0x00,  0x03,  0xFC,  0xAA,  0x00,
-  0x48,  0x55,  0x02,  0x13,  0xC9,  0x55,  0x00,  0x16,  0x00,  0xEC,  0xBA,  0x00,  0x10,  0x44,  0x00,  0xEA,
-  0xBA,  0x00,  0x00,  0x16,  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x10,  0x44,  0x00,  0x4C,
-  0x00,  0x16 };
+  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x08,  0x44,  0x00,  0x4C,  0x82,  0xE7,  0x02,  0x00,
+  0x00,  0xF2,  0x12,  0x11,  0x00,  0xF2,  0x12,  0x11,  0x85,  0xF0,  0x70,  0x03,  0x00,  0xF2,  0x60,  0x0B,
+  0x06,  0xF0,  0x80,  0x03,  0x09,  0xF0,  0x24,  0x09,  0x1E,  0xF0,  0xFC,  0x09,  0x00,  0xF0,  0x02,  0x0A,
+  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0x55,  0xF0,  0xAC,  0x04,  0x01,  0xE6,  0x0C,  0x00,  0x00,  0xF2,
+  0x4E,  0x0D,  0x00,  0xF2,  0x12,  0x11,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,  0xC8,  0x11,  0x01,  0xF0,
+  0x7C,  0x02,  0x00,  0xF0,  0x8A,  0x02,  0x46,  0x1C,  0x0C,  0x1C,  0x67,  0x1B,  0xBF,  0x57,  0x77,  0x57,
+  0x02,  0x4B,  0x48,  0x1C,  0x32,  0x1C,  0x00,  0xF2,  0x92,  0x0D,  0x30,  0x1C,  0x96,  0xF0,  0xBC,  0x03,
+  0xB1,  0xF0,  0xC0,  0x03,  0x1E,  0xF0,  0xFC,  0x09,  0x85,  0xF0,  0x02,  0x0A,  0x00,  0xFC,  0xBE,  0x00,
+  0x98,  0x57,  0x14,  0x12,  0x01,  0xE6,  0x0C,  0x00,  0x00,  0xF2,  0x4E,  0x0D,  0x00,  0xF2,  0x12,  0x11,
+  0x01,  0xF0,  0x7C,  0x02,  0x00,  0xF0,  0x8A,  0x02,  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,
+  0x01,  0x48,  0x55,  0xF0,  0x98,  0x04,  0x03,  0x82,  0x03,  0xFC,  0xA0,  0x00,  0x9B,  0x57,  0x40,  0x12,
+  0x69,  0x18,  0x00,  0xF2,  0x12,  0x11,  0x85,  0xF0,  0x42,  0x04,  0x69,  0x08,  0x00,  0xF2,  0x12,  0x11,
+  0x85,  0xF0,  0x02,  0x0A,  0x68,  0x08,  0x4C,  0x44,  0x28,  0x12,  0x44,  0x48,  0x03,  0xF6,  0xE0,  0x00,
+  0x00,  0xF2,  0x68,  0x0A,  0x45,  0x58,  0x00,  0xF2,  0xF6,  0x0D,  0x00,  0xCC,  0x01,  0x48,  0x55,  0xF0,
+  0x98,  0x04,  0x4C,  0x44,  0xEF,  0x13,  0x00,  0xF2,  0xC6,  0x0F,  0x00,  0xF2,  0x14,  0x10,  0x08,  0x10,
+  0x68,  0x18,  0x45,  0x5A,  0x00,  0xF2,  0xF6,  0x0D,  0x04,  0x80,  0x18,  0xE4,  0x10,  0x00,  0x28,  0x12,
+  0x01,  0xE6,  0x06,  0x00,  0x04,  0x80,  0x18,  0xE4,  0x01,  0x00,  0x04,  0x12,  0x01,  0xE6,  0x0D,  0x00,
+  0x00,  0xF2,  0x4E,  0x0D,  0x00,  0xF2,  0x12,  0x11,  0x04,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,
+  0x01,  0xF0,  0x1C,  0x0A,  0x00,  0xF0,  0x02,  0x0A,  0x69,  0x08,  0x05,  0x80,  0x48,  0xE4,  0x00,  0x00,
+  0x0C,  0x12,  0x00,  0xE6,  0x11,  0x00,  0x00,  0xEA,  0xB8,  0x00,  0x00,  0xF2,  0xB6,  0x10,  0x82,  0xE7,
+  0x02,  0x00,  0x1C,  0x90,  0x40,  0x5C,  0x00,  0x16,  0x01,  0xE6,  0x06,  0x00,  0x00,  0xF2,  0x4E,  0x0D,
+  0x01,  0xF0,  0x80,  0x01,  0x1E,  0xF0,  0x80,  0x01,  0x00,  0xF0,  0xA0,  0x04,  0x42,  0x5B,  0x06,  0xF7,
+  0x03,  0x00,  0x46,  0x59,  0xBF,  0x57,  0x77,  0x57,  0x01,  0xE6,  0x80,  0x00,  0x07,  0x80,  0x31,  0x44,
+  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x56,  0x13,  0x20,  0x80,  0x48,  0xE4,  0x03,  0x00,  0x4E,  0x12,
+  0x00,  0xFC,  0xA2,  0x00,  0x98,  0x57,  0x55,  0xF0,  0x1C,  0x05,  0x31,  0xE4,  0x40,  0x00,  0x00,  0xFC,
+  0xA0,  0x00,  0x98,  0x57,  0x36,  0x12,  0x4C,  0x1C,  0x00,  0xF2,  0x12,  0x11,  0x89,  0x48,  0x00,  0xF2,
+  0x12,  0x11,  0x86,  0xF0,  0x2E,  0x05,  0x82,  0xE7,  0x06,  0x00,  0x1B,  0x80,  0x48,  0xE4,  0x22,  0x00,
+  0x5B,  0xF0,  0x0C,  0x05,  0x48,  0xE4,  0x20,  0x00,  0x59,  0xF0,  0x10,  0x05,  0x00,  0xE6,  0x20,  0x00,
+  0x09,  0x48,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0x2E,  0x05,  0x83,  0x80,  0x04,  0x10,  0x00,  0xF2,
+  0xA2,  0x0D,  0x00,  0xE6,  0x01,  0x00,  0x00,  0xEA,  0x26,  0x01,  0x01,  0xEA,  0x27,  0x01,  0x04,  0x80,
+  0x18,  0xE4,  0x10,  0x00,  0x36,  0x12,  0xB9,  0x54,  0x00,  0xF2,  0xF6,  0x0E,  0x01,  0xE6,  0x06,  0x00,
+  0x04,  0x80,  0x18,  0xE4,  0x01,  0x00,  0x04,  0x12,  0x01,  0xE6,  0x0D,  0x00,  0x00,  0xF2,  0x4E,  0x0D,
+  0x00,  0xF2,  0x12,  0x11,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,  0xC8,  0x11,  0x04,  0xE6,  0x02,  0x00,
+  0x9E,  0xE7,  0x15,  0x00,  0x01,  0xF0,  0x1C,  0x0A,  0x00,  0xF0,  0x02,  0x0A,  0x00,  0xFC,  0x20,  0x01,
+  0x98,  0x57,  0x34,  0x12,  0x00,  0xFC,  0x24,  0x01,  0x98,  0x57,  0x2C,  0x13,  0xB9,  0x54,  0x00,  0xF2,
+  0xF6,  0x0E,  0x86,  0xF0,  0xA8,  0x05,  0x03,  0xF6,  0x01,  0x00,  0x00,  0xF2,  0x8C,  0x0E,  0x85,  0xF0,
+  0x9E,  0x05,  0x82,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x60,  0x0B,  0x82,  0xE7,  0x02,  0x00,  0x00,  0xFC,
+  0x24,  0x01,  0xB0,  0x57,  0x00,  0xFA,  0x24,  0x01,  0x00,  0xFC,  0x9E,  0x00,  0x98,  0x57,  0x5A,  0x12,
+  0x00,  0xFC,  0xB6,  0x00,  0x98,  0x57,  0x52,  0x13,  0x03,  0xE6,  0x0C,  0x00,  0x00,  0xFC,  0x9C,  0x00,
+  0x98,  0x57,  0x04,  0x13,  0x03,  0xE6,  0x19,  0x00,  0x05,  0xE6,  0x08,  0x00,  0x00,  0xF6,  0x00,  0x01,
+  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,  0x00,  0x80,  0x04,  0x13,  0x05,  0xE6,
+  0x0F,  0x00,  0xB9,  0x54,  0x00,  0xF2,  0xF6,  0x0E,  0x86,  0xF0,  0x0A,  0x06,  0x00,  0xF2,  0xBA,  0x0E,
+  0x85,  0xF0,  0x00,  0x06,  0x82,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x60,  0x0B,  0x82,  0xE7,  0x02,  0x00,
+  0x00,  0xFC,  0xB6,  0x00,  0xB0,  0x57,  0x00,  0xFA,  0xB6,  0x00,  0x01,  0xF6,  0x01,  0x00,  0x00,  0xF2,
+  0xF6,  0x0E,  0x9C,  0x32,  0x4E,  0x1C,  0x32,  0x1C,  0x00,  0xF2,  0x92,  0x0D,  0x30,  0x1C,  0x82,  0xE7,
+  0x04,  0x00,  0xB1,  0xF0,  0x22,  0x06,  0x0A,  0xF0,  0x3E,  0x06,  0x05,  0xF0,  0xD6,  0x06,  0x06,  0xF0,
+  0xDC,  0x06,  0x09,  0xF0,  0x24,  0x09,  0x1E,  0xF0,  0xFC,  0x09,  0x00,  0xF0,  0x02,  0x0A,  0x04,  0x80,
+  0x18,  0xE4,  0x20,  0x00,  0x30,  0x12,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x21,  0x80,
+  0x18,  0xE4,  0xE0,  0x00,  0x09,  0x48,  0x00,  0xF2,  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,
+  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x99,  0xA4,  0x00,  0xF2,  0x12,  0x11,
+  0x09,  0xE7,  0x00,  0x00,  0x9A,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x34,  0x12,  0x09,  0xE7,
+  0x1B,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x21,  0x80,  0x18,  0xE4,  0xE0,  0x00,  0x09,  0x48,  0x00,  0xF2,
+  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,
+  0x12,  0x11,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF0,
+  0x0C,  0x09,  0xBB,  0x55,  0x9A,  0x81,  0x03,  0xF7,  0x20,  0x00,  0x09,  0x6F,  0x93,  0x45,  0x55,  0xF0,
+  0xE2,  0x06,  0xB1,  0xF0,  0xC2,  0x06,  0x0A,  0xF0,  0xBA,  0x06,  0x09,  0xF0,  0x24,  0x09,  0x1E,  0xF0,
+  0xFC,  0x09,  0x00,  0xF0,  0x02,  0x0A,  0x00,  0xF2,  0x60,  0x0B,  0x47,  0x10,  0x09,  0xE7,  0x08,  0x00,
+  0x41,  0x10,  0x05,  0x80,  0x48,  0xE4,  0x00,  0x00,  0x1E,  0x12,  0x00,  0xE6,  0x11,  0x00,  0x00,  0xEA,
+  0xB8,  0x00,  0x00,  0xF2,  0xB6,  0x10,  0x2C,  0x90,  0xAE,  0x90,  0x08,  0x50,  0x8A,  0x50,  0x38,  0x54,
+  0x1F,  0x40,  0x00,  0xF2,  0xB4,  0x0D,  0x08,  0x10,  0x08,  0x90,  0x8A,  0x90,  0x30,  0x50,  0xB2,  0x50,
+  0x9C,  0x32,  0x0C,  0x92,  0x8E,  0x92,  0x38,  0x54,  0x04,  0x80,  0x30,  0xE4,  0x08,  0x00,  0x04,  0x40,
+  0x0C,  0x1C,  0x00,  0xF6,  0x03,  0x00,  0xB1,  0xF0,  0x26,  0x07,  0x9E,  0xF0,  0x3A,  0x07,  0x01,  0x48,
+  0x55,  0xF0,  0xFC,  0x09,  0x0C,  0x1C,  0x10,  0x44,  0xED,  0x10,  0x0B,  0xF0,  0x5E,  0x07,  0x0C,  0xF0,
+  0x62,  0x07,  0x05,  0xF0,  0x52,  0x07,  0x06,  0xF0,  0x58,  0x07,  0x09,  0xF0,  0x24,  0x09,  0x00,  0xF0,
+  0x02,  0x0A,  0x00,  0xF2,  0x60,  0x0B,  0xCF,  0x10,  0x09,  0xE7,  0x08,  0x00,  0xC9,  0x10,  0x2E,  0x1C,
+  0x02,  0x10,  0x2C,  0x1C,  0xAA,  0xF0,  0x64,  0x07,  0xAC,  0xF0,  0x72,  0x07,  0x40,  0x10,  0x34,  0x1C,
+  0xF3,  0x10,  0xAD,  0xF0,  0x7C,  0x07,  0xC8,  0x10,  0x36,  0x1C,  0xE9,  0x10,  0x2B,  0xF0,  0x82,  0x08,
+  0x6B,  0x18,  0x18,  0xF4,  0x00,  0xFE,  0x20,  0x12,  0x01,  0x58,  0xD2,  0xF0,  0x82,  0x08,  0x76,  0x18,
+  0x18,  0xF4,  0x03,  0x00,  0xEC,  0x12,  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0xE2,  0x12,
+  0x0B,  0xF0,  0x64,  0x07,  0x0C,  0xF0,  0x64,  0x07,  0x36,  0x1C,  0x34,  0x1C,  0xB7,  0x10,  0x38,  0x54,
+  0xB9,  0x54,  0x84,  0x80,  0x19,  0xE4,  0x20,  0x00,  0xB2,  0x13,  0x85,  0x80,  0x81,  0x48,  0x66,  0x12,
+  0x04,  0x80,  0x18,  0xE4,  0x08,  0x00,  0x58,  0x13,  0x1F,  0x80,  0x08,  0x44,  0xC8,  0x44,  0x9F,  0x12,
+  0x1F,  0x40,  0x34,  0x91,  0xB6,  0x91,  0x44,  0x55,  0xE5,  0x55,  0x02,  0xEC,  0xB8,  0x00,  0x02,  0x49,
+  0xBB,  0x55,  0x82,  0x81,  0xC0,  0x55,  0x48,  0xF4,  0x0F,  0x00,  0x5A,  0xF0,  0x1A,  0x08,  0x4A,  0xE4,
+  0x17,  0x00,  0xD5,  0xF0,  0xFA,  0x07,  0x02,  0xF6,  0x0F,  0x00,  0x02,  0xF4,  0x02,  0x00,  0x02,  0xEA,
+  0xB8,  0x00,  0x04,  0x91,  0x86,  0x91,  0x02,  0x4B,  0x2C,  0x90,  0x08,  0x50,  0x2E,  0x90,  0x0A,  0x50,
+  0x2C,  0x51,  0xAE,  0x51,  0x00,  0xF2,  0xB6,  0x10,  0x38,  0x54,  0x00,  0xF2,  0xB4,  0x0D,  0x56,  0x10,
+  0x34,  0x91,  0xB6,  0x91,  0x0C,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x08,  0x00,  0x41,  0x12,  0x0C,  0x91,
+  0x8E,  0x91,  0x04,  0x80,  0x18,  0xE4,  0xF7,  0x00,  0x04,  0x40,  0x30,  0x90,  0xB2,  0x90,  0x36,  0x10,
+  0x02,  0x80,  0x48,  0xE4,  0x10,  0x00,  0x31,  0x12,  0x82,  0xE7,  0x10,  0x00,  0x84,  0x80,  0x19,  0xE4,
+  0x20,  0x00,  0x10,  0x13,  0x0C,  0x90,  0x8E,  0x90,  0x5D,  0xF0,  0x78,  0x07,  0x0C,  0x58,  0x8D,  0x58,
+  0x00,  0xF0,  0x64,  0x07,  0x38,  0x54,  0xB9,  0x54,  0x19,  0x80,  0xF1,  0x10,  0x3A,  0x55,  0x19,  0x81,
+  0xBB,  0x55,  0x10,  0x90,  0x92,  0x90,  0x10,  0x58,  0x91,  0x58,  0x14,  0x59,  0x95,  0x59,  0x00,  0xF0,
+  0x64,  0x07,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x06,  0x12,  0x6C,  0x19,  0x19,  0x41,  0x7C,  0x10,
+  0x6C,  0x19,  0x0C,  0x51,  0xED,  0x19,  0x8E,  0x51,  0x6B,  0x18,  0x18,  0xF4,  0x00,  0xFF,  0x02,  0x13,
+  0x6A,  0x10,  0x01,  0x58,  0xD2,  0xF0,  0xC0,  0x08,  0x76,  0x18,  0x18,  0xF4,  0x03,  0x00,  0x0A,  0x12,
+  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0x06,  0x13,  0x9E,  0xE7,  0x16,  0x00,  0x4C,  0x10,
+  0xD1,  0xF0,  0xCA,  0x08,  0x9E,  0xE7,  0x17,  0x00,  0x42,  0x10,  0xD0,  0xF0,  0xD4,  0x08,  0x9E,  0xE7,
+  0x19,  0x00,  0x38,  0x10,  0xCF,  0xF0,  0xDE,  0x08,  0x9E,  0xE7,  0x20,  0x00,  0x2E,  0x10,  0xCE,  0xF0,
+  0xE8,  0x08,  0x9E,  0xE7,  0x21,  0x00,  0x24,  0x10,  0xCD,  0xF0,  0xF2,  0x08,  0x9E,  0xE7,  0x22,  0x00,
+  0x1A,  0x10,  0xCC,  0xF0,  0x04,  0x09,  0x84,  0x80,  0x19,  0xE4,  0x04,  0x00,  0x06,  0x12,  0x9E,  0xE7,
+  0x12,  0x00,  0x08,  0x10,  0xCB,  0xF0,  0x0C,  0x09,  0x9E,  0xE7,  0x24,  0x00,  0xB1,  0xF0,  0x0C,  0x09,
+  0x05,  0xF0,  0x1E,  0x09,  0x09,  0xF0,  0x24,  0x09,  0x1E,  0xF0,  0xFC,  0x09,  0xE4,  0x10,  0x00,  0xF2,
+  0x60,  0x0B,  0xE9,  0x10,  0x9C,  0x32,  0x82,  0xE7,  0x20,  0x00,  0x32,  0x1C,  0xE9,  0x09,  0x00,  0xF2,
+  0x12,  0x11,  0x85,  0xF0,  0x02,  0x0A,  0x69,  0x08,  0x01,  0xF0,  0x44,  0x09,  0x1E,  0xF0,  0xFC,  0x09,
+  0x00,  0xF0,  0x38,  0x09,  0x30,  0x44,  0x06,  0x12,  0x9E,  0xE7,  0x42,  0x00,  0xB8,  0x10,  0x04,  0xF6,
+  0x01,  0x00,  0xB3,  0x45,  0x74,  0x12,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x22,  0x13,  0x4B,  0xE4,
+  0x02,  0x00,  0x36,  0x12,  0x4B,  0xE4,  0x28,  0x00,  0xAC,  0x13,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,
+  0xC8,  0x11,  0x03,  0xF6,  0xD0,  0x00,  0xFA,  0x14,  0x82,  0xE7,  0x01,  0x00,  0x00,  0xF0,  0x80,  0x01,
+  0x9E,  0xE7,  0x44,  0x00,  0x4B,  0xE4,  0x02,  0x00,  0x06,  0x12,  0x03,  0xE6,  0x02,  0x00,  0x76,  0x10,
+  0x00,  0xF2,  0xA2,  0x0D,  0x03,  0xE6,  0x02,  0x00,  0x6C,  0x10,  0x00,  0xF2,  0xA2,  0x0D,  0x19,  0x82,
+  0x34,  0x46,  0x0A,  0x13,  0x03,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x43,  0x00,  0x68,  0x10,  0x04,  0x80,
+  0x30,  0xE4,  0x20,  0x00,  0x04,  0x40,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,  0xC8,  0x11,  0x82,  0xE7,
+  0x01,  0x00,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF0,  0x08,  0x03,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,
+  0x06,  0x12,  0x03,  0xE6,  0x02,  0x00,  0x3E,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x3A,  0x12,
+  0x04,  0x80,  0x18,  0xE4,  0xFD,  0x00,  0x04,  0x40,  0x1C,  0x1C,  0x9D,  0xF0,  0xEA,  0x09,  0x1C,  0x1C,
+  0x9D,  0xF0,  0xF0,  0x09,  0xC1,  0x10,  0x9E,  0xE7,  0x13,  0x00,  0x0A,  0x10,  0x9E,  0xE7,  0x41,  0x00,
+  0x04,  0x10,  0x9E,  0xE7,  0x24,  0x00,  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0xD5,  0xF0,  0x8A,  0x02,
+  0x04,  0xE6,  0x04,  0x00,  0x06,  0x10,  0x04,  0xE6,  0x04,  0x00,  0x9D,  0x41,  0x1C,  0x42,  0x9F,  0xE7,
+  0x00,  0x00,  0x06,  0xF7,  0x02,  0x00,  0x03,  0xF6,  0xE0,  0x00,  0x3C,  0x14,  0x44,  0x58,  0x45,  0x58,
+  0x00,  0xF2,  0xF6,  0x0D,  0x00,  0xF2,  0x7E,  0x10,  0x00,  0xF2,  0xC6,  0x0F,  0x3C,  0x14,  0x1E,  0x1C,
+  0x00,  0xF0,  0x80,  0x01,  0x12,  0x1C,  0x22,  0x1C,  0xD2,  0x14,  0x00,  0xF0,  0x72,  0x01,  0x83,  0x59,
+  0x03,  0xDC,  0x73,  0x57,  0x80,  0x5D,  0x00,  0x16,  0x83,  0x59,  0x03,  0xDC,  0x38,  0x54,  0x70,  0x57,
+  0x33,  0x54,  0x3B,  0x54,  0x80,  0x5D,  0x00,  0x16,  0x03,  0x57,  0x83,  0x59,  0x38,  0x54,  0x00,  0xCC,
+  0x00,  0x16,  0x03,  0x57,  0x83,  0x59,  0x00,  0x4C,  0x00,  0x16,  0x02,  0x80,  0x48,  0xE4,  0x01,  0x00,
+  0x0E,  0x12,  0x48,  0xE4,  0x05,  0x00,  0x08,  0x12,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,  0xC8,  0x11,
+  0xC1,  0x5A,  0x3A,  0x55,  0x02,  0xEC,  0xB5,  0x00,  0x45,  0x59,  0x00,  0xF2,  0xF6,  0x0D,  0x83,  0x58,
+  0x30,  0xE7,  0x00,  0x00,  0x10,  0x4D,  0x30,  0xE7,  0x40,  0x00,  0x10,  0x4F,  0x38,  0x90,  0xBA,  0x90,
+  0x10,  0x5C,  0x80,  0x5C,  0x83,  0x5A,  0x10,  0x4E,  0x04,  0xEA,  0xB5,  0x00,  0x43,  0x5B,  0x03,  0xF4,
+  0xE0,  0x00,  0x83,  0x59,  0x04,  0xCC,  0x01,  0x4A,  0x0A,  0x12,  0x45,  0x5A,  0x00,  0xF2,  0xF6,  0x0D,
+  0x00,  0xF2,  0x38,  0x10,  0x00,  0x16,  0x08,  0x1C,  0x00,  0xFC,  0xAC,  0x00,  0x06,  0x58,  0x67,  0x18,
+  0x18,  0xF4,  0x8F,  0xE1,  0x01,  0xFC,  0xAE,  0x00,  0x19,  0xF4,  0x70,  0x1E,  0xB0,  0x54,  0x07,  0x58,
+  0x00,  0xFC,  0xB0,  0x00,  0x08,  0x58,  0x00,  0xFC,  0xB2,  0x00,  0x09,  0x58,  0x0A,  0x1C,  0x00,  0xE6,
+  0x0F,  0x00,  0x00,  0xEA,  0xB9,  0x00,  0x38,  0x54,  0x00,  0xFA,  0x24,  0x01,  0x00,  0xFA,  0xB6,  0x00,
+  0x18,  0x1C,  0x14,  0x1C,  0x10,  0x1C,  0x32,  0x1C,  0x12,  0x1C,  0x00,  0x16,  0x3E,  0x57,  0x0C,  0x14,
+  0x0E,  0x47,  0x07,  0xE6,  0x10,  0x00,  0xCE,  0x47,  0xF5,  0x13,  0x00,  0x16,  0x00,  0xF2,  0xA2,  0x0D,
+  0x02,  0x4B,  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x01,  0x48,  0x20,  0x12,  0x44,  0x58,
+  0x45,  0x58,  0x9E,  0xE7,  0x15,  0x00,  0x9C,  0xE7,  0x04,  0x00,  0x00,  0xF2,  0xF6,  0x0D,  0x00,  0xF2,
+  0x7E,  0x10,  0x00,  0xF2,  0xC6,  0x0F,  0x00,  0xF2,  0x7A,  0x0A,  0x1E,  0x1C,  0xD5,  0x10,  0x00,  0x16,
+  0x69,  0x08,  0x48,  0xE4,  0x04,  0x00,  0x64,  0x12,  0x48,  0xE4,  0x02,  0x00,  0x20,  0x12,  0x48,  0xE4,
+  0x03,  0x00,  0x1A,  0x12,  0x48,  0xE4,  0x08,  0x00,  0x14,  0x12,  0x48,  0xE4,  0x01,  0x00,  0xF0,  0x12,
+  0x48,  0xE4,  0x07,  0x00,  0x12,  0x12,  0x01,  0xE6,  0x07,  0x00,  0x00,  0xF2,  0x4E,  0x0D,  0x00,  0xF2,
+  0x12,  0x11,  0x05,  0xF0,  0x60,  0x0B,  0x00,  0x16,  0x00,  0xE6,  0x01,  0x00,  0x00,  0xEA,  0x99,  0x00,
+  0x02,  0x80,  0x48,  0xE4,  0x03,  0x00,  0xE7,  0x12,  0x48,  0xE4,  0x06,  0x00,  0xE1,  0x12,  0x01,  0xE6,
+  0x06,  0x00,  0x00,  0xF2,  0x4E,  0x0D,  0x00,  0xF2,  0x12,  0x11,  0x04,  0xE6,  0x02,  0x00,  0x9E,  0xE7,
+  0x15,  0x00,  0x01,  0xF0,  0x1C,  0x0A,  0x00,  0xF0,  0x02,  0x0A,  0x00,  0x16,  0x02,  0x80,  0x48,  0xE4,
+  0x10,  0x00,  0x1C,  0x12,  0x82,  0xE7,  0x08,  0x00,  0x3C,  0x56,  0x03,  0x82,  0x00,  0xF2,  0xE2,  0x0D,
+  0x30,  0xE7,  0x08,  0x00,  0x04,  0xF7,  0x70,  0x01,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF0,  0x80,  0x01,
+  0x6C,  0x19,  0xED,  0x19,  0x5D,  0xF0,  0xD4,  0x0B,  0x44,  0x55,  0xE5,  0x55,  0x59,  0xF0,  0x52,  0x0C,
+  0x04,  0x55,  0xA5,  0x55,  0x1F,  0x80,  0x01,  0xEC,  0xB8,  0x00,  0x82,  0x48,  0x82,  0x80,  0x49,  0x44,
+  0x2E,  0x13,  0x01,  0xEC,  0xB8,  0x00,  0x41,  0xE4,  0x02,  0x00,  0x01,  0xEA,  0xB8,  0x00,  0x49,  0xE4,
+  0x11,  0x00,  0x59,  0xF0,  0x2E,  0x0C,  0x01,  0xE6,  0x17,  0x00,  0x01,  0xEA,  0xB8,  0x00,  0x02,  0x4B,
+  0x88,  0x90,  0xAC,  0x50,  0x8A,  0x90,  0xAE,  0x50,  0x01,  0xEC,  0xB8,  0x00,  0x82,  0x48,  0x82,  0x80,
+  0x10,  0x44,  0x02,  0x4B,  0x1F,  0x40,  0xC0,  0x44,  0x00,  0xF2,  0xB4,  0x0D,  0x04,  0x55,  0xA5,  0x55,
+  0x9F,  0x10,  0x0C,  0x51,  0x8E,  0x51,  0x30,  0x90,  0xB2,  0x90,  0x00,  0x56,  0xA1,  0x56,  0x30,  0x50,
+  0xB2,  0x50,  0x34,  0x90,  0xB6,  0x90,  0x40,  0x56,  0xE1,  0x56,  0x34,  0x50,  0xB6,  0x50,  0x65,  0x10,
+  0xB1,  0xF0,  0x70,  0x0C,  0x85,  0xF0,  0xCA,  0x0B,  0xE9,  0x09,  0x4B,  0xE4,  0x03,  0x00,  0x78,  0x12,
+  0x4B,  0xE4,  0x02,  0x00,  0x01,  0x13,  0xB1,  0xF0,  0x86,  0x0C,  0x85,  0xF0,  0xCA,  0x0B,  0x69,  0x08,
+  0x48,  0xE4,  0x03,  0x00,  0xD5,  0xF0,  0x86,  0x0B,  0x00,  0xF2,  0x12,  0x11,  0x85,  0xF0,  0xCA,  0x0B,
+  0xE8,  0x09,  0x3C,  0x56,  0x00,  0xFC,  0x20,  0x01,  0x98,  0x57,  0x02,  0x13,  0xBB,  0x45,  0x4B,  0xE4,
+  0x00,  0x00,  0x08,  0x12,  0x03,  0xE6,  0x01,  0x00,  0x04,  0xF6,  0x00,  0x80,  0xA8,  0x14,  0xD2,  0x14,
+  0x30,  0x1C,  0x02,  0x80,  0x48,  0xE4,  0x03,  0x00,  0x10,  0x13,  0x00,  0xFC,  0xB6,  0x00,  0x98,  0x57,
+  0x02,  0x13,  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF0,  0x8E,  0x0B,  0x00,  0xFC,  0x24,  0x01,  0xB0,  0x57,
+  0x00,  0xFA,  0x24,  0x01,  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0x8E,  0x0B,
+  0x00,  0xF2,  0x8C,  0x0E,  0x00,  0xF0,  0x8E,  0x0B,  0xB1,  0xF0,  0xF8,  0x0C,  0x85,  0xF0,  0x86,  0x0B,
+  0x69,  0x08,  0x48,  0xE4,  0x01,  0x00,  0xD5,  0xF0,  0x86,  0x0B,  0xFC,  0x14,  0x42,  0x58,  0x6C,  0x14,
+  0x80,  0x14,  0x30,  0x1C,  0x4A,  0xF4,  0x02,  0x00,  0x55,  0xF0,  0x86,  0x0B,  0x4A,  0xF4,  0x01,  0x00,
+  0x0E,  0x12,  0x02,  0x80,  0x48,  0xE4,  0x03,  0x00,  0x06,  0x13,  0x3E,  0x1C,  0x00,  0xF0,  0x8E,  0x0B,
+  0x00,  0xFC,  0xB6,  0x00,  0xB0,  0x57,  0x00,  0xFA,  0xB6,  0x00,  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF2,
+  0x12,  0x11,  0x86,  0xF0,  0x8E,  0x0B,  0x00,  0xF2,  0xBA,  0x0E,  0x00,  0xF0,  0x8E,  0x0B,  0x4C,  0x1C,
+  0xB1,  0xF0,  0x50,  0x0D,  0x85,  0xF0,  0x5C,  0x0D,  0x69,  0x08,  0xF3,  0x10,  0x86,  0xF0,  0x64,  0x0D,
+  0x4E,  0x1C,  0x89,  0x48,  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,
+  0x00,  0xDC,  0x18,  0xF4,  0xFF,  0x7F,  0x30,  0x56,  0x00,  0x5C,  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,
+  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,  0x00,  0x80,  0x30,  0x56,  0x00,  0x5C,
+  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x0B,  0x58,
+  0x00,  0x16,  0x03,  0xF6,  0x24,  0x01,  0x00,  0xF2,  0x58,  0x0A,  0x03,  0xF6,  0xB6,  0x00,  0x00,  0xF2,
+  0x58,  0x0A,  0x00,  0x16,  0x02,  0xEC,  0xB8,  0x00,  0x02,  0x49,  0x18,  0xF4,  0xFF,  0x00,  0x00,  0x54,
+  0x00,  0x54,  0x00,  0x54,  0x00,  0xF4,  0x08,  0x00,  0xE1,  0x18,  0x80,  0x54,  0x03,  0x58,  0x00,  0xDD,
+  0x01,  0xDD,  0x02,  0xDD,  0x03,  0xDC,  0x02,  0x4B,  0x30,  0x50,  0xB2,  0x50,  0x34,  0x51,  0xB6,  0x51,
+  0x00,  0x16,  0x45,  0x5A,  0x1D,  0xF4,  0xFF,  0x00,  0x85,  0x56,  0x85,  0x56,  0x85,  0x56,  0x05,  0xF4,
+  0x02,  0x12,  0x83,  0x5A,  0x00,  0x16,  0x1D,  0xF4,  0xFF,  0x00,  0x85,  0x56,  0x85,  0x56,  0x85,  0x56,
+  0x05,  0xF4,  0x00,  0x12,  0x83,  0x5A,  0x00,  0x16,  0x38,  0x54,  0xBB,  0x55,  0x3C,  0x56,  0xBD,  0x56,
+  0x00,  0xF2,  0x12,  0x11,  0x85,  0xF0,  0x82,  0x0E,  0xE9,  0x09,  0xC1,  0x59,  0x00,  0xF2,  0x12,  0x11,
+  0x85,  0xF0,  0x82,  0x0E,  0xE8,  0x0A,  0x83,  0x55,  0x83,  0x55,  0x4B,  0xF4,  0x90,  0x01,  0x5C,  0xF0,
+  0x36,  0x0E,  0xBD,  0x56,  0x40,  0x10,  0x4B,  0xF4,  0x30,  0x00,  0x59,  0xF0,  0x48,  0x0E,  0x01,  0xF6,
+  0x0C,  0x00,  0x00,  0xF6,  0x01,  0x00,  0x2E,  0x10,  0x02,  0xFC,  0x9C,  0x00,  0x9A,  0x57,  0x14,  0x13,
+  0x4B,  0xF4,  0x64,  0x00,  0x59,  0xF0,  0x64,  0x0E,  0x03,  0xF6,  0x64,  0x00,  0x01,  0xF6,  0x19,  0x00,
+  0x00,  0xF6,  0x01,  0x00,  0x43,  0xF4,  0x33,  0x00,  0x56,  0xF0,  0x76,  0x0E,  0x04,  0xF4,  0x00,  0x01,
+  0x43,  0xF4,  0x19,  0x00,  0xF3,  0x10,  0xB4,  0x56,  0xC3,  0x58,  0x02,  0xFC,  0x9E,  0x00,  0x9A,  0x57,
+  0x08,  0x13,  0x3C,  0x56,  0x00,  0xF6,  0x02,  0x00,  0x00,  0x16,  0x00,  0x16,  0x09,  0xE7,  0x01,  0x00,
+  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0xB8,  0x0E,  0x09,  0xE7,  0x02,  0x00,  0x00,  0xF2,  0x12,  0x11,
+  0x86,  0xF0,  0xB8,  0x0E,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0xB8,  0x0E,
+  0x4E,  0x1C,  0x89,  0x49,  0x00,  0xF2,  0x12,  0x11,  0x00,  0x16,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,
+  0x12,  0x11,  0x86,  0xF0,  0xF2,  0x0E,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,
+  0xF2,  0x0E,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0xF2,  0x0E,  0x89,  0x49,
+  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0xF2,  0x0E,  0x4E,  0x1C,  0x89,  0x4A,  0x00,  0xF2,  0x12,  0x11,
+  0x00,  0x16,  0x3C,  0x56,  0x00,  0x16,  0x00,  0xEC,  0x26,  0x01,  0x48,  0xE4,  0x01,  0x00,  0x1E,  0x13,
+  0x38,  0x44,  0x00,  0xEA,  0x26,  0x01,  0x49,  0xF4,  0x00,  0x00,  0x04,  0x12,  0x4E,  0x1C,  0x02,  0x10,
+  0x4C,  0x1C,  0x01,  0xEC,  0x27,  0x01,  0x89,  0x48,  0x00,  0xF2,  0x12,  0x11,  0x02,  0x14,  0x00,  0x16,
+  0x85,  0xF0,  0x52,  0x0F,  0x38,  0x54,  0x00,  0xEA,  0x99,  0x00,  0x00,  0xF2,  0x60,  0x0B,  0x02,  0x80,
+  0x48,  0xE4,  0x06,  0x00,  0x1C,  0x13,  0x00,  0xEC,  0x99,  0x00,  0x48,  0xE4,  0x01,  0x00,  0x0A,  0x12,
+  0x04,  0x80,  0x30,  0xE4,  0x01,  0x00,  0x04,  0x40,  0x08,  0x10,  0x04,  0x80,  0x18,  0xE4,  0xFE,  0x00,
+  0x04,  0x40,  0x00,  0x16,  0x02,  0xF6,  0xE0,  0x00,  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x81,  0x48,
+  0x22,  0x12,  0x00,  0x4E,  0x83,  0x5A,  0x90,  0x4C,  0x20,  0xE7,  0x00,  0x00,  0xC3,  0x58,  0x1B,  0xF4,
+  0xFF,  0x00,  0x83,  0x55,  0x83,  0x55,  0x83,  0x55,  0x03,  0xF4,  0x00,  0x12,  0x8B,  0x55,  0x83,  0x59,
+  0x00,  0x4E,  0x00,  0x16,  0x00,  0x4E,  0x02,  0xF6,  0xF0,  0x00,  0x02,  0x57,  0x03,  0x59,  0x00,  0x4E,
+  0x83,  0x5A,  0x30,  0xE7,  0x00,  0x00,  0x20,  0xE7,  0x00,  0x00,  0x00,  0x16,  0x02,  0xF6,  0xF0,  0x00,
+  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x00,  0x4E,  0x83,  0x5A,  0x30,  0xE7,  0x00,  0x00,  0x80,  0x4C,
+  0xC3,  0x58,  0x1B,  0xF4,  0xFF,  0x00,  0x83,  0x55,  0x83,  0x55,  0x83,  0x55,  0x03,  0xF4,  0x00,  0x12,
+  0x83,  0x59,  0x00,  0x4E,  0x00,  0x16,  0x03,  0xF6,  0xE0,  0x00,  0x03,  0x57,  0x83,  0x59,  0x3A,  0x55,
+  0x02,  0xCC,  0x45,  0x5A,  0x00,  0xF2,  0xF6,  0x0D,  0xC0,  0x5A,  0x40,  0x5C,  0x38,  0x54,  0x00,  0xCD,
+  0x01,  0xCC,  0x4A,  0x46,  0x0A,  0x13,  0x83,  0x59,  0x00,  0x4C,  0x01,  0x48,  0x16,  0x13,  0x0C,  0x10,
+  0xC5,  0x58,  0x00,  0xF2,  0xF6,  0x0D,  0x00,  0x4C,  0x01,  0x48,  0x08,  0x13,  0x05,  0xF6,  0xF0,  0x00,
+  0x05,  0x57,  0x08,  0x10,  0x45,  0x58,  0x00,  0xF2,  0xF6,  0x0D,  0x8D,  0x56,  0x83,  0x5A,  0x80,  0x4C,
+  0x05,  0x17,  0x00,  0x16,  0x02,  0x4B,  0x06,  0xF7,  0x04,  0x00,  0x62,  0x0B,  0x03,  0x82,  0x00,  0xF2,
+  0xE2,  0x0D,  0x02,  0x80,  0x00,  0x4C,  0x45,  0xF4,  0x02,  0x00,  0x52,  0x14,  0x06,  0xF7,  0x02,  0x00,
+  0x06,  0x14,  0x00,  0xF2,  0x54,  0x0F,  0x00,  0x16,  0x02,  0x4B,  0x01,  0xF6,  0xFF,  0x00,  0x38,  0x1C,
+  0x05,  0xF4,  0x04,  0x00,  0x83,  0x5A,  0x18,  0xDF,  0x19,  0xDF,  0x1D,  0xF7,  0x3C,  0x00,  0xB8,  0xF0,
+  0x4E,  0x10,  0x9C,  0x14,  0x01,  0x48,  0x1C,  0x13,  0x0E,  0xF7,  0x3C,  0x00,  0x03,  0xF7,  0x04,  0x00,
+  0xAF,  0x19,  0x03,  0x42,  0x45,  0xF4,  0x02,  0x00,  0x83,  0x5A,  0x02,  0xCC,  0x02,  0x41,  0x45,  0xF4,
+  0x02,  0x00,  0x00,  0x16,  0x91,  0x44,  0xD5,  0xF0,  0x3E,  0x10,  0x00,  0xF0,  0x9E,  0x02,  0x01,  0xF6,
+  0xFF,  0x00,  0x38,  0x1C,  0x05,  0xF4,  0x04,  0x00,  0x83,  0x5A,  0x18,  0xDF,  0x19,  0xDF,  0x0E,  0xF7,
+  0x3C,  0x00,  0x03,  0xF7,  0x04,  0x00,  0x0F,  0x79,  0x1C,  0xF7,  0x3C,  0x00,  0xB8,  0xF0,  0x9C,  0x10,
+  0x4E,  0x14,  0x01,  0x48,  0x06,  0x13,  0x45,  0xF4,  0x04,  0x00,  0x00,  0x16,  0x91,  0x44,  0xD5,  0xF0,
+  0x82,  0x10,  0x00,  0xF0,  0x9E,  0x02,  0x02,  0xF6,  0xFF,  0x00,  0x38,  0x1C,  0x2C,  0xBC,  0xAE,  0xBC,
+  0xE2,  0x08,  0x00,  0xEC,  0xB8,  0x00,  0x02,  0x48,  0x1D,  0xF7,  0x80,  0x00,  0xB8,  0xF0,  0xCC,  0x10,
+  0x1E,  0x14,  0x01,  0x48,  0x0E,  0x13,  0x0E,  0xF7,  0x80,  0x00,  0x38,  0x54,  0x03,  0x58,  0xAF,  0x19,
+  0x82,  0x48,  0x00,  0x16,  0x82,  0x48,  0x12,  0x45,  0xD5,  0xF0,  0xBA,  0x10,  0x00,  0xF0,  0x9E,  0x02,
+  0x39,  0xF0,  0xF8,  0x10,  0x38,  0x44,  0x00,  0x16,  0x7E,  0x18,  0x18,  0xF4,  0x03,  0x00,  0x04,  0x13,
+  0x61,  0x18,  0x00,  0x16,  0x38,  0x1C,  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0xF1,  0x12,
+  0xE3,  0x10,  0x30,  0x44,  0x30,  0x44,  0x30,  0x44,  0xB1,  0xF0,  0x18,  0x11,  0x00,  0x16,  0x3E,  0x57,
+  0x03,  0xF6,  0xE0,  0x00,  0x03,  0x57,  0x83,  0x59,  0x04,  0xCC,  0x01,  0x4A,  0x6A,  0x12,  0x45,  0x5A,
+  0x00,  0xF2,  0xF6,  0x0D,  0x02,  0x4B,  0x70,  0x14,  0x34,  0x13,  0x02,  0x80,  0x48,  0xE4,  0x08,  0x00,
+  0x18,  0x12,  0x9C,  0xE7,  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,  0x00,  0xF2,  0xC6,  0x0F,  0x00,  0xF2,
+  0x7A,  0x0A,  0x1E,  0x1C,  0x01,  0xF6,  0x01,  0x00,  0x00,  0x16,  0x30,  0xE4,  0x10,  0x00,  0x04,  0x40,
+  0x00,  0xF2,  0xE2,  0x0D,  0x20,  0xE7,  0x01,  0x00,  0x01,  0xF6,  0x01,  0x00,  0x00,  0x16,  0x04,  0xDC,
+  0x01,  0x4A,  0x24,  0x12,  0x45,  0x5A,  0x00,  0xF2,  0xF6,  0x0D,  0x43,  0x5B,  0x06,  0xEC,  0x98,  0x00,
+  0x00,  0xF2,  0x38,  0x10,  0xC6,  0x59,  0x20,  0x14,  0x0A,  0x13,  0x00,  0xF2,  0xC6,  0x0F,  0x00,  0xF2,
+  0x14,  0x10,  0xA7,  0x10,  0x83,  0x5A,  0xD7,  0x10,  0x0E,  0x47,  0x07,  0xE6,  0x10,  0x00,  0xCE,  0x47,
+  0x5A,  0xF0,  0x20,  0x11,  0xB9,  0x54,  0x00,  0x16,  0x14,  0x90,  0x96,  0x90,  0x02,  0xFC,  0xA8,  0x00,
+  0x03,  0xFC,  0xAA,  0x00,  0x48,  0x55,  0x02,  0x13,  0xC9,  0x55,  0x00,  0x16,  0x00,  0xEC,  0xBA,  0x00,
+  0x10,  0x44,  0x00,  0xEA,  0xBA,  0x00,  0x00,  0x16,  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x68,  0x0A,
+  0x10,  0x44,  0x00,  0x4C,  0x00,  0x16
+};
 
 unsigned short _adv_mcode_size ASC_INITDATA =
-    sizeof(_adv_mcode_buf); /* 0x11D2 */
-unsigned long  _adv_mcode_chksum ASC_INITDATA = 0x0347D07AUL;
+    sizeof(_adv_mcode_buf); /* 0x11D6 */
+unsigned long  _adv_mcode_chksum ASC_INITDATA = 0x03494981UL;
 
 /* a_init.c */
 /*
index 32ff539f2afee7ecec336ff427953372f94b2f5e..5a9a0fa5b1257173d4713426cfa86323e953dc43 100644 (file)
@@ -609,11 +609,12 @@ static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun)
       prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble)
     ;
 
-  if(ptr)
+  if(ptr){
     if(prev)
       prev->host_scribble = ptr->host_scribble;
     else
       *SC= (Scsi_Cmnd *) ptr->host_scribble;
+  }
 
   return ptr;
 }
@@ -1724,7 +1725,7 @@ void aha152x_intr(int irqno, void *dev_id, struct pt_regs * regs)
 
   /* we are waiting for the result of a selection attempt */
   if(CURRENT_SC->SCp.phase & in_selection) {
-    if(TESTLO(SSTAT1, SELTO))
+    if(TESTLO(SSTAT1, SELTO)) {
       /* no timeout */
       if(TESTHI(SSTAT0, SELDO)) {
         /* clear BUS FREE interrupt */
@@ -1800,7 +1801,7 @@ void aha152x_intr(int irqno, void *dev_id, struct pt_regs * regs)
         return;
       } else
         aha152x_panic(shpnt, "neither timeout nor selection\007");
-    else {
+    else {
 #if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
       if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases))
         printk("SELTO, ");
index dec616c3f7668bc51b92e35862208eae6ee41adf..21eaff620bb0b9f6a72b5f954c404f544cd8f688 100644 (file)
@@ -809,7 +809,7 @@ static int aha1542_mbenable(int base)
      mbenable_cmd[1]=0;
      mbenable_cmd[2]=mbenable_result[1];
 
-     if(mbenable_result[1] & 0x03) retval = BIOS_TRANSLATION_25563;
+     if((mbenable_result[0] & 0x08) && (mbenable_result[1] & 0x03)) retval = BIOS_TRANSLATION_25563;
 
      aha1542_out(base,mbenable_cmd,3);
      WAIT(INTRFLAGS(base),INTRMASK,HACC,0);
index 779aebf247ec7522e734935bafa3178854787e23..1c1684d5d35ba3c3201c8887139271909eef9412 100644 (file)
@@ -1002,7 +1002,7 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
    Write_FIFO_port       = port_base + Write_FIFO;
    Write_SCSI_Data_port  = port_base + Write_SCSI_Data;
 
-   fdomain_16x0_reset( NULL );
+   fdomain_16x0_reset( NULL, 0 );
 
    if (fdomain_test_loopback()) {
 #if DEBUG_DETECT
@@ -1887,7 +1887,7 @@ int fdomain_16x0_abort( Scsi_Cmnd *SCpnt)
    return SCSI_ABORT_SUCCESS;
 }
 
-int fdomain_16x0_reset( Scsi_Cmnd *SCpnt )
+int fdomain_16x0_reset( Scsi_Cmnd *SCpnt, unsigned int flags )
 {
 #if DEBUG_RESET
    static int called_once = 0;
index 429db819e2684ed6583aaf3ef56e3ae379c991dd..c15255819575d707daeefcdde7c5a14ff68cb910 100644 (file)
@@ -29,7 +29,7 @@ int        fdomain_16x0_detect( Scsi_Host_Template * );
 int        fdomain_16x0_command( Scsi_Cmnd * );
 int        fdomain_16x0_abort( Scsi_Cmnd * );
 const char *fdomain_16x0_info( struct Scsi_Host * );
-int        fdomain_16x0_reset( Scsi_Cmnd * ); 
+int        fdomain_16x0_reset( Scsi_Cmnd *, unsigned int ); 
 int        fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
 int        fdomain_16x0_biosparam( Disk *, kdev_t, int * );
 int        fdomain_16x0_proc_info( char *buffer, char **start, off_t offset,
index 93bc46c2f33a010804629eb2c3ba83d095d12c1d..4bb75865b34ba93e1618d81cb2e648b849e8149d 100644 (file)
@@ -358,7 +358,7 @@ scsi_unregister(struct Scsi_Host * sh){
     /* If we are removing the last host registered, it is safe to reuse
      * its host number (this avoids "holes" at boot time) (DB) 
      */
-    if (max_scsi_hosts == next_scsi_host && !scsi_loadable_module_flag)
+    if (max_scsi_hosts == next_scsi_host)
        max_scsi_hosts--;
     
     next_scsi_host--;
index a08ff1e62d8f3ec95cfc298610043fdfac67970f..bd964204a312868284565134b2d5dd8c4df6b33b 100644 (file)
@@ -510,7 +510,7 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
     register unsigned char  *d = dst;
     register unsigned short reg = (unsigned short) (instance->io_port + 
        P_DATA_REG_OFFSET);
-    register i = len;
+    register int i = len;
     int ii = 0;
 
     while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) )
@@ -546,7 +546,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src
     int len) {
     register unsigned char *s = src;
     register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
-    register i = len;
+    register int i = len;
     int ii = 0;
 
     while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) )
index a3784e9264c5320054ef61b570394ecc07e8a3b6..b5cb9dd0aad9e0af941de2edb3c00fef5e4d2f9f 100644 (file)
@@ -656,7 +656,7 @@ int qlogicfas_abort(Scsi_Cmnd * cmd)
 
 /*----------------------------------------------------------------*/
 /* reset SCSI bus */
-int    qlogicfas_reset(Scsi_Cmnd * cmd)
+int    qlogicfas_reset(Scsi_Cmnd * cmd, unsigned int flags)
 {
        qabort = 2;
        ql_zap();
index df2bb73ae309b3e154974efc75fd7f3e231e8dde..5a1dfdbf7d43d49611912d26b3a172b8d8edb3ff 100644 (file)
@@ -6,7 +6,7 @@ const char * qlogicfas_info(struct Scsi_Host *);
 int qlogicfas_command(Scsi_Cmnd *);
 int qlogicfas_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
 int qlogicfas_abort(Scsi_Cmnd *);
-int qlogicfas_reset(Scsi_Cmnd *);
+int qlogicfas_reset(Scsi_Cmnd *, unsigned int flags);
 int qlogicfas_biosparam(Disk *, kdev_t, int[]);
 
 #ifndef NULL
index c0a8ae894170b87c70665207c8a6a69f650f3853..e503dd8498ecc59e8e803b97d84908a1a5ebca2a 100644 (file)
@@ -214,6 +214,7 @@ static void scsi_dump_status(void);
 #define BLIST_SINGLELUN 0x10
 #define BLIST_NOTQ     0x20
 #define BLIST_SPARSELUN 0x40
+#define BLIST_MAX5LUN  0x80
 
 struct dev_info{
     const char * vendor;
@@ -281,7 +282,9 @@ static struct dev_info device_list[] =
 {"INSITE","I325VM","*", BLIST_KEY},
 {"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN},
 {"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"EMULEX","MD21/S2     ESDI","*", BLIST_SINGLELUN},
@@ -813,6 +816,15 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
     *max_dev_lun = 8;
     return 1;
   }
+
+  /*
+   * REGAL CDC-4X: avoid hang after LUN 4
+   */
+  if (bflags & BLIST_MAX5LUN) {
+    *max_dev_lun = 5;
+    return 1;
+  }
+
   /*
    * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
    * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
@@ -874,7 +886,8 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt)
         scsi_reset (SCpnt,
                    SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET);
         return;
-    case (IN_ABORT | IN_RESET | IN_RESET2):
+    case IN_RESET2:
+    case (IN_ABORT | IN_RESET2):
        /* Obviously the bus reset didn't work.
         * Let's try even harder and call for an HBA reset.
          * Maybe the HBA itself crashed and this will shake it loose.
index 495a99b8ff6d5f56143073909cd661889980cefc..c3dfcd45e92644ab377b7ce3be6b7085bb5edea2 100644 (file)
@@ -521,8 +521,23 @@ static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors
            req->nr_sectors -= bh->b_size >> 9;
            req->sector += bh->b_size >> 9;
            bh->b_reqnext = NULL;
-           mark_buffer_uptodate(bh, uptodate);
-           unlock_buffer(bh);
+           /*
+            * This is our 'MD IO has finished' event handler.
+            * note that b_state should be cached in a register
+            * anyways, so the overhead if this checking is almost 
+            * zero. But anyways .. we never get OO for free :)
+            */
+           if (test_bit(BH_MD, &bh->b_state)) {
+               struct md_personality * pers=(struct md_personality *)bh->personality;
+               pers->end_request(bh,uptodate);
+           }
+           /*
+            * the normal (nonmirrored and no RAID5) case:
+            */
+           else {
+               mark_buffer_uptodate(bh, uptodate);
+               unlock_buffer(bh);
+           }
            sectors -= bh->b_size >> 9;
            if ((bh = req->bh) != NULL) {
                req->current_nr_sectors = bh->b_size >> 9;
index b73b3c8b1e19170df78a646c3e13c8e2d2c80ff0..8e0da4b81670973e17056d5fb9e08ed318325924 100644 (file)
@@ -76,7 +76,7 @@ extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 static int check_scsidisk_media_change(kdev_t);
 static int fop_revalidate_scsidisk(kdev_t);
 
-static sd_init_onedisk(int);
+static int sd_init_onedisk(int);
 
 static void requeue_sd_request (Scsi_Cmnd * SCpnt);
 
index fe5fa79bec9d144e6a2ce4dac472a66702fdf0a9..06c6d203b4f6e57951db92d9f3e04a0b67d26b74 100644 (file)
@@ -324,7 +324,7 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
     int len) {
     register unsigned char *reg = (unsigned char *) (instance->base + 
        T_DATA_REG_OFFSET), *d = dst;
-    register i = len;
+    register int i = len;
 
 
 #if 0
@@ -368,7 +368,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src
     int len) {
     register unsigned char *reg = (unsigned char *) (instance->base + 
        T_DATA_REG_OFFSET), *s = src;
-    register i = len;
+    register int i = len;
 
 #if 0
     for (; i; --i) {
index 0e7797780d0debc29c3588048b1536c3a84cac23..9f9560a27f0ba58be8f118ba21b86dfed6dfc780 100644 (file)
@@ -508,7 +508,7 @@ typedef struct icbRevLvl {
 typedef struct icbUnsMask {    /* I'm totally guessing here */
     unchar op;
     volatile unchar mask[14];  /* mask bits                 */
-#ifdef 0
+#if 0
     unchar rsvd[12];           /* reserved                  */
 #endif
     volatile unchar vue;       /* vendor-unique error code  */
@@ -1686,7 +1686,7 @@ int wd7000_abort (Scsi_Cmnd *SCpnt)
 /*
  *  I also have no idea how to do a reset...
  */
-int wd7000_reset (Scsi_Cmnd *SCpnt)
+int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int flags)
 {
     return (SCSI_RESET_PUNT);
 }
index d26fcdcadc003087dceb3c2b7a8bb743a14697df..2469efb5ce25ea1daeaf159c7d36724f136b0e6c 100644 (file)
@@ -20,7 +20,7 @@ int wd7000_detect (Scsi_Host_Template *);
 int wd7000_command (Scsi_Cmnd *);
 int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int wd7000_abort (Scsi_Cmnd *);
-int wd7000_reset (Scsi_Cmnd *);
+int wd7000_reset (Scsi_Cmnd *, unsigned int);
 int wd7000_biosparam (Disk *, kdev_t, int *);
 
 #ifndef NULL
index 92fb802d2fb60fe325ca69f9645169947c66537c..f6b1c90cdb1bf563a69ca19f80fd4e4184403ec7 100644 (file)
@@ -10,7 +10,7 @@ tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
 tristate 'xiafs filesystem support' CONFIG_XIA_FS
 
-tristate 'Native language support (Unicode, codepages)' CONFIG_NLS
+tristate 'Native language support (Needed for FAT and ISO9660)' CONFIG_NLS
 if [ "$CONFIG_NLS" = "y" -o "$CONFIG_NLS" = "m" ]; then
   dep_tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS $CONFIG_NLS
 
index 7d1aec49fde03d1a8547f41a77c5bba7ae65b950..fdb022a8bff69293441ea757a45d82d1f4ea8cb8 100644 (file)
@@ -53,8 +53,8 @@ static struct buffer_head * lru_list[NR_LIST] = {NULL, };
 static struct buffer_head * free_list[NR_SIZES] = {NULL, };
 
 static struct buffer_head * unused_list = NULL;
-static struct buffer_head * reuse_list = NULL;
-static struct wait_queue * buffer_wait = NULL;
+static struct buffer_head * reuse_list  = NULL;
+struct wait_queue *         buffer_wait = NULL;
 
 static int nr_buffers = 0;
 static int nr_buffers_type[NR_LIST] = {0,};
@@ -462,6 +462,11 @@ static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
        return NULL;
 }
 
+struct buffer_head *efind_buffer(kdev_t dev, int block, int size)
+{
+       return find_buffer(dev, block, size);
+}
+
 /*
  * Why like this, I hear you say... The reason is race-conditions.
  * As we don't lock buffers (unless we are reading them, that is),
@@ -650,6 +655,9 @@ static void refill_freelist(int size)
        /* If there are too many dirty buffers, we wake up the update process
           now so as to ensure that there are still clean buffers available
           for user processes to use (and dirty) */
+
+       if (nr_buffers_type[BUF_DIRTY] > nr_buffers * bdf_prm.b_un.nfract/100)
+               wakeup_bdflush(1);
        
        /* We are going to try to locate this much memory */
        needed = bdf_prm.b_un.nrefill * size;  
@@ -1247,7 +1255,9 @@ void unlock_buffer(struct buffer_head * bh)
        struct buffer_head *tmp;
        struct page *page;
 
-       clear_bit(BH_Lock, &bh->b_state);
+       if (!clear_bit(BH_Lock, &bh->b_state))
+               printk ("unlock_buffer: already unlocked on %s\n",
+                       kdevname(bh->b_dev));
        wake_up(&bh->b_wait);
        if (waitqueue_active(&buffer_wait))
                wake_up(&buffer_wait);
@@ -1594,7 +1604,7 @@ asmlinkage int sync_old_buffers(void)
        if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
        printk("Wrote %d/%d buffers\n", nwritten, ndirty);
 #endif
-       
+       run_task_queue(&tq_disk);
        return 0;
 }
 
index d7e2fc621c8b561d4d611c1d47cca2f628d678d3..902d011dfa9d1ca84c2463a491fcc91426a5ed9b 100644 (file)
@@ -597,7 +597,7 @@ int isofs_bmap(struct inode * inode,int block)
         * If we are beyond the end of this file, don't give out any
         * blocks.
         */
-       if( b_off > inode->i_size )
+       if( b_off >= inode->i_size )
          {
            off_t       max_legal_read_offset;
 
index bd929e4ad7c0c1b97ca1d6179294d3172c373db1..514adfdef03224a2bc4e6d39923b2a9a54539492 100644 (file)
@@ -409,7 +409,7 @@ static int get_arg(int pid, char * buffer)
        return get_array(p, (*p)->mm->arg_start, (*p)->mm->arg_end, buffer);
 }
 
-static unsigned long get_wchan(struct task_struct *p)
+unsigned long get_wchan(struct task_struct *p)
 {
        if (!p || p == current || p->state == TASK_RUNNING)
                return 0;
index f9cdecb9e5ed82038ea6e54b34980c8f5a110ec6..bbfbd20e0205c291b6866cd7cdefc9bff080ff0a 100644 (file)
@@ -47,6 +47,7 @@
 extern void wait_for_keypress(void);
 extern struct file_operations * get_blkfops(unsigned int major);
 extern void blkdev_release (struct inode *);
+extern void rd_load_secondary(void);
 
 extern int root_mountflags;
 
index 680d99213936e2b96553394c7b78b320b6e9413b..71f08018f83473de4e0d0438e6f586d0d30cbb22 100644 (file)
@@ -165,7 +165,6 @@ static struct super_block * detected_xenix (struct super_block *sb, struct buffe
        sb->sv_sb_flc_blocks = &sbd1->s_free[0];
        sb->sv_sb_total_free_blocks = &sbd2->s_tfree;
        sb->sv_sb_time = &sbd2->s_time;
-       sb->sv_block_base = 0;
        sb->sv_firstinodezone = 2;
        sb->sv_firstdatazone = sbd1->s_isize;
        sb->sv_nzones = sbd1->s_fsize;
@@ -223,7 +222,6 @@ static struct super_block * detected_sysv4 (struct super_block *sb, struct buffe
        sb->sv_sb_total_free_blocks = &sbd->s_tfree;
        sb->sv_sb_time = &sbd->s_time;
        sb->sv_sb_state = &sbd->s_state;
-       sb->sv_block_base = 0;
        sb->sv_firstinodezone = 2;
        sb->sv_firstdatazone = sbd->s_isize;
        sb->sv_nzones = sbd->s_fsize;
@@ -281,7 +279,6 @@ static struct super_block * detected_sysv2 (struct super_block *sb, struct buffe
        sb->sv_sb_total_free_blocks = &sbd->s_tfree;
        sb->sv_sb_time = &sbd->s_time;
        sb->sv_sb_state = &sbd->s_state;
-       sb->sv_block_base = 0;
        sb->sv_firstinodezone = 2;
        sb->sv_firstdatazone = sbd->s_isize;
        sb->sv_nzones = sbd->s_fsize;
@@ -328,7 +325,6 @@ static struct super_block * detected_coherent (struct super_block *sb, struct bu
        sb->sv_sb_flc_blocks = &sbd->s_free[0];
        sb->sv_sb_total_free_blocks = &sbd->s_tfree;
        sb->sv_sb_time = &sbd->s_time;
-       sb->sv_block_base = 0;
        sb->sv_firstinodezone = 2;
        sb->sv_firstdatazone = sbd->s_isize;
        sb->sv_nzones = from_coh_ulong(sbd->s_fsize);
@@ -355,6 +351,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
        MOD_INC_USE_COUNT;
        lock_super(sb);
        set_blocksize(dev,BLOCK_SIZE);
+       sb->sv_block_base = 0;
 
        /* Try to read Xenix superblock */
        if ((bh = bread(dev, 1, BLOCK_SIZE)) != NULL) {
index 8e4aa52f683c99569dfd738e52be8af18b6a228d..1b1b93bdb77ebf55a49be68fb791270d10d48f04 100644 (file)
@@ -43,6 +43,12 @@ struct pt_regs {
        unsigned short ss, __ssu;
 };
 
+/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */
+#define PTRACE_GETREGS            12
+#define PTRACE_SETREGS            13
+#define PTRACE_GETFPREGS          14
+#define PTRACE_SETFPREGS          15
+
 #ifdef __KERNEL__
 #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs))
 #define instruction_pointer(regs) ((regs)->eip)
index 4c5a25b69b7032d47b86c16b6a0b03e2c0205d87..92634d7a1b654c5ea51e0bffc9775f2e584252d4 100644 (file)
@@ -3,7 +3,9 @@
 
 #include <linux/blkdev.h>
 #include <linux/locks.h>
+#include <linux/malloc.h>
 #include <linux/config.h>
+#include <linux/md.h>
 
 /*
  * NR_REQUEST is the number of entries in the request-queue.
@@ -400,8 +402,24 @@ static void end_request(int uptodate) {
        if ((bh = req->bh) != NULL) {
                req->bh = bh->b_reqnext;
                bh->b_reqnext = NULL;
-               mark_buffer_uptodate(bh, uptodate);
-               unlock_buffer(bh);
+               /*
+                * This is our 'MD IO has finished' event handler.
+                * note that b_state should be cached in a register
+                * anyways, so the overhead if this checking is almost 
+                * zero. But anyways .. we never get OO for free :)
+                */
+               if (test_bit(BH_MD, &bh->b_state)) {
+                       struct md_personality * pers=(struct md_personality *)bh->personality;
+                       pers->end_request(bh,uptodate);
+               }
+               /*
+                * the normal (nonmirrored and no RAID5) case:
+                */
+               else {
+                       mark_buffer_uptodate(bh, uptodate);
+                       unlock_buffer(bh);
+               }
                if ((bh = req->bh) != NULL) {
                        req->current_nr_sectors = bh->b_size >> 9;
                        if (req->nr_sectors < req->current_nr_sectors) {
index e0f578f9b48169fd98c317a1445277068b4099d3..5bfc84ed7d68e8ba1248219fcc711c8ce0e0b392 100644 (file)
@@ -50,9 +50,12 @@ extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
 extern struct wait_queue * wait_for_request;
 extern void resetup_one_dev(struct gendisk *dev, int drive);
 extern void unplug_device(void * data);
+extern void make_request(int major,int rw, struct buffer_head * bh);
 
 /* md needs this function to remap requests */
 extern int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size);
+extern int md_make_request (int minor, int rw, struct buffer_head * bh);
+extern int md_error (kdev_t mddev, kdev_t rdev);
 
 extern int * blk_size[MAX_BLKDEV];
 
index 8492c3657a9785fb97b4cac808977c8d09a7a0a1..37ffba76b5f4c4c2f9a97d513f094ee7d8ae091b 100644 (file)
@@ -141,14 +141,15 @@ extern unsigned long name_cache_init(unsigned long start, unsigned long end);
 typedef char buffer_block[BLOCK_SIZE];
 
 /* bh state bits */
-#define BH_Uptodate    0       /* 1 if the buffer contains valid data */
-#define BH_Dirty       1       /* 1 if the buffer is dirty */
-#define BH_Lock                2       /* 1 if the buffer is locked */
-#define BH_Req         3       /* 0 if the buffer has been invalidated */
-#define BH_Touched     4       /* 1 if the buffer has been touched (aging) */
-#define BH_Has_aged    5       /* 1 if the buffer has been aged (aging) */
-#define BH_Protected   6       /* 1 if the buffer is protected */
-#define BH_FreeOnIO    7       /* 1 to discard the buffer_head after IO */
+#define BH_Uptodate            0       /* 1 if the buffer contains valid data */
+#define BH_Dirty               1       /* 1 if the buffer is dirty */
+#define BH_Lock                        2       /* 1 if the buffer is locked */
+#define BH_Req                 3       /* 0 if the buffer has been invalidated */
+#define BH_Touched             4       /* 1 if the buffer has been touched (aging) */
+#define BH_Has_aged            5       /* 1 if the buffer has been aged (aging) */
+#define BH_Protected           6       /* 1 if the buffer is protected */
+#define BH_FreeOnIO            7       /* 1 to discard the buffer_head after IO */
+#define BH_MD                  8       /* 1 if the buffer is an MD request */
 
 /*
  * Try to keep the most commonly used fields in single cache lines (16
@@ -188,6 +189,13 @@ struct buffer_head {
        struct buffer_head * b_prev;            /* doubly linked list of hash-queue */
        struct buffer_head * b_prev_free;       /* doubly linked list of buffers */
        struct buffer_head * b_reqnext;         /* request queue */
+
+/*
+ * Some MD stuff like RAID5 needs special event handlers and
+ * special private buffer_head fields:
+ */
+       void * personality;
+       void * private_bh;
 };
 
 static inline int buffer_uptodate(struct buffer_head * bh)
index 5a5c9035934a5d7ce83ed20fe95716659375f002..1c5680a0caf23170039c9fc420a55df1aac9a810 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
    md.h : Multiple Devices driver for Linux
           Copyright (C) 1994-96 Marc ZYNGIER
 
 #include <asm/segment.h>
 #include <linux/major.h>
-#include <linux/mm.h>
 #include <linux/ioctl.h>
+#include <linux/types.h>
 
-#define MD_VERSION "0.35"
+/*
+ * Different major versions are not compatible.
+ * Different minor versions are only downward compatible.
+ * Different patchlevel versions are downward and upward compatible.
+ */
+#define MD_MAJOR_VERSION               0
+#define MD_MINOR_VERSION               36
+#define MD_PATCHLEVEL_VERSION          3
 
 /* ioctls */
 #define REGISTER_DEV _IO (MD_MAJOR, 1)
@@ -43,9 +49,9 @@
 #define FAULT_SHIFT       8
 #define PERSONALITY_SHIFT 16
 
-#define FACTOR_MASK       0xFFUL
-#define FAULT_MASK        0xFF00UL
-#define PERSONALITY_MASK  0xFF0000UL
+#define FACTOR_MASK       0x000000FFUL
+#define FAULT_MASK        0x0000FF00UL
+#define PERSONALITY_MASK  0x00FF0000UL
 
 #define MD_RESERVED       0    /* Not used by now */
 #define LINEAR            (1UL << PERSONALITY_SHIFT)
 #define RAID5             (4UL << PERSONALITY_SHIFT)
 #define MAX_PERSONALITY   5
 
+/*
+ * MD superblock.
+ *
+ * The MD superblock maintains some statistics on each MD configuration.
+ * Each real device in the MD set contains it near the end of the device.
+ * Some of the ideas are copied from the ext2fs implementation.
+ *
+ * We currently use 4096 bytes as follows:
+ *
+ *     word offset     function
+ *
+ *        0  -    31   Constant generic MD device information.
+ *        32  -    63   Generic state information.
+ *       64  -   127   Personality specific information.
+ *      128  -   511   12 32-words descriptors of the disks in the raid set.
+ *      512  -   911   Reserved.
+ *      912  -  1023   Disk specific descriptor.
+ */
+
+/*
+ * If x is the real device size in bytes, we return an apparent size of:
+ *
+ *     y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES
+ *
+ * and place the 4kB superblock at offset y.
+ */
+#define MD_RESERVED_BYTES              (64 * 1024)
+#define MD_RESERVED_SECTORS            (MD_RESERVED_BYTES / 512)
+#define MD_RESERVED_BLOCKS             (MD_RESERVED_BYTES / BLOCK_SIZE)
+
+#define MD_NEW_SIZE_SECTORS(x)         ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS)
+#define MD_NEW_SIZE_BLOCKS(x)          ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS)
+
+#define MD_SB_BYTES                    4096
+#define MD_SB_WORDS                    (MD_SB_BYTES / 4)
+#define MD_SB_BLOCKS                   (MD_SB_BYTES / BLOCK_SIZE)
+#define MD_SB_SECTORS                  (MD_SB_BYTES / 512)
+
+/*
+ * The following are counted in 32-bit words
+ */
+#define        MD_SB_GENERIC_OFFSET            0
+#define MD_SB_PERSONALITY_OFFSET       64
+#define MD_SB_DISKS_OFFSET             128
+#define MD_SB_DESCRIPTOR_OFFSET                992
+
+#define MD_SB_GENERIC_CONSTANT_WORDS   32
+#define MD_SB_GENERIC_STATE_WORDS      32
+#define MD_SB_GENERIC_WORDS            (MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS)
+#define MD_SB_PERSONALITY_WORDS                64
+#define MD_SB_DISKS_WORDS              384
+#define MD_SB_DESCRIPTOR_WORDS         32
+#define MD_SB_RESERVED_WORDS           (1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS)
+#define MD_SB_EQUAL_WORDS              (MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS)
+#define MD_SB_DISKS                    (MD_SB_DISKS_WORDS / MD_SB_DESCRIPTOR_WORDS)
+
+/*
+ * Device "operational" state bits
+ */
+#define MD_FAULTY_DEVICE               0       /* Device is faulty / operational */
+#define MD_ACTIVE_DEVICE               1       /* Device is a part or the raid set / spare disk */
+#define MD_SYNC_DEVICE                 2       /* Device is in sync with the raid set */
+
+typedef struct md_device_descriptor_s {
+       __u32 number;           /* 0 Device number in the entire set */
+       __u32 major;            /* 1 Device major number */
+       __u32 minor;            /* 2 Device minor number */
+       __u32 raid_disk;        /* 3 The role of the device in the raid set */
+       __u32 state;            /* 4 Operational state */
+       __u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5];
+} md_descriptor_t;
+
+#define MD_SB_MAGIC            0xa92b4efc
+
+/*
+ * Superblock state bits
+ */
+#define MD_SB_CLEAN            0
+#define MD_SB_ERRORS           1
+
+typedef struct md_superblock_s {
+
+       /*
+        * Constant generic information
+        */
+       __u32 md_magic;         /*  0 MD identifier */
+       __u32 major_version;    /*  1 major version to which the set conforms */
+       __u32 minor_version;    /*  2 minor version to which the set conforms */
+       __u32 patch_version;    /*  3 patchlevel version to which the set conforms */
+       __u32 gvalid_words;     /*  4 Number of non-reserved words in this section */
+       __u32 set_magic;        /*  5 Raid set identifier */
+       __u32 ctime;            /*  6 Creation time */
+       __u32 level;            /*  7 Raid personality (mirroring, raid5, ...) */
+       __u32 size;             /*  8 Apparent size of each individual disk, in kB */
+       __u32 nr_disks;         /*  9 Number of total disks in the raid set */
+       __u32 raid_disks;       /* 10 Number of disks in a fully functional raid set */
+       __u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 11];
+
+       /*
+        * Generic state information
+        */
+       __u32 utime;            /*  0 Superblock update time */
+       __u32 state;            /*  1 State bits (clean, ...) */
+       __u32 active_disks;     /*  2 Number of currently active disks (some non-faulty disks might not be in sync) */
+       __u32 working_disks;    /*  3 Number of working disks */
+       __u32 failed_disks;     /*  4 Number of failed disks */
+       __u32 spare_disks;      /*  5 Number of spare disks */
+       __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 6];
+
+       /*
+        * Personality information
+        */
+       __u32 parity_algorithm;
+       __u32 chunk_size;
+       __u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 2];
+
+       /*
+        * Disks information
+        */
+       md_descriptor_t disks[MD_SB_DISKS];
+
+       /*
+        * Reserved
+        */
+       __u32 reserved[MD_SB_RESERVED_WORDS];
+
+       /*
+        * Active descriptor
+        */
+       md_descriptor_t descriptor;
+} md_superblock_t;
+
 #ifdef __KERNEL__
 
-#include <linux/types.h>
+#include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/blkdev.h>
 
 #define MAX_REAL     8         /* Max number of physical dev per md dev */
 #define MAX_MD_DEV   4         /* Max number of md dev */
+#define MAX_MD_THREADS 2       /* Max number of kernel threads */
 
 #define FACTOR(a)         ((a)->repartition & FACTOR_MASK)
 #define MAX_FAULT(a)      (((a)->repartition & FAULT_MASK)>>8)
@@ -77,6 +216,8 @@ struct real_dev
   int offset;                  /* Real device offset (in blocks) in md dev
                                   (only used in linear mode) */
   struct inode *inode;         /* Lock inode */
+  md_superblock_t *sb;
+  u32 sb_offset;
 };
 
 struct md_dev;
@@ -85,25 +226,39 @@ struct md_personality
 {
   char *name;
   int (*map)(struct md_dev *md_dev, kdev_t *rdev,
-            unsigned long *rsector, unsigned long size);
+                     unsigned long *rsector, unsigned long size);
+  int (*make_request)(struct md_dev *md_dev, int rw, struct buffer_head * bh);
+  void (*end_request)(struct buffer_head * bh, int uptodate);
   int (*run)(int minor, struct md_dev *md_dev);
   int (*stop)(int minor, struct md_dev *md_dev);
   int (*status)(char *page, int minor, struct md_dev *md_dev);
   int (*ioctl)(struct inode *inode, struct file *file,
               unsigned int cmd, unsigned long arg);
   int max_invalid_dev;
+  int (*error_handler)(struct md_dev *md_dev, kdev_t dev);
 };
 
 struct md_dev
 {
   struct real_dev devices[MAX_REAL];
   struct md_personality *pers;
+  md_superblock_t *sb;
+  int sb_dirty;
   int repartition;
   int busy;
   int nb_dev;
   void *private;
 };
 
+struct md_thread {
+       void                    (*run) (void *data);
+       void                    *data;
+       struct wait_queue       *wqueue;
+       __u32                   flags;
+};
+
+#define THREAD_WAKEUP  0
+
 extern struct md_dev md_dev[MAX_MD_DEV];
 extern int md_size[MAX_MD_DEV];
 
@@ -111,6 +266,10 @@ extern char *partition_name (kdev_t dev);
 
 extern int register_md_personality (int p_num, struct md_personality *p);
 extern int unregister_md_personality (int p_num);
+extern struct md_thread *md_register_thread (void (*run) (void *data), void *data);
+extern void md_unregister_thread (struct md_thread *thread);
+extern void md_wakeup_thread(struct md_thread *thread);
+extern int md_update_sb (int minor);
 
 #endif __KERNEL__
 #endif _MD_H
diff --git a/include/linux/pg.h b/include/linux/pg.h
new file mode 100644 (file)
index 0000000..c752a97
--- /dev/null
@@ -0,0 +1,63 @@
+/*     pg.h (c) 1998  Grant R. Guenther <grant@torque.net>
+                      Under the terms of the GNU public license
+
+
+       pg.h defines the user interface to the generic ATAPI packet
+        command driver for parallel port ATAPI devices (pg). The
+       driver is loosely modelled after the generic SCSI driver, sg,
+       although the actual interface is different.
+
+       The pg driver provides a simple character device interface for
+        sending ATAPI commands to a device.  With the exception of the
+       ATAPI reset operation, all operations are performed by a pair
+        of read and write operations to the appropriate /dev/pgN device.
+       A write operation delivers a command and any outbound data in
+        a single buffer.  Normally, the write will succeed unless the
+        device is offline or malfunctioning, or there is already another
+       command pending.  If the write succeeds, it should be followed
+        immediately by a read operation, to obtain any returned data and
+        status information.  A read will fail if there is no operation
+        in progress.
+
+       As a special case, the device can be reset with a write operation,
+        and in this case, no following read is expected, or permitted.
+
+       There are no ioctl() operations.  Any single operation
+       may transfer at most PG_MAX_DATA bytes.  Note that the driver must
+        copy the data through an internal buffer.  In keeping with all
+       current ATAPI devices, command packets are assumed to be exactly
+       12 bytes in length.
+
+       To permit future changes to this interface, the headers in the
+       read and write buffers contain a single character "magic" flag.
+        Currently this flag must be the character "P".
+
+*/
+
+#define PG_MAGIC       'P'
+#define PG_RESET       'Z'
+#define PG_COMMAND     'C'
+
+#define PG_MAX_DATA    32768
+
+struct pg_write_hdr {
+
+       char    magic;          /* == PG_MAGIC */
+       char    func;           /* PG_RESET or PG_COMMAND */
+       int     dlen;           /* number of bytes expected to transfer */
+       int     timeout;        /* number of seconds before timeout */
+       char    packet[12];     /* packet command */
+
+};
+
+struct pg_read_hdr {
+
+       char    magic;          /* == PG_MAGIC */
+       char    scsi;           /* "scsi" status == sense key */
+       int     dlen;           /* size of device transfer request */
+       int     duration;       /* time in seconds command took */
+       char    pad[12];        /* not used */
+
+};
+
+/* end of pg.h */
diff --git a/include/linux/raid1.h b/include/linux/raid1.h
new file mode 100644 (file)
index 0000000..c17adce
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _RAID1_H
+#define _RAID1_H
+
+#include <linux/md.h>
+
+struct mirror_info {
+       int             number;
+       int             raid_disk;
+       kdev_t          dev;
+       int             operational;
+       int             next;
+       int             sect_limit;
+};
+
+struct raid1_data {
+       struct md_dev *mddev;
+       struct mirror_info mirrors[MD_SB_DISKS];        /* RAID1 devices, 2 to MD_SB_DISKS */
+       int raid_disks;
+       int working_disks;                      /* Number of working disks */
+       int last_used;
+       unsigned long   next_sect;
+       int             sect_count;
+};
+
+/*
+ * this is our 'private' 'collective' RAID1 buffer head.
+ * it contains information about what kind of IO operations were started
+ * for this RAID5 operation, and about their status:
+ */
+
+struct raid1_bh {
+       unsigned int remaining;
+       unsigned int state;
+       int cmd;
+       struct md_dev *mddev;                    /* we could use bh->personality? */
+       struct buffer_head *master_bh;
+       struct buffer_head *mirror_bh [MD_SB_DISKS];
+       struct buffer_head *next_retry;
+};
+
+#endif
diff --git a/include/linux/raid5.h b/include/linux/raid5.h
new file mode 100644 (file)
index 0000000..cb26e45
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _RAID5_H
+#define _RAID5_H
+
+#include <linux/md.h>
+#include <asm/atomic.h>
+
+struct disk_info {
+       kdev_t  dev;
+       int     operational;
+       int     number;
+       int     raid_disk;
+};
+
+struct raid5_data {
+       struct md_dev           *mddev;
+       struct md_thread        *thread;
+       struct disk_info        disks[MD_SB_DISKS];
+       int                     buffer_size;
+       int                     chunk_size, level, algorithm;
+       int                     raid_disks, working_disks, failed_disks;
+       int                     sector_count;
+       unsigned long           next_sector;
+       atomic_t                nr_handle;
+};
+
+/*
+ * Our supported algorithms
+ */
+#define ALGORITHM_LEFT_ASYMMETRIC      0
+#define ALGORITHM_RIGHT_ASYMMETRIC     1
+#define ALGORITHM_LEFT_SYMMETRIC       2
+#define ALGORITHM_RIGHT_SYMMETRIC      3
+
+#endif
index 9a0fa860a26a94c56cd4972eb5416e3776975ab3..49858f58612ada7472cb17facf1df62cbf473f69 100644 (file)
@@ -64,6 +64,7 @@ struct msghdr
 #define AF_INET6       10      /* IP version 6                 */
 #endif
 #define AF_MAX         12      /* For now.. */
+#define AF_PACKET      17      /* Forward compat hook          */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC      AF_UNSPEC
@@ -80,7 +81,7 @@ struct msghdr
 #define PF_INET6       AF_INET6
 #endif
 #define PF_MAX         AF_MAX
-
+#define PF_PACKET      AF_PACKET
 /* Maximum queue length specifiable by listen.  */
 #define SOMAXCONN      128
 
index f940324976886779a2687e0e527cc7ebd8349a01..c1c25ff857e149bcb39990b25021ee424ef73300 100644 (file)
@@ -182,6 +182,21 @@ extern void specialix_setup(char *str, int *ints);
 extern void baycom_setup(char *str, int *ints);
 #endif
 
+#ifdef CONFIG_PARIDE_PD
+extern void pd_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PF
+extern void pf_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PT
+extern void pt_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PG
+extern void pg_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PCD
+extern void pcd_setup(char *str, int *ints);
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -255,10 +270,12 @@ static void profile_setup(char *str, int *ints)
 #endif
 }
 
-struct {
+struct kernel_param {
        const char *str;
        void (*setup_func)(char *, int *);
-} bootsetups[] = {
+} ;
+
+struct kernel_param bootsetups[] = {
        { "reserve=", reserve_setup },
        { "profile=", profile_setup },
 #ifdef CONFIG_BLK_DEV_RAM
@@ -444,6 +461,27 @@ struct {
        { 0, 0 }
 };
 
+static struct kernel_param raw_params[] = {
+
+#ifdef CONFIG_PARIDE_PD
+       { "pd.", pd_setup },
+#endif
+#ifdef CONFIG_PARIDE_PCD
+       { "pcd.", pcd_setup },
+#endif
+#ifdef CONFIG_PARIDE_PF
+       { "pf.", pf_setup },
+#endif
+#ifdef CONFIG_PARIDE_PT
+       { "pt.", pt_setup },
+#endif
+#ifdef CONFIG_PARIDE_PG
+       { "pg.", pg_setup },
+#endif
+       { 0, 0 }
+} ;
+
+
 #ifdef CONFIG_BLK_DEV_RAM
 static void ramdisk_start_setup(char *str, int *ints)
 {
@@ -491,6 +529,15 @@ static int checksetup(char *line)
                }
                i++;
        }
+
+        for (i=0; raw_params[i].str; i++) {
+                int n = strlen(raw_params[i].str);
+                if (!strncmp(line,raw_params[i].str,n)) {
+                        raw_params[i].setup_func(line+n, NULL);
+                        return 1;
+                }
+        }
+
        return 0;
 }
 
@@ -599,6 +646,18 @@ static void parse_root_dev(char * line)
                { "gscd",    0x1000 },
                { "sbpcd",   0x1900 },
                { "sonycd",  0x1800 },
+#ifdef CONFIG_PARIDE_PD
+                       { "pda",     0x2d00 },
+                       { "pdb",     0x2d10 },
+                       { "pdc",     0x2d20 },
+                       { "pdd",     0x2d30 },
+#endif
+#ifdef CONFIG_PARIDE_PCD
+               { "pcd",     0x2e00 },
+#endif
+#ifdef CONFIG_PARIDE_PF
+               { "pf",      0x2f00 },
+#endif
                { NULL, 0 }
        };
 
index 1116e915fe7e92a67e4fb19c61d4319fb1f77076..557c07d8fdfb16bedad22ab8c035a78407767ae6 100644 (file)
@@ -203,6 +203,9 @@ struct symbol_table symbol_table = {
        X(blkdev_release),
        X(gendisk_head),
        X(resetup_one_dev),
+       X(unplug_device),
+       X(make_request),
+       X(tq_disk),
 
 #ifdef CONFIG_SERIAL   
        /* Module creation of serial units */
index efcfc99daf689559fc0b218c4416af1adc3f2286..6e3e862f251d3af0a344694e762591195bcd862b 100644 (file)
@@ -87,6 +87,7 @@ unsigned long prof_shift = 0;
 #define _S(nr) (1<<((nr)-1))
 
 extern void mem_use(void);
+extern unsigned long get_wchan(struct task_struct *);
 
 static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, };
 unsigned long init_user_stack[1024] = { STACK_MAGIC, };
@@ -1681,11 +1682,13 @@ static void show_task(int nr,struct task_struct * p)
                printk(" current  ");
        else
                printk(" %08lX ", thread_saved_pc(&p->tss));
+       printk("%08lX ", get_wchan(p));
 #else
        if (p == current)
                printk("   current task   ");
        else
                printk(" %016lx ", thread_saved_pc(&p->tss));
+       printk("%08lX ", get_wchan(p) & 0xffffffffL);
 #endif
        for (free = 1; free < PAGE_SIZE/sizeof(long) ; free++) {
                if (((unsigned long *)p->kernel_stack_page)[free])
@@ -1712,12 +1715,12 @@ void show_state(void)
 
 #if ((~0UL) == 0xffffffff)
        printk("\n"
-              "                         free                        sibling\n");
-       printk("  task             PC    stack   pid father child younger older\n");
+              "                                  free                        sibling\n");
+       printk("  task             PC     wchan   stack   pid father child younger older\n");
 #else
        printk("\n"
-              "                                 free                        sibling\n");
-       printk("  task                 PC        stack   pid father child younger older\n");
+              "                                           free                        sibling\n");
+       printk("  task                 PC         wchan    stack   pid father child younger older\n");
 #endif
        for (i=0 ; i<NR_TASKS ; i++)
                if (task[i])
index cca6b56ed6e160e8c3819bef2f3f4c5460cb22a0..966d121b3991c3c6bf2b35f5f890c7dfe9db2492 100644 (file)
@@ -27,6 +27,8 @@
 int nr_swap_pages = 0;
 int nr_free_pages = 0;
 
+extern struct wait_queue *buffer_wait;
+
 /*
  * Free area management
  *
@@ -120,6 +122,9 @@ static inline void free_pages_ok(unsigned long map_nr, unsigned long order)
 #undef list
 
        restore_flags(flags);
+       if (!waitqueue_active(&buffer_wait))
+               return;
+       wake_up(&buffer_wait);
 }
 
 void __free_page(struct page *page)
index dc8b276165a320c4fc0d9c9ca08cdc05fd26b812..2f44e211cc2089e719bfb2eeb735a3581057f536 100644 (file)
@@ -97,7 +97,7 @@ static struct packet_type rarp_packet_type =
        NULL
 };
 
-static initflag = 1;
+static int initflag = 1;
 
 
 /*
index 411b6d6848b5fb8ad994d520c63a217c3700ea66..db65786bc02ee6daa733973a3a4a216a1b424417 100644 (file)
  *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *     Willy Konynenberg       :       Transparent proxy adapted to new
  *                                     socket hash code.
+ *     J Hadi Salim            :       We assumed that some idiot wasnt going
+ *     Alan Cox                        to idly redefine bits of ToS in an
+ *                                     experimental protocol for other things
+ *                                     (ECN) - wrong!. Mask the bits off. Note
+ *                                     masking the bits if they dont use ECN
+ *                                     then use it for ToS is even more 
+ *                                     broken. 
+ *                                     </RANT>
  */
 
 #include <linux/config.h>
 #include <linux/random.h>
 #include <net/tcp.h>
 
+/*
+ *     Do we assume the IP ToS is entirely for its intended purpose
+ */
+#define TOS_VALID_MASK(x)              ((x)&0x3F)
 /*
  *     Policy code extracted so it's now separate
  */
@@ -764,7 +778,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
         */
 
        newsk->ip_ttl=sk->ip_ttl;
-       newsk->ip_tos=skb->ip_hdr->tos;
+       newsk->ip_tos=TOS_VALID_MASK(skb->ip_hdr->tos);
 
        /*
         *      Use 512 or whatever user asked for 
@@ -1024,7 +1038,7 @@ static int tcp_conn_request_fake(struct sock *sk, struct sk_buff *skb,
         */
 
        newsk->ip_ttl=sk->ip_ttl;
-       newsk->ip_tos=skb->ip_hdr->tos;
+       newsk->ip_tos=TOS_VALID_MASK(skb->ip_hdr->tos);
 
        rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0,
                         sk->bound_device);
@@ -2388,6 +2402,14 @@ retry_search:
         
                if(sk->state==TCP_LISTEN)
                {
+                       /* Don't start connections with illegal address
+                          ranges. Trying to talk TCP to a broken dhcp host
+                          isnt good on a lan with broken SunOS 4.x boxes 
+                          who think its a broadcast */
+                          
+                       if ((saddr | daddr) == 0)
+                               goto discard_it;
+                               
                        if (th->ack) {  /* These use the socket TOS.. might want to be the received TOS */
 #ifdef CONFIG_SYN_COOKIES
                                if (!th->syn && !th->rst) {
index 2cd03603eaa533d8d3705429911b6a250ea05d7d..24f983ec6ee0184ff70d155ea356958d196bf305 100644 (file)
@@ -34,6 +34,8 @@
  *                               refer to the old obsolete destination.
  *              Elliot Poger    : Added support for SO_BINDTODEVICE.
  *     Juan Jose Ciarlante     : Added sock dynamic source address rewriting
+ *             Alan Cox        : Clear reserved fields - bug reported by
+ *                               J Hadi Salim
  */
 
 #include <linux/config.h>
@@ -837,6 +839,8 @@ void tcp_send_synack_probe(unsigned long saddr, unsigned long daddr, struct tcph
        t1->psh = 0;
        t1->fin = 0;            /* In case someone sent us a SYN|FIN frame! */
        t1->doff = sizeof(*t1)/4;
+       t1->res1 = 0;   /* RFC requires this, we upset ECN without it */
+       t1->res2 = 0;
 
        tcp_send_check(t1, saddr, daddr, sizeof(*t1), buff);
        prot->queue_xmit(NULL, ndev, buff, 1);
@@ -1017,6 +1021,8 @@ void tcp_send_synack(struct sock * newsk, struct sock * sk, struct sk_buff * skb
        t1->psh = 0;
        t1->ack_seq = htonl(newsk->acked_seq);
        t1->doff = sizeof(*t1)/4+1;
+       t1->res1 = 0;
+       t1->res2 = 0;
        ptr = skb_put(buff,4);
        ptr[0] = 2;
        ptr[1] = 4;
index 9225e10315f87ac63c85b0e804c5ace94bc25462..ce40e9af4349b6f84e73b8572626b0b7b7b7cd67 100644 (file)
@@ -532,6 +532,12 @@ asmlinkage int sys_socket(int family, int type, int protocol)
        struct socket *sock;
        struct proto_ops *ops;
 
+       if(family==AF_PACKET)
+       {
+               family=AF_INET;
+               type=SOCK_PACKET;
+       }
+       
        /* Locate the correct protocol family. */
        i = find_protocol_family(family);