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
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
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
-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.
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.
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:
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.
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.
--- /dev/null
+
+ 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/
+
+
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
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
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
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
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
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;
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;
}
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]){
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;
}
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/smp.h>
+#include <asm/io.h>
/*
* Tell us the machine setup..
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];
{
unsigned char nr6x86 = 0;
static const char *model[] = {
- "unknown", "6x86", "6x86L", "6x86MX", "6x86MXi"
+ "unknown", "6x86", "6x86L", "6x86MX", "MII"
};
switch (x86) {
case 5:
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 */
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;
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_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
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
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
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
sz+=sprintf (page+sz, "\n");
#endif
+ sz+=sprintf (page+sz, " %dk rounding", 1<<FACTOR_SHIFT(FACTOR(mddev)));
return sz;
}
{
"linear",
linear_map,
+ NULL,
+ NULL,
linear_run,
linear_stop,
linear_status,
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;
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);
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;
}
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;
}
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;
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;
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;
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]);
}
}
#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
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, };
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);
}
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
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)
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 !!! */
read_ahead[MD_MAJOR]=current_ra;
- printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name);
return (0);
}
/* 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;
/* 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);
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)
{
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>
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;
});
}
+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)
{
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)
{
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);
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);
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))
{
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;
#ifdef CONFIG_MD_STRIPED
raid0_init ();
#endif
+#ifdef CONFIG_MD_MIRRORING
+ raid1_init ();
+#endif
+#ifdef CONFIG_MD_RAID5
+ raid5_init ();
+#endif
return (0);
}
--- /dev/null
+#
+# 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
+#
--- /dev/null
+#
+# 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
+
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+#!/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
+
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/* 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);
+
+
data->strip_zone[j].size);
}
#endif
+ sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(mddev)));
return sz;
}
{
"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 */
};
--- /dev/null
+/************************************************************************
+ * 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
--- /dev/null
+/*****************************************************************************
+ * 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
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;
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);
{
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)
* 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)
* 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;
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);
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 */
}
/* 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() */
/****************************************************************************
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 *)
/*
* ftape-io.c defined global vars.
*/
-extern bad_sector_map_changed;
+extern int bad_sector_map_changed;
/*
* ftape-io.c defined global functions.
*/
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;
/* 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
{
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
#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 *);
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,
*/
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 */
/* "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;
/* "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;
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
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.
*/
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;
/* 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);
}
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);
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;
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
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);
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);
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);
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);
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);
#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. */
}
#endif
-#ifdef 0
+#if 0
if(!at_least_one)
{
int i;
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
}
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. */
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. */
* 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
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 */
};
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] = {
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 */
/*****************************************************************************
*
* 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.
*
**************************************************************/
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
);
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" );
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 );
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;
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 */
* 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
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
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);
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++;
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;
}
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",
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;
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;
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;
*/
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;
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 );
}
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;
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 ) {
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;
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 */
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;
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 );
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 */
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;
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;
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");
}
}
* 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.
*
**************************************************************/
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" );
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;
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;
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;
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 */
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;
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 ) {
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;
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;
}
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 )
} /* 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 */
+
+
/***************************************************************
* 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.
*
**************************************************************/
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 );
}
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 );
*
**************************************************************/
-int TLan_Reset( struct device *dev )
+void
+TLan_ResetAdapter( struct device *dev )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
int i;
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 */
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
* 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).
*
********************************************************************/
{
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 */
/*********************************************************************
- * TLan_PhySelect
+ * TLan_PhyDetect
*
* Returns:
* Nothing
* 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 */
* 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
*
**************************************************************/
-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 */
*val = tmp;
- sti();
+ dev->interrupt--;
+ if ( dev->interrupt == 0 )
+ sti();
return err;
* 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.
*
**************************************************************/
-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 );
if ( minten )
TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
- sti();
+ dev->interrupt--;
+ if ( dev->interrupt == 0 )
+ sti();
} /* TLan_MiiWriteReg */
+
+
+
/*****************************************************************************
******************************************************************************
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 );
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 );
}
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 );
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 );
}
*
* 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
*
**************************************************************/
-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;
* 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
****************************************************************/
#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
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;
****************************************************************/
#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
*
****************************************************************/
-#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 */
#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
#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
#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
/* 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
#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
#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 */
#define TLAN_TS_RESERVED 0x0FFF
+#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
/* Routines to access internal registers. */
-
+#if 0
inline void TLan_ClearBit(u8 bit, u16 port)
{
outb_p(inb_p(port) & ~bit, 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 )
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
/* 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
*
* 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;
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
*
* 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.
}
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);
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 */
-/* $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;
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;
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);
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);
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,
*/
#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
#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)
#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
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
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 *);
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);
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;
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++) {
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",
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];
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;
* 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 */
/*
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",
* 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 */
}
/*
*/
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;
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);
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,
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 {
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);
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");
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);
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");
/*
* 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;
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'.
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);
* 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);
* 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;
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:
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;
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.
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);
* 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);
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:
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;
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 &&
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.
*/
asc_srch_pci_dev(PCI_DEVICE *pciDevice)
)
{
- int ret;
+ int ret = PCI_DEVICE_NOT_FOUND;
ASC_DBG(2, "asc_srch_pci_dev: begin\n");
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;
}
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;
}
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'
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;
}
}
* 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.
*
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);
*/
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;
/*
* Third word
*/
- w = serialnum[2];
+ w = serialnum[2] & 0x7FFF;
/* Serial number - 9th digit. */
*cp++ = 'A' + (w / 1000);
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);
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 */
}
/*
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);
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 */
}
/*
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);
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 */
}
/*
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);
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 */
}
/*
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",
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] =
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;
}
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;
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;
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(
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
}
-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,
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 */
/*
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;
}
/* 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 */
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, ");
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);
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
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;
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,
/* 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--;
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) )
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) )
/*----------------------------------------------------------------*/
/* reset SCSI bus */
-int qlogicfas_reset(Scsi_Cmnd * cmd)
+int qlogicfas_reset(Scsi_Cmnd * cmd, unsigned int flags)
{
qabort = 2;
ql_zap();
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
#define BLIST_SINGLELUN 0x10
#define BLIST_NOTQ 0x20
#define BLIST_SPARSELUN 0x40
+#define BLIST_MAX5LUN 0x80
struct dev_info{
const char * vendor;
{"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},
*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
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.
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;
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);
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
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) {
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 */
/*
* 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);
}
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
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
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,};
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),
/* 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;
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);
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;
}
* 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;
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;
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;
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;
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;
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;
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);
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) {
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)
#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.
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) {
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];
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
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)
-
/*
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)
#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)
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;
{
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];
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
--- /dev/null
+/* 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 */
--- /dev/null
+#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
--- /dev/null
+#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
#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
#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
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);
#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
{ 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)
{
}
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;
}
{ "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 }
};
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 */
#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, };
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])
#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])
int nr_swap_pages = 0;
int nr_free_pages = 0;
+extern struct wait_queue *buffer_wait;
+
/*
* Free area management
*
#undef list
restore_flags(flags);
+ if (!waitqueue_active(&buffer_wait))
+ return;
+ wake_up(&buffer_wait);
}
void __free_page(struct page *page)
NULL
};
-static initflag = 1;
+static int initflag = 1;
/*
* 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
*/
*/
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
*/
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);
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) {
* 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>
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);
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;
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);