From 2c49bdb10bbeb89ee3185f86d4cc41df4dd2d577 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:11:47 -0500 Subject: [PATCH] Import 2.0.35pre2 --- CREDITS | 6 +- Documentation/Configure.help | 239 +++ Documentation/networking/tlan.README | 72 +- Documentation/paride.txt | 360 +++++ MAINTAINERS | 6 + Makefile | 4 + arch/i386/kernel/head.S | 79 +- arch/i386/kernel/ptrace.c | 225 ++- arch/i386/kernel/setup.c | 27 +- arch/sparc/config.in | 2 + drivers/block/Config.in | 6 + drivers/block/Makefile | 43 +- drivers/block/linear.c | 3 + drivers/block/ll_rw_blk.c | 53 +- drivers/block/md.c | 392 ++++- drivers/block/paride/Config.in | 24 + drivers/block/paride/Makefile | 174 ++ drivers/block/paride/aten.c | 172 ++ drivers/block/paride/bpck.c | 482 ++++++ drivers/block/paride/comm.c | 228 +++ drivers/block/paride/dstr.c | 243 +++ drivers/block/paride/epat.c | 321 ++++ drivers/block/paride/epia.c | 326 ++++ drivers/block/paride/fit2.c | 161 ++ drivers/block/paride/fit3.c | 221 +++ drivers/block/paride/frpw.c | 310 ++++ drivers/block/paride/kbic.c | 311 ++++ drivers/block/paride/ktti.c | 138 ++ drivers/block/paride/mkd | 30 + drivers/block/paride/on20.c | 163 ++ drivers/block/paride/on26.c | 267 ++++ drivers/block/paride/paride.c | 514 ++++++ drivers/block/paride/paride.h | 164 ++ drivers/block/paride/pcd.c | 807 ++++++++++ drivers/block/paride/pd.c | 1107 +++++++++++++ drivers/block/paride/pf.c | 1080 +++++++++++++ drivers/block/paride/pg.c | 682 ++++++++ drivers/block/paride/pseudo.h | 145 ++ drivers/block/paride/pt.c | 940 +++++++++++ drivers/block/paride/setup.h | 69 + drivers/block/paride/spinlock.h | 6 + drivers/block/raid0.c | 6 +- drivers/block/raid1.c | 766 +++++++++ drivers/block/raid5.c | 1504 ++++++++++++++++++ drivers/block/rd.c | 2 +- drivers/cdrom/cm206.c | 2 +- drivers/cdrom/mcd.c | 5 +- drivers/cdrom/sonycd535.c | 15 +- drivers/char/ftape/ftape-bsm.h | 2 +- drivers/char/ftape/ftape-ctl.h | 2 +- drivers/char/ftape/kernel-interface.h | 4 +- drivers/isdn/isdn_net.c | 2 +- drivers/isdn/sc/interrupt.c | 2 +- drivers/net/3c503.c | 4 +- drivers/net/3c515.c | 4 +- drivers/net/3c59x.c | 4 +- drivers/net/Changelog.tlan | 49 +- drivers/net/dgrs.c | 4 +- drivers/net/e2100.c | 4 +- drivers/net/eth16i.c | 2 +- drivers/net/hp-plus.c | 8 +- drivers/net/hp.c | 4 +- drivers/net/ne2k-pci.c | 2 +- drivers/net/ni52.c | 2 +- drivers/net/smc-ultra.c | 6 +- drivers/net/smc-ultra32.c | 2 +- drivers/net/tlan.c | 1496 ++++++++++-------- drivers/net/tlan.h | 242 +-- drivers/net/wd.c | 2 +- drivers/net/yellowfin.c | 2 +- drivers/scsi/AM53C974.c | 2 +- drivers/scsi/AM53C974.h | 2 +- drivers/scsi/FlashPoint.c | 2 +- drivers/scsi/NCR53c406a.c | 2 +- drivers/scsi/NCR53c406a.h | 2 +- drivers/scsi/advansys.c | 2102 +++++++++++-------------- drivers/scsi/aha152x.c | 7 +- drivers/scsi/aha1542.c | 2 +- drivers/scsi/fdomain.c | 4 +- drivers/scsi/fdomain.h | 2 +- drivers/scsi/hosts.c | 2 +- drivers/scsi/pas16.c | 4 +- drivers/scsi/qlogicfas.c | 2 +- drivers/scsi/qlogicfas.h | 2 +- drivers/scsi/scsi.c | 15 +- drivers/scsi/scsi.h | 19 +- drivers/scsi/sd.c | 2 +- drivers/scsi/t128.c | 4 +- drivers/scsi/wd7000.c | 4 +- drivers/scsi/wd7000.h | 2 +- fs/Config.in | 2 +- fs/buffer.c | 18 +- fs/isofs/inode.c | 2 +- fs/proc/array.c | 2 +- fs/super.c | 1 + fs/sysv/inode.c | 5 +- include/asm-i386/ptrace.h | 6 + include/linux/blk.h | 22 +- include/linux/blkdev.h | 3 + include/linux/fs.h | 24 +- include/linux/md.h | 175 +- include/linux/pg.h | 63 + include/linux/raid1.h | 41 + include/linux/raid5.h | 34 + include/linux/socket.h | 3 +- init/main.c | 63 +- kernel/ksyms.c | 3 + kernel/sched.c | 11 +- mm/page_alloc.c | 5 + net/ipv4/rarp.c | 2 +- net/ipv4/tcp_input.c | 26 +- net/ipv4/tcp_output.c | 6 + net/socket.c | 6 + 113 files changed, 15197 insertions(+), 2260 deletions(-) create mode 100644 Documentation/paride.txt create mode 100644 drivers/block/paride/Config.in create mode 100644 drivers/block/paride/Makefile create mode 100644 drivers/block/paride/aten.c create mode 100644 drivers/block/paride/bpck.c create mode 100644 drivers/block/paride/comm.c create mode 100644 drivers/block/paride/dstr.c create mode 100644 drivers/block/paride/epat.c create mode 100644 drivers/block/paride/epia.c create mode 100644 drivers/block/paride/fit2.c create mode 100644 drivers/block/paride/fit3.c create mode 100644 drivers/block/paride/frpw.c create mode 100644 drivers/block/paride/kbic.c create mode 100644 drivers/block/paride/ktti.c create mode 100644 drivers/block/paride/mkd create mode 100644 drivers/block/paride/on20.c create mode 100644 drivers/block/paride/on26.c create mode 100644 drivers/block/paride/paride.c create mode 100644 drivers/block/paride/paride.h create mode 100644 drivers/block/paride/pcd.c create mode 100644 drivers/block/paride/pd.c create mode 100644 drivers/block/paride/pf.c create mode 100644 drivers/block/paride/pg.c create mode 100644 drivers/block/paride/pseudo.h create mode 100644 drivers/block/paride/pt.c create mode 100644 drivers/block/paride/setup.h create mode 100644 drivers/block/paride/spinlock.h create mode 100644 drivers/block/raid1.c create mode 100644 drivers/block/raid5.c create mode 100644 include/linux/pg.h create mode 100644 include/linux/raid1.h create mode 100644 include/linux/raid5.h diff --git a/CREDITS b/CREDITS index bc06a6f0943f..1780cb1514d7 100644 --- a/CREDITS +++ b/CREDITS @@ -1594,11 +1594,11 @@ S: Fin-00150 Helsinki S: Finland N: Roger E. Wolff -E: wolff@dutecai.et.tudelft.nl +E: R.E.Wolff@BitWizard.nl D: Written kmalloc/kfree D: Written Specialix IO8+ driver -S: Oosterstraat 23 -S: 2611 TT Delft +S: Van Bronckhorststraat 12 +S: 2612 XV Delft S: The Netherlands N: Frank Xia diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 36f9e4c06863..9031256d1eb7 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -320,6 +320,220 @@ CONFIG_BLK_DEV_XD Documentation/modules.txt. It's pretty unlikely that you have one of these: say N. +Parallel port IDE device support +CONFIG_PARIDE + There are many external CD-ROM and disk devices that connect through + your computer's parallel port. Most of them are actually IDE devices + using a parallel port IDE adapter. This option enables the PARIDE + subsystem which contains drivers for many of these external drives. + Read linux/Documentation/paride.txt for more information. If you + built PARIDE support into your kernel, you may still build the + individual protocol modules and high-level drivers as loadable + modules. If you build this support as a module, it will be called + paride.o. To use the PARIDE support, you must say Y or M here + and also to at least one high-level driver (e.g. "Parallel port + IDE disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI + disks" etc.) and to at least one protocol driver (e.g. "ATEN + EH-100 protocol", "MicroSolutions backpack protocol", "DataStor + Commuter protocol" etc.). + +Parallel port IDE disks +CONFIG_PARIDE_PD + This option enables the high-level driver for IDE-type disk devices + connected through a parallel port. If you chose to build PARIDE + support into your kernel, you may answer Y here to build in the + parallel port IDE driver, otherwise you should answer M to build + it as a loadable module. The module will be called pd.o. You + must also have at least one parallel port protocol driver in your + system. Among the devices supported by this driver are the SyQuest + EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack + hard drives from MicroSolutions. + +Parallel port ATAPI CD-ROMs +CONFIG_PARIDE_PCD + This option enables the high-level driver for ATAPI CD-ROM devices + connected through a parallel port. If you chose to build PARIDE + support into your kernel, you may answer Y here to build in the + parallel port ATAPI CD-ROM driver, otherwise you should answer M to + build it as a loadable module. The module will be called pcd.o. You + must also have at least one parallel port protocol driver in your + system. Among the devices supported by this driver are the + MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If + you have such a CD-ROM drive, you should also say Y to "ISO9660 + cdrom filesystem support" below, because that's the filesystem used + on CDROMs. + +Parallel port ATAPI disks +CONFIG_PARIDE_PF + This option enables the high-level driver for ATAPI disk devices + connected through a parallel port. If you chose to build PARIDE + support into your kernel, you may answer Y here to build in the + parallel port ATAPI disk driver, otherwise you should answer M + to build it as a loadable module. The module will be called pf.o. + You must also have at least one parallel port protocol driver in + your system. Among the devices supported by this driver are the + MicroSolutions backpack PD/CD drive and the Imation Superdisk + LS-120 drive. + +Parallel port ATAPI tapes +CONFIG_PARIDE_PT + This option enables the high-level driver for ATAPI tape devices + connected through a parallel port. If you chose to build PARIDE + support into your kernel, you may answer Y here to build in the + parallel port ATAPI tape driver, otherwise you should answer M + to build it as a loadable module. The module will be called pt.o. + You must also have at least one parallel port protocol driver in + your system. Among the devices supported by this driver is the + parallel port version of the HP 5GB drive. + +Parallel port generic ATAPI devices +CONFIG_PARIDE_PG + This option enables a special high-level driver for generic ATAPI + devices connected through a parallel port. The driver allows user + programs, such as cdrecord, to send ATAPI commands directly to a + device. If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the parallel port generic ATAPI driver, + otherwise you should answer M to build it as a loadable module. + The module will be called pg.o. You must also have at least one + parallel port protocol driver in your system. This driver + implements an API loosely related to the generic SCSI driver. + See /usr/include/linux/pg.h for details, or visit + http://www.torque.net/parport/cdr.html for more information and + the required patches to cdrecord. + +ATEN EH-100 protocol +CONFIG_PARIDE_ATEN + This option enables support for the ATEN EH-100 parallel port IDE + protocol. This protocol is used in some inexpensive low performance + parallel port kits made in Hong Kong. If you chose to build PARIDE + support into your kernel, you may answer Y here to build in the + protocol driver, otherwise you should answer M to build it as a + loadable module. The module will be called aten.o. You must also + have a high-level driver for the type of device that you want to + support. + +MicroSolutions backpack protocol +CONFIG_PARIDE_BPCK + This option enables support for the MicroSolutions backpack + parallel port IDE protocol. If you chose to build PARIDE support + into your kernel, you may answer Y here to build in the protocol + driver, otherwise you should answer M to build it as a loadable + module. The module will be called bpck.o. You must also have + a high-level driver for the type of device that you want to support. + +DataStor Commuter protocol +CONFIG_PARIDE_COMM + This option enables support for the Commuter parallel port IDE + protocol from DataStor. If you chose to build PARIDE support + into your kernel, you may answer Y here to build in the protocol + driver, otherwise you should answer M to build it as a loadable + module. The module will be called comm.o. You must also have + a high-level driver for the type of device that you want to support. + +DataStor EP-2000 protocol +CONFIG_PARIDE_DSTR + This option enables support for the EP-2000 parallel port IDE + protocol from DataStor. If you chose to build PARIDE support + into your kernel, you may answer Y here to build in the protocol + driver, otherwise you should answer M to build it as a loadable + module. The module will be called dstr.o. You must also have + a high-level driver for the type of device that you want to support. + +Shuttle EPAT/EPEZ protocol +CONFIG_PARIDE_EPAT + This option enables support for the EPAT parallel port IDE + protocol. EPAT is a parallel port IDE adapter manufactured by + Shuttle Technology and widely used in devices from major vendors + such as Hewlett-Packard, SyQuest, Imation and Avatar. If you + chose to build PARIDE support into your kernel, you may answer Y + here to build in the protocol driver, otherwise you should answer M + to build it as a loadable module. The module will be called epat.o. + You must also have a high-level driver for the type of device that + you want to support. + +Shuttle EPIA protocol +CONFIG_PARIDE_EPIA + This option enables support for the (obsolete) EPIA parallel port + IDE protocol from Shuttle Technology. This adapter can still be found + in some no-name kits. If you chose to build PARIDE support into your + kernel, you may answer Y here to build in the protocol driver, + otherwise you should answer M to build it as a loadable module. + The module will be called epia.o. You must also have a high-level + driver for the type of device that you want to support. + +FIT TD-2000 protocol +CONFIG_PARIDE_FIT2 + This option enables support for the TD-2000 parallel port IDE protocol + from Fidelity International Technology. This is a simple (low speed) + adapter that is used in some portable hard drives. If you chose to + build PARIDE support into your kernel, you may answer Y here to + build in the protocol driver, otherwise you should answer M to + build it as a loadable module. The module will be called fit2.o. + You must also have a high-level driver for the type of device + that you want to support. + +FIT TD-3000 protocol +CONFIG_PARIDE_FIT3 + This option enables support for the TD-3000 parallel port IDE protocol + from Fidelity International Technology. This protocol is used in newer + models of their portable disk, CD-ROM and PD/CD devices. If you chose + to build PARIDE support into your kernel, you may answer Y here to + build in the protocol driver, otherwise you should answer M to + build it as a loadable module. The module will be called fit3.o. + You must also have a high-level driver for the type of device + that you want to support. + +FreeCom power protocol +CONFIG_PARIDE_FRPW + This option enables support for the Freecom power parallel port IDE + protocol. If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the protocol driver, otherwise you + should answer M to build it as a loadable module. The module will be + called frpw.o. You must also have a high-level driver for the type + of device that you want to support. + +KingByte KBIC-951A/971A protocols +CONFIG_PARIDE_KBIC + This option enables support for the KBIC-951A and KBIC-971A parallel + port IDE protocols from KingByte Information Corp. KingByte's adapters + appear in many no-name portable disk and CD-ROM products, especially + in Europe. If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the protocol driver, otherwise you should + answer M to build it as a loadable module. The module will be called + kbic.o. You must also have a high-level driver for the type of device + that you want to support. + +KT PHd protocol +CONFIG_PARIDE_KTTI + This option enables support for the "PHd" parallel port IDE protocol + from KT Technology. This is a simple (low speed) adapter that is + used in some 2.5" portable hard drives. If you chose to build PARIDE + support into your kernel, you may answer Y here to build in the + protocol driver, otherwise you should answer M to build it as a + loadable module. The module will be called ktti.o. You must also + have a high-level driver for the type of device that you want to + support. + +OnSpec 90c20 protocol +CONFIG_PARIDE_ON20 + This option enables support for the (obsolete) 90c20 parallel port + IDE protocol from OnSpec (often marketed under the ValuStore brand + name). If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the protocol driver, otherwise you + should answer M to build it as a loadable module. The module will + be called on20.o. You must also have a high-level driver for the + type of device that you want to support. + +OnSpec 90c26 protocol +CONFIG_PARIDE_ON26 + This option enables support for the 90c26 parallel port IDE protocol + from OnSpec Electronics (often marketed under the ValuStore brand + name). If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the protocol driver, otherwise you + should answer M to build it as a loadable module. The module will + be called on26.o. You must also have a high-level driver for the + type of device that you want to support. + Multiple devices driver support CONFIG_BLK_DEV_MD This driver lets you combine several harddisk partitions into one @@ -349,6 +563,31 @@ CONFIG_MD_STRIPED in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. If unsure, say Y. +RAID-1 (mirroring) mode +CONFIG_MD_MIRRORING + A RAID-1 set consists of several disk drives which are exact copies + of each other. In the event of a mirror failture, the RAID driver + will continue to use the operational mirrors in the set, providing + an error free MD device to the higher levels of the kernel. In + a set with N drives, the available space is the capacity of a single + drive, and the set protects against a failture of (N - 1) drives. + raidtools, a set of user-space tools which create and maintain + RAID1/4/5 sets, is available at: + http://luthien.nuclecu.unam.mx/~miguel/raid + +RAID-4/RAID-5 mode +CONFIG_MD_RAID5 + A RAID-5 set of N drives with a capacity of C MB per drive provides + the capacity of C * (N - 1) drives, and protects against a failture + of a single drive. For a given sector (row) number, (N - 1) drives + contain data sectors, and one drive contains the parity protection. + For a RAID-4 set, the parity blocks are present on a single drive, + while a RAID-5 set distributes the parity accross the drives in one + of the available parity distribution methods. + raidtools, a set of user-space tools which create and maintain + RAID1/4/5 sets, is available at: + http://luthien.nuclecu.unam.mx/~miguel/raid + Support for Deskstation RPC44 CONFIG_DESKSTATION_RPC44 This is a machine with a R4400 100 MHz CPU. To compile a Linux diff --git a/Documentation/networking/tlan.README b/Documentation/networking/tlan.README index a4818a3e663c..5e9b68e63f3f 100644 --- a/Documentation/networking/tlan.README +++ b/Documentation/networking/tlan.README @@ -1,6 +1,11 @@ -Caldera TLAN driver for Linux, version 0.42 +TLAN driver for Linux, version 0.43 README +Note: I (James) am not maintaining this driver anymore, as I no longer + have the equipment to do so. So it is available to anyone who + wishes to take it over ;) If someone needs to reach me about + it, my new email address is james@sovereign.org. + I. Supported Devices. @@ -8,20 +13,32 @@ I. Supported Devices. Supported: Vendor ID Device ID Name - 0e11 ae32 Compaq Netelligent 10/100 TX - 0e11 ae34 Compaq Netelligent 10 T + 0e11 ae32 Compaq Netelligent 10/100 TX PCI UTP + 0e11 ae34 Compaq Netelligent 10 T PCI UTP 0e11 ae35 Compaq Integrated NetFlex 3/P - 0e11 ae43 Compaq ProLiant Integrated Netelligent 10/100 TX - 0e11 ae40 Compaq Dual Port Netelligent 10/100 TX - 0e11 b011 Compaq Deskpro 4000 5233MMX + 0e11 ae40 Compaq Netelligent Dual 10/100 TX PCI UTP + 0e11 ae43 Compaq Netelligent Integrated 10/100 TX UTP + 0e11 b011 Compaq Netelligent 10/100 TX Embedded UTP + 0e11 b012 Compaq Netelligent 10 T/2 PCI UTP/Coax + 0e11 b030 Compaq Netelligent 10/100 TX UTP 0e11 f130 Compaq NetFlex 3/P 0e11 f150 Compaq NetFlex 3/P + 108d 0012 Olicom OC-2325 + 108d 0013 Olicom OC-2183 108d 0014 Olicom OC-2326 + Caveats: - I don't believe 100BaseTX daughterboards will work. I am interested - in any reports. + I am not sure if 100BaseTX daughterboards (for those cards which + support such things) will work. I haven't had any solid evidence + either way. + + However, if a card supports 100BaseTx without requiring an add + on daughterboard, it should work with 100BaseTx. + + The "Netelligent 10 T/2 PCI UTP/Coax" (b012) device is untested, + but I do not expect any problems. II. Building the Driver. @@ -77,6 +94,14 @@ III. Driver Options device that does not have an AUI/BNC connector will probably cause it to not function correctly.) + 4. You can set duplex=1 to force half duplex, and duplex=2 to + force full duplex. + + 5. You can set speed=10 to force 10Mbs operation, and speed=100Mbs + to force 100Mbs operation. (I'm not sure what will happen + if a card which only supports 10Mbs is forced into 100Mbs + mode.) + 3. If the driver is built into the kernel, you can use the 3rd and 4th parameters to set aui and debug respectively. For example: @@ -90,6 +115,10 @@ III. Driver Options 0x01 = aui 0x02 = use SA_INTERRUPT flag when reserving the irq. + 0x04 = use half duplex + 0x08 = use full duplex + 0x10 = use 10BaseT + 0x20 = use 100BaseTx IV. Things to try if you have problems. @@ -98,33 +127,8 @@ IV. Things to try if you have problems. 1. Make sure routing is correct. 2. If you are using a 2.1.x kernel, try to duplicate the problem on a 2.0.x (preferably 2.0.29 or 2.0.30) kernel. - 3. Set debug to 7, either in tlan.c or through insmod as in - section III.1 above. - 4. Make sure klog is running so the kernel messages are - being recorded somewhere. - 5. Run the following sequence of programs in order (you - may want to do this within an xterm, as background - traffic may cause a lot of TLAN RECEIVED: messages - on the console): - ifconfig eth0 your.ip.address netmask your.net.mask up - route add -net local.net.address eth0 - ifconfig - ping some.computer.on.local.net - ifconfig eth0 down - - 6. Mail the log of what occurred to me. Also include the - kernel version and what media/connector type (eg, - 10 BaseT/RJ45, 100 BaseTX/RJ45, Thinnet/BNC, etc). - - - -Please e-mail me with any comments, successes, or failures. Thanks. There is also a tlan mailing list which you can join by sending "subscribe tlan" -in the body of an email to majordomo@vuser.vu.union.edu. I will announce new -releases of the TLAN driver there. - -James -james.banks@caldera.com +in the body of an email to majordomo@vuser.vu.union.edu. diff --git a/Documentation/paride.txt b/Documentation/paride.txt new file mode 100644 index 000000000000..951d9601e9f8 --- /dev/null +++ b/Documentation/paride.txt @@ -0,0 +1,360 @@ + + Linux and parallel port IDE devices + + +PARIDE-2.0.35 (c) 1997-8 Grant Guenther + +************************************************************************* + +Special notes for the 2.0.35 version: + +(i) This is the paride from 2.1.107 retrofitted to work with 2.0.34. + +(ii) PARPORT is _not_ supported. If you obtain the PARPORT patches + for 2.0 and try to use them, it might work. I have not tried + it. + +(iii) There is no guarantee of any ongoing support or future development + for this special retrofit. + +(iv) I have not built or tested PARIDE with SMP support in 2.0.35, + use it at your own risk. + +************************************************************************* + +1. Introduction + +Owing to the simplicity and near universality of the parallel port interface +to personal computers, many external devices such as portable hard-disk, +CD-ROM, LS-120 and tape drives use the parallel port to connect to their +host computer. While some devices (notably scanners) use ad-hoc methods +to pass commands and data through the parallel port interface, most +external devices are actually identical to an internal model, but with +a parallel-port adapter chip added in. Some of the original parallel port +adapters were little more than mechanisms for multiplexing a SCSI bus. +(The Iomega PPA-3 adapter used in the ZIP drives is an example of this +approach). Most current designs, however, take a different approach. +The adapter chip reproduces a small ISA or IDE bus in the external device +and the communication protocol provides operations for reading and writing +device registers, as well as data block transfer functions. Sometimes, +the device being addressed via the parallel cable is a standard SCSI +controller like an NCR 5380. The "ditto" family of external tape +drives use the ISA replicator to interface a floppy disk controller, +which is then connected to a floppy-tape mechanism. The vast majority +of external parallel port devices, however, are now based on standard +IDE type devices, which require no intermediate controller. If one +were to open up a parallel port CD-ROM drive, for instance, one would +find a standard ATAPI CD-ROM drive, a power supply, and a single adapter +that interconnected a standard PC parallel port cable and a standard +IDE cable. It is usually possible to exchange the CD-ROM device with +any other device using the IDE interface. + +The document describes the support in Linux for parallel port IDE +devices. It does not cover parallel port SCSI devices, "ditto" tape +drives or scanners. Many different devices are supported by the +parallel port IDE subsystem, including: + + MicroSolutions backpack CD-ROM + MicroSolutions backpack PD/CD + MicroSolutions backpack hard-drives + MicroSolutions backpack 8000t tape drive + SyQuest EZ-135, EZ-230 & SparQ drives + Avatar Shark + Imation Superdisk LS-120 + FreeCom Power CD + Hewlett-Packard 5GB tape drive + Hewlett-Packard 7100 and 7200 CD-RW drives + +as well as most of the clone and no-name products on the market. + +To support such a wide range of devices, PARIDE, the parallel port IDE +subsystem, is actually structured in three parts. There is a base +paride module which provides a registry and some common methods for +accessing the parallel ports. The second component is a set of +high-level drivers for each of the different type of supported device: + + pd IDE disk + pcd ATAPI CD-ROM + pf ATAPI disk + pt ATAPI tape + pg ATAPI generic + +(Currently, the pg driver is only used with CD-R drives). + +The high-level drivers function according to the relevant standards. +The third component of PARIDE is a set of low-level protocol drivers +for each of the parallel port IDE adapter chips. Thanks to the interest +and encouragement of Linux users from many parts of the world, +support is available for almost all known adapter protocols: + + aten ATEN EH-100 (HK) + bpck Microsolutions backpack (US) + comm DataStor (old-type) "commuter" adapter (TW) + dstr DataStor EP-2000 (TW) + epat Shuttle EPAT (UK) + epia Shuttle EPIA (UK) + fit2 FIT TD-2000 (US) + fit3 FIT TD-3000 (US) + frpw Freecom Power (DE) + kbic KingByte KBIC-951A and KBIC-971A (TW) + ktti KT Technology PHd adapter (SG) + on20 OnSpec 90c20 (US) + on26 OnSpec 90c26 (US) + + +2. Using the PARIDE subsystem + +While configuring the Linux kernel, you may choose either to build +the PARIDE drivers into your kernel, or to build them as modules. + +In either case, you will need to select "Parallel port IDE device support" +as well as at least one of the high-level drivers and at least one +of the parallel port communication protocols. If you do not know +what kind of parallel port adapter is used in your drive, you could +begin by checking the file names and any text files on your DOS +installation floppy. Alternatively, you can look at the markings on +the adapter chip itself. That's usually sufficient to identify the +correct device. + +You can actually select all the protocol modules, and allow the PARIDE +subsystem to try them all for you. + +For the "brand-name" products listed above, here are the protocol +and high-level drivers that you would use: + + Manufacturer Model Driver Protocol + + MicroSolutions CD-ROM pcd bpck + MicroSolutions PD drive pf bpck + MicroSolutions hard-drive pd bpck + MicroSolutions 8000t tape pt bpck + SyQuest EZ, SparQ pd epat + Imation Superdisk pf epat + Avatar Shark pd epat + FreeCom CD-ROM pcd frpw + Hewlett-Packard 5GB Tape pt epat + Hewlett-Packard 7100/7200 pg epat + +2.1 Configuring built-in drivers + +We recommend that you get to know how the drivers work and how to +configure them as loadable modules, before attempting to compile a +kernel with the drivers built-in. + +If you built all of your PARIDE support directly into your kernel, +and you have just a single parallel port IDE device, your kernel should +locate it automatically for you. If you have more than one device, +you may need to give some command line options to your bootloader +(eg: LILO), how to do that is beyond the scope of this document. + +The high-level drivers accept a number of command line parameters, all +of which are documented in the source files in linux/drivers/block/paride. +By default, each driver will automatically try all parallel ports it +can find, and all protocol types that have been installed, until it finds +a parallel port IDE adapter. Once it finds one, the probe stops. So, +if you have more than one device, you will need to tell the drivers +how to identify them. This requires specifying the port address, the +protocol identification number and, for some devices, the drive's +chain ID. While your system is booting, a number of messages are +displayed on the console. Like all such messages, they can be +reviewed with the 'dmesg' command. Among those messages will be +some lines like: + + paride: bpck registered as protocol 0 + paride: epat registered as protocol 1 + +The numbers will always be the same until you build a new kernel with +different protocol selections. You should note these numbers as you +will need them to identify the devices. + +If you happen to be using a MicroSolutions backpack device, you will +also need to know the unit ID number for each drive. This is usually +the last two digits of the drive's serial number (but read MicroSolutions' +documentation about this). + +As an example, let's assume that you have a MicroSolutions PD/CD drive +with unit ID number 36 connected to the parallel port at 0x378, a SyQuest +EZ-135 connected to the chained port on the PD/CD drive and also an +Imation Superdisk connected to port 0x278. You could give the following +options on your boot command: + + pd.drive0=0x378,1 pf.drive0=0x278,1 pf.drive1=0x378,0,36 + +In the last option, pf.drive1 configures device /dev/pf1, the 0x378 +is the parallel port base address, the 0 is the protocol registration +number and 36 is the chain ID. + +This (2.0.34) version of PARIDE does not support chained devices on the +same parallel port. + +2.2 Loading and configuring PARIDE as modules + +It is much faster and simpler to get to understand the PARIDE drivers +if you use them as loadable kernel modules. + +Note: using these drivers with the "kerneld" automatic module loading +system is not recommended, and is not documented here. + +To use PARIDE, you must begin by + + insmod paride + +this loads a base module which provides a registry for the protocols, +among other tasks. + +Then, load as many of the protocol modules as you think you might need. +As you load each module, it will register the protocols that it supports, +and print a log message to your kernel log file and your console. For +example: + + # insmod epat + paride: epat registered as protocol 0 + # insmod kbic + paride: k951 registered as protocol 1 + paride: k971 registered as protocol 2 + +Finally, you can load high-level drivers for each kind of device that +you have connected. By default, each driver will autoprobe for a single +device, but you can support up to four similar devices by giving their +individual co-ordinates when you load the driver. + +For example, if you had two no-name CD-ROM drives both using the +KingByte KBIC-951A adapter, one on port 0x378 and the other on 0x3bc +you could give the following command: + + # insmod pcd drive0=0x378,1 drive1=0x3bc,1 + +For most adapters, giving a port address and protocol number is sufficient, +but check the source files in linux/drivers/block/paride for more +information. (Hopefully someone will write some man pages one day !). + +As another example, here's what happens when PARPORT is installed, and +a SyQuest EZ-135 is attached to port 0x378: + + # insmod paride + paride: version 1.0 installed + # insmod epat + paride: epat registered as protocol 0 + # insmod pd + pd: pd version 1.0, major 45, cluster 64, nice 0 + pda: Sharing parport1 at 0x378 + pda: epat 1.0, Shuttle EPAT chip c3 at 0x378, mode 5 (EPP-32), delay 1 + pda: SyQuest EZ135A, 262144 blocks [128M], (512/16/32), removable media + pda: pda1 + +Note that the last line is the output from the generic partition table +scanner - in this case it reports that it has found a disk with one partition. + +2.3 Using a PARIDE device + +Once the drivers have been loaded, you can access PARIDE devices in the +same way as their traditional counterparts. You will probably need to +create the device "special files". Here is a simple script that you can +cut to a file and execute: + +#!/bin/bash +# +# mkd -- a script to create the device special files for the PARIDE subsystem +# +function mkdev { + mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1 +} +# +function pd { + D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) ) + mkdev pd$D b 45 $[ $1 * 16 ] + for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + do mkdev pd$D$P b 45 $[ $1 * 16 + $P ] + done +} +# +cd /dev +# +for u in 0 1 2 3 ; do pd $u ; done +for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done +for u in 0 1 2 3 ; do mkdev pf$u b 47 $u ; done +for u in 0 1 2 3 ; do mkdev pt$u c 96 $u ; done +for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done +for u in 0 1 2 3 ; do mkdev pg$u c 97 $u ; done +# +# end of mkd + +With the device files and drivers in place, you can access PARIDE devices +like any other Linux device. For example, to mount a CD-ROM in pcd0, use: + + mount /dev/pcd0 /cdrom + +If you have a fresh Avatar Shark cartridge, and the drive is pda, you +might do something like: + + fdisk /dev/pda -- make a new partition table with + partition 1 of type 83 + + mke2fs /dev/pda1 -- to build the file system + + mkdir /shark -- make a place to mount the disk + + mount /dev/pda1 /shark + +Devices like the Imation superdisk work in the same way, except that +they do not have a partition table. For example to make a 120MB +floppy that you could share with a DOS system: + + mkdosfs /dev/pf0 + mount /dev/pf0 /mnt + +2.4 Using the pg driver + +The pg driver can be used in conjunction with the cdrecord program +to create CD-ROMs. For more information, and the required patches +to cdrecord, please visit http://www.torque.net/parport/cdr.html . + +3. Troubleshooting + +While a lot of testing has gone into these drivers to make them work +as smoothly as possible, problems will arise. If you do have problems, +please check all the obvious things first: does the drive work in +DOS with the manufacturer's drivers ? If that doesn't yield any useful +clues, then please make sure that only one drive is hooked to your system, +and that no other device driver is using your parallel port (check in +/proc/ioports). Then, load the appropriate drivers (you can load several +protocol modules if you want) as in: + + # insmod paride + # insmod epat + # insmod bpck + # insmod kbic + ... + # insmod pd verbose=1 + +(using the correct driver for the type of device you have, of course). +The verbose=1 parameter will cause the drivers to log a trace of their +activity as they attempt to locate your drive. + +Use 'dmesg' to capture a log of all the PARIDE messages (any messages +beginning with paride:, a protocol module's name or a driver's name) and +include that with your bug report. You can submit a bug report in one +of two ways. Either send it directly to the author of the PARIDE suite, +by e-mail to grant@torque.net, or join the linux-parport mailing list +and post your report there. + +You can join the linux-parport mailing list by sending a mail message +to + linux-parport-request@torque.net + +with the single word + + subscribe + +in the body of the mail message (not in the subject line). Please be +sure that your mail program is correctly set up when you do this, as +the list manager is a robot that will subscribe you using the reply +address in your mail headers. REMOVE any anti-spam gimmicks you may +have in your mail headers, when sending mail to the list server. + +You might also find some useful information on the linux-parport +web pages (although they are not always up to date) at + + http://www.torque.net/parport/ + + diff --git a/MAINTAINERS b/MAINTAINERS index 5900b12d839f..e2552bc2347a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -389,6 +389,12 @@ M: Juergen Fischer L: linux-scsi@vger.rutgers.edu S: Maintained +GSCD CDROM DRIVER +P: Oliver Raupach +M: oliver@mm.gop.de +L: linux-kernel@vger.rutgers.edu +S: Maintained + SBPCD CDROM DRIVER P: Eberhard Moenkeberg M: emoenke@gwdg.de diff --git a/Makefile b/Makefile index 4c2db3eae6b9..97e1ea9f60bb 100644 --- a/Makefile +++ b/Makefile @@ -146,6 +146,10 @@ ifdef CONFIG_SBUS DRIVERS := $(DRIVERS) drivers/sbus/sbus.a endif +ifeq ($(CONFIG_PARIDE),y) +DRIVERS := $(DRIVERS) drivers/block/paride/paride.a +endif + include arch/$(ARCH)/Makefile ifdef SMP diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index 4c3d6c5b8060..3fd3af0be1fe 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -103,16 +103,17 @@ startup_32: checkCPUtype: #endif -/* check if it is 486 or 386. */ +/* check Processor type: 386, 486, 6x86(L) or CPUID capable processor */ /* * XXX - this does a lot of unnecessary setup. Alignment checks don't * apply at our cpl of 0 and the stack ought to be aligned already, and * we don't need to preserve eflags. */ + movl $3, SYMBOL_NAME(x86) pushfl # push EFLAGS popl %eax # get EFLAGS - movl %eax,%ecx # save original EFLAGS + movl %eax,%ecx # save original EFLAGS in ecx xorl $0x40000,%eax # flip AC bit in EFLAGS pushl %eax # copy to EFLAGS popfl # set EFLAGS @@ -127,10 +128,11 @@ checkCPUtype: pushl %eax popfl # if we are on a straight 486DX, SX, or pushfl # 487SX we can't change it - popl %eax - xorl %ecx,%eax + popl %eax # Also if we are on a Cyrix 6x86(L) + xorl %ecx,%eax # OTOH 6x86MXs and MIIs check OK andl $0x200000,%eax - je is486 + je is486x + isnew: pushl %ecx # restore original EFLAGS popfl incl SYMBOL_NAME(have_cpuid) # we have CPUID @@ -168,7 +170,72 @@ isnew: pushl %ecx # restore original EFLAGS andl $0x80000011,%eax # Save PG,PE,ET orl $0x50022,%eax # set AM, WP, NE and MP jmp 2f -is486: pushl %ecx # restore original EFLAGS + +/* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid + * clobbering the new BX chipset used with the Pentium II, which has a register + * at the same addresses as those used to access the Cyrix special configuration + * registers (CCRs). + */ + /* + * A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2 + * (and it _must_ be 5 divided by 2) while other CPUs change + * them in undefined ways. We need to know this since we may + * need to enable the CPUID instruction at least. + * We couldn't use this test before since the PPro and PII behave + * like Cyrix chips in this respect. + */ +is486x: xor %ax,%ax + sahf + movb $5,%ax + movb $2,%bx + div %bl + lahf + cmpb $2,%ah + jne ncyrix + /* + * N.B. The pattern of accesses to 0x22 and 0x23 is *essential* + * so do not try to "optimize" it! For the same reason we + * do all this with interrupts off. + */ +#define setCx86(reg, val) \ + movb reg,%ax; \ + outb %ax,$0x22; \ + movb val,%ax; \ + outb %ax,$0x23 + +#define getCx86(reg) \ + movb reg,%ax; \ + outb %ax,$0x22; \ + inb $0x23,%ax + + cli + getCx86($0xc3) # get CCR3 + movb %ax,%cx # Save old value + movb %ax,%bx + andb $0x0f,%bx # Enable access to all config registers + orb $0x10,%bx # by setting bit 4 + setCx86($0xc3,%bx) + + getCx86($0xe8) # now we can get CCR4 + orb $0x80,%ax # and set bit 7 (CPUIDEN) + movb %ax,%bx # to enable CPUID execution + setCx86($0xe8,%bx) + + getCx86($0xfe) # DIR0 : let's check this is a 6x86(L) + andb $0xf0,%ax # should be 3xh + cmpb $0x30,%ax # + jne n6x86 + getCx86($0xe9) # CCR5 : we reset the SLOP bit + andb $0xfd,%ax # so that udelay calculation + movb %ax,%bx # is correct on 6x86(L) CPUs + setCx86($0xe9,%bx) + setCx86($0xc3,%cx) # Restore old CCR3 + sti + jmp isnew # We enabled CPUID now + +n6x86: setCx86($0xc3,%cx) # Restore old CCR3 + sti +ncyrix: pushl %ecx # restore original EFLAGS popfl movl %cr0,%eax # 486 andl $0x80000011,%eax # Save PG,PE,ET diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index be1f46467de2..5d68a9d359fc 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -417,6 +417,55 @@ static unsigned long get_fpreg_word(struct task_struct *child, return tmp; } +static int putreg(struct task_struct *child, + unsigned long regno, unsigned long value) +{ + switch (regno >> 2) { + case ORIG_EAX: + return -EIO; + case FS: + case GS: + case DS: + case ES: + if (value && (value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case SS: + case CS: + if ((value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case EFL: + value &= FLAG_MASK; + value |= get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~FLAG_MASK; + } + put_stack_long(child, regno - sizeof(struct pt_regs), value); + return 0; +} + +static unsigned long getreg(struct task_struct *child, + unsigned long regno) +{ + unsigned long retval = ~0UL; + + switch (regno >> 2) { + case FS: + case GS: + case DS: + case ES: + case SS: + case CS: + retval = 0xffff; + /* fall through */ + default: + regno = regno - sizeof(struct pt_regs); + retval &= get_stack_long(child, regno); + } + return retval; +} + asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; @@ -490,39 +539,23 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) unsigned long tmp; int res; - if ((addr & 3 && - (addr < (long) (&dummy->i387) || - addr > (long) (&dummy->i387.st_space[20]) )) || - addr < 0 || addr > sizeof(struct user) - 3) + if ((addr & 3) || addr < 0 + || addr > sizeof(struct user) - 3) return -EIO; res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); if (res) return res; tmp = 0; /* Default return condition */ - if (addr >= (long) (&dummy->i387) && - addr < (long) (&dummy->i387.st_space[20]) ) { -#ifndef CONFIG_MATH_EMULATION - if (!hard_math) - return -EIO; -#endif /* defined(CONFIG_MATH_EMULATION) */ - tmp = get_fpreg_word(child, addr); - } - if(addr < 17*sizeof(long)) { - addr = addr >> 2; /* temporary hack. */ - - tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER); - if (addr == DS || addr == ES || - addr == FS || addr == GS || - addr == CS || addr == SS) - tmp &= 0xffff; - }; - if(addr >= (long) &dummy->u_debugreg[0] && - addr <= (long) &dummy->u_debugreg[7]){ + if(addr < 17*sizeof(long)) + tmp = getreg(child, addr); + else if(addr >= (long) &dummy->u_debugreg[0] + && addr <= (long) &dummy->u_debugreg[7]) + { addr -= (long) &dummy->u_debugreg[0]; addr = addr >> 2; tmp = child->debugreg[addr]; - }; + } put_fs_long(tmp,(unsigned long *) data); return 0; } @@ -533,50 +566,18 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) return write_long(child,addr,data); case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - if ((addr & 3 && - (addr < (long) (&dummy->i387.st_space[0]) || - addr > (long) (&dummy->i387.st_space[20]) )) || - addr < 0 || - addr > sizeof(struct user) - 3) + if ((addr & 3) || addr < 0 + || addr > sizeof(struct user) - 3) return -EIO; - - if (addr >= (long) (&dummy->i387.st_space[0]) && - addr < (long) (&dummy->i387.st_space[20]) ) { -#ifndef CONFIG_MATH_EMULATION - if (!hard_math) - return -EIO; -#endif /* defined(CONFIG_MATH_EMULATION) */ - return put_fpreg_word(child, addr, data); - } - addr = addr >> 2; /* temporary hack. */ - - if (addr == ORIG_EAX) - return -EIO; - if (addr == DS || addr == ES || - addr == FS || addr == GS || - addr == CS || addr == SS) { - data &= 0xffff; - if (data && (data & 3) != 3) - return -EIO; - } - if (addr == EFL) { /* flags. */ - data &= FLAG_MASK; - data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER) & ~FLAG_MASK; - } - /* Do not allow the user to set the debug register for kernel - address space */ - if(addr < 17){ - if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data)) - return -EIO; - return 0; - }; + + if(addr < 17*sizeof(long)) + return putreg(child, addr, data); /* We need to be very careful here. We implicitly want to modify a portion of the task_struct, and we have to be selective about what portions we allow someone to modify. */ - addr = addr << 2; /* Convert back again */ if(addr >= (long) &dummy->u_debugreg[0] && addr <= (long) &dummy->u_debugreg[7]){ @@ -666,6 +667,108 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) return 0; } + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ +#ifdef CONFIG_MATH_EMULATION + if (!hard_math) + /* Not supported. */ + return -EIO; +#endif + + if (verify_area(VERIFY_WRITE, (void *) data, + 17*sizeof(long))) + return -EIO; + for (i = 0; i < 17*sizeof(long); + i += sizeof(long), data += sizeof(long)) + put_fs_long (getreg(child, i), (unsigned long *) data); + return 0; + }; + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + +#ifdef CONFIG_MATH_EMULATION + if (!hard_math) + /* Not supported. */ + return -EIO; +#endif + + if (verify_area(VERIFY_READ, (void *) data, + 17*sizeof(long))) + return -EIO; + for (i = 0; i < 17*sizeof(long); + i += sizeof(long), data += sizeof(long)) + { + tmp = get_fs_long ((unsigned long *) data); + putreg(child, i, tmp); + } + return 0; + }; + + case PTRACE_GETFPREGS: { /* Get the child FPU state. */ + unsigned long *tmp; + +#ifdef CONFIG_MATH_EMULATION + if (!hard_math) + /* Not supported. */ + return -EIO; +#endif + + if (verify_area(VERIFY_WRITE, (void *) data, + sizeof(struct user_i387_struct))) + return -EIO; + if ( !child->used_math ) { + /* Simulate an empty FPU. */ + child->tss.i387.hard.cwd = 0xffff037f; + child->tss.i387.hard.swd = 0xffff0000; + child->tss.i387.hard.twd = 0xffffffff; + } + if (last_task_used_math == child) + { + clts(); + __asm__("fnsave %0; fwait":"=m" (child->tss.i387.hard)); + last_task_used_math = NULL; + stts(); + } + tmp = (unsigned long *) &child->tss.i387.hard; + for ( i = 0; i < sizeof(struct user_i387_struct); i += sizeof(long) ) + { + put_fs_long (*tmp, (unsigned long *) data); + data += sizeof(long); + tmp++; + } + + return 0; + }; + + case PTRACE_SETFPREGS: { /* Set the child FPU state. */ + unsigned long *tmp; + +#ifdef CONFIG_MATH_EMULATION + if (!hard_math) + /* Not supported. */ + return -EIO; +#endif + + if (verify_area(VERIFY_READ, (void *) data, + sizeof(struct user_i387_struct))) + return -EIO; + child->used_math = 1; + if (last_task_used_math == child) + { + /* Discard the state of the FPU */ + last_task_used_math = NULL; + } + tmp = (unsigned long *) &child->tss.i387.hard; + for ( i = 0; i < sizeof(struct user_i387_struct); i += sizeof(long) ) + { + *tmp = get_fs_long ((unsigned long *) data); + data += sizeof(long); + tmp++; + } + child->flags &= ~PF_USEDFPU; + return 0; + }; + default: return -EIO; } diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 1d3cd6311f66..643863bca9d5 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -32,6 +32,7 @@ #include #include #include +#include /* * Tell us the machine setup.. @@ -224,9 +225,7 @@ static const char * i486model(unsigned int nr) static const char * i586model(unsigned int nr) { static const char *model[] = { - "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83", - "Pentium MMX", NULL, NULL, "Mobile Pentium 75+", - "Mobile Pentium MMX" + "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83" }; if (nr < sizeof(model)/sizeof(char *)) return model[nr]; @@ -237,7 +236,7 @@ static const char * Cx86model(void) { unsigned char nr6x86 = 0; static const char *model[] = { - "unknown", "6x86", "6x86L", "6x86MX", "6x86MXi" + "unknown", "6x86", "6x86L", "6x86MX", "MII" }; switch (x86) { case 5: @@ -249,6 +248,10 @@ static const char * Cx86model(void) default: nr6x86 = 0; } + + /* We must get the stepping number by reading DIR1 */ + outb(0xff, 0x22); x86_mask=inb(0x23); + switch (x86_mask) { case 0x03: Cx86_step = 1; /* 6x86MX Rev 1.3 */ @@ -256,20 +259,26 @@ static const char * Cx86model(void) case 0x04: Cx86_step = 2; /* 6x86MX Rev 1.4 */ break; + case 0x05: + Cx86_step = 3; /* 6x86MX Rev 1.5 */ + break; + case 0x06: + Cx86_step = 4; /* 6x86MX Rev 1.6 */ + break; case 0x14: - Cx86_step = 3; /* 6x86 Rev 2.4 */ + Cx86_step = 5; /* 6x86 Rev 2.4 */ break; case 0x15: - Cx86_step = 4; /* 6x86 Rev 2.5 */ + Cx86_step = 6; /* 6x86 Rev 2.5 */ break; case 0x16: - Cx86_step = 5; /* 6x86 Rev 2.6 */ + Cx86_step = 7; /* 6x86 Rev 2.6 */ break; case 0x17: - Cx86_step = 6; /* 6x86 Rev 2.7 or 3.7 */ + Cx86_step = 8; /* 6x86 Rev 2.7 or 3.7 */ break; case 0x22: - Cx86_step = 7; /* 6x86L Rev 4.2 */ + Cx86_step = 9; /* 6x86L Rev 4.2 */ break; default: Cx86_step = 0; diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 9829bbb17ad5..e7db9ab5235a 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -40,6 +40,8 @@ bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then tristate ' Linear (append) mode' CONFIG_MD_LINEAR tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED + tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING + tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM diff --git a/drivers/block/Config.in b/drivers/block/Config.in index 5f6ea38142c8..1436e59c2141 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -45,6 +45,8 @@ bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then tristate ' Linear (append) mode' CONFIG_MD_LINEAR tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED + tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING + tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then @@ -52,6 +54,10 @@ if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then fi tristate 'XT harddisk support' CONFIG_BLK_DEV_XD +tristate 'Parallel port IDE device support' CONFIG_PARIDE +if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then + source drivers/block/paride/Config.in +fi if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then define_bool CONFIG_BLK_DEV_HD y diff --git a/drivers/block/Makefile b/drivers/block/Makefile index a2ed99202c04..5f6b91b828ff 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -109,6 +109,15 @@ else endif endif +ifeq ($(CONFIG_PARIDE),y) +SUB_DIRS += paride +MOD_SUB_DIRS += paride +else + ifeq ($(CONFIG_PARIDE),m) + MOD_SUB_DIRS += paride + endif +endif + ifeq ($(CONFIG_BLK_DEV_MD),y) LX_OBJS += md.o @@ -128,25 +137,21 @@ else endif endif -#ifeq ($(CONFIG_MD_RAID1),y) -#L_OBJS += raid1.o -#else -# ifeq ($(CONFIG_MD_SUPPORT_RAID1),y) -# ifeq ($(CONFIG_MD_RAID1),m) -# M_OBJS += raid1.o -# endif -# endif -#endif -# -#ifeq ($(CONFIG_MD_RAID5),y) -#L_OBJS += raid5.o -#else -# ifeq ($(CONFIG_MD_SUPPORT_RAID5),y) -# ifeq ($(CONFIG_MD_RAID5),m) -# M_OBJS += raid5.o -# endif -# endif -#endif +ifeq ($(CONFIG_MD_MIRRORING),y) +L_OBJS += raid1.o +else + ifeq ($(CONFIG_MD_MIRRORING),m) + M_OBJS += raid1.o + endif +endif + +ifeq ($(CONFIG_MD_RAID5),y) +L_OBJS += raid5.o +else + ifeq ($(CONFIG_MD_RAID5),m) + M_OBJS += raid5.o + endif +endif endif diff --git a/drivers/block/linear.c b/drivers/block/linear.c index dfbe6f31f2dd..47f44d70cf77 100644 --- a/drivers/block/linear.c +++ b/drivers/block/linear.c @@ -161,6 +161,7 @@ static int linear_status (char *page, int minor, struct md_dev *mddev) sz+=sprintf (page+sz, "\n"); #endif + sz+=sprintf (page+sz, " %dk rounding", 1<next; + + if (!next) + return; + if (req->sector + req->nr_sectors != next->sector) + return; + if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS) + return; +#if 0 + printk ("%s: merge %ld, %ld + %ld == %ld\n", kdevname(req->rq_dev), req->sector, req->nr_sectors, next->nr_sectors, req->nr_sectors + next->nr_sectors); +#endif + req->bhtail->b_reqnext = next->bh; + req->bhtail = next->bhtail; + req->nr_sectors += next->nr_sectors; + next->rq_status = RQ_INACTIVE; + req->next = next->next; + wake_up (&wait_for_request); +} + +void make_request(int major,int rw, struct buffer_head * bh) { unsigned int sector, count; struct request * req; @@ -290,8 +313,12 @@ static void make_request(int major,int rw, struct buffer_head * bh) sector = bh->b_rsector; /* Uhhuh.. Nasty dead-lock possible here.. */ - if (buffer_locked(bh)) + if (buffer_locked(bh)) { +#if 0 + printk("make_request(): buffer already locked\n"); +#endif return; + } /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */ lock_buffer(bh); @@ -319,6 +346,9 @@ static void make_request(int major,int rw, struct buffer_head * bh) rw = READ; /* drop into READ */ case READ: if (buffer_uptodate(bh)) { +#if 0 + printk ("make_request(): buffer uptodate for READ\n"); +#endif unlock_buffer(bh); /* Hmmph! Already have it */ return; } @@ -330,6 +360,9 @@ static void make_request(int major,int rw, struct buffer_head * bh) rw = WRITE; /* drop into WRITE */ case WRITE: if (!buffer_dirty(bh)) { +#if 0 + printk ("make_request(): buffer clean for WRITE\n"); +#endif unlock_buffer(bh); /* Hmmph! Nothing to write */ return; } @@ -391,7 +424,7 @@ static void make_request(int major,int rw, struct buffer_head * bh) continue; if (req->cmd != rw) continue; - if (req->nr_sectors >= 244) + if (req->nr_sectors >= MAX_SECTORS) continue; if (req->rq_dev != bh->b_rdev) continue; @@ -399,6 +432,9 @@ static void make_request(int major,int rw, struct buffer_head * bh) if (req->sector + req->nr_sectors == sector) { req->bhtail->b_reqnext = bh; req->bhtail = bh; + req->nr_sectors += count; + /* Can we now merge this req with the next? */ + attempt_merge(req); /* or to the beginning? */ } else if (req->sector - count == sector) { bh->b_reqnext = req->bh; @@ -406,10 +442,10 @@ static void make_request(int major,int rw, struct buffer_head * bh) req->buffer = bh->b_data; req->current_nr_sectors = count; req->sector = sector; + req->nr_sectors += count; } else continue; - req->nr_sectors += count; mark_buffer_clean(bh); sti(); return; @@ -512,6 +548,12 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[]) for (i = 0; i < nr; i++) { if (bh[i]) { set_bit(BH_Req, &bh[i]->b_state); +#ifdef CONFIG_BLK_DEV_MD + if (MAJOR(bh[i]->b_dev) == MD_MAJOR) { + md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]); + continue; + } +#endif make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]); } } @@ -646,6 +688,9 @@ int blk_dev_init(void) #ifdef CONFIG_BLK_DEV_XD xd_init(); #endif +#ifdef CONFIG_PARIDE + { extern void paride_init(void); paride_init(); }; +#endif #ifdef CONFIG_BLK_DEV_FD floppy_init(); #else diff --git a/drivers/block/md.c b/drivers/block/md.c index dfc0cede1267..98ffe3a7e12c 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -9,6 +9,9 @@ kerneld support by Boris Tobotras + RAID-1/RAID-5 extensions by: + Ingo Molnar, Miguel de Icaza, Gadi Oxman + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) @@ -31,18 +34,27 @@ #include #include #include +#include #ifdef CONFIG_KERNELD #include #endif #include +/* + * For kernel_thread() + */ +#define __KERNEL_SYSCALLS__ +#include #define MAJOR_NR MD_MAJOR #define MD_DRIVER #include +#include +#include static struct hd_struct md_hd_struct[MAX_MD_DEV]; static int md_blocksizes[MAX_MD_DEV]; +static struct md_thread md_threads[MAX_MD_THREADS]; int md_size[MAX_MD_DEV]={0, }; @@ -91,8 +103,7 @@ char *partition_name (kdev_t dev) if (!hd) { - printk ("No gendisk entry for dev %s\n", kdevname(dev)); - sprintf (name, "dev %s", kdevname(dev)); + sprintf (name, "[dev %s]", kdevname(dev)); return (name); } @@ -117,23 +128,196 @@ static void set_ra (void) read_ahead[MD_MAJOR]=minra; } +static int legacy_raid_sb (int minor, int pnum) +{ + int i, factor; + + factor = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); + + /***** + * do size and offset calculations. + */ + for (i=0; isb) { + free_page((unsigned long) mddev->sb); + mddev->sb = NULL; + } + for (i = 0; i nb_dev; i++) { + realdev = mddev->devices + i; + if (realdev->sb) { + free_page((unsigned long) realdev->sb); + realdev->sb = NULL; + } + } +} + +static int analyze_sb (int minor, int pnum) +{ + int i; + struct md_dev *mddev = md_dev + minor; + struct buffer_head *bh; + kdev_t dev; + struct real_dev *realdev; + u32 sb_offset, device_size; + md_superblock_t *sb = NULL; + + /* + * raid-0 and linear don't use a raid superblock + */ + if (pnum == RAID0 >> PERSONALITY_SHIFT || pnum == LINEAR >> PERSONALITY_SHIFT) + return legacy_raid_sb(minor, pnum); + + /* + * Verify the raid superblock on each real device + */ + for (i = 0; i < mddev->nb_dev; i++) { + realdev = mddev->devices + i; + dev = realdev->dev; + device_size = blk_size[MAJOR(dev)][MINOR(dev)]; + realdev->sb_offset = sb_offset = MD_NEW_SIZE_BLOCKS(device_size); + set_blocksize(dev, MD_SB_BYTES); + bh = bread(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); + if (bh) { + sb = (md_superblock_t *) bh->b_data; + if (sb->md_magic != MD_SB_MAGIC) { + printk("md: %s: invalid raid superblock magic (%x) on block %u\n", kdevname(dev), sb->md_magic, sb_offset); + goto abort; + } + if (!mddev->sb) { + mddev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL); + if (!mddev->sb) + goto abort; + memcpy(mddev->sb, sb, MD_SB_BYTES); + } + realdev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL); + if (!realdev->sb) + goto abort; + memcpy(realdev->sb, bh->b_data, MD_SB_BYTES); + + if (memcmp(mddev->sb, sb, MD_SB_GENERIC_CONSTANT_WORDS * 4)) { + printk(KERN_ERR "md: superblock inconsistenty -- run ckraid\n"); + goto abort; + } + /* + * Find the newest superblock version + */ + if (sb->utime != mddev->sb->utime) { + printk(KERN_ERR "md: superblock update time inconsistenty -- using the most recent one\n"); + if (sb->utime > mddev->sb->utime) + memcpy(mddev->sb, sb, MD_SB_BYTES); + } + realdev->size = sb->size; + } else + printk(KERN_ERR "md: disabled device %s\n", kdevname(dev)); + } + if (!mddev->sb) { + printk(KERN_ERR "md: couldn't access raid array %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + sb = mddev->sb; + + /* + * Check if we can support this raid array + */ + if (sb->major_version != MD_MAJOR_VERSION || sb->minor_version > MD_MINOR_VERSION) { + printk("md: %s: unsupported raid array version %d.%d.%d\n", kdevname(MKDEV(MD_MAJOR, minor)), + sb->major_version, sb->minor_version, sb->patch_version); + goto abort; + } + if (sb->state != (1 << MD_SB_CLEAN)) { + printk(KERN_ERR "md: %s: raid array is not clean -- run ckraid\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + switch (sb->level) { + case 1: + md_size[minor] = sb->size; + break; + case 4: + case 5: + md_size[minor] = sb->size * (sb->raid_disks - 1); + break; + default: + printk(KERN_ERR "md: %s: unsupported raid level %d\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + goto abort; + } + return 0; +abort: + free_sb(mddev); + return 1; +} + +int md_update_sb(int minor) +{ + struct md_dev *mddev = md_dev + minor; + struct buffer_head *bh; + md_superblock_t *sb = mddev->sb; + struct real_dev *realdev; + kdev_t dev; + int i; + u32 sb_offset; + + sb->utime = CURRENT_TIME; + for (i = 0; i < mddev->nb_dev; i++) { + realdev = mddev->devices + i; + if (!realdev->sb) + continue; + dev = realdev->dev; + sb_offset = realdev->sb_offset; + set_blocksize(dev, MD_SB_BYTES); + printk("md: updating raid superblock on device %s, sb_offset == %u\n", kdevname(dev), sb_offset); + bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); + if (bh) { + sb = (md_superblock_t *) bh->b_data; + memcpy(sb, mddev->sb, MD_SB_BYTES); + memcpy(&sb->descriptor, sb->disks + realdev->sb->descriptor.number, MD_SB_DESCRIPTOR_WORDS * 4); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + } else + printk(KERN_ERR "md: getblk failed for device %s\n", kdevname(dev)); + } + return 0; +} static int do_md_run (int minor, int repart) { - int pnum, i, min, current_ra, err; - + int pnum, i, min, factor, current_ra, err; + if (!md_dev[minor].nb_dev) return -EINVAL; if (md_dev[minor].pers) return -EBUSY; - + md_dev[minor].repartition=repart; - if ((pnum=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT)) + if ((pnum=PERSONALITY(&md_dev[minor]) >> (PERSONALITY_SHIFT)) >= MAX_PERSONALITY) return -EINVAL; - + + /* Only RAID-1 and RAID-5 can have MD devices as underlying devices */ + if (pnum != (RAID1 >> PERSONALITY_SHIFT) && pnum != (RAID5 >> PERSONALITY_SHIFT)){ + for (i = 0; i < md_dev [minor].nb_dev; i++) + if (MAJOR (md_dev [minor].devices [i].dev) == MD_MAJOR) + return -EINVAL; + } if (!pers[pnum]) { #ifdef CONFIG_KERNELD @@ -145,7 +329,7 @@ static int do_md_run (int minor, int repart) return -EINVAL; } - min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); + factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); for (i=0; irun (minor, md_dev+minor))) { md_dev[minor].pers=NULL; + free_sb(md_dev + minor); return (err); } - + + if (pnum != RAID0 >> PERSONALITY_SHIFT && pnum != LINEAR >> PERSONALITY_SHIFT) + { + md_dev[minor].sb->state &= ~(1 << MD_SB_CLEAN); + md_update_sb(minor); + } + /* FIXME : We assume here we have blocks that are twice as large as sectors. THIS MAY NOT BE TRUE !!! */ @@ -191,7 +386,6 @@ static int do_md_run (int minor, int repart) read_ahead[MD_MAJOR]=current_ra; - printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name); return (0); } @@ -211,38 +405,40 @@ static int do_md_stop (int minor, struct inode *inode) /* The device won't exist anymore -> flush it now */ fsync_dev (inode->i_rdev); invalidate_buffers (inode->i_rdev); + if (md_dev[minor].sb) + { + md_dev[minor].sb->state |= 1 << MD_SB_CLEAN; + md_update_sb(minor); + } md_dev[minor].pers->stop (minor, md_dev+minor); } /* Remove locks. */ + if (md_dev[minor].sb) + free_sb(md_dev + minor); for (i=0; isizes[MINOR(dev)]; +/* md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; HACKHACK*/ + + if (blk_size[MAJOR(dev)][MINOR(dev)] == 0) { + printk("md_add(): zero device size, huh, bailing out.\n"); + } + + md_dev[minor].devices[i].size=blk_size[MAJOR(dev)][MINOR(dev)]; printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor); return (0); @@ -420,6 +622,27 @@ int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size) return (md_dev[minor].pers->map(md_dev+minor, rdev, rsector, size)); } +int md_make_request (int minor, int rw, struct buffer_head * bh) +{ + if (md_dev [minor].pers->make_request) { + if (buffer_locked(bh)) + return 0; + if (rw == WRITE || rw == WRITEA) { + if (!buffer_dirty(bh)) + return 0; + set_bit(BH_Lock, &bh->b_state); + } + if (rw == READ || rw == READA) { + if (buffer_uptodate(bh)) + return 0; + set_bit (BH_Lock, &bh->b_state); + } + return (md_dev[minor].pers->make_request(md_dev+minor, rw, bh)); + } else { + make_request (MAJOR(bh->b_rdev), rw, bh); + return 0; + } +} static void do_md_request (void) { @@ -427,6 +650,40 @@ static void do_md_request (void) return; } +/* + * We run MAX_MD_THREADS from md_init() and arbitrate them in run time. + * This is not so elegant, but how can we use kernel_thread() from within + * loadable modules? + */ +struct md_thread *md_register_thread (void (*run) (void *), void *data) +{ + int i; + for (i = 0; i < MAX_MD_THREADS; i++) { + if (md_threads[i].run == NULL) { + md_threads[i].run = run; + md_threads[i].data = data; + return md_threads + i; + } + } + return NULL; +} + + +void md_unregister_thread (struct md_thread *thread) +{ + thread->run = NULL; + thread->data = NULL; + thread->flags = 0; +} + +void md_wakeup_thread(struct md_thread *thread) +{ + set_bit(THREAD_WAKEUP, &thread->flags); + wake_up(&thread->wqueue); +} + +struct buffer_head *efind_buffer(kdev_t dev, int block, int size); + static struct symbol_table md_symbol_table= { #include @@ -435,11 +692,18 @@ static struct symbol_table md_symbol_table= X(register_md_personality), X(unregister_md_personality), X(partition_name), + X(md_dev), + X(md_error), + X(md_register_thread), + X(md_unregister_thread), + X(md_update_sb), + X(md_map), + X(md_wakeup_thread), + X(efind_buffer), #include }; - static void md_geninit (struct gendisk *gdisk) { int i; @@ -463,6 +727,17 @@ static void md_geninit (struct gendisk *gdisk) }); } +int md_error (kdev_t mddev, kdev_t rdev) +{ + unsigned int minor = MINOR (mddev); + if (MAJOR(mddev) != MD_MAJOR || minor > MAX_MD_DEV) + panic ("md_error gets unknown device\n"); + if (!md_dev [minor].pers) + panic ("md_error gets an error for an unknown device\n"); + if (md_dev [minor].pers->error_handler) + return (md_dev [minor].pers->error_handler (md_dev+minor, rdev)); + return 0; +} int get_md_status (char *page) { @@ -495,9 +770,13 @@ int get_md_status (char *page) partition_name(md_dev[i].devices[j].dev)); size+=md_dev[i].devices[j].size; } - - if (md_dev[i].nb_dev) - sz+=sprintf (page+sz, " %d blocks", size); + + if (md_dev[i].nb_dev) { + if (md_dev[i].pers) + sz+=sprintf (page+sz, " %d blocks", md_size[i]); + else + sz+=sprintf (page+sz, " %d blocks", size); + } if (!md_dev[i].pers) { @@ -508,11 +787,8 @@ int get_md_status (char *page) if (md_dev[i].pers->max_invalid_dev) sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i)); - sz+=sprintf (page+sz, " %dk %s\n", 1<>PERSONALITY_SHIFT] ? - "rounding" : "chunks"); - sz+=md_dev[i].pers->status (page+sz, i, md_dev+i); + sz+=sprintf (page+sz, "\n"); } return (sz); @@ -545,6 +821,32 @@ int unregister_md_personality (int p_num) return 0; } +int md_thread(void * arg) +{ + struct md_thread *thread = arg; + + current->session = 1; + current->pgrp = 1; + sprintf(current->comm, "md_thread"); + +#ifdef __SMP__ + lock_kernel(); + syscall_count++; +#endif + for (;;) { + sti(); + clear_bit(THREAD_WAKEUP, &thread->flags); + if (thread->run) { + thread->run(thread->data); + run_task_queue(&tq_disk); + } + current->signal = 0; + cli(); + if (!test_bit(THREAD_WAKEUP, &thread->flags)) + interruptible_sleep_on(&thread->wqueue); + } +} + void linear_init (void); void raid0_init (void); void raid1_init (void); @@ -552,7 +854,11 @@ void raid5_init (void); int md_init (void) { - printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL); + int i; + + printk ("md driver %d.%d.%d MAX_MD_DEV=%d, MAX_REAL=%d\n", + MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION, + MAX_MD_DEV, MAX_REAL); if (register_blkdev (MD_MAJOR, "md", &md_fops)) { @@ -560,9 +866,17 @@ int md_init (void) return (-1); } + for (i = 0; i < MAX_MD_THREADS; i++) { + md_threads[i].run = NULL; + init_waitqueue(&md_threads[i].wqueue); + md_threads[i].flags = 0; + kernel_thread (md_thread, md_threads + i, 0); + } + blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST; blk_dev[MD_MAJOR].current_request=NULL; read_ahead[MD_MAJOR]=INT_MAX; + memset(md_dev, 0, MAX_MD_DEV * sizeof (struct md_dev)); md_gendisk.next=gendisk_head; gendisk_head=&md_gendisk; @@ -573,6 +887,12 @@ int md_init (void) #ifdef CONFIG_MD_STRIPED raid0_init (); #endif +#ifdef CONFIG_MD_MIRRORING + raid1_init (); +#endif +#ifdef CONFIG_MD_RAID5 + raid5_init (); +#endif return (0); } diff --git a/drivers/block/paride/Config.in b/drivers/block/paride/Config.in new file mode 100644 index 000000000000..00dd9c8e4b78 --- /dev/null +++ b/drivers/block/paride/Config.in @@ -0,0 +1,24 @@ +# +# PARIDE configuration +# +comment 'Parallel IDE high-level drivers' +dep_tristate ' Parallel port IDE disks' CONFIG_PARIDE_PD $CONFIG_PARIDE +dep_tristate ' Parallel port ATAPI CD-ROMs' CONFIG_PARIDE_PCD $CONFIG_PARIDE +dep_tristate ' Parallel port ATAPI disks' CONFIG_PARIDE_PF $CONFIG_PARIDE +dep_tristate ' Parallel port ATAPI tapes' CONFIG_PARIDE_PT $CONFIG_PARIDE +dep_tristate ' Parallel port generic ATAPI devices' CONFIG_PARIDE_PG $CONFIG_PARIDE +comment 'Parallel IDE protocol modules' +dep_tristate ' ATEN EH-100 protocol' CONFIG_PARIDE_ATEN $CONFIG_PARIDE +dep_tristate ' MicroSolutions backpack protocol' CONFIG_PARIDE_BPCK $CONFIG_PARIDE +dep_tristate ' DataStor Commuter protocol' CONFIG_PARIDE_COMM $CONFIG_PARIDE +dep_tristate ' DataStor EP-2000 protocol' CONFIG_PARIDE_DSTR $CONFIG_PARIDE +dep_tristate ' FIT TD-2000 protocol' CONFIG_PARIDE_FIT2 $CONFIG_PARIDE +dep_tristate ' FIT TD-3000 protocol' CONFIG_PARIDE_FIT3 $CONFIG_PARIDE +dep_tristate ' Shuttle EPAT/EPEZ protocol' CONFIG_PARIDE_EPAT $CONFIG_PARIDE +dep_tristate ' Shuttle EPIA protocol' CONFIG_PARIDE_EPIA $CONFIG_PARIDE +dep_tristate ' FreeCom power protocol' CONFIG_PARIDE_FRPW $CONFIG_PARIDE +dep_tristate ' KingByte KBIC-951A/971A protocols' CONFIG_PARIDE_KBIC $CONFIG_PARIDE +dep_tristate ' KT PHd protocol' CONFIG_PARIDE_KTTI $CONFIG_PARIDE +dep_tristate ' OnSpec 90c20 protocol' CONFIG_PARIDE_ON20 $CONFIG_PARIDE +dep_tristate ' OnSpec 90c26 protocol' CONFIG_PARIDE_ON26 $CONFIG_PARIDE +# diff --git a/drivers/block/paride/Makefile b/drivers/block/paride/Makefile new file mode 100644 index 000000000000..abb45d2fc84c --- /dev/null +++ b/drivers/block/paride/Makefile @@ -0,0 +1,174 @@ +# +# Makefile for PARIDE +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +L_TARGET := paride.a +MX_OBJS := +LX_OBJS := +MI_OBJS := +MIX_OBJS := + +ifeq ($(CONFIG_PARIDE),y) + LX_OBJS += paride.o +else + ifeq ($(CONFIG_PARIDE),m) + MX_OBJS += paride.o + endif +endif + +ifeq ($(CONFIG_PARIDE_PD),y) + LX_OBJS += pd.o +else + ifeq ($(CONFIG_PARIDE_PD),m) + MX_OBJS += pd.o + endif +endif + +ifeq ($(CONFIG_PARIDE_PCD),y) + LX_OBJS += pcd.o +else + ifeq ($(CONFIG_PARIDE_PCD),m) + MX_OBJS += pcd.o + endif +endif + +ifeq ($(CONFIG_PARIDE_PF),y) + LX_OBJS += pf.o +else + ifeq ($(CONFIG_PARIDE_PF),m) + MX_OBJS += pf.o + endif +endif + +ifeq ($(CONFIG_PARIDE_PT),y) + LX_OBJS += pt.o +else + ifeq ($(CONFIG_PARIDE_PT),m) + MX_OBJS += pt.o + endif +endif + +ifeq ($(CONFIG_PARIDE_PG),y) + LX_OBJS += pg.o +else + ifeq ($(CONFIG_PARIDE_PG),m) + MX_OBJS += pg.o + endif +endif + +ifeq ($(CONFIG_PARIDE_ATEN),y) + LX_OBJS += aten.o +else + ifeq ($(CONFIG_PARIDE_ATEN),m) + MX_OBJS += aten.o + endif +endif + +ifeq ($(CONFIG_PARIDE_BPCK),y) + LX_OBJS += bpck.o +else + ifeq ($(CONFIG_PARIDE_BPCK),m) + MX_OBJS += bpck.o + endif +endif + +ifeq ($(CONFIG_PARIDE_COMM),y) + LX_OBJS += comm.o +else + ifeq ($(CONFIG_PARIDE_COMM),m) + MX_OBJS += comm.o + endif +endif + +ifeq ($(CONFIG_PARIDE_DSTR),y) + LX_OBJS += dstr.o +else + ifeq ($(CONFIG_PARIDE_DSTR),m) + MX_OBJS += dstr.o + endif +endif + +ifeq ($(CONFIG_PARIDE_KBIC),y) + LX_OBJS += kbic.o +else + ifeq ($(CONFIG_PARIDE_KBIC),m) + MX_OBJS += kbic.o + endif +endif + +ifeq ($(CONFIG_PARIDE_EPAT),y) + LX_OBJS += epat.o +else + ifeq ($(CONFIG_PARIDE_EPAT),m) + MX_OBJS += epat.o + endif +endif + +ifeq ($(CONFIG_PARIDE_EPIA),y) + LX_OBJS += epia.o +else + ifeq ($(CONFIG_PARIDE_EPIA),m) + MX_OBJS += epia.o + endif +endif + +ifeq ($(CONFIG_PARIDE_FIT2),y) + LX_OBJS += fit2.o +else + ifeq ($(CONFIG_PARIDE_FIT2),m) + MX_OBJS += fit2.o + endif +endif + +ifeq ($(CONFIG_PARIDE_FIT3),y) + LX_OBJS += fit3.o +else + ifeq ($(CONFIG_PARIDE_FIT3),m) + MX_OBJS += fit3.o + endif +endif + +ifeq ($(CONFIG_PARIDE_FRPW),y) + LX_OBJS += frpw.o +else + ifeq ($(CONFIG_PARIDE_FRPW),m) + MX_OBJS += frpw.o + endif +endif + +ifeq ($(CONFIG_PARIDE_ON20),y) + LX_OBJS += on20.o +else + ifeq ($(CONFIG_PARIDE_ON20),m) + MX_OBJS += on20.o + endif +endif + +ifeq ($(CONFIG_PARIDE_ON26),y) + LX_OBJS += on26.o +else + ifeq ($(CONFIG_PARIDE_ON26),m) + MX_OBJS += on26.o + endif +endif + +ifeq ($(CONFIG_PARIDE_KTTI),y) + LX_OBJS += ktti.o +else + ifeq ($(CONFIG_PARIDE_KTTI),m) + MX_OBJS += ktti.o + endif +endif + +include $(TOPDIR)/Rules.make + diff --git a/drivers/block/paride/aten.c b/drivers/block/paride/aten.c new file mode 100644 index 000000000000..6f80be57e282 --- /dev/null +++ b/drivers/block/paride/aten.c @@ -0,0 +1,172 @@ +/* + aten.c (c) 1997-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xc); +} + +static void aten_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void aten_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[2] = {"4-bit","8-bit"}; + + printk("%s: aten %s, ATEN EH-100 at 0x%x, ", + pi->device,ATEN_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void aten_init_proto( PIA *pi ) + +{ MOD_INC_USE_COUNT; +} + +static void aten_release_proto( PIA *pi ) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol aten = {"aten",0,2,2,1,1, + aten_write_regr, + aten_read_regr, + aten_write_block, + aten_read_block, + aten_connect, + aten_disconnect, + 0, + 0, + 0, + aten_log_adapter, + aten_init_proto, + aten_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &aten ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &aten ); +} + +#endif + +/* end of aten.c */ diff --git a/drivers/block/paride/bpck.c b/drivers/block/paride/bpck.c new file mode 100644 index 000000000000..4b241c737c71 --- /dev/null +++ b/drivers/block/paride/bpck.c @@ -0,0 +1,482 @@ +/* + bpck.c (c) 1996-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;imode) { + + case 0: WR(4,0x40); + w0(0x40); t2(2); + for (i=0;iunit; + 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;imode; + 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;idevice,pi->port,pi->unit,pi->mode); + for (i=0;imode; od = pi->delay; + pi->mode = 0; pi->delay = 6; + + bpck_connect(pi); + + n = 0; + WR(4,0); + for (i=0;i<64;i++) { + WR(6,8); + WR(6,0xc); + p = 0x100; + for (k=0;k<9;k++) { + f = (((i + 0x180) & p) != 0) * 2; + WR(6,f+0xc); + WR(6,f+0xd); + WR(6,f+0xc); + p = (p >> 1); + } + for (j=0;j<2;j++) { + v = 0; + for (k=0;k<8;k++) { + WR(6,0xc); + WR(6,0xd); + WR(6,0xc); + f = RR(0); + v = 2*v + (f == 0x84); + } + buf[2*i+1-j] = v; + } + } + WR(6,8); + WR(6,0); + WR(5,8); + + bpck_disconnect(pi); + + if (om >= 2) { + bpck_connect(pi); + WR(7,3); + WR(4,8); + bpck_disconnect(pi); + } + + pi->mode = om; pi->delay = od; +} + +static int bpck_test_port ( PIA *pi ) /* check for 8-bit port */ + +{ int i, r, m; + + w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i); + m = -1; + if (r == i) m = 2; + if (r == (255-i)) m = 0; + + w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i); + if (r != (255-i)) m = -1; + + if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); } + if (m == 2) { w2(0x26); w2(0xc); } + + if (m == -1) return 0; + return 5; +} + +static void bpck_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = { "4-bit","8-bit","EPP-8", + "EPP-16","EPP-32" }; + +#ifdef DUMP_EEPROM + int i; +#endif + + bpck_read_eeprom(pi,scratch); + +#ifdef DUMP_EEPROM + if (verbose) { + for(i=0;i<128;i++) + if ((scratch[i] < ' ') || (scratch[i] > '~')) + scratch[i] = '.'; + printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch); + printk("%s: %64.64s\n",pi->device,&scratch[64]); + } +#endif + + printk("%s: bpck %s, backpack %8.8s unit %d", + pi->device,BPCK_VERSION,&scratch[110],pi->unit); + printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port, + pi->mode,mode_string[pi->mode],pi->delay); +} + +static void bpck_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void bpck_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol bpck = { "bpck",0,5,2,1,256, + bpck_write_regr, + bpck_read_regr, + bpck_write_block, + bpck_read_block, + bpck_connect, + bpck_disconnect, + bpck_test_port, + bpck_probe_unit, + bpck_test_proto, + bpck_log_adapter, + bpck_init_proto, + bpck_release_proto + }; + +#ifdef MODULE + +int init_module(void) + +{ return pi_register(&bpck) - 1; +} + +void cleanup_module(void) + +{ pi_unregister(&bpck); +} + +#endif + +/* end of bpck.c */ diff --git a/drivers/block/paride/comm.c b/drivers/block/paride/comm.c new file mode 100644 index 000000000000..99660e218c40 --- /dev/null +++ b/drivers/block/paride/comm.c @@ -0,0 +1,228 @@ +/* + comm.c (c) 1997-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;imode) { + + case 0: + case 1: w0(0x68); P1; + for (k=0;kdevice,COMM_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void comm_init_proto(PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void comm_release_proto(PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol comm = {"comm",0,5,2,1,1, + comm_write_regr, + comm_read_regr, + comm_write_block, + comm_read_block, + comm_connect, + comm_disconnect, + 0, + 0, + 0, + comm_log_adapter, + comm_init_proto, + comm_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &comm ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &comm ); +} + +#endif + +/* end of comm.c */ diff --git a/drivers/block/paride/dstr.c b/drivers/block/paride/dstr.c new file mode 100644 index 000000000000..cc522fab2bc1 --- /dev/null +++ b/drivers/block/paride/dstr.c @@ -0,0 +1,243 @@ +/* + dstr.c (c) 1997-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;kmode) { w0(0x19); } else { w0(9); } + P2; w0(0x82); P1; P3; w0(0x20); P1; + + switch (pi->mode) { + + case 0: + case 1: for (k=0;kdevice,DSTR_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void dstr_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void dstr_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol dstr = {"dstr",0,5,2,1,1, + dstr_write_regr, + dstr_read_regr, + dstr_write_block, + dstr_read_block, + dstr_connect, + dstr_disconnect, + 0, + 0, + 0, + dstr_log_adapter, + dstr_init_proto, + dstr_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &dstr ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &dstr ); +} + +#endif + +/* end of dstr.c */ diff --git a/drivers/block/paride/epat.c b/drivers/block/paride/epat.c new file mode 100644 index 000000000000..980eb695f834 --- /dev/null +++ b/drivers/block/paride/epat.c @@ -0,0 +1,321 @@ +/* + epat.c (c) 1997-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;kmode) { + + case 0: + case 1: + case 2: w0(0x67); w2(1); w2(5); + ph = 0; + for(k=0;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + CCP(0); CCP(0xe0); + w0(0); w2(1); w2(4); + if (pi->mode >= 3) { + w0(0); w2(1); w2(4); w2(0xc); + w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4); + } + WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10); +} + +static void epat_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static int epat_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int k, j, f, cc; + int e[2] = {0,0}; + + epat_connect(pi); + cc = RR(0xd); + epat_disconnect(pi); + + epat_connect(pi); + for (j=0;j<2;j++) { + WRi(6,0xa0+j*0x10); + for (k=0;k<256;k++) { + WRi(2,k^0xaa); + WRi(3,k^0x55); + if (RRi(2) != (k^0xaa)) e[j]++; + } + } + epat_disconnect(pi); + + f = 0; + epat_connect(pi); + WR(0x13,1); WR(0x13,0); WR(0xa,0x11); + epat_read_block(pi,scratch,512); + + for (k=0;k<256;k++) { + if ((scratch[2*k] & 0xff) != k) f++; + if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++; + } + epat_disconnect(pi); + + if (verbose) { + printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n", + pi->device,pi->port,pi->mode,cc,e[0],e[1],f); + } + + return (e[0] && e[1]) || f; +} + +static void epat_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ int ver; + char *mode_string[6] = + {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"}; + + epat_connect(pi); + WR(0xa,0x38); /* read the version code */ + ver = RR(0xb); + epat_disconnect(pi); + + printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ", + pi->device,EPAT_VERSION,ver,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void epat_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void epat_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol epat = {"epat",0,6,3,1,1, + epat_write_regr, + epat_read_regr, + epat_write_block, + epat_read_block, + epat_connect, + epat_disconnect, + 0, + 0, + epat_test_proto, + epat_log_adapter, + epat_init_proto, + epat_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &epat) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &epat); +} + +#endif + +/* end of epat.c */ diff --git a/drivers/block/paride/epia.c b/drivers/block/paride/epia.c new file mode 100644 index 000000000000..4bdf65707fc1 --- /dev/null +++ b/drivers/block/paride/epia.c @@ -0,0 +1,326 @@ +/* + epia.c (c) 1997-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;kmode) { + + case 0: + case 1: + case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5); + ph = 0; last = 0x8000; + for (k=0;kdevice,pi->port,pi->mode,e[0],e[1],f); + } + + return (e[0] && e[1]) || f; + +} + + +static void epia_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[6] = {"4-bit","5/3","8-bit", + "EPP-8","EPP-16","EPP-32"}; + + printk("%s: epia %s, Shuttle EPIA at 0x%x, ", + pi->device,EPIA_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void epia_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void epia_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol epia = {"epia",0,6,3,1,1, + epia_write_regr, + epia_read_regr, + epia_write_block, + epia_read_block, + epia_connect, + epia_disconnect, + 0, + 0, + epia_test_proto, + epia_log_adapter, + epia_init_proto, + epia_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &epia ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &epia ); +} + +#endif + +/* end of epia.c */ + diff --git a/drivers/block/paride/fit2.c b/drivers/block/paride/fit2.c new file mode 100644 index 000000000000..b93fff049f42 --- /dev/null +++ b/drivers/block/paride/fit2.c @@ -0,0 +1,161 @@ +/* + fit2.c (c) 1998 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xcc); +} + +static void fit2_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void fit2_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ printk("%s: fit2 %s, FIT 2000 adapter at 0x%x, delay %d\n", + pi->device,FIT2_VERSION,pi->port,pi->delay); + +} + +static void fit2_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void fit2_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol fit2 = {"fit2",0,1,2,1,1, + fit2_write_regr, + fit2_read_regr, + fit2_write_block, + fit2_read_block, + fit2_connect, + fit2_disconnect, + 0, + 0, + 0, + fit2_log_adapter, + fit2_init_proto, + fit2_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &fit2 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &fit2 ); +} + +#endif + +/* end of fit2.c */ diff --git a/drivers/block/paride/fit3.c b/drivers/block/paride/fit3.c new file mode 100644 index 000000000000..f449bcf2f33e --- /dev/null +++ b/drivers/block/paride/fit3.c @@ -0,0 +1,221 @@ +/* + fit3.c (c) 1998 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;kmode) { + + case 0: + case 1: w2(0xc); w0(0); w2(0x8); w2(0xc); + for (k=0;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xc); w0(0); w2(0xa); + if (pi->mode == 2) { + w2(0xc); w0(0x9); w2(0x8); w2(0xc); + } +} + +static void fit3_disconnect ( PIA *pi ) + +{ w2(0xc); w0(0xa); w2(0x8); w2(0xc); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void fit3_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[3] = {"4-bit","8-bit","EPP"}; + + printk("%s: fit3 %s, FIT 3000 adapter at 0x%x, " + "mode %d (%s), delay %d\n", + pi->device,FIT3_VERSION,pi->port, + pi->mode,mode_string[pi->mode],pi->delay); + +} + +static void fit3_init_proto(PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void fit3_release_proto(PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol fit3 = {"fit3",0,3,2,1,1, + fit3_write_regr, + fit3_read_regr, + fit3_write_block, + fit3_read_block, + fit3_connect, + fit3_disconnect, + 0, + 0, + 0, + fit3_log_adapter, + fit3_init_proto, + fit3_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &fit3 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &fit3 ); +} + +#endif + +/* end of fit3.c */ diff --git a/drivers/block/paride/frpw.c b/drivers/block/paride/frpw.c new file mode 100644 index 000000000000..f965fb9da478 --- /dev/null +++ b/drivers/block/paride/frpw.c @@ -0,0 +1,310 @@ +/* + frpw.c (c) 1996-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;kmode) { + + case 0: + case 1: + case 2: w2(4); w0(8); cec4; w2(5); + for (k=0;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); +} + +static void frpw_disconnect ( PIA *pi ) + +{ w2(4); w0(0x20); cec4; + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +/* Stub logic to see if PNP string is available - used to distinguish + between the Xilinx and ASIC implementations of the Freecom adapter. +*/ + +static int frpw_test_pnp ( PIA *pi ) + +/* returns chip_type: 0 = Xilinx, 1 = ASIC */ + +{ int olddelay, a, b; + + olddelay = pi->delay; + pi->delay = 10; + + pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4); w0(4); w2(6); w2(7); + a = r1() & 0xff; w2(4); b = r1() & 0xff; + w2(0xc); w2(0xe); w2(4); + + pi->delay = olddelay; + w0(pi->saved_r0); + w2(pi->saved_r2); + + return ((~a&0x40) && (b&0x40)); +} + +/* We use the pi->private to remember the result of the PNP test. + To make this work, private = port*2 + chip. Yes, I know it's + a hack :-( +*/ + +static int frpw_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int j, k, r; + int e[2] = {0,0}; + + if ((pi->private>>1) != pi->port) + pi->private = frpw_test_pnp(pi) + 2*pi->port; + + if (((pi->private%2) == 0) && (pi->mode > 2)) { + if (verbose) + printk("%s: frpw: Xilinx does not support mode %d\n", + pi->device, pi->mode); + return 1; + } + + if (((pi->private%2) == 1) && (pi->mode == 2)) { + if (verbose) + printk("%s: frpw: ASIC does not support mode 2\n", + pi->device); + return 1; + } + + frpw_connect(pi); + for (j=0;j<2;j++) { + frpw_write_regr(pi,0,6,0xa0+j*0x10); + for (k=0;k<256;k++) { + frpw_write_regr(pi,0,2,k^0xaa); + frpw_write_regr(pi,0,3,k^0x55); + if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++; + } + } + frpw_disconnect(pi); + + frpw_connect(pi); + frpw_read_block_int(pi,scratch,512,0x10); + r = 0; + for (k=0;k<128;k++) if (scratch[k] != k) r++; + frpw_disconnect(pi); + + if (verbose) { + printk("%s: frpw: port 0x%x, chip %d, mode %d, test=(%d,%d,%d)\n", + pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r); + } + + return (r || (e[0] && e[1])); +} + + +static void frpw_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[6] = {"4-bit","8-bit","EPP", + "EPP-8","EPP-16","EPP-32"}; + + printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device, + FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void frpw_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; + pi->private = 0; +} + +static void frpw_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol frpw = {"frpw",0,6,2,2,1, + frpw_write_regr, + frpw_read_regr, + frpw_write_block, + frpw_read_block, + frpw_connect, + frpw_disconnect, + 0, + 0, + frpw_test_proto, + frpw_log_adapter, + frpw_init_proto, + frpw_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &frpw ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &frpw ); +} + +#endif + +/* end of frpw.c */ diff --git a/drivers/block/paride/kbic.c b/drivers/block/paride/kbic.c new file mode 100644 index 000000000000..589ac00d0748 --- /dev/null +++ b/drivers/block/paride/kbic.c @@ -0,0 +1,311 @@ +/* + kbic.c (c) 1997-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;kmode) { + + case 0: + case 1: + case 2: w0(0x90); w2(4); w2(6); w2(4); + for(k=0;kdevice,KBIC_VERSION,chip,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void k951_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ kbic_log_adapter(pi,scratch,verbose,"KBIC-951A"); +} + +static void k971_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ kbic_log_adapter(pi,scratch,verbose,"KBIC-971A"); +} + +static void kbic_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void kbic_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol k951 = {"k951",0,6,3,1,1, + kbic_write_regr, + kbic_read_regr, + kbic_write_block, + kbic_read_block, + k951_connect, + k951_disconnect, + 0, + 0, + 0, + k951_log_adapter, + kbic_init_proto, + kbic_release_proto + }; + + +struct pi_protocol k971 = {"k971",0,6,3,1,1, + kbic_write_regr, + kbic_read_regr, + kbic_write_block, + kbic_read_block, + k971_connect, + k971_disconnect, + 0, + 0, + 0, + k971_log_adapter, + kbic_init_proto, + kbic_release_proto + }; + +#ifdef MODULE + +int init_module(void) + +{ int s5,s7; + + s5 = pi_register(&k951); + s7 = pi_register(&k971); + + return (s5 || s7) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &k951 ); + pi_unregister( &k971 ); +} + +#endif + +/* end of kbic.c */ diff --git a/drivers/block/paride/ktti.c b/drivers/block/paride/ktti.c new file mode 100644 index 000000000000..606bcfde98cb --- /dev/null +++ b/drivers/block/paride/ktti.c @@ -0,0 +1,138 @@ +/* + ktti.c (c) 1998 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xb); w2(0xa); w0(0); w2(3); w2(6); +} + +static void ktti_disconnect ( PIA *pi ) + +{ w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void ktti_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ printk("%s: ktti %s, KT adapter at 0x%x, delay %d\n", + pi->device,KTTI_VERSION,pi->port,pi->delay); + +} + +static void ktti_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void ktti_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol ktti = {"ktti",0,1,2,1,1, + ktti_write_regr, + ktti_read_regr, + ktti_write_block, + ktti_read_block, + ktti_connect, + ktti_disconnect, + 0, + 0, + 0, + ktti_log_adapter, + ktti_init_proto, + ktti_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &ktti ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &ktti ); +} + +#endif + +/* end of ktti.c */ diff --git a/drivers/block/paride/mkd b/drivers/block/paride/mkd new file mode 100644 index 000000000000..971f099b40aa --- /dev/null +++ b/drivers/block/paride/mkd @@ -0,0 +1,30 @@ +#!/bin/bash +# +# mkd -- a script to create the device special files for the PARIDE subsystem +# +# block devices: pd (45), pcd (46), pf (47) +# character devices: pt (96), pg (97) +# +function mkdev { + mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1 +} +# +function pd { + D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) ) + mkdev pd$D b 45 $[ $1 * 16 ] + for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + do mkdev pd$D$P b 45 $[ $1 * 16 + $P ] + done +} +# +cd /dev +# +for u in 0 1 2 3 ; do pd $u ; done +for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done +for u in 0 1 2 3 ; do mkdev pf$u b 47 $u ; done +for u in 0 1 2 3 ; do mkdev pt$u c 96 $u ; done +for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done +for u in 0 1 2 3 ; do mkdev pg$u c 97 $u ; done +# +# end of mkd + diff --git a/drivers/block/paride/on20.c b/drivers/block/paride/on20.c new file mode 100644 index 000000000000..749879c7375f --- /dev/null +++ b/drivers/block/paride/on20.c @@ -0,0 +1,163 @@ +/* + on20.c (c) 1996-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;kmode) { + 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;kdevice,ON20_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void on20_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void on20_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol on20 = {"on20",0,2,2,1,1, + on20_write_regr, + on20_read_regr, + on20_write_block, + on20_read_block, + on20_connect, + on20_disconnect, + 0, + 0, + 0, + on20_log_adapter, + on20_init_proto, + on20_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &on20 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &on20 ); +} + +#endif + +/* end of on20.c */ diff --git a/drivers/block/paride/on26.c b/drivers/block/paride/on26.c new file mode 100644 index 000000000000..89457cc801fb --- /dev/null +++ b/drivers/block/paride/on26.c @@ -0,0 +1,267 @@ +/* + on26.c (c) 1997-8 Grant R. Guenther + 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 +#include +#include +#include +#include + +#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;kmode) { + + 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;kmode); 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;kdevice,ON26_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void on26_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void on26_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol on26 = {"on26",0,5,2,1,1, + on26_write_regr, + on26_read_regr, + on26_write_block, + on26_read_block, + on26_connect, + on26_disconnect, + 0, + 0, + 0, + on26_log_adapter, + on26_init_proto, + on26_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &on26 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &on26 ); +} + +#endif + +/* end of on26.c */ + diff --git a/drivers/block/paride/paride.c b/drivers/block/paride/paride.c new file mode 100644 index 000000000000..e4ae7894aded --- /dev/null +++ b/drivers/block/paride/paride.c @@ -0,0 +1,514 @@ +/* + paride.c (c) 1997-8 Grant R. Guenther + 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 +#include +#include +#include +#include +#include +#include "spinlock.h" + +#ifdef CONFIG_PARPORT_MODULE +#define CONFIG_PARPORT +#endif + +#ifdef CONFIG_PARPORT +#include +#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;kname,protocols[k]->name)) { + printk("paride: %s protocol already registered\n",pr->name); + return 0; + } + k = 0; + while((kindex = 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->modemode++) { + 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->unitunit++) + 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;pproto = 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 + Under the terms of the GPL. + + This file defines the interface between the high-level parallel + IDE device drivers (pd, pf, pcd, pt) and the adapter chips. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 init_proto, release_proto +*/ + +#define PARIDE_H_VERSION "1.01" + +/* Some adapters need to know what kind of device they are in + + Values for devtype: +*/ + +#define PI_PD 0 /* IDE disk */ +#define PI_PCD 1 /* ATAPI CDrom */ +#define PI_PF 2 /* ATAPI disk */ +#define PI_PT 3 /* ATAPI tape */ +#define PI_PG 4 /* ATAPI generic */ + +/* The paride module contains no state, instead the drivers allocate + a pi_adapter data structure and pass it to paride in every operation. + +*/ + +struct pi_adapter { + + struct pi_protocol *proto; /* adapter protocol */ + int port; /* base address of parallel port */ + int mode; /* transfer mode in use */ + int delay; /* adapter delay setting */ + int devtype; /* device type: PI_PD etc. */ + char *device; /* name of driver */ + int unit; /* unit number for chained adapters */ + int saved_r0; /* saved port state */ + int saved_r2; /* saved port state */ + int reserved; /* number of ports reserved */ + int private; /* for protocol module */ + + struct wait_queue *parq; /* semaphore for parport sharing */ + void *pardev; /* pointer to pardevice */ + char *parname; /* parport name */ + int claimed; /* parport has already been claimed */ + void (*claim_cont)(void); /* continuation for parport wait */ +}; + +typedef struct pi_adapter PIA; + +/* functions exported by paride to the high level drivers */ + +extern int pi_init(PIA *pi, + int autoprobe, /* 1 to autoprobe */ + int port, /* base port address */ + int mode, /* -1 for autoprobe */ + int unit, /* unit number, if supported */ + int protocol, /* protocol to use */ + int delay, /* -1 to use adapter specific default */ + char * scratch, /* address of 512 byte buffer */ + int devtype, /* device type: PI_PD, PI_PCD, etc ... */ + int verbose, /* log verbose data while probing */ + char *device /* name of the driver */ + ); /* returns 0 on failure, 1 on success */ + +extern void pi_release(PIA *pi); + +/* registers are addressed as (cont,regr) + + cont: 0 for command register file, 1 for control register(s) + regr: 0-7 for register number. + +*/ + +extern void pi_write_regr(PIA *pi, int cont, int regr, int val); + +extern int pi_read_regr(PIA *pi, int cont, int regr); + +extern void pi_write_block(PIA *pi, char * buf, int count); + +extern void pi_read_block(PIA *pi, char * buf, int count); + +extern void pi_connect(PIA *pi); + +extern void pi_disconnect(PIA *pi); + +extern void pi_do_claimed(PIA *pi, void (*cont)(void)); + +/* macros and functions exported to the protocol modules */ + +#define delay_p (pi->delay?udelay(pi->delay):0) +#define out_p(offs,byte) outb(byte,pi->port+offs); delay_p; +#define in_p(offs) (delay_p,inb(pi->port+offs)) + +#define w0(byte) {out_p(0,byte);} +#define r0() (in_p(0) & 0xff) +#define w1(byte) {out_p(1,byte);} +#define r1() (in_p(1) & 0xff) +#define w2(byte) {out_p(2,byte);} +#define r2() (in_p(2) & 0xff) +#define w3(byte) {out_p(3,byte);} +#define w4(byte) {out_p(4,byte);} +#define r4() (in_p(4) & 0xff) +#define w4w(data) {outw(data,pi->port+4); delay_p;} +#define w4l(data) {outl(data,pi->port+4); delay_p;} +#define r4w() (delay_p,inw(pi->port+4)&0xffff) +#define r4l() (delay_p,inl(pi->port+4)&0xffffffff) + +static inline u16 pi_swab16( char *b, int k) + +{ union { u16 u; char t[2]; } r; + + r.t[0]=b[2*k+1]; r.t[1]=b[2*k]; + return r.u; +} + +static inline u32 pi_swab32( char *b, int k) + +{ union { u32 u; char f[4]; } r; + + r.f[0]=b[4*k+1]; r.f[1]=b[4*k]; + r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2]; + return r.u; +} + +struct pi_protocol { + + char name[8]; /* name for this protocol */ + int index; /* index into protocol table */ + + int max_mode; /* max mode number */ + int epp_first; /* modes >= this use 8 ports */ + + int default_delay; /* delay parameter if not specified */ + int max_units; /* max chained units probed for */ + + void (*write_regr)(PIA *,int,int,int); + int (*read_regr)(PIA *,int,int); + void (*write_block)(PIA *,char *,int); + void (*read_block)(PIA *,char *,int); + + void (*connect)(PIA *); + void (*disconnect)(PIA *); + + int (*test_port)(PIA *); + int (*probe_unit)(PIA *); + int (*test_proto)(PIA *,char *,int); + void (*log_adapter)(PIA *,char *,int); + + void (*init_proto)(PIA *); + void (*release_proto)(PIA *); +}; + +typedef struct pi_protocol PIP; + +extern int pi_register( PIP * ); +extern void pi_unregister ( PIP * ); + +/* end of paride.h */ diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c new file mode 100644 index 000000000000..038e2c24cdfe --- /dev/null +++ b/drivers/block/paride/pcd.c @@ -0,0 +1,807 @@ +/* + pcd.c (c) 1997-8 Grant R. Guenther + 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 ,,,,, + + Where, + + is the base of the parallel port address for + the corresponding drive. (required) + + 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) + + 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) + + this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + 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. + + 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 +#include +#include +#include +#include +#include + +#include +#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 + +#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;uniti_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_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> 8; + } + + if (pcd_command(unit,rd_cmd,2048,"read block")) { + pcd_bufblk = -1; + spin_lock_irqsave(&io_request_lock,saved_flags); + pcd_busy = 0; + end_request(0); + do_pcd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + + udelay(1000); + + ps_set_intr(do_pcd_read_drq,pcd_ready,PCD_TMO,nice); + +} + +static void do_pcd_read( void ) + + +{ int unit = pcd_unit; + long saved_flags; + + pcd_busy = 1; + pcd_retries = 0; + pcd_transfer(); + if (!pcd_count) { + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pcd_busy = 0; + do_pcd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + + pi_do_claimed(PI,pcd_start); +} + +static void do_pcd_read_drq( void ) + +{ int unit = pcd_unit; + long saved_flags; + + if (pcd_completion(unit,pcd_buffer,"read block")) { + if (pcd_retries < PCD_RETRIES) { + udelay(1000); + pcd_retries++; + pi_do_claimed(PI,pcd_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + pcd_busy = 0; + pcd_bufblk = -1; + end_request(0); + do_pcd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + + do_pcd_read(); + spin_lock_irqsave(&io_request_lock,saved_flags); + do_pcd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +/* end of pcd.c */ + diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c new file mode 100644 index 000000000000..afb782d414e9 --- /dev/null +++ b/drivers/block/paride/pd.c @@ -0,0 +1,1107 @@ +/* + pd.c (c) 1997-8 Grant R. Guenther + 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 ,,,,,,, + + Where, + + is the base of the parallel port address for + the corresponding drive. (required) + + 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) + + 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) + + this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + 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) + + set this to zero to disable the power saving + standby mode, if needed. (1 if not given) + + 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) + + 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 +#include +#include +#include +#include +#include +#include +#include /* for the eject ioctl */ + +#include "spinlock.h" +#include + +#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 + +#include "pseudo.h" + +#define PD_PARTNS (1<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;unitnext)) + if (*gdp == &pd_gendisk) break; + if (*gdp) *gdp = (*gdp)->next; + + for (unit=0;unit= 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= 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<rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + + pd_dev = MINOR(CURRENT->rq_dev); + pd_unit = unit = DEVICE_NR(CURRENT->rq_dev); + pd_block = CURRENT->sector; + pd_count = CURRENT->nr_sectors; + + bh = CURRENT->bh; + req = CURRENT; + if (bh->b_reqnext) + printk("%s: OUCH: b_reqnext != NULL\n",PD.name); + + if ((pd_dev >= PD_DEVS) || + ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) { + end_request(0); + goto repeat; + } + + pd_cmd = CURRENT->cmd; + pd_run = pd_count; + while ((pd_run <= cluster) && + (req = req->next) && + (pd_block+pd_run == req->sector) && + (pd_cmd == req->cmd) && + (pd_dev == MINOR(req->rq_dev))) + pd_run += req->nr_sectors; + + pd_poffs = pd_hd[pd_dev].start_sect; + pd_block += pd_poffs; + pd_buf = CURRENT->buffer; + pd_retries = 0; + + pd_busy = 1; + if (pd_cmd == READ) pi_do_claimed(PI,do_pd_read); + else if (pd_cmd == WRITE) pi_do_claimed(PI,do_pd_write); + else { pd_busy = 0; + end_request(0); + goto repeat; + } +} + +static void pd_next_buf( int unit ) + +{ long saved_flags; + + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + if (!pd_run) { spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + +/* paranoia */ + + if ((!CURRENT) || + (CURRENT->cmd != pd_cmd) || + (MINOR(CURRENT->rq_dev) != pd_dev) || + (CURRENT->rq_status == RQ_INACTIVE) || + (CURRENT->sector+pd_poffs != pd_block)) + printk("%s: OUCH: request list changed unexpectedly\n", + PD.name); + + pd_count = CURRENT->nr_sectors; + pd_buf = CURRENT->buffer; + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +static void do_pd_read( void ) + +{ ps_set_intr(do_pd_read_start,0,0,nice); +} + +static void do_pd_read_start( void ) + +{ int unit = pd_unit; + long saved_flags; + + pd_busy = 1; + + pi_connect(PI); + if (pd_wait_for(unit,STAT_READY,"do_pd_read") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_read_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pd_ide_command(unit,IDE_READ,pd_block,pd_run); + ps_set_intr(do_pd_read_drq,pd_ready,PD_TMO,nice); +} + +static void do_pd_read_drq( void ) + +{ int unit = pd_unit; + long saved_flags; + + while (1) { + if (pd_wait_for(unit,STAT_DRQ,"do_pd_read_drq") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_read_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_read_block(PI,pd_buf,512); + pd_count--; pd_run--; + pd_buf += 512; + pd_block++; + if (!pd_run) break; + if (!pd_count) pd_next_buf(unit); + } + pi_disconnect(PI); + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +static void do_pd_write( void ) + +{ ps_set_intr(do_pd_write_start,0,0,nice); +} + +static void do_pd_write_start( void ) + +{ int unit = pd_unit; + long saved_flags; + + pd_busy = 1; + + pi_connect(PI); + if (pd_wait_for(unit,STAT_READY,"do_pd_write") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pd_ide_command(unit,IDE_WRITE,pd_block,pd_run); + while (1) { + if (pd_wait_for(unit,STAT_DRQ,"do_pd_write_drq") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_write_block(PI,pd_buf,512); + pd_count--; pd_run--; + pd_buf += 512; + pd_block++; + if (!pd_run) break; + if (!pd_count) pd_next_buf(unit); + } + ps_set_intr(do_pd_write_done,pd_ready,PD_TMO,nice); +} + +static void do_pd_write_done( void ) + +{ int unit = pd_unit; + long saved_flags; + + if (pd_wait_for(unit,STAT_READY,"do_pd_write_done") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_disconnect(PI); + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +/* end of pd.c */ + diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c new file mode 100644 index 000000000000..3d6f70c4cb7f --- /dev/null +++ b/drivers/block/paride/pf.c @@ -0,0 +1,1080 @@ +/* + pf.c (c) 1997-8 Grant R. Guenther + 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 ,,,,,, + + Where, + + is the base of the parallel port address for + the corresponding drive. (required) + + 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) + + 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) + + this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + 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. + + 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) + + 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 +#include +#include +#include +#include +#include +#include +#include +#include "spinlock.h" + +#include + +#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 + +#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;uniti_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_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> 8; + } + + io_cmd[8] = c & 0xff; + io_cmd[7] = (c >> 8) & 0xff; + + i = pf_command(unit,io_cmd,c*512,"start i/o"); + + udelay(1000); + + return i; +} + +static int pf_ready( void ) + +{ int unit = pf_unit; + + return (((RR(1,6)&(STAT_BUSY|pf_mask)) == pf_mask)); +} + +static void do_pf_request (void) + +{ struct buffer_head * bh; + struct request * req; + int unit; + + if (pf_busy) return; +repeat: + if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + + pf_unit = unit = DEVICE_NR(CURRENT->rq_dev); + pf_block = CURRENT->sector; + pf_count = CURRENT->nr_sectors; + + bh = CURRENT->bh; + req = CURRENT; + if (bh->b_reqnext) + printk("%s: OUCH: b_reqnext != NULL\n",PF.name); + + if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) { + end_request(0); + goto repeat; + } + + pf_cmd = CURRENT->cmd; + pf_run = pf_count; + while ((pf_run <= cluster) && + (req = req->next) && + (pf_block+pf_run == req->sector) && + (pf_cmd == req->cmd) && + (pf_unit == DEVICE_NR(req->rq_dev))) + pf_run += req->nr_sectors; + + pf_buf = CURRENT->buffer; + pf_retries = 0; + + pf_busy = 1; + if (pf_cmd == READ) pi_do_claimed(PI,do_pf_read); + else if (pf_cmd == WRITE) pi_do_claimed(PI,do_pf_write); + else { pf_busy = 0; + end_request(0); + goto repeat; + } +} + +static void pf_next_buf( int unit ) + +{ long saved_flags; + + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + if (!pf_run) { spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + +/* paranoia */ + + if ((!CURRENT) || + (CURRENT->cmd != pf_cmd) || + (DEVICE_NR(CURRENT->rq_dev) != pf_unit) || + (CURRENT->rq_status == RQ_INACTIVE) || + (CURRENT->sector != pf_block)) + printk("%s: OUCH: request list changed unexpectedly\n", + PF.name); + + pf_count = CURRENT->nr_sectors; + pf_buf = CURRENT->buffer; + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +static void do_pf_read( void ) + +/* detach from the calling context - in case the spinlock is held */ + +{ ps_set_intr(do_pf_read_start,0,0,nice); +} + +static void do_pf_read_start( void ) + +{ int unit = pf_unit; + long saved_flags; + + pf_busy = 1; + + if (pf_start(unit,ATAPI_READ_10,pf_block,pf_run)) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_retries++; + pi_do_claimed(PI,do_pf_read_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pf_mask = STAT_DRQ; + ps_set_intr(do_pf_read_drq,pf_ready,PF_TMO,nice); +} + +static void do_pf_read_drq( void ) + +{ int unit = pf_unit; + long saved_flags; + + while (1) { + if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR, + "read block","completion") & STAT_ERR) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_req_sense(unit,0); + pf_retries++; + pi_do_claimed(PI,do_pf_read_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_read_block(PI,pf_buf,512); + pf_count--; pf_run--; + pf_buf += 512; + pf_block++; + if (!pf_run) break; + if (!pf_count) pf_next_buf(unit); + } + pi_disconnect(PI); + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +static void do_pf_write( void ) + +{ ps_set_intr(do_pf_write_start,0,0,nice); +} + +static void do_pf_write_start( void ) + +{ int unit = pf_unit; + long saved_flags; + + pf_busy = 1; + + if (pf_start(unit,ATAPI_WRITE_10,pf_block,pf_run)) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_retries++; + pi_do_claimed(PI,do_pf_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + + while (1) { + if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR, + "write block","data wait") & STAT_ERR) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_retries++; + pi_do_claimed(PI,do_pf_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_write_block(PI,pf_buf,512); + pf_count--; pf_run--; + pf_buf += 512; + pf_block++; + if (!pf_run) break; + if (!pf_count) pf_next_buf(unit); + } + pf_mask = 0; + ps_set_intr(do_pf_write_done,pf_ready,PF_TMO,nice); +} + +static void do_pf_write_done( void ) + +{ int unit = pf_unit; + long saved_flags; + + if (pf_wait(unit,STAT_BUSY,0,"write block","done") & STAT_ERR) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_retries++; + pi_do_claimed(PI,do_pf_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_disconnect(PI); + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +/* end of pf.c */ + diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c new file mode 100644 index 000000000000..53cecb03f676 --- /dev/null +++ b/drivers/block/paride/pg.c @@ -0,0 +1,682 @@ +/* + pg.c (c) 1998 Grant R. Guenther + 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 ,,,,, + + Where, + + is the base of the parallel port address for + the corresponding drive. (required) + + 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) + + 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) + + this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + 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. + + 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#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;unitstate = 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)) { + 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;ki_rdev); + + if ((unit >= PG_UNITS) || (!PG.present)) return -ENODEV; + + PG.access++; + + if (PG.access > 1) { + PG.access--; + return -EBUSY; + } + + MOD_INC_USE_COUNT; + + if (PG.busy) { + pg_reset(unit); + PG.busy = 0; + } + + pg_identify(unit,(verbose>1)); + + + PG.bufptr = kmalloc(PG_MAX_DATA,GFP_KERNEL); + if (PG.bufptr == NULL) { + PG.access--; + MOD_DEC_USE_COUNT; + printk("%s: buffer allocation failed\n",PG.name); + return -ENOMEM; + } + + return 0; +} + +static void pg_release (struct inode *inode, struct file *file) +{ + int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PG_UNITS) || (PG.access <= 0)) + return; + + PG.access--; + + kfree(PG.bufptr); + PG.bufptr = NULL; + + MOD_DEC_USE_COUNT; + +} + +static int pg_write(struct inode *inode, struct file *filp, + const char *buf, int count) + +{ int unit = DEVICE_NR(inode->i_rdev); + struct pg_write_hdr hdr; + int hs = sizeof(hdr); + + if (PG.busy) return -EBUSY; + if (count < hs) return -EINVAL; + + memcpy_fromfs((char *)&hdr,buf,hs); + + if (hdr.magic != PG_MAGIC) return -EINVAL; + if (hdr.dlen > PG_MAX_DATA) return -EINVAL; + if ((count - hs) > PG_MAX_DATA) return -EINVAL; + + if (hdr.func == PG_RESET) { + if (count != hs) return -EINVAL; + if (pg_reset(unit)) return -EIO; + return count; + } + + if (hdr.func != PG_COMMAND) return -EINVAL; + + PG.start = jiffies; + PG.timeout = hdr.timeout*HZ + HZ/2 + jiffies; + + if (pg_command(unit,hdr.packet,hdr.dlen,jiffies+PG_CMD_TMO)) { + if (PG.status & 0x10) return -ETIME; + return -EIO; + } + + PG.busy = 1; + + memcpy_fromfs(PG.bufptr,buf+hs,count-hs); + + return count; +} + +static int pg_read(struct inode *inode, struct file *filp, char *buf, int count) + +{ int unit = DEVICE_NR(inode->i_rdev); + struct pg_read_hdr hdr; + int hs = sizeof(hdr); + int copy; + + if (!PG.busy) return -EINVAL; + if (count < hs) return -EINVAL; + + PG.busy = 0; + + if (pg_completion(unit,PG.bufptr,PG.timeout)) + if (PG.status & 0x10) return -ETIME; + + hdr.magic = PG_MAGIC; + hdr.dlen = PG.dlen; + copy = 0; + + if (hdr.dlen < 0) { + hdr.dlen = -1 * hdr.dlen; + copy = hdr.dlen; + if (copy > (count - hs)) copy = count - hs; + } + + hdr.duration = (jiffies - PG.start + HZ/2) / HZ; + hdr.scsi = PG.status & 0x0f; + + memcpy_tofs(buf,(char *)&hdr,hs); + if (copy > 0) memcpy_tofs(buf+hs,PG.bufptr,copy); + + return copy+hs; +} + +/* end of pg.c */ + diff --git a/drivers/block/paride/pseudo.h b/drivers/block/paride/pseudo.h new file mode 100644 index 000000000000..97d6fdbcd7f3 --- /dev/null +++ b/drivers/block/paride/pseudo.h @@ -0,0 +1,145 @@ +/* + pseudo.h (c) 1997-8 Grant R. Guenther + 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 +#include +#include + +static void ps_timer_int( unsigned long data); +static void ps_tq_int( void *data); + +static int ps_use_tq = 1; +static void (* ps_continuation)(void); +static int (* ps_ready)(void); +static int ps_then; +static int ps_timeout; +static int ps_timer_active = 0; +static int ps_tq_active = 0; + +/* static spinlock_t ps_spinlock = SPIN_LOCK_UNLOCKED; */ + +static struct timer_list ps_timer = {0,0,0,0,ps_timer_int}; +static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL}; + +static void ps_set_intr( void (*continuation)(void), + int (*ready)(void), + int timeout, int nice ) + +{ long flags; + + spin_lock_irqsave(&ps_spinlock,flags); + + ps_continuation = continuation; + ps_ready = ready; + ps_then = jiffies; + ps_timeout = jiffies + timeout; + ps_use_tq = !nice; + + if (ps_use_tq && !ps_tq_active) { +#ifdef HAVE_DISABLE_HLT + disable_hlt(); +#endif + ps_tq_active = 1; + queue_task(&ps_tq,&tq_scheduler); + } + + if (!ps_timer_active) { + ps_timer_active = 1; + ps_timer.expires = jiffies; + add_timer(&ps_timer); + } + + spin_unlock_irqrestore(&ps_spinlock,flags); +} + +static void ps_tq_int( void *data ) + +{ void (*con)(void); + long flags; + + spin_lock_irqsave(&ps_spinlock,flags); + + con = ps_continuation; + +#ifdef HAVE_DISABLE_HLT + enable_hlt(); +#endif + + ps_tq_active = 0; + + if (!con) { + spin_unlock_irqrestore(&ps_spinlock,flags); + return; + } + if (!ps_ready || ps_ready() || (jiffies >= ps_timeout)) { + ps_continuation = NULL; + spin_unlock_irqrestore(&ps_spinlock,flags); + con(); + return; + } + +#ifdef HAVE_DISABLE_HLT + disable_hlt(); +#endif + + ps_tq_active = 1; + queue_task(&ps_tq,&tq_scheduler); + spin_unlock_irqrestore(&ps_spinlock,flags); +} + +static void ps_timer_int( unsigned long data) + +{ void (*con)(void); + long flags; + + spin_lock_irqsave(&ps_spinlock,flags); + + con = ps_continuation; + ps_timer_active = 0; + if (!con) { + spin_unlock_irqrestore(&ps_spinlock,flags); + return; + } + if (!ps_ready || ps_ready() || (jiffies >= ps_timeout)) { + ps_continuation = NULL; + spin_unlock_irqrestore(&ps_spinlock,flags); + con(); + return; + } + ps_timer_active = 1; + ps_timer.expires = jiffies; + add_timer(&ps_timer); + spin_unlock_irqrestore(&ps_spinlock,flags); +} + +/* end of pseudo.h */ + diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c new file mode 100644 index 000000000000..dd1b20952cd0 --- /dev/null +++ b/drivers/block/paride/pt.c @@ -0,0 +1,940 @@ +/* + pt.c (c) 1998 Grant R. Guenther + 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 ,,,,, + + Where, + + is the base of the parallel port address for + the corresponding drive. (required) + + 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) + + 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) + + this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + 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. + + 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 +#include +#include +#include +#include +#include +#include + +#include + +#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_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;ki_rdev); + + if ((unit >= PT_UNITS) || (!PT.present)) return -ENODEV; + + PT.access++; + + if (PT.access > 1) { + PT.access--; + return -EBUSY; + } + + MOD_INC_USE_COUNT; + + pt_identify(unit); + + if (!PT.flags & PT_MEDIA) { + PT.access--; + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + if ((!PT.flags & PT_WRITE_OK) && (file ->f_mode & 2)) { + PT.access--; + MOD_DEC_USE_COUNT; + return -EROFS; + } + + if (!(MINOR(inode->i_rdev) & 128)) + PT.flags |= PT_REWIND; + + PT.bufptr = kmalloc(PT_BUFSIZE,GFP_KERNEL); + if (PT.bufptr == NULL) { + PT.access--; + MOD_DEC_USE_COUNT; + printk("%s: buffer allocation failed\n",PT.name); + return -ENOMEM; + } + + return 0; +} + +static int pt_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg) +{ + int unit; + struct mtop mtop; + + if (!inode || !inode->i_rdev) + return -EINVAL; + unit = DEVICE_NR(inode->i_rdev); + if (unit >= PT_UNITS) + return -EINVAL; + if (!PT.present) + return -ENODEV; + + switch (cmd) { + case MTIOCTOP: + memcpy_fromfs((char *)&mtop, (char *)arg, + sizeof(struct mtop)); + + switch (mtop.mt_op) { + + case MTREW: + pt_rewind(unit); + return 0; + + default: + printk("%s: Unimplemented mt_op %d\n",PT.name, + mtop.mt_op); + return -EINVAL; + } + + default: + printk("%s: Unimplemented ioctl 0x%x\n",PT.name,cmd); + return -EINVAL; + + } +} + + +static void pt_release (struct inode *inode, struct file *file) +{ + int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PT_UNITS) || (PT.access <= 0)) + return; + + if (PT.flags & PT_WRITING) pt_write_fm(unit); + + if (PT.flags & PT_REWIND) pt_rewind(unit); + + PT.access--; + + kfree(PT.bufptr); + PT.bufptr = NULL; + + MOD_DEC_USE_COUNT; + +} + +static int pt_read(struct inode *inode, struct file *filp, char *buf, int count) + +{ int unit = DEVICE_NR(inode->i_rdev); + char rd_cmd[12] = {ATAPI_READ_6,1,0,0,0,0,0,0,0,0,0,0}; + int k, n, r, p, s, t, b; + + if (!(PT.flags & (PT_READING|PT_WRITING))) { + PT.flags |= PT_READING; + if (pt_atapi(unit,rd_cmd,0,NULL,"start read-ahead")) + return -EIO; + } else if (PT.flags & PT_WRITING) return -EIO; + + if (PT.flags & PT_EOF) return 0; + + t = 0; + + while (count > 0) { + + if (!pt_poll_dsc(unit,1,PT_TMO,"read")) return -EIO; + + n = count; + if (n > 32768) n = 32768; /* max per command */ + b = (n-1+PT.bs)/PT.bs; + n = b*PT.bs; /* rounded up to even block */ + + rd_cmd[4] = b; + + r = pt_command(unit,rd_cmd,n,"read"); + + udelay(1000); + + if (r) { + pt_req_sense(unit,0); + return -EIO; + } + + while (1) { + + r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY, + DBMSG("read DRQ"),""); + + if (r & STAT_SENSE) { + pi_disconnect(PI); + pt_req_sense(unit,0); + return -EIO; + } + + if (r) PT.flags |= PT_EOF; + + s = RR(0,7); + + if (!(s & STAT_DRQ)) break; + + n = (RR(0,4)+256*RR(0,5)); + p = (RR(0,2)&3); + if (p != 2) { + pi_disconnect(PI); + printk("%s: Phase error on read: %d\n",PT.name,p); + return -EIO; + } + + while (n > 0) { + k = n; + if (k > PT_BUFSIZE) k = PT_BUFSIZE; + pi_read_block(PI,PT.bufptr,k); + n -= k; + b = k; + if (b > count) b = count; + memcpy_tofs(buf+t,PT.bufptr,b); + t += b; + count -= b; + } + + } + pi_disconnect(PI); + if (PT.flags & PT_EOF) break; + } + + return t; + +} + +static int pt_write(struct inode *inode, struct file *filp, + const char *buf, int count) + +{ int unit = DEVICE_NR(inode->i_rdev); + char wr_cmd[12] = {ATAPI_WRITE_6,1,0,0,0,0,0,0,0,0,0,0}; + int k, n, r, p, s, t, b; + + if (!(PT.flags & PT_WRITE_OK)) return -EROFS; + + if (!(PT.flags & (PT_READING|PT_WRITING))) { + PT.flags |= PT_WRITING; + if (pt_atapi(unit,wr_cmd,0,NULL,"start buffer-available mode")) + return -EIO; + } else if (PT.flags&PT_READING) return -EIO; + + if (PT.flags & PT_EOF) return -ENOSPC; + + t = 0; + + while (count > 0) { + + if (!pt_poll_dsc(unit,1,PT_TMO,"write")) return -EIO; + + n = count; + if (n > 32768) n = 32768; /* max per command */ + b = (n-1+PT.bs)/PT.bs; + n = b*PT.bs; /* rounded up to even block */ + + wr_cmd[4] = b; + + r = pt_command(unit,wr_cmd,n,"write"); + + udelay(1000); + + if (r) { /* error delivering command only */ + pt_req_sense(unit,0); + return -EIO; + } + + while (1) { + + r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY, + DBMSG("write DRQ"),NULL); + + if (r & STAT_SENSE) { + pi_disconnect(PI); + pt_req_sense(unit,0); + return -EIO; + } + + if (r) PT.flags |= PT_EOF; + + s = RR(0,7); + + if (!(s & STAT_DRQ)) break; + + n = (RR(0,4)+256*RR(0,5)); + p = (RR(0,2)&3); + if (p != 0) { + pi_disconnect(PI); + printk("%s: Phase error on write: %d \n",PT.name,p); + return -EIO; + } + + while (n > 0) { + k = n; + if (k > PT_BUFSIZE) k = PT_BUFSIZE; + b = k; + if (b > count) b = count; + memcpy_fromfs(PT.bufptr,buf+t,b); + pi_write_block(PI,PT.bufptr,k); + t += b; + count -= b; + n -= k; + } + + } + pi_disconnect(PI); + if (PT.flags & PT_EOF) break; + } + + return t; +} + +/* end of pt.c */ + diff --git a/drivers/block/paride/setup.h b/drivers/block/paride/setup.h new file mode 100644 index 000000000000..931924179cec --- /dev/null +++ b/drivers/block/paride/setup.h @@ -0,0 +1,69 @@ +/* + setup.h (c) 1997-8 Grant R. Guenther + 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 +#include + +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: + + =[,...] +*/ + +static void generic_setup( STT t[], int n, char *ss ) + +{ int j,k, sgn; + + k = 0; + for (j=0;jstrip_zone[j].size); } #endif + sz+=sprintf (page+sz, " %dk chunks", 1< +#include +#include +#include +#include +#include +#include + +#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; imirrors[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; imirror_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<b_count = 1; + mirror_bh [i]->b_size = bh->b_size; + mirror_bh [i]->b_data = bh->b_data; + mirror_bh [i]->b_list = BUF_LOCKED; + mirror_bh [i]->personality = &raid1_personality; + mirror_bh [i]->private_bh = (void*)(r1_bh); + + r1_bh->mirror_bh[i] = mirror_bh[i]; + sum_bhs++; + } + + r1_bh->remaining = sum_bhs; + + PRINTK(("raid1_make_request(), write branch, sum_bhs=%d.\n",sum_bhs)); + + /* + * We have to be a bit careful about the semaphore above, thats why we + * start the requests separately. Since kmalloc() could fail, sleep and + * make_request() can sleep too, this is the safer solution. Imagine, + * end_request decreasing the semaphore before we could have set it up ... + * We could play tricks with the semaphore (presetting it and correcting + * at the end if sum_bhs is not 'n' but we have to do end_request by hand + * if all requests finish until we had a chance to set up the semaphore + * correctly ... lots of races). + */ + for (i = 0; i < n; i++) + if (mirror_bh [i] != NULL) + map_and_make_request (rw, mirror_bh [i]); + + return (0); +} + +static int raid1_status (char *page, int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + int sz = 0, i; + + sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks); + for (i = 0; i < raid_conf->raid_disks; i++) + sz += sprintf (page+sz, "%s", raid_conf->mirrors [i].operational ? "U" : "_"); + sz += sprintf (page+sz, "]"); + return sz; +} + +static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index) +{ + int disks = raid_conf->raid_disks; + int j; + + for (j = 0; j < disks; j++) + if (raid_conf->mirrors [j].next == failed_index) + raid_conf->mirrors [j].next = raid_conf->mirrors [failed_index].next; +} + +static int raid1_error (struct md_dev *mddev, kdev_t dev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + struct mirror_info *mirror; + md_superblock_t *sb = mddev->sb; + int disks = raid_conf->raid_disks; + int i; + + PRINTK(("raid1_error called\n")); + + if (raid_conf->working_disks == 1) { + /* + * Uh oh, we can do nothing if this is our last disk, but + * first check if this is a queued request for a device + * which has just failed. + */ + for (i = 0, mirror = raid_conf->mirrors; i < disks; i++, mirror++) + if (mirror->dev == dev && !mirror->operational) + return 0; + printk (KERN_ALERT "RAID1: only one disk left and IO error.\n"); + return 0; + } + + /* Mark disk as unusable */ + for (i = 0, mirror = raid_conf->mirrors; i < disks; i++, mirror++) { + if (mirror->dev == dev && mirror->operational){ + mirror->operational = 0; + raid1_fix_links (raid_conf, i); + sb->disks[mirror->number].state |= (1 << MD_FAULTY_DEVICE); + sb->disks[mirror->number].state &= ~(1 << MD_SYNC_DEVICE); + sb->disks[mirror->number].state &= ~(1 << MD_ACTIVE_DEVICE); + sb->active_disks--; + sb->working_disks--; + sb->failed_disks++; + mddev->sb_dirty = 1; + md_wakeup_thread(raid1_thread); + raid_conf->working_disks--; + printk (KERN_ALERT + "RAID1: Disk failure on %s, disabling device." + "Operation continuing on %d devices\n", + kdevname (dev), raid_conf->working_disks); + } + } + + return 0; +} + +/* + * This is a kernel thread which: + * + * 1. Retries failed read operations on working mirrors. + * 2. Updates the raid superblock when problems are encountered. + */ +void raid1d (void *data) +{ + struct buffer_head *bh; + kdev_t dev; + unsigned long flags; + struct raid1_bh * r1_bh; + struct md_dev *mddev; + + PRINTK(("raid1d() active\n")); + save_flags(flags); + cli(); + while (raid1_retry_list) { + bh = raid1_retry_list; + r1_bh = (struct raid1_bh *)(bh->private_bh); + raid1_retry_list = r1_bh->next_retry; + restore_flags(flags); + + mddev = md_dev + MINOR(bh->b_dev); + if (mddev->sb_dirty) { + mddev->sb_dirty = 0; + md_update_sb(MINOR(bh->b_dev)); + } + dev = bh->b_rdev; + __raid1_map (md_dev + MINOR(bh->b_dev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9); + if (bh->b_rdev == dev) { + printk (KERN_ALERT + "raid1: %s: unrecoverable I/O read error for block %lu\n", + kdevname(bh->b_dev), bh->b_blocknr); + raid1_end_buffer_io (bh, 0); + } else { + printk (KERN_ERR "raid1: %s: redirecting sector %lu to another mirror\n", + kdevname(bh->b_dev), bh->b_blocknr); + clear_bit (BH_Lock, &bh->b_state); + map_and_make_request (r1_bh->cmd, bh); + } + cli(); + } + restore_flags(flags); + +} + +/* + * This will catch the scenario in which one of the mirrors was + * mounted as a normal device rather than as a part of a raid set. + */ +static int check_consistenty (struct md_dev *mddev) +{ + struct raid1_data *raid_conf = mddev->private; + kdev_t dev; + struct buffer_head *bh = NULL; + int i, rc = 0; + char *buffer = NULL; + + for (i = 0; i < raid_conf->raid_disks; i++) { + if (!raid_conf->mirrors[i].operational) + continue; + dev = raid_conf->mirrors[i].dev; + set_blocksize(dev, 4096); + if ((bh = bread(dev, 0, 4096)) == NULL) + break; + if (!buffer) { + buffer = (char *) __get_free_page(GFP_KERNEL); + if (!buffer) + break; + memcpy(buffer, bh->b_data, 4096); + } else if (memcmp(buffer, bh->b_data, 4096)) { + rc = 1; + break; + } + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + bh = NULL; + } + if (buffer) + free_page((unsigned long) buffer); + if (bh) { + dev = bh->b_dev; + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + } + return rc; +} + +static int raid1_run (int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf; + int i, j, raid_disk; + md_superblock_t *sb = mddev->sb; + md_descriptor_t *descriptor; + struct real_dev *realdev; + + MOD_INC_USE_COUNT; + + if (sb->level != 1) { + printk("raid1: %s: raid level not set to mirroring (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + MOD_DEC_USE_COUNT; + return -EIO; + } + /**** + * copy the now verified devices into our private RAID1 bookkeeping area: + * + * [whatever we allocate in raid1_run(), should be freed in raid1_stop()] + */ + + while (!( /* FIXME: now we are rather fault tolerant than nice */ + mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL) + ) ) + printk ("raid1_run(): out of memory\n"); + raid_conf = mddev->private; + memset(raid_conf, 0, sizeof(*raid_conf)); + + PRINTK(("raid1_run(%d) called.\n", minor)); + + for (i = 0; i < mddev->nb_dev; i++) { + realdev = &mddev->devices[i]; + if (!realdev->sb) { + printk(KERN_ERR "raid1: disabled mirror %s (couldn't access raid superblock)\n", kdevname(realdev->dev)); + continue; + } + + /* + * This is important -- we are using the descriptor on + * the disk only to get a pointer to the descriptor on + * the main superblock, which might be more recent. + */ + descriptor = &sb->disks[realdev->sb->descriptor.number]; + if (descriptor->state & (1 << MD_FAULTY_DEVICE)) { + printk(KERN_ERR "raid1: disabled mirror %s (errors detected)\n", kdevname(realdev->dev)); + continue; + } + if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) { + if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) { + printk(KERN_ERR "raid1: disabled mirror %s (not in sync)\n", kdevname(realdev->dev)); + continue; + } + raid_disk = descriptor->raid_disk; + if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) { + printk(KERN_ERR "raid1: disabled mirror %s (inconsistent descriptor)\n", kdevname(realdev->dev)); + continue; + } + if (raid_conf->mirrors[raid_disk].operational) { + printk(KERN_ERR "raid1: disabled mirror %s (mirror %d already operational)\n", kdevname(realdev->dev), raid_disk); + continue; + } + printk(KERN_INFO "raid1: device %s operational as mirror %d\n", kdevname(realdev->dev), raid_disk); + raid_conf->mirrors[raid_disk].number = descriptor->number; + raid_conf->mirrors[raid_disk].raid_disk = raid_disk; + raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev; + raid_conf->mirrors[raid_disk].operational = 1; + raid_conf->mirrors[raid_disk].sect_limit = 128; + raid_conf->working_disks++; + } + } + if (!raid_conf->working_disks) { + printk(KERN_ERR "raid1: no operational mirrors for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + kfree(raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return -EIO; + } + + raid_conf->raid_disks = sb->raid_disks; + raid_conf->mddev = mddev; + + for (j = 0; !raid_conf->mirrors[j].operational; j++); + raid_conf->last_used = j; + for (i = raid_conf->raid_disks - 1; i >= 0; i--) { + if (raid_conf->mirrors[i].operational) { + PRINTK(("raid_conf->mirrors[%d].next == %d\n", i, j)); + raid_conf->mirrors[i].next = j; + j = i; + } + } + + if (check_consistenty(mddev)) { + printk(KERN_ERR "raid1: detected mirror differences -- run ckraid\n"); + sb->state |= 1 << MD_SB_ERRORS; + kfree(raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return -EIO; + } + + /* + * Regenerate the "device is in sync with the raid set" bit for + * each device. + */ + for (i = 0; i < sb->nr_disks ; i++) { + sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE); + for (j = 0; j < sb->raid_disks; j++) { + if (!raid_conf->mirrors[j].operational) + continue; + if (sb->disks[i].number == raid_conf->mirrors[j].number) + sb->disks[i].state |= 1 << MD_SYNC_DEVICE; + } + } + sb->active_disks = raid_conf->working_disks; + + printk("raid1: raid set %s active with %d out of %d mirrors\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks); + /* Ok, everything is just fine now */ + return (0); +} + +static int raid1_stop (int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + + kfree (raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return 0; +} + +static struct md_personality raid1_personality= +{ + "raid1", + raid1_map, + raid1_make_request, + raid1_end_request, + raid1_run, + raid1_stop, + raid1_status, + NULL, /* no ioctls */ + 0, + raid1_error +}; + +int raid1_init (void) +{ + if ((raid1_thread = md_register_thread(raid1d, NULL)) == NULL) + return -EBUSY; + return register_md_personality (RAID1, &raid1_personality); +} + +#ifdef MODULE +int init_module (void) +{ + return raid1_init(); +} + +void cleanup_module (void) +{ + md_unregister_thread (raid1_thread); + unregister_md_personality (RAID1); +} +#endif diff --git a/drivers/block/raid5.c b/drivers/block/raid5.c new file mode 100644 index 000000000000..19a73db9b44c --- /dev/null +++ b/drivers/block/raid5.c @@ -0,0 +1,1504 @@ +/***************************************************************************** + * raid5.c : Multiple Devices driver for Linux + * Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman + * + * RAID-5 management functions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +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<b_state); + make_request(MAJOR(raid_conf->disks[i].dev), WRITE, bh); + } + } + return; + } + + lock_stripe(sh); + sh->nr_pending++; + if (method1 < method2) { + sh->write_method = RECONSTRUCT_WRITE; + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i] || sh->bh_old[i]) + continue; + sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + reading++; + } + } else { + sh->write_method = READ_MODIFY_WRITE; + for (i = 0; i < disks; i++) { + if (sh->bh_old[i]) + continue; + if (!sh->bh_new[i] && i != sh->pd_idx) + continue; + sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + reading++; + } + } + sh->phase = PHASE_READ_OLD; + sh->nr_pending = reading; + PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (!sh->bh_old[i]) + continue; + if (buffer_uptodate(sh->bh_old[i])) + continue; + clear_bit(BH_Lock, &sh->bh_old[i]->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]); + } + } else { + /* + * Reading + */ + method1 = nr_read - nr_cache_overwrite; + lock_stripe(sh); + sh->nr_pending++; + + PRINTK(("handle_stripe(), sector %lu, nr_read %d, nr_cache %d, method1 %d\n", sh->sector, nr_read, nr_cache, method1)); + if (!method1 || (method1 == 1 && nr_cache == disks - 1)) { + PRINTK(("read %lu completed from cache\n", sh->sector)); + for (i = 0; i < disks; i++) { + if (!sh->bh_new[i]) + continue; + if (!sh->bh_old[i]) + compute_block(sh, i); + memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size); + } + sh->nr_pending--; + complete_stripe(sh); + return; + } + if (nr_failed_overwrite) { + sh->phase = PHASE_READ_OLD; + sh->nr_pending = (disks - 1) - nr_cache; + PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (sh->bh_old[i]) + continue; + if (!operational[i]) + continue; + sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + clear_bit(BH_Lock, &sh->bh_old[i]->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]); + } + } else { + sh->phase = PHASE_READ; + sh->nr_pending = nr_read - nr_cache_overwrite; + PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (!sh->bh_new[i]) + continue; + if (sh->bh_old[i]) { + memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size); + continue; + } + clear_bit(BH_Lock, &sh->bh_new[i]->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_new[i]); + } + } + } +} + +static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + const unsigned int raid_disks = raid_conf->raid_disks; + const unsigned int data_disks = raid_disks - 1; + unsigned int dd_idx, pd_idx; + unsigned long new_sector; + + struct stripe_head *sh; + + if (rw == READA) rw = READ; + if (rw == WRITEA) rw = WRITE; + + new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks, + &dd_idx, &pd_idx, raid_conf); + + PRINTK(("raid5_make_request, sector %lu\n", new_sector)); + sh = get_stripe(raid_conf, new_sector, bh->b_size); + if ((rw == READ && sh->cmd == STRIPE_WRITE) || (rw == WRITE && sh->cmd == STRIPE_READ)) { + printk("raid5: lock contention, rw == %d, sh->cmd == %d\n", rw, sh->cmd); + lock_stripe(sh); + if (!sh->nr_pending) + handle_stripe(sh); + wait_on_stripe(sh); + } + sh->pd_idx = pd_idx; + if (sh->phase != PHASE_COMPLETE && sh->phase != PHASE_BEGIN) + PRINTK(("stripe %lu catching the bus!\n", sh->sector)); + add_stripe_bh(sh, bh, dd_idx, rw); + + md_wakeup_thread(raid_conf->thread); + return 0; +} + +/* + * This is our raid5 kernel thread. + * + * We scan the hash table for stripes which can be handled now. + * During the scan, completed stripes are saved for us by the interrupt + * handler, so that they will not have to wait for our next wakeup. + */ +static void raid5d (void *data) +{ + struct stripe_head *sh; + struct raid5_data *raid_conf = data; + struct md_dev *mddev = raid_conf->mddev; + int i, handled = 0, unplug = 0; + unsigned long flags; + + PRINTK(("+++ raid5d active\n")); + + if (mddev->sb_dirty) { + mddev->sb_dirty = 0; + md_update_sb((int) (mddev - md_dev)); + } + save_flags(flags); + cli(); + stripe_handle_list = stripe_handle_tail = NULL; + restore_flags(flags); + + for (i = 0; i < NR_HASH; i++) { +repeat: + sh = stripe_hashtbl[i]; + for (; sh; sh = sh->hash_next) { + if (sh->raid_conf != raid_conf) + continue; + if (sh->phase == PHASE_COMPLETE) + continue; + if (sh->nr_pending) + continue; + if (sh->sector == raid_conf->next_sector) { + raid_conf->sector_count += (sh->size >> 9); + if (raid_conf->sector_count >= 128) + unplug = 1; + } else + unplug = 1; + if (unplug) { + PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, raid_conf->sector_count)); + unplug_devices(sh); + unplug = 0; + raid_conf->sector_count = 0; + } + raid_conf->next_sector = sh->sector + (sh->size >> 9); + handled++; + handle_stripe(sh); + goto repeat; + } + } + if (raid_conf) { + PRINTK(("%d stripes handled, nr_handle %d\n", handled, raid_conf->nr_handle)); + save_flags(flags); + cli(); + if (!raid_conf->nr_handle) + clear_bit(THREAD_WAKEUP, &raid_conf->thread->flags); + } + PRINTK(("--- raid5d inactive\n")); +} + +static int raid5_run (int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf; + int i, j, raid_disk; + md_superblock_t *sb = mddev->sb; + md_descriptor_t *descriptor; + struct real_dev *realdev; + + MOD_INC_USE_COUNT; + + if (sb->level != 5 && sb->level != 4) { + printk("raid5: %s: raid level not set to 4/5 (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + MOD_DEC_USE_COUNT; + return -EIO; + } + + mddev->private = kmalloc (sizeof (struct raid5_data), GFP_KERNEL); + raid_conf = mddev->private; + memset (raid_conf, 0, sizeof (*raid_conf)); + raid_conf->mddev = mddev; + + PRINTK(("raid5_run(%d) called.\n", minor)); + + for (i = 0; i < mddev->nb_dev; i++) { + realdev = &mddev->devices[i]; + if (!realdev->sb) { + printk(KERN_ERR "raid5: disabled device %s (couldn't access raid superblock)\n", kdevname(realdev->dev)); + continue; + } + + /* + * This is important -- we are using the descriptor on + * the disk only to get a pointer to the descriptor on + * the main superblock, which might be more recent. + */ + descriptor = &sb->disks[realdev->sb->descriptor.number]; + if (descriptor->state & (1 << MD_FAULTY_DEVICE)) { + printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", kdevname(realdev->dev)); + continue; + } + if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) { + if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) { + printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", kdevname(realdev->dev)); + continue; + } + raid_disk = descriptor->raid_disk; + if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) { + printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", kdevname(realdev->dev)); + continue; + } + if (raid_conf->disks[raid_disk].operational) { + printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", kdevname(realdev->dev), raid_disk); + continue; + } + printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", kdevname(realdev->dev), raid_disk); + + raid_conf->disks[raid_disk].number = descriptor->number; + raid_conf->disks[raid_disk].raid_disk = raid_disk; + raid_conf->disks[raid_disk].dev = mddev->devices[i].dev; + raid_conf->disks[raid_disk].operational = 1; + + raid_conf->working_disks++; + } + } + raid_conf->raid_disks = sb->raid_disks; + raid_conf->failed_disks = raid_conf->raid_disks - raid_conf->working_disks; + raid_conf->mddev = mddev; + raid_conf->chunk_size = sb->chunk_size; + raid_conf->level = sb->level; + raid_conf->algorithm = sb->parity_algorithm; + + if (!raid_conf->chunk_size || raid_conf->chunk_size % 4) { + printk(KERN_ERR "raid5: invalid chunk size %d for %s\n", raid_conf->chunk_size, kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + if (raid_conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) { + printk(KERN_ERR "raid5: unsupported parity algorithm %d for %s\n", raid_conf->algorithm, kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + if (raid_conf->failed_disks > 1) { + printk(KERN_ERR "raid5: not enough operational devices for %s (%d/%d failed)\n", kdevname(MKDEV(MD_MAJOR, minor)), raid_conf->failed_disks, raid_conf->raid_disks); + goto abort; + } + +#if 0 + if (check_consistenty(mddev)) { + printk(KERN_ERR "raid5: detected raid-5 xor inconsistenty -- run ckraid\n"); + sb->state |= 1 << MD_SB_ERRORS; + goto abort; + } +#endif + + if ((raid_conf->thread = md_register_thread(raid5d, raid_conf)) == NULL) { + printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + + /* + * Regenerate the "device is in sync with the raid set" bit for + * each device. + */ + for (i = 0; i < sb->nr_disks ; i++) { + sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE); + for (j = 0; j < sb->raid_disks; j++) { + if (!raid_conf->disks[j].operational) + continue; + if (sb->disks[i].number == raid_conf->disks[j].number) + sb->disks[i].state |= 1 << MD_SYNC_DEVICE; + } + } + sb->active_disks = raid_conf->working_disks; + + if (sb->active_disks == sb->raid_disks) + printk("raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm); + else + printk(KERN_ALERT "raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm); + + /* Ok, everything is just fine now */ + return (0); +abort: + if (raid_conf) + kfree(raid_conf); + mddev->private = NULL; + printk(KERN_ALERT "raid5: failed to run raid set %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + MOD_DEC_USE_COUNT; + return -EIO; +} + +static int raid5_stop (int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + + md_unregister_thread(raid_conf->thread); + kfree (raid_conf); + shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE); + shrink_buffers(RAID5_POOL_SIZE); + MOD_DEC_USE_COUNT; + return 0; +} + +static int raid5_status (char *page, int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + md_superblock_t *sb = mddev->sb; + int sz = 0, i; + + sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->parity_algorithm); + sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks); + for (i = 0; i < raid_conf->raid_disks; i++) + sz += sprintf (page+sz, "%s", raid_conf->disks[i].operational ? "U" : "_"); + sz += sprintf (page+sz, "]"); + return sz; +} + +static struct md_personality raid5_personality= +{ + "raid5", + raid5_map, + raid5_make_request, + raid5_end_request, + raid5_run, + raid5_stop, + raid5_status, + NULL, /* no ioctls */ + 0, + raid5_error +}; + +int raid5_init (void) +{ + if ((stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0)) == NULL) + return -ENOMEM; + memset(stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE); + return register_md_personality (RAID5, &raid5_personality); +} + +#ifdef MODULE +int init_module (void) +{ + return raid5_init(); +} + +void cleanup_module (void) +{ + free_pages((unsigned long) stripe_hashtbl, HASH_PAGES_ORDER); + shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE); + shrink_buffers(RAID5_POOL_SIZE); + unregister_md_personality (RAID5); +} +#endif diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 0376f64243d4..4e629f17c886 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -582,7 +582,7 @@ static uch *window; static unsigned insize = 0; /* valid bytes in inbuf */ static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ static unsigned outcnt = 0; /* bytes in output buffer */ -static exit_code = 0; +static int exit_code = 0; static long bytes_out = 0; static struct file *crd_infp, *crd_outfp; diff --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c index 3196ac2120ce..a2f111513ade 100644 --- a/drivers/cdrom/cm206.c +++ b/drivers/cdrom/cm206.c @@ -739,7 +739,7 @@ int get_toc_lba(uch track) uch * q = cd->q; uch ct; /* current track */ int binary=0; - const skip = 3*60*75; + const int skip = 3*60*75; for (i=track; i>0; i--) if (cd->toc[i].track) { min = fsm2lba(cd->toc[i].fsm); diff --git a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c index 155ed92d54a9..fa3c90e97d1f 100644 --- a/drivers/cdrom/mcd.c +++ b/drivers/cdrom/mcd.c @@ -311,7 +311,10 @@ mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, { i = updateToc(); if (i < 0) - return i; /* error reading TOC */ + /* Hermann.Lauer@IWR.Uni-Heidelberg.De: + We _can_ open the door even without a CD */ + if (cmd != CDROMEJECT) + return i; /* error reading TOC */ } switch (cmd) diff --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c index 80151430a323..32c411b4a2a9 100644 --- a/drivers/cdrom/sonycd535.c +++ b/drivers/cdrom/sonycd535.c @@ -31,6 +31,10 @@ * More changes to support CDU-510/515 series * (Claudio Porfiri) * + * 1997-11-18 + * Blocksize awareness + * Dong Liu + * * Things to do: * - handle errors and status better, put everything into a single word * - use interrupts (code mostly there, but a big hole still missing) @@ -589,11 +593,13 @@ set_drive_mode(int mode, Byte status[2]) * The routine returns number of bytes read in if successful, otherwise * it returns one of the standard error returns. ***************************************************************************/ + +static int sonycd535_block_size = 2048; + static int seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2], Byte **buff, int buf_size) { - const int block_size = 2048; Byte cmd_buff[7]; int i; int read_status; @@ -601,7 +607,7 @@ seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2], Byte *data_buff; int sector_count = 0; - if (buf_size < ((long)block_size) * n_blocks) + if (buf_size < sonycd535_block_size * n_blocks) return NO_ROOM; set_drive_mode(SONY535_CDROM_DRIVE_MODE, status); @@ -626,7 +632,7 @@ seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2], if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) { /* data is ready, read it */ data_buff = buff[sector_count++]; - for (i = 0; i < block_size; i++) + for (i = 0; i < sonycd535_block_size; i++) *data_buff++ = inb(data_reg); /* unrolling this loop does not seem to help */ break; /* exit the timeout loop */ } @@ -639,7 +645,7 @@ seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2], /* read all the data, now read the status */ if ((i = read_exec_status(status)) != 0) return i; - return block_size * sector_count; + return sonycd535_block_size * sector_count; } /* seek_and_read_N_blocks() */ /**************************************************************************** @@ -1594,6 +1600,7 @@ sony535_init(void) return -EIO; } blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + blksize_size[MAJOR_NR] = &sonycd535_block_size; read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ sony_toc = (struct s535_sony_toc *) diff --git a/drivers/char/ftape/ftape-bsm.h b/drivers/char/ftape/ftape-bsm.h index e84363d45dce..c7054877f189 100644 --- a/drivers/char/ftape/ftape-bsm.h +++ b/drivers/char/ftape/ftape-bsm.h @@ -44,7 +44,7 @@ /* * ftape-io.c defined global vars. */ -extern bad_sector_map_changed; +extern int bad_sector_map_changed; /* * ftape-io.c defined global functions. diff --git a/drivers/char/ftape/ftape-ctl.h b/drivers/char/ftape/ftape-ctl.h index 59fcfdc76bff..9a084a76192a 100644 --- a/drivers/char/ftape/ftape-ctl.h +++ b/drivers/char/ftape/ftape-ctl.h @@ -61,7 +61,7 @@ typedef struct { */ extern int ftape_failure; extern int write_protected; -extern ftape_offline; +extern int ftape_offline; extern int formatted; extern int no_tape; extern history_record history; diff --git a/drivers/char/ftape/kernel-interface.h b/drivers/char/ftape/kernel-interface.h index 9843390c78c8..f835be5dc991 100644 --- a/drivers/char/ftape/kernel-interface.h +++ b/drivers/char/ftape/kernel-interface.h @@ -59,7 +59,7 @@ asmlinkage extern void cleanup_module(void); /* kernel global functions not (yet) standard accessible * (linked at load time by modules package). */ -asmlinkage extern sys_sgetmask(void); -asmlinkage extern sys_ssetmask(int); +asmlinkage extern int sys_sgetmask(void); +asmlinkage extern int sys_ssetmask(int); #endif diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index ad5c09689ce2..c193a38ec588 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -229,7 +229,7 @@ isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason) { printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n", dev->name, reason); - if(skb->proto==htons(ETH_P_IP)) + if(skb->protocol==htons(ETH_P_IP)) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0 #if (LINUX_VERSION_CODE < 0x02010f) /* 2.1.15 */ ,dev diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c index 18f6029da7b4..6b5b369e363b 100644 --- a/drivers/isdn/sc/interrupt.c +++ b/drivers/isdn/sc/interrupt.c @@ -32,7 +32,7 @@ #include "message.h" #include "card.h" -extern indicate_status(int, int, ulong, char *); +extern int indicate_status(int, int, ulong, char *); extern void check_phystat(unsigned long); extern void dump_messages(int); extern int receivemessage(int, RspMessage *); diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c index 7c38191e6a56..a562db350c8f 100644 --- a/drivers/net/3c503.c +++ b/drivers/net/3c503.c @@ -75,7 +75,7 @@ static int el2_close(struct device *dev); static void el2_reset_8390(struct device *dev); static void el2_init_card(struct device *dev); static void el2_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static void el2_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, @@ -432,7 +432,7 @@ el2_init_card(struct device *dev) */ static void el2_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page) + const unsigned char *buf, const int start_page) { unsigned short int *wrd; int boguscount; /* timeout counter */ diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c index f7fb292d069d..634941c935b9 100644 --- a/drivers/net/3c515.c +++ b/drivers/net/3c515.c @@ -18,9 +18,9 @@ static char *version = "3c515.c:v0.99 4/7/98 becker@cesdis.gsfc.nasa.gov\n"; /* "Knobs" that adjust features and parameters. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1512 effectively disables this feature. */ -static const rx_copybreak = 200; +static const int rx_copybreak = 200; /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ -static const mtu = 1500; +static const int mtu = 1500; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index a6d5fc03b2ee..b9331e60d431 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -20,9 +20,9 @@ static char *version = /* "Knobs" that adjust features and parameters. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1512 effectively disables this feature. */ -static const rx_copybreak = 200; +static const int rx_copybreak = 200; /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ -static const mtu = 1500; +static const int mtu = 1500; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; diff --git a/drivers/net/Changelog.tlan b/drivers/net/Changelog.tlan index d026ede2b0e0..33e3b81f0c48 100644 --- a/drivers/net/Changelog.tlan +++ b/drivers/net/Changelog.tlan @@ -1,5 +1,52 @@ TLan Device Driver change log. +0.43 - Changed the id strings of the Compaq devices to their real + names. + - Added 2 other Olicom devices with a patch provided by + Henrik Storner of Olicom. + - Changed AN so the driver won't try to autonegotiate with + a partner that doesn't autonegotiate. + - Added a section to verify whether in full or half duplex. + - Added value checking for speed and duplex inputs. + - Added B012 and B030 Compaq devices. + +0.42 - Coverted tranceiver from misused per-tranceiver functions + to a timer-oriented path that covers four possible classes + of tranceivers: + 1. Unmanaged + 2. Manual configuration + 3. Autonegotiation w/ manual configuration + 4. Autonegotiation w/ auto configuration + - Added ability to force speed and duplex settings. + - Made speed, duplex, sa_int, etc, to be set per adapter with + ether= command. + - Added support for Olicom OC-2326 + +0.41 - Added non-bounce buffer paths. Added TLan_FreeLists to + dispose of unused sk_buff's at device close time. + - Discovered inlined functions aren't being inlined, or at + least take up more space than macros would. + +0.40 - Refined polarity checking to handle case when polarity + changes to normal from abnormal. + - Cleaned up TLan_Probe routine. + - Added an option for the SA_INTERRUPT flag to be set. + - Created FAQ. + - Removed all C++ style comments. + - Added error message if devices busmastering is inactive. + Also will now skip device. + - Put cli and sti back into TLan_HandleInterrupt. It makes + me feel better. + - Moved the code that checks for boot parameter options to + tlan_probe. + +0.39 - Minor cosmetic cleanups (especially variable declarations). + - Changes low level TLAN functions to use dev structures instead + individual data elements. + - Changed low level TLAN functions not to play with sti and cli + if in an interrupt routine. + - Removed cli and sti from TLan_HandleInterrupt. + 0.38 - Added code to isolate the external PHY if the internal PHY is being used for AUI/BNC connectivity. Also set the aui and debug variables from mem_start and mem_end if the driver is @@ -19,7 +66,7 @@ TLan Device Driver change log. 100Mbs should work now on 0xAE32. - Fixed a small bug where heartbeat and PHY interrupts were always being enabled. - - Force the driver into Unmanaged PHY mode for 0xF130 devices, + - Force the the driver into Unmanaged PHY mode for 0xF130 devices, even if a managed (ie, the built-in one) PHY is detected. - Moved the PHY initialization to after the onboard PHY is enabled, if selected. diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c index 19896ff95fc7..15c1bcac9388 100644 --- a/drivers/net/dgrs.c +++ b/drivers/net/dgrs.c @@ -405,7 +405,7 @@ do_plx_dma( */ udelay(1); - csr = (volatile) priv->vplxdma[PLX_DMA_CSR/4]; + csr = (volatile int) priv->vplxdma[PLX_DMA_CSR/4]; if (csr & PLX_DMA_CSR_0_DONE) break; @@ -876,7 +876,7 @@ dgrs_ioctl(struct device *devN, struct ifreq *ifr, int cmd) /* Wait for old command to finish */ for (i = 0; i < 1000; ++i) { - if ( (volatile) privN->bcomm->bc_filter_cmd <= 0 ) + if ( (volatile int) privN->bcomm->bc_filter_cmd <= 0 ) break; udelay(1); } diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c index 141c444846d4..7ba12d311d07 100644 --- a/drivers/net/e2100.c +++ b/drivers/net/e2100.c @@ -101,7 +101,7 @@ static void e21_reset_8390(struct device *dev); static void e21_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void e21_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static void e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); @@ -330,7 +330,7 @@ e21_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_off static void e21_block_output(struct device *dev, int count, const unsigned char *buf, - int start_page) + const int start_page) { short ioaddr = dev->base_addr; volatile char *shared_mem = (char *)dev->mem_start; diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c index b887087fe8ba..39fd240c11d3 100644 --- a/drivers/net/eth16i.c +++ b/drivers/net/eth16i.c @@ -714,7 +714,7 @@ static int eth16i_check_signature(short ioaddr) creg[0] &= 0x0F; /* Mask collision cnr */ creg[2] &= 0x7F; /* Mask DCLEN bit */ -#ifdef 0 +#if 0 /* This was removed because the card was sometimes left to state from which it couldn't be find anymore. If there is need diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c index b2074435822b..c8e361114a99 100644 --- a/drivers/net/hp-plus.c +++ b/drivers/net/hp-plus.c @@ -101,13 +101,13 @@ static int hpp_close(struct device *dev); static void hpp_mem_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void hpp_mem_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static void hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void hpp_io_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void hpp_io_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static void hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); @@ -385,7 +385,7 @@ hpp_mem_block_input(struct device *dev, int count, struct sk_buff *skb, int ring It's always safe to round up, so we do. */ static void hpp_io_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page) + const unsigned char *buf, const int start_page) { int ioaddr = dev->base_addr - NIC_OFFSET; outw(start_page << 8, ioaddr + HPP_OUT_ADDR); @@ -395,7 +395,7 @@ hpp_io_block_output(struct device *dev, int count, static void hpp_mem_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page) + const unsigned char *buf, const int start_page) { int ioaddr = dev->base_addr - NIC_OFFSET; int option_reg = inw(ioaddr + HPP_OPTION); diff --git a/drivers/net/hp.c b/drivers/net/hp.c index fc965f77efe4..741924f954ef 100644 --- a/drivers/net/hp.c +++ b/drivers/net/hp.c @@ -65,7 +65,7 @@ static void hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, static void hp_block_input(struct device *dev, int count, struct sk_buff *skb , int ring_offset); static void hp_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static void hp_init_card(struct device *dev); @@ -309,7 +309,7 @@ hp_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offs static void hp_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page) + const unsigned char *buf, const int start_page) { int nic_base = dev->base_addr; int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE); diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c index c3e7f6abc97e..226f077f33b6 100644 --- a/drivers/net/ne2k-pci.c +++ b/drivers/net/ne2k-pci.c @@ -45,7 +45,7 @@ static const char *version = #include "8390.h" /* Set statically or when loading the driver module. */ -static debug = 1; +static int debug = 1; /* Some defines that people can play with if so inclined. */ diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c index c72805a0bf93..6d486e95d052 100644 --- a/drivers/net/ni52.c +++ b/drivers/net/ni52.c @@ -984,7 +984,7 @@ static void ni52_rcv_int(struct device *dev) } #endif -#ifdef 0 +#if 0 if(!at_least_one) { int i; diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c index 9bf545be47dc..074a235bfdda 100644 --- a/drivers/net/smc-ultra.c +++ b/drivers/net/smc-ultra.c @@ -75,13 +75,13 @@ static void ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, static void ultra_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void ultra_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void ultra_pio_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void ultra_pio_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static int ultra_close_card(struct device *dev); @@ -385,7 +385,7 @@ static void ultra_pio_input(struct device *dev, int count, } static void ultra_pio_output(struct device *dev, int count, - const unsigned char *buf, const start_page) + const unsigned char *buf, const int start_page) { int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ diff --git a/drivers/net/smc-ultra32.c b/drivers/net/smc-ultra32.c index 4043b8f24754..52e0cbd49ca8 100644 --- a/drivers/net/smc-ultra32.c +++ b/drivers/net/smc-ultra32.c @@ -68,7 +68,7 @@ static void ultra32_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, static void ultra32_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void ultra32_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static int ultra32_close(struct device *dev); #define ULTRA32_CMDREG 0 /* Offset to ASIC command register. */ diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index 1e23421e6ed3..128f0683fee1 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -3,20 +3,22 @@ * Linux ThunderLAN Driver * * tlan.c - * by James Banks, james.banks@caldera.com + * by James Banks * - * (C) 1997 Caldera, Inc. + * (C) 1997-1998 Caldera, Inc. * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. * - ** This file is best viewed/edited with tabstop=4 and colums>=132. + ** This file is best viewed/edited with columns>=132. * ** Useful (if not required) reading: * * Texas Instruments, ThunderLAN Programmer's Guide, * TI Literature Number SPWU013A * available in PDF format from www.ti.com + * Level One, LXT901 and LXT970 Data Sheets + * available in PDF format from www.level1.com * National Semiconductor, DP83840A Data Sheet * available in PDF format from www.national.com * Microchip Technology, 24C01A/02A/04A Data Sheet @@ -50,48 +52,100 @@ static int TLanDevicesInstalled = 0; static int debug = 0; static int aui = 0; +static int sa_int = 0; +static int bbuf = 0; +static int duplex = 0; +static int speed = 0; static u8 *TLanPadBuffer; static char TLanSignature[] = "TLAN"; static int TLanVersionMajor = 0; -static int TLanVersionMinor = 38; +static int TLanVersionMinor = 43; -static TLanPciId TLanDeviceList[] = { +static TLanAdapterEntry TLanAdapterList[] = { { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10, - "Compaq Netelligent 10" + "Compaq Netelligent 10 T PCI UTP", + TLAN_ADAPTER_ACTIVITY_LED, + 0x83 }, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100, - "Compaq Netelligent 10/100" + "Compaq Netelligent 10/100 TX PCI UTP", + TLAN_ADAPTER_ACTIVITY_LED, + 0x83 }, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED, - "Compaq Integrated NetFlex-3/P" + "Compaq Integrated NetFlex-3/P", + TLAN_ADAPTER_NONE, + 0x83 }, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P, - "Compaq NetFlex-3/P" + "Compaq NetFlex-3/P", + TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY, + 0x83 }, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P_BNC, - "Compaq NetFlex-3/P" + "Compaq NetFlex-3/P", + TLAN_ADAPTER_NONE, + 0x83 }, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT, - "Compaq ProLiant Netelligent 10/100" + "Compaq Netelligent Integrated 10/100 TX UTP", + TLAN_ADAPTER_NONE, + 0x83 }, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL, - "Compaq Dual Port Netelligent 10/100" + "Compaq Netelligent Dual 10/100 TX PCI UTP", + TLAN_ADAPTER_NONE, + 0x83 }, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_DESKPRO_4000_5233MMX, - "Compaq Deskpro 4000 5233MMX" + "Compaq Netelligent 10/100 TX Embedded UTP", + TLAN_ADAPTER_NONE, + 0x83 + }, + { PCI_VENDOR_ID_OLICOM, + PCI_DEVICE_ID_OLICOM_OC2183, + "Olicom OC-2183/2185", + TLAN_ADAPTER_USE_INTERN_10, + 0xF8 + }, + { PCI_VENDOR_ID_OLICOM, + PCI_DEVICE_ID_OLICOM_OC2325, + "Olicom OC-2325", + TLAN_ADAPTER_UNMANAGED_PHY, + 0xF8 + }, + { PCI_VENDOR_ID_OLICOM, + PCI_DEVICE_ID_OLICOM_OC2326, + "Olicom OC-2326", + TLAN_ADAPTER_USE_INTERN_10, + 0xF8 + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100, + "Compaq Netelligent 10/100 TX UTP", + TLAN_ADAPTER_ACTIVITY_LED, + 0x83 + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10_T2, + "Compaq Netelligent 10 T/2 PCI UTP/Coax", + TLAN_ADAPTER_NONE, + 0x83 }, { 0, 0, - NULL + NULL, + 0, + 0 } /* End of List */ }; @@ -117,28 +171,37 @@ static u32 TLan_HandleRxEOC( struct device *, u16 ); static void TLan_Timer( unsigned long ); static void TLan_ResetLists( struct device * ); +static void TLan_FreeLists( struct device * ); static void TLan_PrintDio( u16 ); static void TLan_PrintList( TLanList *, char *, int ); static void TLan_ReadAndClearStats( struct device *, int ); -static int TLan_Reset( struct device * ); +static void TLan_ResetAdapter( struct device * ); +static void TLan_FinishReset( struct device * ); static void TLan_SetMac( struct device *, int areg, char *mac ); -static int TLan_PhyNop( struct device * ); static void TLan_PhyPrint( struct device * ); -static void TLan_PhySelect( struct device * ); +static void TLan_PhyDetect( struct device * ); +static void TLan_PhyPowerDown( struct device * ); +static void TLan_PhyPowerUp( struct device * ); +static void TLan_PhyReset( struct device * ); +static void TLan_PhyStartLink( struct device * ); +static void TLan_PhyFinishAutoNeg( struct device * ); +/* +static int TLan_PhyNop( struct device * ); static int TLan_PhyInternalCheck( struct device * ); static int TLan_PhyInternalService( struct device * ); static int TLan_PhyDp83840aCheck( struct device * ); +*/ -static int TLan_MiiReadReg(u16, u16, u16, u16 *); +static int TLan_MiiReadReg( struct device *, u16, u16, u16 * ); static void TLan_MiiSendData( u16, u32, unsigned ); -static void TLan_MiiSync(u16); -static void TLan_MiiWriteReg(u16, u16, u16, u16); +static void TLan_MiiSync( u16 ); +static void TLan_MiiWriteReg( struct device *, u16, u16, u16 ); static void TLan_EeSendStart( u16 ); static int TLan_EeSendByte( u16, u8, int ); static void TLan_EeReceiveByte( u16, u8 *, int ); -static int TLan_EeReadByte( u16, u8, u8 * ); +static int TLan_EeReadByte( struct device *, u8, u8 * ); static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { @@ -152,6 +215,25 @@ static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { TLan_HandleRxEOC }; +static inline void +TLan_SetTimer( struct device *dev, u32 ticks, u32 type ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + cli(); + if ( priv->timer.function != NULL ) { + return; + } + priv->timer.function = &TLan_Timer; + sti(); + + priv->timer.data = (unsigned long) dev; + priv->timer.expires = jiffies + ticks; + priv->timerSetAt = jiffies; + priv->timerType = type; + add_timer( &priv->timer ); + +} /* TLan_SetTimer */ /***************************************************************************** @@ -177,7 +259,7 @@ static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { * * This function begins the setup of the driver creating a * pad buffer, finding all TLAN devices (matching - * TLanDeviceList entries), and creating and initializing a + * TLanAdapterList entries), and creating and initializing a * device structure for each adapter. * **************************************************************/ @@ -189,14 +271,14 @@ extern int init_module(void) struct device *dev; size_t dev_size; u8 dfn; - u32 dl_ix; + u32 index; int failed; int found; u32 io_base; u8 irq; u8 rev; - printk( "TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n", + printk( "TLAN driver, v%d.%d, (C) 1997-8 Caldera, Inc.\n", TLanVersionMajor, TLanVersionMinor ); @@ -212,7 +294,7 @@ extern int init_module(void) dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo); - while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) { + while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ) ) ) { dev = (struct device *) kmalloc( dev_size, GFP_KERNEL ); if ( dev == NULL ) { printk( "TLAN: Could not allocate memory for device.\n" ); @@ -227,27 +309,41 @@ extern int init_module(void) dev->irq = irq; dev->init = TLan_Init; - priv->pciBus = bus; - priv->pciDeviceFn = dfn; - priv->pciRevision = rev; - priv->pciEntry = dl_ix; + priv->adapter = &TLanAdapterList[index]; + priv->adapterRev = rev; + priv->aui = aui; + if ( ( duplex != 1 ) && ( duplex != 2 ) ) { + duplex = 0; + } + priv->duplex = duplex; + if ( ( speed != 10 ) && ( speed != 100 ) ) { + speed = 0; + } + priv->speed = speed; + priv->sa_int = sa_int; + priv->debug = debug; ether_setup( dev ); failed = register_netdev( dev ); if ( failed ) { - printk( "TLAN: Could not register network device. Freeing struct.\n" ); + printk( "TLAN: Could not register device.\n" ); kfree( dev ); } else { priv->nextDevice = TLanDevices; TLanDevices = dev; TLanDevicesInstalled++; - printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName ); + printk("TLAN: %s irq=%2d io=%04x, %s, Rev. %d\n", + dev->name, + (int) dev->irq, + (int) dev->base_addr, + priv->adapter->deviceLabel, + priv->adapterRev ); } } - // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); + /* printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); */ return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV ); @@ -276,11 +372,12 @@ extern void cleanup_module(void) struct device *dev; TLanPrivateInfo *priv; - while (TLanDevicesInstalled) { + while ( TLanDevicesInstalled ) { dev = TLanDevices; priv = (TLanPrivateInfo *) dev->priv; - if ( priv->dmaStorage ) + if ( priv->dmaStorage ) { kfree( priv->dmaStorage ); + } release_region( dev->base_addr, 0x10 ); unregister_netdev( dev ); TLanDevices = priv->nextDevice; @@ -315,53 +412,79 @@ extern void cleanup_module(void) extern int tlan_probe( struct device *dev ) { + TLanPrivateInfo *priv; static int pad_allocated = 0; int found; - TLanPrivateInfo *priv; u8 bus, dfn, irq, rev; - u32 io_base, dl_ix; + u32 io_base, index; - found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ); - if ( found ) { - dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL ); - if ( dev->priv == NULL ) { - printk( "TLAN: Could not allocate memory for device.\n" ); + found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ); + + if ( ! found ) { + return -ENODEV; + } + + dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL ); + + if ( dev->priv == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + return -ENOMEM; + } + + memset( dev->priv, 0, sizeof(TLanPrivateInfo) ); + + if ( ! pad_allocated ) { + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, +// ( GFP_KERNEL | GFP_DMA ) + ( GFP_KERNEL ) + ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for padding.\n" ); + kfree( dev->priv ); + return -ENOMEM; + } else { + pad_allocated = 1; + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); } - memset( dev->priv, 0, sizeof(TLanPrivateInfo) ); - priv = (TLanPrivateInfo *) dev->priv; + } - dev->name = priv->devName; - strcpy( priv->devName, " " ); + priv = (TLanPrivateInfo *) dev->priv; - dev = init_etherdev( dev, sizeof(TLanPrivateInfo) ); + dev->name = priv->devName; + strcpy( priv->devName, " " ); - dev->base_addr = io_base; - dev->irq = irq; + dev = init_etherdev( dev, sizeof(TLanPrivateInfo) ); - priv->pciBus = bus; - priv->pciDeviceFn = dfn; - priv->pciRevision = rev; - priv->pciEntry = dl_ix; + dev->base_addr = io_base; + dev->irq = irq; - if ( ! pad_allocated ) { - TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); - if ( TLanPadBuffer == NULL ) { - printk( "TLAN: Could not allocate memory for pad buffer.\n" ); - } else { - pad_allocated = 1; - memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); - } - } - printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n",TLanVersionMajor, - TLanVersionMinor, - dev->name, - (int) irq, - io_base, - TLanDeviceList[dl_ix].deviceName ); - TLan_Init( dev ); + + priv->adapter = &TLanAdapterList[index]; + priv->adapterRev = rev; + priv->aui = dev->mem_start & 0x01; + priv->duplex = ( ( dev->mem_start & 0x0C ) == 0x0C ) ? 0 : ( dev->mem_start & 0x0C ) >> 2; + priv->speed = ( ( dev->mem_start & 0x30 ) == 0x30 ) ? 0 : ( dev->mem_start & 0x30 ) >> 4; + if ( priv->speed == 0x1 ) { + priv->speed = TLAN_SPEED_10; + } else if ( priv->speed == 0x2 ) { + priv->speed = TLAN_SPEED_100; } + priv->sa_int = dev->mem_start & 0x02; + priv->debug = dev->mem_end; + + + printk("TLAN %d.%d: %s irq=%2d io=%04x, %s, Rev. %d\n", + TLanVersionMajor, + TLanVersionMinor, + dev->name, + (int) irq, + io_base, + priv->adapter->deviceLabel, + priv->adapterRev ); + + TLan_Init( dev ); - return ( ( found ) ? 0 : -ENODEV ); + return 0; } /* tlan_probe */ @@ -389,7 +512,7 @@ extern int tlan_probe( struct device *dev ) * of the adapter. * * This function searches for an adapter with PCI vendor - * and device IDs matching those in the TLanDeviceList. + * and device IDs matching those in the TLanAdapterList. * The function 'remembers' the last device it found, * and so finds a new device (if anymore are to be found) * each time the function is called. It then looks up @@ -413,11 +536,11 @@ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_ return 0; } - for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) { + for (; TLanAdapterList[dl_index].vendorId != 0; dl_index++) { not_found = pcibios_find_device( - TLanDeviceList[dl_index].vendorId, - TLanDeviceList[dl_index].deviceId, + TLanAdapterList[dl_index].vendorId, + TLanAdapterList[dl_index].deviceId, pci_index, pci_bus, pci_dfn @@ -428,8 +551,8 @@ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n", - TLanDeviceList[dl_index].vendorId, - TLanDeviceList[dl_index].deviceId + TLanAdapterList[dl_index].vendorId, + TLanAdapterList[dl_index].deviceId ); pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev); @@ -457,8 +580,12 @@ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_ if ( *pci_io_base == 0 ) printk("TLAN: IO mapping not available, ignoring device.\n"); - if (pci_command & PCI_COMMAND_MASTER) { - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Bus mastering is active.\n"); + if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) { + pcibios_write_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER ); + printk( "TLAN: Attempting to activate busmastering.\n" ); + printk( "TLAN: You may need to set busmastering to on in the CMOS\n" ); + printk( "TLAN: before this card will work.\n" ); + *pci_io_base = 0; } pci_index++; @@ -499,9 +626,9 @@ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_ int TLan_Init( struct device *dev ) { - int dma_size; - int err; - int i; + int dma_size; + int err; + int i; TLanPrivateInfo *priv; priv = (TLanPrivateInfo *) dev->priv; @@ -516,8 +643,14 @@ int TLan_Init( struct device *dev ) } request_region( dev->base_addr, 0x10, TLanSignature ); - dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) + if ( bbuf ) { + dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE ); + } else { + dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) + * ( sizeof(TLanList) ); + } + priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA ); if ( priv->dmaStorage == NULL ) { printk( "TLAN: Could not allocate lists and buffers for %s.\n", @@ -528,19 +661,24 @@ int TLan_Init( struct device *dev ) priv->rxList = (TLanList *) ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 ); priv->txList = priv->rxList + TLAN_NUM_RX_LISTS; - priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS ); - priv->txBuffer = priv->rxBuffer - + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE ); + + if ( bbuf ) { + priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS ); + priv->txBuffer = priv->rxBuffer + + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE ); + } err = 0; for ( i = 0; i < 6 ; i++ ) - err |= TLan_EeReadByte( dev->base_addr, - (u8) 0x83 + i, + err |= TLan_EeReadByte( dev, + (u8) priv->adapter->addrOfs + i, (u8 *) &dev->dev_addr[i] ); - if ( err ) + if ( err ) { printk( "TLAN: %s: Error reading MAC from eeprom: %d\n", dev->name, err ); + } + dev->addr_len = 6; dev->open = &TLan_Open; @@ -549,12 +687,6 @@ int TLan_Init( struct device *dev ) dev->get_stats = &TLan_GetStats; dev->set_multicast_list = &TLan_SetMulticastList; -#ifndef MODULE - - aui = dev->mem_start & 0x01; - debug = dev->mem_end; - -#endif /* MODULE */ return 0; @@ -582,16 +714,21 @@ int TLan_Init( struct device *dev ) int TLan_Open( struct device *dev ) { - int err; TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int err; priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION ); - err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); + if ( priv->sa_int ) { + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Using SA_INTERRUPT\n" ); + err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev ); + } else { + err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); + } if ( err ) { printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq ); return -EAGAIN; } - + MOD_INC_USE_COUNT; dev->tbusy = 0; @@ -603,27 +740,9 @@ int TLan_Open( struct device *dev ) */ TLan_ResetLists( dev ); TLan_ReadAndClearStats( dev, TLAN_IGNORE ); - TLan_Reset( dev ); - TLan_Reset( dev ); - TLan_SetMac( dev, 0, dev->dev_addr ); - outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); - if ( debug >= 1 ) - outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); - - init_timer( &priv->timer ); - priv->timer.data = (unsigned long) dev; - priv->timer.function = &TLan_Timer; - if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { - priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; - priv->timerSetAt = jiffies; - priv->timerType = TLAN_TIMER_LINK; - add_timer( &priv->timer ); - } else { - outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); - outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); - } + TLan_ResetAdapter( dev ); - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s opened. Revision = %x\n", dev->name, priv->tlanRev ); + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Opened. TLAN Chip Rev: %x\n", dev->name, priv->tlanRev ); return 0; @@ -656,9 +775,9 @@ int TLan_Open( struct device *dev ) int TLan_StartTx( struct sk_buff *skb, struct device *dev ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - TLanList *tail_list; - u8 *tail_buffer; - int pad; + TLanList *tail_list; + u8 *tail_buffer; + int pad; if ( ! priv->phyOnline ) { TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name ); @@ -667,16 +786,26 @@ int TLan_StartTx( struct sk_buff *skb, struct device *dev ) } tail_list = priv->txList + priv->txTail; + if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) { TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail ); dev->tbusy = 1; priv->txBusyCount++; return 1; } + tail_list->forward = 0; - tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); - memcpy( tail_buffer, skb->data, skb->len ); + + if ( bbuf ) { + tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); + memcpy( tail_buffer, skb->data, skb->len ); + } else { + tail_list->buffer[0].address = virt_to_bus( skb->data ); + tail_list->buffer[9].address = (u32) skb; + } + pad = TLAN_MIN_FRAME_SIZE - skb->len; + if ( pad > 0 ) { tail_list->frameSize = (u16) skb->len + pad; tail_list->buffer[0].count = (u32) skb->len; @@ -688,7 +817,7 @@ int TLan_StartTx( struct sk_buff *skb, struct device *dev ) tail_list->buffer[1].count = 0; tail_list->buffer[1].address = 0; } - // are we transferring? + cli(); tail_list->cStat = TLAN_CSTAT_READY; if ( ! priv->txInProgress ) { @@ -699,17 +828,19 @@ int TLan_StartTx( struct sk_buff *skb, struct device *dev ) outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD ); } else { TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", priv->txTail ); - if ( priv->txTail == 0 ) + if ( priv->txTail == 0 ) { ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list ); - else + } else { ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list ); + } } sti(); - priv->txTail++; - if ( priv->txTail >= TLAN_NUM_TX_LISTS ) - priv->txTail = 0; - dev_kfree_skb( skb, FREE_WRITE ); + CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS ); + + if ( bbuf ) { + dev_kfree_skb( skb, FREE_WRITE ); + } dev->trans_start = jiffies; return 0; @@ -742,35 +873,34 @@ int TLan_StartTx( struct sk_buff *skb, struct device *dev ) void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs) { - u32 ack; + u32 ack; struct device *dev; - u32 host_cmd; - u16 host_int; - int type; + u32 host_cmd; + u16 host_int; + int type; dev = (struct device *) dev_id; - if ( dev->interrupt ) + cli(); + if ( dev->interrupt ) { printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt ); + } dev->interrupt++; - cli(); - host_int = inw( dev->base_addr + TLAN_HOST_INT ); - outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints + outw( host_int, dev->base_addr + TLAN_HOST_INT ); type = ( host_int & TLAN_HI_IT_MASK ) >> 2; ack = TLanIntVector[type]( dev, host_int ); - sti(); - if ( ack ) { host_cmd = TLAN_HC_ACK | ack | ( type << 18 ); outl( host_cmd, dev->base_addr + TLAN_HOST_CMD ); } dev->interrupt--; + sti(); } /* TLan_HandleInterrupts */ @@ -801,10 +931,11 @@ int TLan_Close(struct device *dev) TLan_ReadAndClearStats( dev, TLAN_RECORD ); outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); - if ( priv->timerSetAt != 0 ) + if ( priv->timer.function != NULL ) del_timer( &priv->timer ); free_irq( dev->irq, dev ); - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name ); + TLan_FreeLists( dev ); + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name ); MOD_DEC_USE_COUNT; @@ -881,11 +1012,11 @@ struct net_device_stats *TLan_GetStats( struct device *dev ) void TLan_SetMulticastList( struct device *dev ) { struct dev_mc_list *dmi = dev->mc_list; - u32 hash1 = 0; - u32 hash2 = 0; - int i; - u32 offset; - u8 tmp; + u32 hash1 = 0; + u32 hash2 = 0; + int i; + u32 offset; + u8 tmp; if ( dev->flags & IFF_PROMISC ) { tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); @@ -955,7 +1086,7 @@ void TLan_SetMulticastList( struct device *dev ) u32 TLan_HandleInvalid( struct device *dev, u16 host_int ) { host_int = 0; - // printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); + /* printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); */ return 0; } /* TLan_HandleInvalid */ @@ -988,19 +1119,24 @@ u32 TLan_HandleInvalid( struct device *dev, u16 host_int ) u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - int eoc = 0; - TLanList *head_list; - u32 ack = 1; + int eoc = 0; + TLanList *head_list; + u32 ack = 1; TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); host_int = 0; head_list = priv->txList + priv->txHead; + + if ( ! bbuf ) { + dev_kfree_skb( (struct sk_buff *) head_list->buffer[9].address, FREE_WRITE ); + head_list->buffer[9].address = 0; + } + if ( head_list->cStat & TLAN_CSTAT_EOC ) eoc = 1; if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { printk( "TLAN: Received interrupt for uncompleted TX frame.\n" ); } - // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat ); #if LINUX_KERNEL_VERSION > 0x20100 priv->stats->tx_bytes += head_list->frameSize; @@ -1008,9 +1144,7 @@ u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) head_list->cStat = TLAN_CSTAT_UNUSED; dev->tbusy = 0; - priv->txHead++; - if ( priv->txHead >= TLAN_NUM_TX_LISTS ) - priv->txHead = 0; + CIRC_INC( priv->txHead, TLAN_NUM_TX_LISTS ); if ( eoc ) { TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); head_list = priv->txList + priv->txHead; @@ -1021,17 +1155,13 @@ u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) priv->txInProgress = 0; } } - TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); - if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { - if ( priv->timerSetAt == 0 ) { - // printk("TxEOF Starting timer...\n"); - priv->timerSetAt = jiffies; - priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; - priv->timerType = TLAN_TIMER_ACT; - add_timer( &priv->timer ); - } else if ( priv->timerType == TLAN_TIMER_ACT ) { + + if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) { + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->timer.function == NULL ) { + TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY ); + } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) { priv->timerSetAt = jiffies; - // printk("TxEOF continuing timer...\n"); } } @@ -1092,7 +1222,7 @@ u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int ) * of the list. If the frame was the last in the Rx * channel (EOC), the function restarts the receive channel * by sending an Rx Go command to the adapter. Then it - * activates/continues the activity LED. + * activates/continues the the activity LED. * **************************************************************/ @@ -1111,11 +1241,14 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) host_int = 0; head_list = priv->rxList + priv->rxHead; tail_list = priv->rxList + priv->rxTail; - if ( head_list->cStat & TLAN_CSTAT_EOC ) + + if ( head_list->cStat & TLAN_CSTAT_EOC ) { eoc = 1; + } + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { printk( "TLAN: Received interrupt for uncompleted RX frame.\n" ); - } else { + } else if ( bbuf ) { skb = dev_alloc_skb( head_list->frameSize + 7 ); if ( skb == NULL ) { printk( "TLAN: Couldn't allocate memory for received data.\n" ); @@ -1124,7 +1257,6 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) skb->dev = dev; skb_reserve( skb, 2 ); t = (void *) skb_put( skb, head_list->frameSize ); - // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t ); #if LINUX_KERNEL_VERSION > 0x20100 priv->stats->rx_bytes += head_list->frameSize; @@ -1134,17 +1266,39 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) skb->protocol = eth_type_trans( skb, dev ); netif_rx( skb ); } + } else { + skb = (struct sk_buff *) head_list->buffer[9].address; + head_list->buffer[9].address = 0; + skb_trim( skb, head_list->frameSize ); + +#if LINUX_KERNEL_VERSION > 0x20100 + priv->stats->rx_bytes += head_list->frameSize; +#endif + + skb->protocol = eth_type_trans( skb, dev ); + netif_rx( skb ); + + skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 ); + if ( skb == NULL ) { + printk( "TLAN: Couldn't allocate memory for received data.\n" ); + /* If this ever happened it would be a problem */ + } else { + skb->dev = dev; + skb_reserve( skb, 2 ); + t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE ); + head_list->buffer[0].address = virt_to_bus( t ); + head_list->buffer[9].address = (u32) skb; + } } + head_list->forward = 0; head_list->frameSize = TLAN_MAX_FRAME_SIZE; head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; tail_list->forward = virt_to_bus( head_list ); - priv->rxHead++; - if ( priv->rxHead >= TLAN_NUM_RX_LISTS ) - priv->rxHead = 0; - priv->rxTail++; - if ( priv->rxTail >= TLAN_NUM_RX_LISTS ) - priv->rxTail = 0; + + CIRC_INC( priv->rxHead, TLAN_NUM_RX_LISTS ); + CIRC_INC( priv->rxTail, TLAN_NUM_RX_LISTS ); + if ( eoc ) { TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); head_list = priv->rxList + priv->rxHead; @@ -1152,19 +1306,16 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) ack |= TLAN_HC_GO | TLAN_HC_RT; priv->rxEocCount++; } - TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); - if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { - if ( priv->timerSetAt == 0 ) { - // printk("RxEOF Starting timer...\n"); - priv->timerSetAt = jiffies; - priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; - priv->timerType = TLAN_TIMER_ACT; - add_timer( &priv->timer ); - } else if ( priv->timerType == TLAN_TIMER_ACT ) { - // printk("RxEOF tarting continuing timer...\n"); + + if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) { + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->timer.function == NULL ) { + TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY ); + } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) { priv->timerSetAt = jiffies; } } + dev->last_rx = jiffies; return ack; @@ -1194,7 +1345,7 @@ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) u32 TLan_HandleDummy( struct device *dev, u16 host_int ) { host_int = 0; - printk( "TLAN: Dummy interrupt on %s.\n", dev->name ); + printk( "TLAN: Test interrupt on %s.\n", dev->name ); return 1; } /* TLan_HandleDummy */ @@ -1269,45 +1420,49 @@ u32 TLan_HandleTxEOC( struct device *dev, u16 host_int ) u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int ) { - u32 ack; - u32 error; - u8 net_sts; TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u32 ack; + u32 error; + u8 net_sts; + u32 phy; + u16 tlphy_ctl; + u16 tlphy_sts; ack = 1; if ( host_int & TLAN_HI_IV_MASK ) { error = inl( dev->base_addr + TLAN_CH_PARM ); - printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error ); + printk( "TLAN: %s: Adaptor Error = 0x%x\n", dev->name, error ); TLan_ReadAndClearStats( dev, TLAN_RECORD ); outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + TLan_FreeLists( dev ); TLan_ResetLists( dev ); - TLan_Reset( dev ); + TLan_ResetAdapter( dev ); dev->tbusy = 0; - TLan_SetMac( dev, 0, dev->dev_addr ); - if ( priv->timerType == 0 ) { - if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { - priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; - priv->timerSetAt = jiffies; - priv->timerType = TLAN_TIMER_LINK; - add_timer( &priv->timer ); - } else { - //printk( " RX GO---->\n" ); - outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); - outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); - } - } ack = 0; } else { + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Status Check\n", dev->name ); + phy = priv->phy[priv->phyNum]; + net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS ); - if ( net_sts ) + if ( net_sts ) { TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts ); - if ( net_sts & TLAN_NET_STS_MIRQ ) { - (*priv->phyService)( dev ); + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Net_Sts = %x\n", dev->name, (unsigned) net_sts ); + } + if ( ( net_sts & TLAN_NET_STS_MIRQ ) && ( priv->phyNum == 0 ) ) { + TLan_MiiReadReg( dev, phy, TLAN_TLPHY_STS, &tlphy_sts ); + TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl ); + if ( ! ( tlphy_sts & TLAN_TS_POLOK ) && ! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) { + tlphy_ctl |= TLAN_TC_SWAPOL; + TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl); + } else if ( ( tlphy_sts & TLAN_TS_POLOK ) && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) { + tlphy_ctl &= ~TLAN_TC_SWAPOL; + TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl); + } + if (debug) { TLan_PhyPrint( dev ); } } - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts ); } return ack; @@ -1340,8 +1495,8 @@ u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int ) u32 TLan_HandleRxEOC( struct device *dev, u16 host_int ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - TLanList *head_list; - u32 ack = 1; + TLanList *head_list; + u32 ack = 1; host_int = 0; if ( priv->tlanRev < 0x30 ) { @@ -1401,34 +1556,44 @@ u32 TLan_HandleRxEOC( struct device *dev, u16 host_int ) void TLan_Timer( unsigned long data ) { struct device *dev = (struct device *) data; - u16 gen_sts; TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u32 elapsed; - // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType ); + priv->timer.function = NULL; switch ( priv->timerType ) { - case TLAN_TIMER_LINK: - TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts ); - if ( gen_sts & MII_GS_LINK ) { - priv->phyOnline = 1; - outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); - outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); - priv->timerSetAt = 0; - priv->timerType = 0; - } else { - priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 ); - add_timer( &priv->timer ); - } + case TLAN_TIMER_PHY_PDOWN: + TLan_PhyPowerDown( dev ); break; - case TLAN_TIMER_ACT: - if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) { - TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); - priv->timerSetAt = 0; - priv->timerType = 0; - } else { - priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY; - add_timer( &priv->timer ); + case TLAN_TIMER_PHY_PUP: + TLan_PhyPowerUp( dev ); + break; + case TLAN_TIMER_PHY_RESET: + TLan_PhyReset( dev ); + break; + case TLAN_TIMER_PHY_START_LINK: + TLan_PhyStartLink( dev ); + break; + case TLAN_TIMER_PHY_FINISH_AN: + TLan_PhyFinishAutoNeg( dev ); + break; + case TLAN_TIMER_FINISH_RESET: + TLan_FinishReset( dev ); + break; + case TLAN_TIMER_ACTIVITY: + cli(); + if ( priv->timer.function == NULL ) { + elapsed = jiffies - priv->timerSetAt; + if ( elapsed >= TLAN_TIMER_ACT_DELAY ) { + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->timer.function = &TLan_Timer; + priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY; + sti(); + add_timer( &priv->timer ); + } } + sti(); break; default: break; @@ -1467,13 +1632,19 @@ void TLan_ResetLists( struct device *dev ) TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; int i; TLanList *list; + struct sk_buff *skb; + void *t = NULL; priv->txHead = 0; priv->txTail = 0; for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { list = priv->txList + i; list->cStat = TLAN_CSTAT_UNUSED; - list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + if ( bbuf ) { + list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + } else { + list->buffer[0].address = 0; + } list->buffer[2].count = 0; list->buffer[2].address = 0; } @@ -1485,7 +1656,21 @@ void TLan_ResetLists( struct device *dev ) list->cStat = TLAN_CSTAT_READY; list->frameSize = TLAN_MAX_FRAME_SIZE; list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; - list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + if ( bbuf ) { + list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + } else { + skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 ); + if ( skb == NULL ) { + printk( "TLAN: Couldn't allocate memory for received data.\n" ); + /* If this ever happened it would be a problem */ + } else { + skb->dev = dev; + skb_reserve( skb, 2 ); + t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE ); + } + list->buffer[0].address = virt_to_bus( t ); + list->buffer[9].address = (u32) skb; + } list->buffer[1].count = 0; list->buffer[1].address = 0; if ( i < TLAN_NUM_RX_LISTS - 1 ) @@ -1497,6 +1682,36 @@ void TLan_ResetLists( struct device *dev ) } /* TLan_ResetLists */ +void TLan_FreeLists( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + TLanList *list; + struct sk_buff *skb; + + if ( ! bbuf ) { + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { + list = priv->txList + i; + skb = (struct sk_buff *) list->buffer[9].address; + if ( skb ) { + dev_kfree_skb( skb, FREE_WRITE ); + list->buffer[9].address = 0; + } + } + + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) { + list = priv->rxList + i; + skb = (struct sk_buff *) list->buffer[9].address; + if ( skb ) { + dev_kfree_skb( skb, FREE_READ ); + list->buffer[9].address = 0; + } + } + } + +} /* TLan_FreeLists */ + + /*************************************************************** @@ -1508,7 +1723,7 @@ void TLan_ResetLists( struct device *dev ) * io_base Base IO port of the device of * which to print DIO registers. * - * This function prints out all the internal (DIO) + * This function prints out all the the internal (DIO) * registers of a TLAN chip. * **************************************************************/ @@ -1556,7 +1771,7 @@ void TLan_PrintList( TLanList *list, char *type, int num) printk( "TLAN: Forward = 0x%08x\n", list->forward ); printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat ); printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize ); - // for ( i = 0; i < 10; i++ ) { + /* for ( i = 0; i < 10; i++ ) { */ for ( i = 0; i < 2; i++ ) { printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address ); } @@ -1587,11 +1802,11 @@ void TLan_PrintList( TLanList *list, char *type, int num) void TLan_ReadAndClearStats( struct device *dev, int record ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u32 tx_good, tx_under; - u32 rx_good, rx_over; - u32 def_tx, crc, code; - u32 multi_col, single_col; - u32 excess_col, late_col, loss; + u32 tx_good, tx_under; + u32 rx_good, rx_over; + u32 def_tx, crc, code; + u32 multi_col, single_col; + u32 excess_col, late_col, loss; outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR ); tx_good = inb( dev->base_addr + TLAN_DIO_DATA ); @@ -1659,7 +1874,8 @@ void TLan_ReadAndClearStats( struct device *dev, int record ) * **************************************************************/ -int TLan_Reset( struct device *dev ) +void +TLan_ResetAdapter( struct device *dev ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; int i; @@ -1667,77 +1883,140 @@ int TLan_Reset( struct device *dev ) u32 data; u8 data8; -// 1. Assert reset bit. + priv->tlanFullDuplex = FALSE; +/* 1. Assert reset bit. */ data = inl(dev->base_addr + TLAN_HOST_CMD); data |= TLAN_HC_AD_RST; outl(data, dev->base_addr + TLAN_HOST_CMD); + + udelay(1000); -// 2. Turn off interrupts. ( Probably isn't necessary ) +/* 2. Turn off interrupts. ( Probably isn't necessary ) */ data = inl(dev->base_addr + TLAN_HOST_CMD); data |= TLAN_HC_INT_OFF; outl(data, dev->base_addr + TLAN_HOST_CMD); -// 3. Clear AREGs and HASHs. +/* 3. Clear AREGs and HASHs. */ for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) { TLan_DioWrite32( dev->base_addr, (u16) i, 0 ); } -// 4. Setup NetConfig register. +/* 4. Setup NetConfig register. */ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data ); -// 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. +/* 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */ outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD ); outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD ); -// 6. Unreset the MII by setting NMRST (in NetSio) to 1. +/* 6. Unreset the MII by setting NMRST (in NetSio) to 1. */ outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR ); addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO; TLan_SetBit( TLAN_NET_SIO_NMRST, addr ); -// 7. Setup the remaining registers. +/* 7. Setup the remaining registers. */ if ( priv->tlanRev >= 0x30 ) { data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC; TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 ); } - TLan_PhySelect( dev ); + TLan_PhyDetect( dev ); data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN; - if ( priv->phyFlags & TLAN_PHY_BIT_RATE ) { + if ( priv->adapter->flags & TLAN_ADAPTER_BIT_RATE_PHY ) { data |= TLAN_NET_CFG_BIT; - if ( aui == 1 ) { + if ( priv->aui == 1 ) { TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a ); + } else if ( priv->duplex == TLAN_DUPLEX_FULL ) { + TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x00 ); + priv->tlanFullDuplex = TRUE; } else { TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 ); } } - if ( priv->phyFlags & TLAN_PHY_INTERNAL ) { + if ( priv->phyNum == 0 ) { data |= TLAN_NET_CFG_PHY_EN; } TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data ); - (*priv->phyCheck)( dev ); - data8 = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP; - TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data8 ); - data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; - if ( priv->phyFlags & TLAN_PHY_INTS ) { - data8 |= TLAN_NET_MASK_MASK7; - } - TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 ); + + if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) { + TLan_FinishReset( dev ); + } else { + TLan_PhyPowerDown( dev ); + } + +} /* TLan_ResetAdapter */ + + + + +void +TLan_FinishReset( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u8 data; + u32 phy; + u8 sio; + u16 status; + u16 tlphy_ctl; + + phy = priv->phy[priv->phyNum]; + + data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP; + if ( priv->tlanFullDuplex ) { + data |= TLAN_NET_CMD_DUPLEX; + } + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data ); + data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; + if ( priv->phyNum == 0 ) { + data |= TLAN_NET_MASK_MASK7; + } + TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data ); TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE ); - if ( priv->phyFlags & TLAN_PHY_UNMANAGED ) { - priv->phyOnline = 1; + if ( ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) || ( priv->aui ) ) { + status = MII_GS_LINK; + printk( "TLAN: %s: Link forced.\n", dev->name ); + } else { + TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); + udelay( 1000 ); + TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); + if ( status & MII_GS_LINK ) { + printk( "TLAN: %s: Link active.\n", dev->name ); + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); + } } - return 0; + if ( priv->phyNum == 0 ) { + TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl ); + tlphy_ctl |= TLAN_TC_INTEN; + TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl ); + sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO ); + sio |= TLAN_NET_SIO_MINTEN; + TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio ); + } + + if ( status & MII_GS_LINK ) { + TLan_SetMac( dev, 0, dev->dev_addr ); + priv->phyOnline = 1; + outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + if ( debug >= 1 ) { + outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + } + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } else { + printk( "TLAN: %s: Link inactive, will retry in 10 secs...\n", dev->name ); + TLan_SetTimer( dev, 1000, TLAN_TIMER_FINISH_RESET ); + return; + } -} /* TLan_Reset */ +} /* TLan_FinishReset */ @@ -1787,56 +2066,10 @@ void TLan_SetMac( struct device *dev, int areg, char *mac ) ThunderLAN Driver PHY Layer Routines - The TLAN chip can drive any number of PHYs (physical devices). Rather - than having lots of 'if' or '#ifdef' statements, I have created a - second driver layer for the PHYs. Each PHY can be identified from its - id in registers 2 and 3, and can be given a Check and Service routine - that will be called when the adapter is reset and when the adapter - receives a Network Status interrupt, respectively. - ****************************************************************************** *****************************************************************************/ -static TLanPhyIdEntry TLanPhyIdTable[] = { - { 0x4000, - 0x5014, - &TLan_PhyInternalCheck, - &TLan_PhyInternalService, - TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, - { 0x4000, - 0x5015, - &TLan_PhyInternalCheck, - &TLan_PhyInternalService, - TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, - { 0x4000, - 0x5016, - &TLan_PhyInternalCheck, - &TLan_PhyInternalService, - TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, - { 0x2000, - 0x5C00, - &TLan_PhyDp83840aCheck, - &TLan_PhyNop, - TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, - { 0x2000, - 0x5C01, - &TLan_PhyDp83840aCheck, - &TLan_PhyNop, - TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, - { 0x7810, - 0x0000, - &TLan_PhyDp83840aCheck, - &TLan_PhyNop, - TLAN_PHY_AUTONEG }, - { 0x0000, - 0x0000, - NULL, - NULL, - 0 - } - }; - /********************************************************************* * TLan_PhyPrint @@ -1844,10 +2077,10 @@ static TLanPhyIdEntry TLanPhyIdTable[] = { * Returns: * Nothing * Parms: - * dev A pointer to the device structure of the adapter - * which the desired PHY is located. + * dev A pointer to the device structure of the + * TLAN device having the PHYs to be detailed. * - * This function prints the registers a PHY. + * This function prints the registers a PHY (aka tranceiver). * ********************************************************************/ @@ -1855,30 +2088,27 @@ void TLan_PhyPrint( struct device *dev ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; u16 i, data0, data1, data2, data3, phy; - u32 io; - phy = priv->phyAddr; - io = dev->base_addr; + phy = priv->phy[priv->phyNum]; - if ( ( phy > 0 ) && ( phy <= TLAN_PHY_MAX_ADDR ) ) { + if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) { + printk( "TLAN: Device %s, Unmanaged PHY.\n", dev->name ); + } else if ( phy <= TLAN_PHY_MAX_ADDR ) { printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy ); printk( "TLAN: Off. +0 +1 +2 +3 \n" ); for ( i = 0; i < 0x20; i+= 4 ) { printk( "TLAN: 0x%02x", i ); - TLan_MiiReadReg( io, phy, i, &data0 ); + TLan_MiiReadReg( dev, phy, i, &data0 ); printk( " 0x%04hx", data0 ); - TLan_MiiReadReg( io, phy, i + 1, &data1 ); + TLan_MiiReadReg( dev, phy, i + 1, &data1 ); printk( " 0x%04hx", data1 ); - TLan_MiiReadReg( io, phy, i + 2, &data2 ); + TLan_MiiReadReg( dev, phy, i + 2, &data2 ); printk( " 0x%04hx", data2 ); - TLan_MiiReadReg( io, phy, i + 3, &data3 ); + TLan_MiiReadReg( dev, phy, i + 3, &data3 ); printk( " 0x%04hx\n", data3 ); } } else { - printk( "TLAN: Device %s, PHY 0x%02x (Unmanaged/Unknown).\n", - dev->name, - phy - ); + printk( "TLAN: Device %s, Invalid PHY.\n", dev->name ); } } /* TLan_PhyPrint */ @@ -1887,7 +2117,7 @@ void TLan_PhyPrint( struct device *dev ) /********************************************************************* - * TLan_PhySelect + * TLan_PhyDetect * * Returns: * Nothing @@ -1895,355 +2125,271 @@ void TLan_PhyPrint( struct device *dev ) * dev A pointer to the device structure of the adapter * for which the PHY needs determined. * - * This function decides which PHY amoung those attached to the - * TLAN chip is to be used. The TLAN chip can be attached to - * multiple PHYs, and the driver needs to decide which one to - * talk to. Currently this routine picks the PHY with the lowest - * address as the internal PHY address is 0x1F, the highest - * possible. This strategy assumes that there can be only one - * other PHY, and, if it exists, it is the one to be used. If - * token ring PHYs are ever supported, this routine will become - * a little more interesting... + * So far I've found that adapters which have external PHYs + * may also use the internal PHY for part of the functionality. + * (eg, AUI/Thinnet). This function finds out if this TLAN + * chip has an internal PHY, and then finds the first external + * PHY (starting from address 0) if it exists). * ********************************************************************/ -void TLan_PhySelect( struct device *dev ) +void TLan_PhyDetect( struct device *dev ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - int phy; - int entry; - u16 id_hi[TLAN_PHY_MAX_ADDR + 1]; - u16 id_lo[TLAN_PHY_MAX_ADDR + 1]; - u16 hi; - u16 lo; - u16 vendor; - u16 device; - - priv->phyCheck = &TLan_PhyNop; // Make sure these aren't ever NULL - priv->phyService = &TLan_PhyNop; - - vendor = TLanDeviceList[priv->pciEntry].vendorId; - device = TLanDeviceList[priv->pciEntry].deviceId; - - // This is a bit uglier than I'd like, but the 0xF130 device must - // NOT be assigned a valid PHY as it uses an unmanaged, bit-rate - // PHY. It is simplest just to use another goto, rather than - // nesting the two for loops in the if statement. + u16 control; + u16 hi; + u16 lo; + u32 phy; - if ( ( vendor == PCI_VENDOR_ID_COMPAQ ) && - ( device == PCI_DEVICE_ID_NETFLEX_3P ) ) { - entry = 0; - phy = 0; - goto FINISH; + if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) { + priv->phyNum = 0xFFFF; + return; } - for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { - hi = lo = 0; - TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &hi ); - TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &lo ); - id_hi[phy] = hi; - id_lo[phy] = lo; - TLAN_DBG( TLAN_DEBUG_GNRL, - "TLAN: Phy %2x, hi = %hx, lo = %hx\n", - phy, - hi, - lo - ); + TLan_MiiReadReg( dev, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi ); + + if ( hi != 0xFFFF ) { + priv->phy[0] = TLAN_PHY_MAX_ADDR; + } else { + priv->phy[0] = TLAN_PHY_NONE; } + priv->phy[1] = TLAN_PHY_NONE; for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { - if ( ( aui == 1 ) && ( phy != TLAN_PHY_MAX_ADDR ) ) { - if ( id_hi[phy] != 0xFFFF ) { - TLan_MiiSync(dev->base_addr); - TLan_MiiWriteReg(dev->base_addr, - phy, - MII_GEN_CTL, - MII_GC_PDOWN | - MII_GC_LOOPBK | - MII_GC_ISOLATE ); - - } - continue; - } - for ( entry = 0; TLanPhyIdTable[entry].check; entry++) { - if ( ( id_hi[phy] == TLanPhyIdTable[entry].idHi ) && - ( id_lo[phy] == TLanPhyIdTable[entry].idLo ) ) { - TLAN_DBG( TLAN_DEBUG_GNRL, - "TLAN: Selected Phy %hx\n", - phy - ); - goto FINISH; + TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &control ); + TLan_MiiReadReg( dev, phy, MII_GEN_ID_HI, &hi ); + TLan_MiiReadReg( dev, phy, MII_GEN_ID_LO, &lo ); + if ( ( control != 0xFFFF ) || ( hi != 0xFFFF ) || ( lo != 0xFFFF ) ) { + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: PHY found at %02x %04x %04x %04x\n", phy, control, hi, lo ); + if ( ( priv->phy[1] == TLAN_PHY_NONE ) && ( phy != TLAN_PHY_MAX_ADDR ) ) { + priv->phy[1] = phy; } } } - entry = 0; - phy = 0; - -FINISH: - - if ( ( entry == 0 ) && ( phy == 0 ) ) { - priv->phyAddr = phy; - priv->phyEntry = entry; - priv->phyCheck = TLan_PhyNop; - priv->phyService = TLan_PhyNop; - priv->phyFlags = TLAN_PHY_BIT_RATE | - TLAN_PHY_UNMANAGED | - TLAN_PHY_ACTIVITY; + if ( priv->phy[1] != TLAN_PHY_NONE ) { + priv->phyNum = 1; + } else if ( priv->phy[0] != TLAN_PHY_NONE ) { + priv->phyNum = 0; } else { - priv->phyAddr = phy; - priv->phyEntry = entry; - priv->phyCheck = TLanPhyIdTable[entry].check; - priv->phyService = TLanPhyIdTable[entry].service; - priv->phyFlags = TLanPhyIdTable[entry].flags; + printk( "TLAN: Cannot initialize device, no PHY was found!\n" ); } -} /* TLan_PhySelect */ +} /* TLan_PhyDetect */ - /*************************************************************** - * TLan_PhyNop - * - * Returns: - * Nothing - * Parms: - * dev A pointer to a device structure. - * - * This function does nothing and is meant as a stand-in - * for when a Check or Service function would be - * meaningless. - * - **************************************************************/ - -int TLan_PhyNop( struct device *dev ) +void TLan_PhyPowerDown( struct device *dev ) { - dev = NULL; - return 0; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 value; + + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Powering down PHY(s).\n", dev->name ); + value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE; + TLan_MiiSync( dev->base_addr ); + TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value ); + if ( ( priv->phyNum == 0 ) && ( priv->phy[1] != TLAN_PHY_NONE ) && ( ! ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) ) ) { + TLan_MiiSync( dev->base_addr ); + TLan_MiiWriteReg( dev, priv->phy[1], MII_GEN_CTL, value ); + } -} /* TLan_PhyNop */ + /* Wait for 5 jiffies (50 ms) and powerup + * This is abitrary. It is intended to make sure the + * tranceiver settles. + */ + TLan_SetTimer( dev, 5, TLAN_TIMER_PHY_PUP ); +} /* TLan_PhyPowerDown */ - /*************************************************************** - * TLan_PhyInternalCheck - * - * Returns: - * Nothing - * Parms: - * dev A pointer to a device structure of the - * adapter holding the PHY to be checked. - * - * This function resets the internal PHY on a TLAN chip. - * See Chap. 7, "Physical Interface (PHY)" of "ThunderLAN - * Programmer's Guide" - * - **************************************************************/ -int TLan_PhyInternalCheck( struct device *dev ) +void TLan_PhyPowerUp( struct device *dev ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u16 gen_ctl; - u32 io; - u16 phy; - u16 value; - u8 sio; - - io = dev->base_addr; - phy = priv->phyAddr; - - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); - if ( gen_ctl & MII_GC_PDOWN ) { - TLan_MiiSync( io ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); - udelay(50000); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); - TLan_MiiSync( io ); - } - - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); - while ( value & MII_GC_RESET ) - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); - - // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); - // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); - - udelay(500000); - - TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); - if ( aui ) - value |= TLAN_TC_AUISEL; - else - value &= ~TLAN_TC_AUISEL; - TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); - - // Read Possible Latched Link Status - TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); - // Read Real Link Status - TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); - if ( ( value & MII_GS_LINK ) || aui ) { - priv->phyOnline = 1; - TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); - } else { - priv->phyOnline = 0; - TLan_DioWrite8( io, TLAN_LED_REG, 0 ); - } + u16 value; - // Enable Interrupts - TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); - value |= TLAN_TC_INTEN; - TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Powering up PHY.\n", dev->name ); + TLan_MiiSync( dev->base_addr ); + value = MII_GC_LOOPBK; + TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value ); - sio = TLan_DioRead8( io, TLAN_NET_SIO ); - sio |= TLAN_NET_SIO_MINTEN; - TLan_DioWrite8( io, TLAN_NET_SIO, sio ); - - return 0; + /* Wait for 50 jiffies (500 ms) and reset the + * tranceiver. The TLAN docs say both 50 ms and + * 500 ms, so do the longer, just in case + */ + TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_RESET ); -} /* TLanPhyInternalCheck */ +} /* TLan_PhyPowerUp */ - /*************************************************************** - * TLan_PhyInternalService - * - * Returns: - * Nothing - * Parms: - * dev A pointer to a device structure of the - * adapter holding the PHY to be serviced. - * - * This function services an interrupt generated by the - * internal PHY. It can turn on/off the link LED. See - * Chap. 7, "Physical Interface (PHY)" of "ThunderLAN - * Programmer's Guide". - * - **************************************************************/ - -int TLan_PhyInternalService( struct device *dev ) +void TLan_PhyReset( struct device *dev ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u16 tlphy_sts; - u16 gen_sts; - u16 an_exp; - u32 io; - u16 phy; - - io = dev->base_addr; - phy = priv->phyAddr; - - TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts ); - TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts ); - TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp ); - if ( ( gen_sts & MII_GS_LINK ) || aui ) { - priv->phyOnline = 1; - TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); - } else { - priv->phyOnline = 0; - TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + u16 phy; + u16 value; + + phy = priv->phy[priv->phyNum]; + + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Reseting PHY.\n", dev->name ); + TLan_MiiSync( dev->base_addr ); + value = MII_GC_LOOPBK | MII_GC_RESET; + TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, value ); + TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) { + TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value ); } + TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 ); - if ( ( tlphy_sts & TLAN_TS_POLOK ) == 0) { - u16 value; - TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value); - value |= TLAN_TC_SWAPOL; - TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value); - } + /* Wait for 50 jiffies (500 ms) and initialize. + * I don't remember why I wait this long. + */ + TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_START_LINK ); - return 0; +} /* TLan_PhyReset */ -} /* TLan_PhyInternalService */ +void TLan_PhyStartLink( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 ability; + u16 control; + u16 data; + u16 phy; + u16 status; + u16 tctl; + + phy = priv->phy[priv->phyNum]; + + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Trying to activate link.\n", dev->name ); + TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); + if ( ( status & MII_GS_AUTONEG ) && + ( priv->duplex == TLAN_DUPLEX_DEFAULT ) && + ( priv->speed == TLAN_SPEED_DEFAULT ) && + ( ! priv->aui ) ) { + ability = status >> 11; + + if ( priv->speed == TLAN_SPEED_10 ) { + ability &= 0x0003; + } else if ( priv->speed == TLAN_SPEED_100 ) { + ability &= 0x001C; + } + + if ( priv->duplex == TLAN_DUPLEX_FULL ) { + ability &= 0x000A; + } else if ( priv->duplex == TLAN_DUPLEX_HALF ) { + ability &= 0x0005; + } + + TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 ); + TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 ); + TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 ); + + /* Wait for 400 jiffies (4 sec) for autonegotiation + * to complete. The max spec time is less than this + * but the card need additional time to start AN. + * .5 sec should be plenty extra. + */ + printk( "TLAN: %s: Starting autonegotiation.\n", dev->name ); + TLan_SetTimer( dev, 400, TLAN_TIMER_PHY_FINISH_AN ); + return; + } + + if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) { + priv->phyNum = 0; + data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; + TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data ); + TLan_SetTimer( dev, 4, TLAN_TIMER_PHY_PDOWN ); + return; + } else if ( priv->phyNum == 0 ) { + TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl ); + if ( priv->aui ) { + tctl |= TLAN_TC_AUISEL; + } else { + tctl &= ~TLAN_TC_AUISEL; + control = 0; + if ( priv->duplex == TLAN_DUPLEX_FULL ) { + control |= MII_GC_DUPLEX; + priv->tlanFullDuplex = TRUE; + } + if ( priv->speed == TLAN_SPEED_100 ) { + control |= MII_GC_SPEEDSEL; + } + TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, control ); + } + TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl ); + } + + /* Wait for 100 jiffies (1 sec) to give the tranceiver time + * to establish link. + */ + TLan_SetTimer( dev, 100, TLAN_TIMER_FINISH_RESET ); + +} /* TLan_PhyStartLink */ - /*************************************************************** - * TLan_PhyDp83840aCheck - * - * Returns: - * Nothing - * Parms: - * dev A pointer to a device structure of the - * adapter holding the PHY to be reset. - * - * This function resets a National Semiconductor DP83840A - * 10/100 Mb/s PHY device. See National Semiconductor's - * data sheet for more info. This PHY is used on Compaq - * Netelligent 10/100 cards. - * - **************************************************************/ -static int TLan_PhyDp83840aCheck( struct device *dev ) + + +void TLan_PhyFinishAutoNeg( struct device *dev ) { TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u16 gen_ctl; - int i; - u32 io; - u16 phy; - u16 value; - u8 sio; - - io = dev->base_addr; - phy = priv->phyAddr; - - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); - if ( gen_ctl & MII_GC_PDOWN ) { - TLan_MiiSync( io ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); - for ( i = 0; i < 500000; i++ ) - SLOW_DOWN_IO; - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); - TLan_MiiSync( io ); - } - - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); - while ( value & MII_GC_RESET ) - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); - - // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); - // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); - TLan_MiiReadReg( io, phy, MII_AN_ADV, &value ); - value &= ~0x0140; - TLan_MiiWriteReg( io, phy, MII_AN_ADV, value ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 ); - - for ( i = 0; i < 50000; i++ ) - SLOW_DOWN_IO; + u16 an_adv; + u16 an_lpa; + u16 data; + u16 mode; + u16 phy; + u16 status; + + phy = priv->phy[priv->phyNum]; + + TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); + if ( ! ( status & MII_GS_AUTOCMPLT ) ) { + /* Wait for 800 jiffies (8 sec) to give the process + * more time. Perhaps we should fail after a while. + */ + printk( "TLAN: Giving autonegotiation more time.\n" ); + TLan_SetTimer( dev, 800, TLAN_TIMER_PHY_FINISH_AN ); + return; + } -/* - // Read Possible Latched Link Status - TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); - // Read Real Link Status - TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); - if ( value & MII_GS_LINK ) { - priv->phyOnline = 1; - TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); - } else { - priv->phyOnline = 0; - TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + printk( "TLAN: %s: Autonegotiation complete.\n", dev->name ); + TLan_MiiReadReg( dev, phy, MII_AN_ADV, &an_adv ); + TLan_MiiReadReg( dev, phy, MII_AN_LPA, &an_lpa ); + mode = an_adv & an_lpa & 0x03E0; + if ( mode & 0x0100 ) { + priv->tlanFullDuplex = TRUE; + } else if ( ! ( mode & 0x0080 ) && ( mode & 0x0040 ) ) { + priv->tlanFullDuplex = TRUE; } - // Enable Interrupts - TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); - value |= TLAN_TC_INTEN; - TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); -*/ - sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO ); - sio &= ~TLAN_NET_SIO_MINTEN; - TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio ); -// priv->phyOnline = 1; - - return 0; + if ( ( ! ( mode & 0x0180 ) ) && ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) && ( priv->phyNum != 0 ) ) { + priv->phyNum = 0; + data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; + TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data ); + TLan_SetTimer( dev, 40, TLAN_TIMER_PHY_PDOWN ); + return; + } + + if ( priv->phyNum == 0 ) { + if ( ( priv->duplex == TLAN_DUPLEX_FULL ) || ( an_adv & an_lpa & 0x0040 ) ) { + TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX ); + printk( "TLAN: Starting internal PHY with DUPLEX\n" ); + } else { + TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB ); + printk( "TLAN: Starting internal PHY with HALF-DUPLEX\n" ); + } + } -} /* TLan_PhyDp83840aCheck */ + /* Wait for 10 jiffies (100 ms). No reason in partiticular. + */ + TLan_SetTimer( dev, 10, TLAN_TIMER_FINISH_RESET ); + +} /* TLan_PhyFinishAutoNeg */ @@ -2268,9 +2414,10 @@ static int TLan_PhyDp83840aCheck( struct device *dev ) * 1 otherwise. * * Parms: - * base_port The base IO port of the adapter in - * question. - * dev The address of the PHY to be queried. + * dev The device structure containing + * The io address and interrupt count + * for this device. + * phy The address of the PHY to be queried. * reg The register whose contents are to be * retreived. * val A pointer to a variable to store the @@ -2283,30 +2430,32 @@ static int TLan_PhyDp83840aCheck( struct device *dev ) * **************************************************************/ -int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val) +int TLan_MiiReadReg( struct device *dev, u16 phy, u16 reg, u16 *val ) { u8 nack; - u16 sio, tmp; - u32 i; + u16 sio, tmp; + u32 i; int err; - int minten; + int minten; err = FALSE; - outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); - sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR); + sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO; - cli(); + if ( dev->interrupt == 0 ) + cli(); + dev->interrupt++; - TLan_MiiSync(base_port); + TLan_MiiSync(dev->base_addr); minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); if ( minten ) TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); - TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ - TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */ - TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ - TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( dev->base_addr, 0x2, 2 ); /* Read ( 10b ) */ + TLan_MiiSendData( dev->base_addr, phy, 5 ); /* Device # */ + TLan_MiiSendData( dev->base_addr, reg, 5 ); /* Register # */ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */ @@ -2342,7 +2491,9 @@ int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val) *val = tmp; - sti(); + dev->interrupt--; + if ( dev->interrupt == 0 ) + sti(); return err; @@ -2436,9 +2587,9 @@ void TLan_MiiSync( u16 base_port ) * Returns: * Nothing * Parms: - * base_port The base IO port of the adapter in - * question. - * dev The address of the PHY to be written to. + * dev The device structure for the device + * to write to. + * phy The address of the PHY to be written to. * reg The register whose contents are to be * written. * val The value to be written to the register. @@ -2450,29 +2601,31 @@ void TLan_MiiSync( u16 base_port ) * **************************************************************/ -void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val) +void TLan_MiiWriteReg( struct device *dev, u16 phy, u16 reg, u16 val ) { - u16 sio; + u16 sio; int minten; - outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); - sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR); + sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO; - cli(); + if ( dev->interrupt == 0 ) + cli(); + dev->interrupt++; - TLan_MiiSync( base_port ); + TLan_MiiSync( dev->base_addr ); minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); if ( minten ) TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio ); - TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ - TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */ - TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ - TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Write ( 01b ) */ + TLan_MiiSendData( dev->base_addr, phy, 5 ); /* Device # */ + TLan_MiiSendData( dev->base_addr, reg, 5 ); /* Register # */ - TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */ - TLan_MiiSendData( base_port, val, 16 ); /* Send Data */ + TLan_MiiSendData( dev->base_addr, 0x2, 2 ); /* Send ACK */ + TLan_MiiSendData( dev->base_addr, val, 16 ); /* Send Data */ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */ TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); @@ -2480,10 +2633,15 @@ void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val) if ( minten ) TLan_SetBit( TLAN_NET_SIO_MINTEN, sio ); - sti(); + dev->interrupt--; + if ( dev->interrupt == 0 ) + sti(); } /* TLan_MiiWriteReg */ + + + /***************************************************************************** ****************************************************************************** @@ -2562,7 +2720,7 @@ int TLan_EeSendByte( u16 io_base, u8 data, int stop ) outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; - // Assume clock is low, tx is enabled; + /* Assume clock is low, tx is enabled; */ for ( place = 0x80; place != 0; place >>= 1 ) { if ( place & data ) TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); @@ -2578,7 +2736,7 @@ int TLan_EeSendByte( u16 io_base, u8 data, int stop ) TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); if ( ( ! err ) && stop ) { - TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* STOP, raise data while clock is high */ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); } @@ -2623,7 +2781,7 @@ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; *data = 0; - // Assume clock is low, tx is enabled; + /* Assume clock is low, tx is enabled; */ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); for ( place = 0x80; place; place >>= 1 ) { TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); @@ -2634,14 +2792,14 @@ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); if ( ! stop ) { - TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0 + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* Ack = 0 */ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); } else { - TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?) + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); /* No ack = 1 (?) */ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* STOP, raise data while clock is high */ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); } @@ -2656,7 +2814,7 @@ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) * * Returns: * No error = 0, else, the stage at which the error - * occurred. + * occured. * Parms: * io_base The IO port base address for the * TLAN device with the EEPROM to @@ -2672,26 +2830,30 @@ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) * **************************************************************/ -int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data ) +int TLan_EeReadByte( struct device *dev, u8 ee_addr, u8 *data ) { int err; - cli(); + if ( dev->interrupt == 0 ) + cli(); + dev->interrupt++; - TLan_EeSendStart( io_base ); - err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK ); + TLan_EeSendStart( dev->base_addr ); + err = TLan_EeSendByte( dev->base_addr, 0xA0, TLAN_EEPROM_ACK ); if (err) return 1; - err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK ); + err = TLan_EeSendByte( dev->base_addr, ee_addr, TLAN_EEPROM_ACK ); if (err) return 2; - TLan_EeSendStart( io_base ); - err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK ); + TLan_EeSendStart( dev->base_addr ); + err = TLan_EeSendByte( dev->base_addr, 0xA1, TLAN_EEPROM_ACK ); if (err) return 3; - TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP ); + TLan_EeReceiveByte( dev->base_addr, data, TLAN_EEPROM_STOP ); - sti(); + dev->interrupt--; + if ( dev->interrupt == 0 ) + sti(); return 0; diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h index 94613d3a6493..a66e26c2873c 100644 --- a/drivers/net/tlan.h +++ b/drivers/net/tlan.h @@ -5,9 +5,9 @@ * Linux ThunderLAN Driver * * tlan.h - * by James Banks, james.banks@caldera.com + * by James Banks * - * (C) 1997 Caldera, Inc. + * (C) 1997-1998 Caldera, Inc. * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. @@ -33,23 +33,23 @@ * ****************************************************************/ -#define FALSE 0 -#define TRUE 1 +#define FALSE 0 +#define TRUE 1 #define TLAN_MIN_FRAME_SIZE 64 #define TLAN_MAX_FRAME_SIZE 1600 -#define TLAN_NUM_RX_LISTS 4 -#define TLAN_NUM_TX_LISTS 8 +#define TLAN_NUM_RX_LISTS 4 +#define TLAN_NUM_TX_LISTS 8 -#define TLAN_IGNORE 0 -#define TLAN_RECORD 1 +#define TLAN_IGNORE 0 +#define TLAN_RECORD 1 #define TLAN_DBG(lvl, format, args...) if (debug&lvl) printk( format, ##args ); -#define TLAN_DEBUG_GNRL 0x0001 -#define TLAN_DEBUG_TX 0x0002 -#define TLAN_DEBUG_RX 0x0004 -#define TLAN_DEBUG_LIST 0x0008 +#define TLAN_DEBUG_GNRL 0x0001 +#define TLAN_DEBUG_TX 0x0002 +#define TLAN_DEBUG_RX 0x0004 +#define TLAN_DEBUG_LIST 0x0008 @@ -59,23 +59,47 @@ * ****************************************************************/ - /* NOTE: These have been moved to pci.h, will use them - eventually */ -#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34 -#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32 -#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35 -#define PCI_DEVICE_ID_NETFLEX_3P 0xF130 -#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150 -#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43 -#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40 -#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011 - - -typedef struct tlan_pci_id { +#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34 +#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32 +#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35 +#define PCI_DEVICE_ID_NETFLEX_3P 0xF130 +#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40 +#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011 +#define PCI_DEVICE_ID_NETELLIGENT_10_T2 0xB012 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100 0xB030 +#ifndef PCI_DEVICE_ID_OLICOM_OC2183 +#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013 +#endif +#ifndef PCI_DEVICE_ID_OLICOM_OC2325 +#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012 +#endif +#ifndef PCI_DEVICE_ID_OLICOM_OC2326 +#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014 +#endif + +typedef struct tlan_adapter_entry { u16 vendorId; u16 deviceId; - char *deviceName; -} TLanPciId; + char *deviceLabel; + u32 flags; + u16 addrOfs; +} TLanAdapterEntry; + +#define TLAN_ADAPTER_NONE 0x00000000 +#define TLAN_ADAPTER_UNMANAGED_PHY 0x00000001 +#define TLAN_ADAPTER_BIT_RATE_PHY 0x00000002 +#define TLAN_ADAPTER_USE_INTERN_10 0x00000004 +#define TLAN_ADAPTER_ACTIVITY_LED 0x00000008 + +#define TLAN_SPEED_DEFAULT 0 +#define TLAN_SPEED_10 10 +#define TLAN_SPEED_100 100 + +#define TLAN_DUPLEX_DEFAULT 0 +#define TLAN_DUPLEX_HALF 1 +#define TLAN_DUPLEX_FULL 2 @@ -121,25 +145,7 @@ typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; ****************************************************************/ #define TLAN_PHY_MAX_ADDR 0x1F - -#define TLAN_PHY_ACTIVITY 0x00000001 -#define TLAN_PHY_AUTONEG 0x00000002 -#define TLAN_PHY_INTS 0x00000004 -#define TLAN_PHY_BIT_RATE 0x00000008 -#define TLAN_PHY_UNMANAGED 0x00000010 -#define TLAN_PHY_INTERNAL 0x00000020 - - -typedef int (TLanPhyFunc)( struct device * ); - - -typedef struct tlan_phy_id_entry_tag { - u16 idHi; - u16 idLo; - TLanPhyFunc *check; - TLanPhyFunc *service; - u32 flags; -} TLanPhyIdEntry; +#define TLAN_PHY_NONE 0x20 @@ -164,21 +170,22 @@ typedef struct tlan_private_tag { u32 txInProgress; u32 txTail; u32 txBusyCount; - u32 phyAddr; - u32 phyEntry; u32 phyOnline; - u32 phyFlags; - TLanPhyFunc *phyCheck; - TLanPhyFunc *phyService; u32 timerSetAt; u32 timerType; struct timer_list timer; struct net_device_stats stats; - u32 pciEntry; - u8 pciRevision; - u8 pciBus; - u8 pciDeviceFn; + TLanAdapterEntry *adapter; + u32 adapterRev; + u32 aui; + u32 debug; + u32 duplex; + u32 phy[2]; + u32 phyNum; + u32 sa_int; + u32 speed; u8 tlanRev; + u8 tlanFullDuplex; char devName[8]; } TLanPrivateInfo; @@ -191,10 +198,15 @@ typedef struct tlan_private_tag { ****************************************************************/ #define TLAN_TIMER_LINK 1 -#define TLAN_TIMER_ACT 2 +#define TLAN_TIMER_ACTIVITY 2 +#define TLAN_TIMER_PHY_PDOWN 3 +#define TLAN_TIMER_PHY_PUP 4 +#define TLAN_TIMER_PHY_RESET 5 +#define TLAN_TIMER_PHY_START_LINK 6 +#define TLAN_TIMER_PHY_FINISH_AN 7 +#define TLAN_TIMER_FINISH_RESET 8 -#define TLAN_TIMER_LINK_DELAY 230 -#define TLAN_TIMER_ACT_DELAY 10 +#define TLAN_TIMER_ACT_DELAY 10 @@ -215,29 +227,29 @@ typedef struct tlan_private_tag { * ****************************************************************/ -#define TLAN_HOST_CMD 0x00 +#define TLAN_HOST_CMD 0x00 #define TLAN_HC_GO 0x80000000 -#define TLAN_HC_STOP 0x40000000 +#define TLAN_HC_STOP 0x40000000 #define TLAN_HC_ACK 0x20000000 -#define TLAN_HC_CS_MASK 0x1FE00000 +#define TLAN_HC_CS_MASK 0x1FE00000 #define TLAN_HC_EOC 0x00100000 #define TLAN_HC_RT 0x00080000 #define TLAN_HC_NES 0x00040000 -#define TLAN_HC_AD_RST 0x00008000 -#define TLAN_HC_LD_TMR 0x00004000 -#define TLAN_HC_LD_THR 0x00002000 -#define TLAN_HC_REQ_INT 0x00001000 -#define TLAN_HC_INT_OFF 0x00000800 -#define TLAN_HC_INT_ON 0x00000400 -#define TLAN_HC_AC_MASK 0x000000FF -#define TLAN_CH_PARM 0x04 -#define TLAN_DIO_ADR 0x08 -#define TLAN_DA_ADR_INC 0x8000 -#define TLAN_DA_RAM_ADR 0x4000 -#define TLAN_HOST_INT 0x0A -#define TLAN_HI_IV_MASK 0x1FE0 -#define TLAN_HI_IT_MASK 0x001C -#define TLAN_DIO_DATA 0x0C +#define TLAN_HC_AD_RST 0x00008000 +#define TLAN_HC_LD_TMR 0x00004000 +#define TLAN_HC_LD_THR 0x00002000 +#define TLAN_HC_REQ_INT 0x00001000 +#define TLAN_HC_INT_OFF 0x00000800 +#define TLAN_HC_INT_ON 0x00000400 +#define TLAN_HC_AC_MASK 0x000000FF +#define TLAN_CH_PARM 0x04 +#define TLAN_DIO_ADR 0x08 +#define TLAN_DA_ADR_INC 0x8000 +#define TLAN_DA_RAM_ADR 0x4000 +#define TLAN_HOST_INT 0x0A +#define TLAN_HI_IV_MASK 0x1FE0 +#define TLAN_HI_IT_MASK 0x001C +#define TLAN_DIO_DATA 0x0C /* ThunderLAN Internal Register DIO Offsets */ @@ -264,7 +276,7 @@ typedef struct tlan_private_tag { #define TLAN_NET_STS_MIRQ 0x80 #define TLAN_NET_STS_HBEAT 0x40 #define TLAN_NET_STS_TXSTOP 0x20 -#define TLAN_NET_STS_RXSTOP 0x10 +#define TLAN_NET_STS_RXSTOP 0x10 #define TLAN_NET_STS_RSRVD 0x0F #define TLAN_NET_MASK 0x03 #define TLAN_NET_MASK_MASK7 0x80 @@ -283,36 +295,36 @@ typedef struct tlan_private_tag { #define TLAN_NET_CFG_MTEST 0x0100 #define TLAN_NET_CFG_PHY_EN 0x0080 #define TLAN_NET_CFG_MSMASK 0x007F -#define TLAN_MAN_TEST 0x06 -#define TLAN_DEF_VENDOR_ID 0x08 -#define TLAN_DEF_DEVICE_ID 0x0A -#define TLAN_DEF_REVISION 0x0C -#define TLAN_DEF_SUBCLASS 0x0D -#define TLAN_DEF_MIN_LAT 0x0E -#define TLAN_DEF_MAX_LAT 0x0F +#define TLAN_MAN_TEST 0x06 +#define TLAN_DEF_VENDOR_ID 0x08 +#define TLAN_DEF_DEVICE_ID 0x0A +#define TLAN_DEF_REVISION 0x0C +#define TLAN_DEF_SUBCLASS 0x0D +#define TLAN_DEF_MIN_LAT 0x0E +#define TLAN_DEF_MAX_LAT 0x0F #define TLAN_AREG_0 0x10 #define TLAN_AREG_1 0x16 #define TLAN_AREG_2 0x1C #define TLAN_AREG_3 0x22 #define TLAN_HASH_1 0x28 #define TLAN_HASH_2 0x2C -#define TLAN_GOOD_TX_FRMS 0x30 -#define TLAN_TX_UNDERUNS 0x33 -#define TLAN_GOOD_RX_FRMS 0x34 -#define TLAN_RX_OVERRUNS 0x37 -#define TLAN_DEFERRED_TX 0x38 -#define TLAN_CRC_ERRORS 0x3A -#define TLAN_CODE_ERRORS 0x3B -#define TLAN_MULTICOL_FRMS 0x3C -#define TLAN_SINGLECOL_FRMS 0x3E -#define TLAN_EXCESSCOL_FRMS 0x40 -#define TLAN_LATE_COLS 0x41 -#define TLAN_CARRIER_LOSS 0x42 -#define TLAN_ACOMMIT 0x43 -#define TLAN_LED_REG 0x44 -#define TLAN_LED_ACT 0x10 -#define TLAN_LED_LINK 0x01 -#define TLAN_BSIZE_REG 0x45 +#define TLAN_GOOD_TX_FRMS 0x30 +#define TLAN_TX_UNDERUNS 0x33 +#define TLAN_GOOD_RX_FRMS 0x34 +#define TLAN_RX_OVERRUNS 0x37 +#define TLAN_DEFERRED_TX 0x38 +#define TLAN_CRC_ERRORS 0x3A +#define TLAN_CODE_ERRORS 0x3B +#define TLAN_MULTICOL_FRMS 0x3C +#define TLAN_SINGLECOL_FRMS 0x3E +#define TLAN_EXCESSCOL_FRMS 0x40 +#define TLAN_LATE_COLS 0x41 +#define TLAN_CARRIER_LOSS 0x42 +#define TLAN_ACOMMIT 0x43 +#define TLAN_LED_REG 0x44 +#define TLAN_LED_ACT 0x10 +#define TLAN_LED_LINK 0x01 +#define TLAN_BSIZE_REG 0x45 #define TLAN_MAX_RX 0x46 #define TLAN_INT_DIS 0x48 #define TLAN_ID_TX_EOC 0x04 @@ -327,11 +339,11 @@ typedef struct tlan_private_tag { #define TLAN_INT_NONE 0x0000 #define TLAN_INT_TX_EOF 0x0001 -#define TLAN_INT_STAT_OVERFLOW 0x0002 +#define TLAN_INT_STAT_OVERFLOW 0x0002 #define TLAN_INT_RX_EOF 0x0003 #define TLAN_INT_DUMMY 0x0004 #define TLAN_INT_TX_EOC 0x0005 -#define TLAN_INT_STATUS_CHECK 0x0006 +#define TLAN_INT_STATUS_CHECK 0x0006 #define TLAN_INT_RX_EOC 0x0007 @@ -340,7 +352,7 @@ typedef struct tlan_private_tag { /* Generic MII/PHY Registers */ -#define MII_GEN_CTL 0x00 +#define MII_GEN_CTL 0x00 #define MII_GC_RESET 0x8000 #define MII_GC_LOOPBK 0x4000 #define MII_GC_SPEEDSEL 0x2000 @@ -351,7 +363,7 @@ typedef struct tlan_private_tag { #define MII_GC_DUPLEX 0x0100 #define MII_GC_COLTEST 0x0080 #define MII_GC_RESERVED 0x007F -#define MII_GEN_STS 0x01 +#define MII_GEN_STS 0x01 #define MII_GS_100BT4 0x8000 #define MII_GS_100BTXFD 0x4000 #define MII_GS_100BTXHD 0x2000 @@ -359,19 +371,19 @@ typedef struct tlan_private_tag { #define MII_GS_10BTHD 0x0800 #define MII_GS_RESERVED 0x07C0 #define MII_GS_AUTOCMPLT 0x0020 -#define MII_GS_RFLT 0x0010 +#define MII_GS_RFLT 0x0010 #define MII_GS_AUTONEG 0x0008 -#define MII_GS_LINK 0x0004 +#define MII_GS_LINK 0x0004 #define MII_GS_JABBER 0x0002 #define MII_GS_EXTCAP 0x0001 #define MII_GEN_ID_HI 0x02 #define MII_GEN_ID_LO 0x03 -#define MII_GIL_OUI 0xFC00 +#define MII_GIL_OUI 0xFC00 #define MII_GIL_MODEL 0x03F0 #define MII_GIL_REVISION 0x000F -#define MII_AN_ADV 0x04 -#define MII_AN_LPA 0x05 -#define MII_AN_EXP 0x06 +#define MII_AN_ADV 0x04 +#define MII_AN_LPA 0x05 +#define MII_AN_EXP 0x06 /* ThunderLAN Specific MII/PHY Registers */ @@ -394,6 +406,7 @@ typedef struct tlan_private_tag { #define TLAN_TS_RESERVED 0x0FFF +#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0 /* Routines to access internal registers. */ @@ -456,7 +469,7 @@ inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data) - +#if 0 inline void TLan_ClearBit(u8 bit, u16 port) { outb_p(inb_p(port) & ~bit, port); @@ -477,6 +490,11 @@ inline void TLan_SetBit(u8 bit, u16 port) { outb_p(inb_p(port) | bit, port); } +#endif + +#define TLan_ClearBit( bit, port ) outb_p(inb_p(port) & ~bit, port) +#define TLan_GetBit( bit, port ) ((int) (inb_p(port) & bit)) +#define TLan_SetBit( bit, port ) outb_p(inb_p(port) | bit, port) inline u32 xor( u32 a, u32 b ) diff --git a/drivers/net/wd.c b/drivers/net/wd.c index 7af3be77a47c..a737a01da1c5 100644 --- a/drivers/net/wd.c +++ b/drivers/net/wd.c @@ -54,7 +54,7 @@ static void wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, static void wd_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void wd_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const int start_page); static int wd_close_card(struct device *dev); diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 211dd6ae803f..c8e5314dfc74 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -38,7 +38,7 @@ static int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1518 effectively disables this feature. */ -static const rx_copybreak = 100; +static const int rx_copybreak = 100; /* Keep the ring sizes a power of two for efficiency. Making the Tx ring too large decreases the effectiveness of channel diff --git a/drivers/scsi/AM53C974.c b/drivers/scsi/AM53C974.c index fc779ada4a7d..9ff5a2e41bda 100644 --- a/drivers/scsi/AM53C974.c +++ b/drivers/scsi/AM53C974.c @@ -2204,7 +2204,7 @@ return(SCSI_ABORT_NOT_RUNNING); * * Returns : status (SCSI_ABORT_SUCCESS) **************************************************************************/ -int AM53C974_reset(Scsi_Cmnd *cmd) +int AM53C974_reset(Scsi_Cmnd *cmd, unsigned int flags) { AM53C974_local_declare(); int i; diff --git a/drivers/scsi/AM53C974.h b/drivers/scsi/AM53C974.h index 8481e5e06468..ab51d2941e36 100644 --- a/drivers/scsi/AM53C974.h +++ b/drivers/scsi/AM53C974.h @@ -290,7 +290,7 @@ const char *AM53C974_info(struct Scsi_Host *); int AM53C974_command(Scsi_Cmnd *SCpnt); int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); int AM53C974_abort(Scsi_Cmnd *cmd); -int AM53C974_reset (Scsi_Cmnd *cmd); +int AM53C974_reset (Scsi_Cmnd *cmd, unsigned int flags); #define AM53C974_local_declare() unsigned long io_port #define AM53C974_setio(instance) io_port = instance->io_port diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c index a8515921ef80..d94631a416e9 100644 --- a/drivers/scsi/FlashPoint.c +++ b/drivers/scsi/FlashPoint.c @@ -4944,7 +4944,7 @@ int SccbMgr_isr(ULONG pCurrCard) * * Function: Sccb_bad_isr * - * Description: Some type of interrupt has occurred which is slightly + * Description: Some type of interrupt has occured which is slightly * out of the ordinary. We will now decode it fully, in * this routine. This is broken up in an attempt to save * processing time. diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c index f37de04e4681..9f2de9a0c589 100644 --- a/drivers/scsi/NCR53c406a.c +++ b/drivers/scsi/NCR53c406a.c @@ -729,7 +729,7 @@ NCR53c406a_abort(Scsi_Cmnd *SCpnt){ } int -NCR53c406a_reset(Scsi_Cmnd *SCpnt){ +NCR53c406a_reset(Scsi_Cmnd *SCpnt, unsigned int flags){ DEB(printk("NCR53c406a_reset called\n")); outb(C4_IMG, CONFIG4); /* Select reg set 0 */ outb(CHIP_RESET, CMD_REG); diff --git a/drivers/scsi/NCR53c406a.h b/drivers/scsi/NCR53c406a.h index dcb48870bfdc..88e45e5e6a92 100644 --- a/drivers/scsi/NCR53c406a.h +++ b/drivers/scsi/NCR53c406a.h @@ -57,7 +57,7 @@ const char* NCR53c406a_info(struct Scsi_Host *); int NCR53c406a_command(Scsi_Cmnd *); int NCR53c406a_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int NCR53c406a_abort(Scsi_Cmnd *); -int NCR53c406a_reset(Scsi_Cmnd *); +int NCR53c406a_reset(Scsi_Cmnd *, unsigned int); int NCR53c406a_biosparm(Disk *, kdev_t, int []); #endif /* _NCR53C406A_H */ diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index ff07954d8c35..76fd05cb714e 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -1,5 +1,5 @@ -/* $Id: advansys.c,v 1.49 1998/01/22 20:19:25 bobf Exp bobf $ */ -#define ASC_VERSION "3.1D" /* AdvanSys Driver Version */ +/* $Id: advansys.c,v 1.50 1998/05/08 23:39:15 bobf Exp bobf $ */ +#define ASC_VERSION "3.1E" /* AdvanSys Driver Version */ /* * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters @@ -571,7 +571,7 @@ be page aligned. 3.1C (1/10/98): - 1. Update latest BIOS version to 3.1E. + 1. Update latest BIOS version checked for from the /proc file. 2. Don't set microcode SDTR variable at initialization. Instead wait until device capabilities have been detected from an Inquiry command. @@ -580,6 +580,18 @@ 1. Improve performance when the driver is compiled as module by allowing up to 64 scatter-gather elements instead of 8. + 3.1E (5/1/98): + 1. Set time delay in AscWaitTixISRDone() to 1000 ms. + 2. Include SMP locking changes. + 3. For v2.1.93 and newer kernels use CONFIG_PCI and new PCI BIOS + access functions. + 4. Update board serial number printing. + 5. Try allocating an IRQ both with and without the SA_INTERRUPT + flag set to allow IRQ sharing with drivers that do not set + the SA_INTERRUPT flag. Also display a more descriptive error + message if request_irq() fails. + 5. Update to latest Asc and Adv Libraries. + J. Known Problems or Issues 1. Remove conditional constants (ASC_QUEUE_FLOW_CONTROL) around @@ -639,6 +651,7 @@ * --- Linux Include Files */ +#include #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) #ifdef MODULE #include @@ -668,10 +681,25 @@ #include #include #endif /* version >= v1.3.0 */ +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,95) +#include +#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 +#endif /* CONFIG_PCI */ +#else /* version < v2.1.93 */ +/* + * For earlier than v2.1.93 the driver has its own PCI configuration. + * If PCI is not needed in a kernel before v2.1.93 this define can be + * turned-off to make the driver object smaller. + */ +#define ASC_CONFIG_PCI +#endif /* version < v2.1.93 */ /* * If Linux eventually defines a DID_UNDERRUN, the constant here can be @@ -689,7 +717,7 @@ #define ADVANSYS_ASSERT /* Enable driver tracing. */ -/*#define ADVANSYS_DEBUG*/ +/* #define ADVANSYS_DEBUG */ /* * Because of no /proc to display them, statistics are disabled @@ -719,7 +747,7 @@ #define ASC_LIB_VERSION_MAJOR 1 #define ASC_LIB_VERSION_MINOR 22 -#define ASC_LIB_SERIAL_NUMBER 111 +#define ASC_LIB_SERIAL_NUMBER 113 typedef unsigned char uchar; @@ -1484,7 +1512,7 @@ typedef struct asc_dvc_cfg { ASC_SCSI_BIT_ID_TYPE can_tagged_qng; ASC_SCSI_BIT_ID_TYPE cmd_qng_enabled; ASC_SCSI_BIT_ID_TYPE disc_enable; - uchar res; + ASC_SCSI_BIT_ID_TYPE sdtr_enable; uchar chip_scsi_id:4; uchar isa_dma_speed:4; uchar isa_dma_channel; @@ -1947,8 +1975,6 @@ STATIC ushort AscInitAscDvcVar(ASC_DVC_VAR asc_ptr_type *); STATIC ushort AscInitFromEEP(ASC_DVC_VAR asc_ptr_type *); STATIC ushort AscInitFromAscDvcVar(ASC_DVC_VAR asc_ptr_type *); STATIC ushort AscInitMicroCodeVar(ASC_DVC_VAR asc_ptr_type * asc_dvc); -STATIC void AscInitPollIsrCallBack(ASC_DVC_VAR asc_ptr_type *, - ASC_QDONE_INFO *); STATIC int AscTestExternalLram(ASC_DVC_VAR asc_ptr_type *); STATIC uchar AscMsgOutSDTR(ASC_DVC_VAR asc_ptr_type *, uchar, uchar); STATIC uchar AscCalSDTRData(ASC_DVC_VAR asc_ptr_type *, uchar, uchar); @@ -1991,31 +2017,6 @@ STATIC int AscIsrChipHalted(ASC_DVC_VAR asc_ptr_type *); STATIC uchar _AscCopyLramScsiDoneQ(PortAddr, ushort, ASC_QDONE_INFO *, ulong); STATIC int AscIsrQDone(ASC_DVC_VAR asc_ptr_type *); -STATIC int AscScsiSetupCmdQ(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, uchar *, ulong); -STATIC int AscScsiInquiry(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, uchar *, int); -STATIC int AscScsiTestUnitReady(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *); -STATIC int AscScsiStartStopUnit(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, uchar); -STATIC int AscScsiReadCapacity(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, uchar *); -STATIC ulong *swapfarbuf4(uchar *); -STATIC int PollQueueDone(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, int); -STATIC int PollScsiReadCapacity(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, ASC_CAP_INFO *); -STATIC int PollScsiInquiry(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, uchar *, int); -STATIC int PollScsiTestUnitReady(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *); -STATIC int PollScsiStartUnit(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *); -STATIC int InitTestUnitReady(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *); -STATIC int AscPollQDone(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, int); STATIC int AscCompareString(uchar *, uchar *, int); STATIC ushort AscGetEisaChipCfg(PortAddr); STATIC ulong AscGetEisaProductID(PortAddr); @@ -2051,10 +2052,11 @@ STATIC PortAddr AscSearchIOPortAddr(PortAddr, ushort); STATIC ushort AscInitGetConfig(ASC_DVC_VAR asc_ptr_type *); STATIC ushort AscInitSetConfig(ASC_DVC_VAR asc_ptr_type *); STATIC ushort AscInitAsc1000Driver(ASC_DVC_VAR asc_ptr_type *); -STATIC int AscInitPollBegin(ASC_DVC_VAR asc_ptr_type *); -STATIC int AscInitPollEnd(ASC_DVC_VAR asc_ptr_type *); -STATIC int AscInitPollTarget(ASC_DVC_VAR asc_ptr_type *, - ASC_SCSI_REQ_Q *, ASC_SCSI_INQUIRY *, ASC_CAP_INFO *); +STATIC void AscAsyncFix(ASC_DVC_VAR asc_ptr_type *, uchar, + ASC_SCSI_INQUIRY *); +STATIC int AscTagQueuingSafe(ASC_SCSI_INQUIRY *); +STATIC void AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *, + uchar, ASC_SCSI_INQUIRY *); STATIC int AscExeScsiQueue(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q *); STATIC int AscISR(ASC_DVC_VAR asc_ptr_type *); STATIC uint AscGetNumOfFreeQueue(ASC_DVC_VAR asc_ptr_type *, uchar, @@ -2074,7 +2076,7 @@ STATIC ulong AscGetMaxDmaCount(ushort); */ #define ADV_LIB_VERSION_MAJOR 3 -#define ADV_LIB_VERSION_MINOR 34 +#define ADV_LIB_VERSION_MINOR 45 /* d_os_dep.h */ #define ADV_OS_LINUX @@ -3145,10 +3147,6 @@ extern ADVEEP_CONFIG Default_EEPROM_Config; #define ADV_IS_DATA_FLAG 0x08 /* 'addr' is data virtual pointer */ #define ADV_IS_SGLIST_FLAG 0x10 /* 'addr' is sglist virtual pointer */ -/* 'IS_SCSIQ_FLAG is now obsolete; Instead use ADV_IS_SCSIQ_FLAG. */ -#define IS_SCSIQ_FLAG ADV_IS_SCSIQ_FLAQ - - /* Return the address that is aligned at the next doubleword >= to 'addr'. */ #define ADV_DWALIGN(addr) (((ulong) (addr) + 0x3) & ~0x3) @@ -3355,6 +3353,7 @@ typedef Scsi_Cmnd REQ, *REQP; #define PCI_MAX_BUS 0xFF #define PCI_IOADDRESS_MASK 0xFFFE #define ASC_PCI_VENDORID 0x10CD +#define ASC_PCI_DEVICE_ID_CNT 4 /* PCI Device ID count. */ #define ASC_PCI_DEVICE_ID_1100 0x1100 #define ASC_PCI_DEVICE_ID_1200 0x1200 #define ASC_PCI_DEVICE_ID_1300 0x1300 @@ -3792,7 +3791,11 @@ STATIC ushort asc_bus[ASC_NUM_BUS] ASC_INITDATA = { ASC_IS_PCI, }; +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI STATIC int pci_scan_method ASC_INITDATA = -1; +#endif /* ASC_CONFIG_PCI */ +#endif /* version < v2.1.93 */ /* * Used with the LILO 'advansys' option to eliminate or @@ -3854,7 +3857,8 @@ STATIC int adv_build_req(asc_board_t *, Scsi_Cmnd *, ADV_SCSI_REQ_Q **); STATIC int adv_get_sglist(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *, Scsi_Cmnd *); STATIC void asc_isr_callback(ASC_DVC_VAR *, ASC_QDONE_INFO *); STATIC void adv_isr_callback(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *); -STATIC int asc_init_dev(ASC_DVC_VAR *, Scsi_Cmnd *); +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI STATIC int asc_srch_pci_dev(PCI_DEVICE *); STATIC uchar asc_scan_method(void); STATIC int asc_pci_find_dev(PCI_DEVICE *); @@ -3862,6 +3866,8 @@ STATIC void asc_get_pci_cfg(PCI_DEVICE *, PCI_CONFIG_SPACE *); STATIC ushort asc_get_cfg_word(PCI_DATA *); STATIC uchar asc_get_cfg_byte(PCI_DATA *); STATIC void asc_put_cfg_byte(PCI_DATA *, uchar); +#endif /* ASC_CONFIG_PCI */ +#endif /* version < v2.1.93 */ STATIC void asc_enqueue(asc_queue_t *, REQP, int); STATIC REQP asc_dequeue(asc_queue_t *, int); STATIC REQP asc_dequeue_list(asc_queue_t *, REQP *, int); @@ -4172,8 +4178,27 @@ advansys_detect(Scsi_Host_Template *tpnt) ADV_DVC_VAR *adv_dvc_varp = NULL; int ioport = 0; int share_irq = FALSE; +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI PCI_DEVICE pciDevice; PCI_CONFIG_SPACE pciConfig; +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) + unsigned long pci_memory_address; +#endif /* version >= v1,3,0 */ +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + struct pci_dev *pci_devp = NULL; + int pci_device_id_cnt = 0; + unsigned int pci_device_id[ASC_PCI_DEVICE_ID_CNT] = { + ASC_PCI_DEVICE_ID_1100, + ASC_PCI_DEVICE_ID_1200, + ASC_PCI_DEVICE_ID_1300, + ASC_PCI_DEVICE_ID_2300 + }; + unsigned long pci_memory_address; +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ int warn_code, err_code; int ret; @@ -4217,10 +4242,14 @@ advansys_detect(Scsi_Host_Template *tpnt) ioport = 0; } +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI memset(&pciDevice, 0, sizeof(PCI_DEVICE)); memset(&pciConfig, 0, sizeof(PCI_CONFIG_SPACE)); pciDevice.maxBusNumber = PCI_MAX_BUS; pciDevice.endSlot = PCI_MAX_SLOT; +#endif /* ASC_CONFIG_PCI */ +#endif /* version < v2.1.93 */ for (bus = 0; bus < ASC_NUM_BUS; bus++) { @@ -4301,22 +4330,52 @@ advansys_detect(Scsi_Host_Template *tpnt) iop = AscSearchIOPortAddr(iop, asc_bus[bus]); break; +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI + case ASC_IS_PCI: + if (asc_srch_pci_dev(&pciDevice) != PCI_DEVICE_FOUND) { + iop = 0; + } else { + ASC_DBG2(2, + "advansys_detect: slotFound %d, busNumber %d\n", + pciDevice.slotFound, pciDevice.busNumber); + asc_get_pci_cfg(&pciDevice, &pciConfig); + iop = pciConfig.baseAddress[0] & PCI_IOADDRESS_MASK; + ASC_DBG2(1, + "advansys_detect: vendorID %X, deviceID %X\n", + pciConfig.vendorID, pciConfig.deviceID); + ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n", + iop, pciConfig.irqLine); + } + break; +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI case ASC_IS_PCI: - if (asc_srch_pci_dev(&pciDevice) != PCI_DEVICE_FOUND) { - iop = 0; + while (pci_device_id_cnt < ASC_PCI_DEVICE_ID_CNT) { + if ((pci_devp = pci_find_device(ASC_PCI_VENDORID, + pci_device_id[pci_device_id_cnt], pci_devp)) == NULL) { + pci_device_id_cnt++; } else { - ASC_DBG2(2, - "advansys_detect: slotFound %d, busNumber %d\n", - pciDevice.slotFound, pciDevice.busNumber); - asc_get_pci_cfg(&pciDevice, &pciConfig); - iop = pciConfig.baseAddress[0] & PCI_IOADDRESS_MASK; - ASC_DBG2(1, - "advansys_detect: vendorID %X, deviceID %X\n", - pciConfig.vendorID, pciConfig.deviceID); - ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n", - iop, pciConfig.irqLine); + break; } + } + if (pci_devp == NULL) { + iop = 0; + } else { + ASC_DBG2(2, + "advansys_detect: devfn %d, bus number %d\n", + pci_devp->devfn, pci_devp->bus->number); + iop = pci_devp->base_address[0] & PCI_IOADDRESS_MASK; + ASC_DBG2(1, + "advansys_detect: vendorID %X, deviceID %X\n", + pci_devp->vendor, pci_devp->device); + ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n", + iop, pci_devp->irq); + } break; +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ default: ASC_PRINT1("advansys_detect: unknown bus type: %d\n", @@ -4350,14 +4409,29 @@ advansys_detect(Scsi_Host_Template *tpnt) boardp->id = asc_board_count - 1; /* - * Handle both narrow and wide PCI boards. + * Handle both narrow and wide boards. * * If a Wide board was detected, set the board structure * wide board flag. Set-up the board structure based on * the board type. */ - if ((asc_bus[bus] == ASC_IS_PCI && - pciConfig.deviceID == ASC_PCI_DEVICE_ID_2300) == 0) { +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI + if (asc_bus[bus] == ASC_IS_PCI && + pciConfig.deviceID == ASC_PCI_DEVICE_ID_2300) { + boardp->flags |= ASC_IS_WIDE_BOARD; + } +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + if (asc_bus[bus] == ASC_IS_PCI && + pci_devp->device == ASC_PCI_DEVICE_ID_2300) { + boardp->flags |= ASC_IS_WIDE_BOARD; + } +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ + + if (ASC_NARROW_BOARD(boardp)) { ASC_DBG(1, "advansys_detect: narrow board\n"); asc_dvc_varp = &boardp->dvc_var.asc_dvc_var; asc_dvc_varp->bus_type = asc_bus[bus]; @@ -4368,7 +4442,6 @@ advansys_detect(Scsi_Host_Template *tpnt) asc_dvc_varp->isr_callback = (Ptr2Func) asc_isr_callback; } else { ASC_DBG(1, "advansys_detect: wide board\n"); - boardp->flags |= ASC_IS_WIDE_BOARD; adv_dvc_varp = &boardp->dvc_var.adv_dvc_var; adv_dvc_varp->drv_ptr = (ulong) boardp; adv_dvc_varp->cfg = &boardp->dvc_cfg.adv_dvc_cfg; @@ -4388,20 +4461,41 @@ advansys_detect(Scsi_Host_Template *tpnt) * PCI register base address will not cross a page * boundary. */ +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI + pci_memory_address = pciConfig.baseAddress[1]; + if ((boardp->ioremap_addr = + ioremap(pci_memory_address & PAGE_MASK, + PAGE_SIZE)) == 0) { + ASC_PRINT3( +"advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n", + boardp->id, pci_memory_address, ADV_CONDOR_IOLEN); + scsi_unregister(shp); + asc_board_count--; + continue; + } + adv_dvc_varp->iop_base = (AdvPortAddr) + (boardp->ioremap_addr + + (pci_memory_address - (pci_memory_address & PAGE_MASK))); +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + pci_memory_address = pci_devp->base_address[1]; if ((boardp->ioremap_addr = - ioremap(pciConfig.baseAddress[1] & PAGE_MASK, + ioremap(pci_memory_address & PAGE_MASK, PAGE_SIZE)) == 0) { ASC_PRINT3( "advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n", - boardp->id, pciConfig.baseAddress[1], ADV_CONDOR_IOLEN); + boardp->id, pci_memory_address, ADV_CONDOR_IOLEN); scsi_unregister(shp); asc_board_count--; continue; } adv_dvc_varp->iop_base = (AdvPortAddr) (boardp->ioremap_addr + - (pciConfig.baseAddress[1] - - (pciConfig.baseAddress[1] & PAGE_MASK))); + (pci_memory_address - (pci_memory_address & PAGE_MASK))); +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ #endif /* version >= v1,3,0 */ /* @@ -4447,16 +4541,33 @@ advansys_detect(Scsi_Host_Template *tpnt) shp->unchecked_isa_dma = FALSE; share_irq = TRUE; break; +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI case ASC_IS_PCI: shp->irq = asc_dvc_varp->irq_no = pciConfig.irqLine; - shp->unchecked_isa_dma = FALSE; - share_irq = TRUE; asc_dvc_varp->cfg->pci_device_id = pciConfig.deviceID; asc_dvc_varp->cfg->pci_slot_info = ASC_PCI_MKID(pciDevice.busNumber, pciDevice.slotFound, pciDevice.devFunc); + shp->unchecked_isa_dma = FALSE; + share_irq = TRUE; break; +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + case ASC_IS_PCI: + shp->irq = asc_dvc_varp->irq_no = pci_devp->irq; + asc_dvc_varp->cfg->pci_device_id = pci_devp->device; + asc_dvc_varp->cfg->pci_slot_info = + ASC_PCI_MKID(pci_devp->bus->number, + PCI_SLOT(pci_devp->devfn), + PCI_FUNC(pci_devp->devfn)); + shp->unchecked_isa_dma = FALSE; + share_irq = TRUE; + break; +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ default: ASC_PRINT2( "advansys_detect: board %d: unknown adapter type: %d\n", @@ -4470,14 +4581,29 @@ advansys_detect(Scsi_Host_Template *tpnt) * For Wide boards set PCI information before calling * AdvInitGetConfig(). */ +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI shp->irq = adv_dvc_varp->irq_no = pciConfig.irqLine; + adv_dvc_varp->cfg->pci_device_id = pciConfig.deviceID; + adv_dvc_varp->cfg->pci_slot_info = + ASC_PCI_MKID(pciDevice.busNumber, + pciDevice.slotFound, + pciDevice.devFunc); shp->unchecked_isa_dma = FALSE; share_irq = TRUE; - adv_dvc_varp->cfg->pci_device_id = pciConfig.deviceID; +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + shp->irq = adv_dvc_varp->irq_no = pci_devp->irq; + adv_dvc_varp->cfg->pci_device_id = pci_devp->device; adv_dvc_varp->cfg->pci_slot_info = - ASC_PCI_MKID(pciDevice.busNumber, - pciDevice.slotFound, - pciDevice.devFunc); + ASC_PCI_MKID(pci_devp->bus->number, + PCI_SLOT(pci_devp->devfn), + PCI_FUNC(pci_devp->devfn)); + shp->unchecked_isa_dma = FALSE; + share_irq = TRUE; +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ } /* @@ -4572,7 +4698,7 @@ advansys_detect(Scsi_Host_Template *tpnt) */ ep = &boardp->eep_config.asc_eep; - ep->init_sdtr = asc_dvc_varp->init_sdtr; + ep->init_sdtr = asc_dvc_varp->cfg->sdtr_enable; ep->disc_enable = asc_dvc_varp->cfg->disc_enable; ep->use_cmd_qng = asc_dvc_varp->cfg->cmd_qng_enabled; ep->isa_dma_speed = asc_dvc_varp->cfg->isa_dma_speed; @@ -4879,14 +5005,36 @@ advansys_detect(Scsi_Host_Template *tpnt) if ((ret = request_irq(shp->irq, advansys_interrupt, SA_INTERRUPT, "advansys")) != 0) #else /* version >= v1.3.70 */ - if ((ret = request_irq(shp->irq, advansys_interrupt, + /* + * If request_irq() fails with the SA_INTERRUPT flag set, + * then try again without the SA_INTERRUPT flag set. This + * allows IRQ sharing to work even with other drivers that + * do not set the SA_INTERRUPT flag. + * + * If SA_INTERRUPT is not set, then interrupts are enabled + * before the driver interrupt function is called. + */ + if (((ret = request_irq(shp->irq, advansys_interrupt, SA_INTERRUPT | (share_irq == TRUE ? SA_SHIRQ : 0), - "advansys", boardp)) != 0) + "advansys", boardp)) != 0) && + ((ret = request_irq(shp->irq, advansys_interrupt, + (share_irq == TRUE ? SA_SHIRQ : 0), + "advansys", boardp)) != 0)) #endif /* version >= v1.3.70 */ { - ASC_PRINT2( -"advansys_detect: board %d: request_irq() failed %d\n", - boardp->id, ret); + if (ret == -EBUSY) { + ASC_PRINT2( +"advansys_detect: board %d: request_irq(): IRQ %d already in use.\n", + boardp->id, shp->irq); + } else if (ret == -EINVAL) { + ASC_PRINT2( +"advansys_detect: board %d: request_irq(): IRQ %d not valid.\n", + boardp->id, shp->irq); + } else { + ASC_PRINT3( +"advansys_detect: board %d: request_irq(): IRQ %d failed with %d\n", + boardp->id, shp->irq, ret); + } release_region(shp->io_port, shp->n_io_port); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) iounmap(boardp->ioremap_addr); @@ -5140,7 +5288,11 @@ advansys_info(struct Scsi_Host *shp) busname = "ISA"; } sprintf(info, +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92) "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u, DMA %u", +#else /* version >= v2.1.92 */ +"AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u, DMA %u", +#endif /* version >= v2.1.92 */ ASC_VERSION, busname, asc_dvc_varp->max_total_qng, (unsigned) shp->base, shp->io_port, shp->n_io_port - 1, @@ -5153,7 +5305,11 @@ advansys_info(struct Scsi_Host *shp) busname = "PCI"; } sprintf(info, +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92) "AdvanSys SCSI %s: %s %u CDB: IO %X/%X, IRQ %u", +#else /* version >= v2.1.92 */ + "AdvanSys SCSI %s: %s %u CDB: IO %lX/%X, IRQ %u", +#endif /* version >= v2.1.92 */ ASC_VERSION, busname, asc_dvc_varp->max_total_qng, shp->io_port, shp->n_io_port - 1, shp->irq); } else { @@ -5168,7 +5324,11 @@ advansys_info(struct Scsi_Host *shp) boardp->id, asc_dvc_varp->bus_type); } sprintf(info, +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92) "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u", +#else /* version >= v2.1.92 */ + "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u", +#endif /* version >= v2.1.92 */ ASC_VERSION, busname, asc_dvc_varp->max_total_qng, (unsigned) shp->base, shp->io_port - 1, shp->n_io_port, shp->irq); @@ -5718,13 +5878,6 @@ advansys_reset(Scsi_Cmnd *scp, unsigned int reset_flags) status = AscResetDevice(asc_dvc_varp, scp->target); cli(); - /* - * If the device has been reset, try to initialize it. - */ - if (status == ASC_TRUE) { - status = asc_init_dev(asc_dvc_varp, scp); - } - switch (status) { case ASC_TRUE: ASC_DBG(1, "advansys_reset: AscResetDevice() success\n"); @@ -5945,7 +6098,7 @@ advansys_reset(Scsi_Cmnd *scp, unsigned int reset_flags) asc_scsi_done_list(done_scp); } - ASC_DBG1(1, "advansys_reset: ret %d", ret); + ASC_DBG1(1, "advansys_reset: ret %d\n", ret); /* Re-enable interrupts, if they were enabled on entry. */ restore_flags(flags); @@ -6109,15 +6262,27 @@ advansys_interrupt(int irq, struct pt_regs *regs) advansys_interrupt(int irq, void *dev_id, struct pt_regs *regs) #endif /* version >= v1.3.70 */ { +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95) int flags; +#else /* version >= v2.1.95 */ + unsigned long flags; +#endif /* version >= v2.1.95 */ int i; asc_board_t *boardp; Scsi_Cmnd *done_scp = NULL, *last_scp = NULL; Scsi_Cmnd *new_last_scp; +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95) /* Disable interrupts, if they aren't already disabled. */ save_flags(flags); cli(); +#else /* version >= v2.1.95 */ + /* + * Disable interrupts, if they aren't already disabled and acquire + * the I/O spinlock. + */ + spin_lock_irqsave(&io_request_lock, flags); +#endif /* version >= v2.1.95 */ ASC_DBG(1, "advansys_interrupt: begin\n"); @@ -6191,13 +6356,26 @@ advansys_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* * It is possible for the request done function to re-enable - * interrupts without confusing the driver. But here interrupts - * aren't enabled until all requests have been completed. + * interrupts without confusing the driver. But here the + * original flags aren't restored until all requests have been + * completed. */ asc_scsi_done_list(done_scp); - /* Re-enable interrupts, if they were enabled on entry. */ +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95) + /* + * Restore the original flags which will enable interrupts + * if and only if they were enabled on entry. + */ restore_flags(flags); +#else /* version >= v2.1.95 */ + /* + * Release the I/O spinlock and restore the original flags + * which will enable interrupts if and only if they were + * enabled on entry. + */ + spin_unlock_irqrestore(&io_request_lock, flags); +#endif /* version >= v2.1.95 */ ASC_DBG(1, "advansys_interrupt: end\n"); return; @@ -6341,21 +6519,6 @@ asc_execute_scsi_cmnd(Scsi_Cmnd *scp) asc_dvc_varp = &boardp->dvc_var.asc_dvc_var; - /* - * Narrow Board - Asc Library requires special device initialization. - * - * If this is the first command, then initialize the device. If - * no device is found set 'DID_BAD_TARGET' and return. - */ - if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0) { - if (asc_init_dev(asc_dvc_varp, scp) == ASC_FALSE) { - scp->result = HOST_BYTE(DID_BAD_TARGET); - asc_enqueue(&boardp->done, scp, ASC_BACK); - return ASC_ERROR; - } - boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->target); - } - /* * Build Asc Library request structure using the * global structures 'asc_scsi_req' and 'asc_sg_head'. @@ -6901,7 +7064,7 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep) asc_board_t *boardp; Scsi_Cmnd *scp; struct Scsi_Host *shp; - int underrun = ASC_FALSE; + int underrun = ASC_FALSE; int i; ASC_ASSERT(interrupts_enabled() == ASC_FALSE); @@ -6947,6 +7110,7 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep) * Display a message and return. */ boardp = ASC_BOARDP(shp); + ASC_ASSERT(asc_dvc_varp == &boardp->dvc_var.asc_dvc_var); if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) { ASC_PRINT2("asc_isr_callback: board %d: scp %x not on active queue\n", boardp->id, (unsigned) scp); @@ -6957,7 +7121,7 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep) * Check for an underrun condition. */ if (scp->request_bufflen != 0 && qdonep->remain_bytes != 0 && - qdonep->remain_bytes <= scp->request_bufflen != 0) { + qdonep->remain_bytes <= scp->request_bufflen != 0) { ASC_DBG1(1, "asc_isr_callback: underrun condition %u bytes\n", (unsigned) qdonep->remain_bytes); underrun = ASC_TRUE; @@ -6978,13 +7142,28 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep) scp->result = HOST_BYTE(DID_ERROR); break; } - /* - * If there was an underrun without any other error, - * set DID_ERROR to indicate the underrun error. - */ - if (scp->result == 0 && underrun == ASC_TRUE) { + + /* + * If an INQUIRY command completed successfully, then call + * the AscInquiryHandling() function to set-up the device. + */ + if (scp->cmnd[0] == SCSICMD_Inquiry && scp->lun == 0 && + (scp->request_bufflen - qdonep->remain_bytes) >= 8) + { + AscInquiryHandling(asc_dvc_varp, scp->target & 0x7, + (ASC_SCSI_INQUIRY *) scp->request_buffer); + } + + /* + * If there was an underrun without any other error, + * set DID_ERROR to indicate the underrun error. + * + * Note: There is no way yet to indicate the number + * of underrun bytes. + */ + if (scp->result == 0 && underrun == ASC_TRUE) { scp->result = HOST_BYTE(DID_UNDERRUN); - } + } break; case QD_WITH_ERROR: @@ -7014,10 +7193,9 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep) default: /* QHSTA error occurred */ - ASC_DBG1(2, "asc_isr_callback: host_stat %x\n", + ASC_DBG1(1, "asc_isr_callback: host_stat %x\n", qdonep->d3.host_stat); - scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) | - STATUS_BYTE(qdonep->d3.scsi_stat); + scp->result = HOST_BYTE(DID_BAD_TARGET); break; } break; @@ -7029,12 +7207,23 @@ asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep) break; default: - ASC_PRINT1("asc_isr_callback: done_stat %x\n", qdonep->d3.done_stat); + ASC_DBG1(1, "asc_isr_callback: done_stat %x\n", qdonep->d3.done_stat); scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) | STATUS_BYTE(qdonep->d3.scsi_stat); break; } + /* + * If the 'init_tidmask' bit isn't already set for the target and the + * current request finished normally, then set the bit for the target + * to indicate that a device is present. + */ + if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0 && + qdonep->d3.done_stat == QD_NO_ERROR && + qdonep->d3.host_stat == QHSTA_NO_ERROR) { + boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->target); + } + /* * Because interrupts may be enabled by the 'Scsi_Cmnd' done * function, add the command to the end of the board's done queue. @@ -7058,7 +7247,7 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) adv_req_t *reqp; Scsi_Cmnd *scp; struct Scsi_Host *shp; - int underrun = ASC_FALSE; + int underrun = ASC_FALSE; int i; ASC_ASSERT(interrupts_enabled() == ASC_FALSE); @@ -7126,6 +7315,7 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) * to free the adv_req_t and adv_sgblk_t, if any, structures. */ boardp = ASC_BOARDP(shp); + ASC_ASSERT(adv_dvc_varp == &boardp->dvc_var.adv_dvc_var); if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) { ASC_PRINT2("adv_isr_callback: board %d: scp %x not on active queue\n", boardp->id, (unsigned) scp); @@ -7158,13 +7348,16 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) scp->result = HOST_BYTE(DID_ERROR); break; } - /* - * If there was an underrun without any other error, - * set DID_ERROR to indicate the underrun error. - */ - if (scp->result == 0 && underrun == ASC_TRUE) { - scp->result = HOST_BYTE(DID_UNDERRUN); - } + /* + * If there was an underrun without any other error, + * set DID_ERROR to indicate the underrun error. + * + * Note: There is no way yet to indicate the number + * of underrun bytes. + */ + if (scp->result == 0 && underrun == ASC_TRUE) { + scp->result = HOST_BYTE(DID_UNDERRUN); + } break; case QD_WITH_ERROR: @@ -7194,7 +7387,7 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) default: /* Some other QHSTA error occurred. */ - ASC_DBG1(2, "adv_isr_callback: host_status %x\n", + ASC_DBG1(1, "adv_isr_callback: host_status %x\n", scsiqp->host_status); scp->result = HOST_BYTE(DID_BAD_TARGET); break; @@ -7207,15 +7400,15 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) break; default: - ASC_PRINT1("adv_isr_callback: done_status %x\n", scsiqp->done_status); + ASC_DBG1(1, "adv_isr_callback: done_status %x\n", scsiqp->done_status); scp->result = HOST_BYTE(DID_ERROR) | STATUS_BYTE(scsiqp->scsi_status); break; } /* * If the 'init_tidmask' bit isn't already set for the target and the - * current request did not finish with a Selection Timeout, then set - * the bit for the target to indicate that a device is present. + * current request finished normally, then set the bit for the target + * to indicate that a device is present. */ if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0 && scsiqp->done_status == QD_NO_ERROR && @@ -7252,119 +7445,8 @@ adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) return; } -/* - * asc_init_dev() - Narrow Board initialization function. - * - * Perform one-time initialization of a device for Asc Library - */ -STATIC int -asc_init_dev(ASC_DVC_VAR *asc_dvc_varp, Scsi_Cmnd *scp) -{ - asc_board_t *boardp; - ASC_SCSI_REQ_Q *scsireqq; - ASC_CAP_INFO *cap_info; - ASC_SCSI_INQUIRY *inquiry; - int found; - ASC_SCSI_BIT_ID_TYPE save_use_tagged_qng; - ASC_SCSI_BIT_ID_TYPE save_can_tagged_qng; - int ret; -#ifdef ADVANSYS_DEBUG - ASC_SCSI_BIT_ID_TYPE tidmask; /* target id bit mask: 1 - 128 */ -#endif /* ADVANSYS_DEBUG */ - - ASC_DBG1(1, "asc_init_dev: target %d\n", (unsigned) scp->target); - - /* The host's target id is set in init_tidmask during initialization. */ - ASC_ASSERT(asc_dvc_varp->cfg->chip_scsi_id != scp->target); - - boardp = ASC_BOARDP(scp->host); - - /* Set-up AscInitPollTarget() arguments. */ - scsireqq = &boardp->scsireqq; - memset(scsireqq, 0, sizeof(ASC_SCSI_REQ_Q)); - cap_info = &boardp->cap_info; - memset(cap_info, 0, sizeof(ASC_CAP_INFO)); - inquiry = &boardp->inquiry; - memset(inquiry, 0, sizeof(ASC_SCSI_INQUIRY)); - - /* - * AscInitPollBegin() re-initializes these bitmask fields to zero. - * Save the current bitmask value and 'or' them back in after calling - * AscInitPollEnd() below.. - */ - save_use_tagged_qng = asc_dvc_varp->use_tagged_qng; - save_can_tagged_qng = asc_dvc_varp->cfg->can_tagged_qng; - - ASC_DBG(2, "asc_init_dev: AscInitPollBegin()\n"); - if (AscInitPollBegin(asc_dvc_varp)) { - ASC_PRINT1("asc_init_dev: board %d: AscInitPollBegin() failed\n", - boardp->id); - return ASC_FALSE; - } - - scsireqq->sense_ptr = &scsireqq->sense[0]; - scsireqq->r1.sense_len = ASC_MIN_SENSE_LEN; - scsireqq->r1.target_id = ASC_TID_TO_TARGET_ID(scp->target); - scsireqq->r1.target_lun = 0; - scsireqq->r2.target_ix = ASC_TIDLUN_TO_IX(scp->target, 0); - - found = ASC_FALSE; - ASC_DBG(2, "asc_init_dev: AscInitPollTarget()\n"); - switch (ret = AscInitPollTarget(asc_dvc_varp, scsireqq, inquiry, - cap_info)) { - case ASC_TRUE: - found = ASC_TRUE; -#ifdef ADVANSYS_DEBUG - tidmask = ADV_TID_TO_TIDMASK(scp->target); - ASC_DBG2(1, "asc_init_dev: lba %lu, blk_size %lu\n", - cap_info->lba, cap_info->blk_size); - ASC_DBG1(1, "asc_init_dev: peri_dvc_type %x\n", - inquiry->byte0.peri_dvc_type); - if (asc_dvc_varp->use_tagged_qng & tidmask) { - ASC_DBG1(1, "asc_init_dev: command queuing enabled: %d\n", - asc_dvc_varp->max_dvc_qng[scp->target]); - } else { - ASC_DBG(1, "asc_init_dev: command queuing disabled\n"); - } - if (asc_dvc_varp->init_sdtr & tidmask) { - ASC_DBG(1, "asc_init_dev: synchronous transfers enabled\n"); - } else { - ASC_DBG(1, "asc_init_dev: synchronous transfers disabled\n"); - } - /* Set bit means fix disabled. */ - if (asc_dvc_varp->pci_fix_asyn_xfer & tidmask) { - ASC_DBG(1, "asc_init_dev: synchronous transfer fix disabled\n"); - } else { - ASC_DBG(1, "asc_init_dev: synchronous transfer fix enabled\n"); - } -#endif /* ADVANSYS_DEBUG */ - break; - case ASC_FALSE: - ASC_DBG(1, "asc_init_dev: no device found\n"); - break; - case ASC_ERROR: - ASC_PRINT1("asc_init_dev: board %d: AscInitPollTarget() ASC_ERROR\n", - boardp->id); - break; - default: - ASC_PRINT2( -"asc_init_dev: board %d: AscInitPollTarget() unknown ret %d\n", - boardp->id, ret); - break; - } - - /* Restore previously set bits in the bitmask fields. */ - asc_dvc_varp->use_tagged_qng |= save_use_tagged_qng; - asc_dvc_varp->cfg->can_tagged_qng |= save_can_tagged_qng; - - ASC_DBG(2, "asc_init_dev: AscInitPollEnd()\n"); - AscInitPollEnd(asc_dvc_varp); - - ASC_DBG1(1, "asc_init_dev: found %d\n", found); - - return found; -} - +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI /* * Search for an AdvanSys PCI device in the PCI configuration space. */ @@ -7373,7 +7455,7 @@ STATIC int asc_srch_pci_dev(PCI_DEVICE *pciDevice) ) { - int ret; + int ret = PCI_DEVICE_NOT_FOUND; ASC_DBG(2, "asc_srch_pci_dev: begin\n"); @@ -7400,6 +7482,7 @@ asc_srch_pci_dev(PCI_DEVICE *pciDevice) ASC_DBG1(2, "asc_srch_pci_dev: recursive call return %d\n", ret); } } + ASC_DBG1(2, "asc_srch_pci_dev: return %d\n", ret); return ret; } @@ -7500,7 +7583,7 @@ asc_get_pci_cfg(PCI_DEVICE *pciDevice, PCI_CONFIG_SPACE *pciConfig) uchar counter; uchar *localConfig; - ASC_DBG1(4, "asc_get_pci_cfg: slot found - %d\n ", + ASC_DBG1(4, "asc_get_pci_cfg: slotFound %d\n ", pciDevice->slotFound); pciData.type = pciDevice->type; @@ -7767,6 +7850,8 @@ asc_put_cfg_byte(PCI_DATA *pciData, uchar byte_data) } ASC_DBG(4, "asc_put_cfg_byte: end\n"); } +#endif /* ASC_CONFIG_PCI */ +#endif /* version < v2.1.93 */ /* * Add a 'REQP' to the end of specified queue. Set 'tidmask' @@ -8176,9 +8261,9 @@ asc_prt_adv_bios(struct Scsi_Host *shp, char *cp, int cplen) major, minor, letter >= 26 ? '?' : letter + 'A'); ASC_PRT_NEXT(); - /* Current available ROM BIOS release is 3.1E. */ + /* Current available ROM BIOS release is 3.1C. */ if (major < 3 || (major <= 3 && minor < 1) || - (major <= 3 && minor <= 1 && letter < ('E'- 'A'))) { + (major <= 3 && minor <= 1 && letter < ('C'- 'A'))) { upgrade = ASC_TRUE; } } @@ -8195,18 +8280,18 @@ asc_prt_adv_bios(struct Scsi_Host *shp, char *cp, int cplen) * Add serial number to information bar if signature AAh * is found in at bit 15-9 (7 bits) of word 1. * - * Serial Number consists 12 alpha-numeric digits. + * Serial Number consists fo 12 alpha-numeric digits. * - * 1 - Product type (A,B,C,D..) Word0: 15-13 (3 bits) - * 2 - MFG Location (A,B,C,D..) Word0: 12-10 (3 bits) - * 3-4 - Product ID (0-99) Word0: 10-0 (11 bits) - * 5 - Product revision Word0: " " + * 1 - Product type (A,B,C,D..) Word0: 15-13 (3 bits) + * 2 - MFG Location (A,B,C,D..) Word0: 12-10 (3 bits) + * 3-4 - Product ID (0-99) Word0: 9-0 (10 bits) + * 5 - Product revision (A-J) Word0: " " * - * Signature Word1: 15-9 (7 bits) - * 6 - Year (4-9) Word1: 8-6 (3 bits) - * 7-8 - Week of the year Word1: 5-0 (6 bits) + * Signature Word1: 15-9 (7 bits) + * 6 - Year (0-9) Word1: 8-6 (3 bits) & Word2: 15 (1 bit) + * 7-8 - Week of the year (1-52) Word1: 5-0 (6 bits) * - * 9-12 - Serial Number Word2: 15-0 (16 bits) + * 9-12 - Serial Number (A001-Z999) Word2: 14-0 (15 bits) * * Note 1: Only production cards will have a serial number. * @@ -8228,7 +8313,11 @@ asc_get_eeprom_string(ushort *serialnum, uchar *cp) w = serialnum[0]; /* Product type - 1st digit. */ - *cp++ = 'A' + ((w & 0xE000) >> 13); + if ((*cp = 'A' + ((w & 0xE000) >> 13)) == 'H') { + /* Product type is P=Prototype */ + *cp += 0x8; + } + cp++; /* Manufacturing location - 2nd digit. */ *cp++ = 'A' + ((w & 0x1C00) >> 10); @@ -8247,8 +8336,17 @@ asc_get_eeprom_string(ushort *serialnum, uchar *cp) */ w = serialnum[1]; - /* Year - 6th digit. */ - *cp++ = '0' + ((w & 0x1C0) >> 6); + /* + * Year - 6th digit. + * + * If bit 15 of third word is set, then the + * last digit of the year is greater than 7. + */ + if (serialnum[2] & 0x8000) { + *cp++ = '8' + ((w & 0x1C0) >> 6); + } else { + *cp++ = '0' + ((w & 0x1C0) >> 6); + } /* Week of year - 7th, 8th digits. */ num = w & 0x003F; @@ -8259,7 +8357,7 @@ asc_get_eeprom_string(ushort *serialnum, uchar *cp) /* * Third word */ - w = serialnum[2]; + w = serialnum[2] & 0x7FFF; /* Serial number - 9th digit. */ *cp++ = 'A' + (w / 1000); @@ -9275,6 +9373,8 @@ DvcReadPCIConfigByte( ushort offset) ) { +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI PCI_DATA pciData; pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info); @@ -9283,6 +9383,21 @@ DvcReadPCIConfigByte( pciData.offset = offset; pciData.type = pci_scan_method; return asc_get_cfg_byte(&pciData); +#else /* ASC_CONFIG_PCI */ + return 0; +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + uchar byte_data; + pcibios_read_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info), + PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info), + ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)), + offset, &byte_data); + return byte_data; +#else /* CONFIG_PCI */ + return 0; +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ } /* @@ -9296,6 +9411,8 @@ DvcWritePCIConfigByte( uchar byte_data) ) { +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI PCI_DATA pciData; pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info); @@ -9304,6 +9421,15 @@ DvcWritePCIConfigByte( pciData.offset = offset; pciData.type = pci_scan_method; asc_put_cfg_byte(&pciData, byte_data); +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + pcibios_write_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info), + PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info), + ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)), + offset, byte_data); +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ } /* @@ -9400,6 +9526,8 @@ DvcAdvReadPCIConfigByte( ushort offset) ) { +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI PCI_DATA pciData; pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info); @@ -9408,6 +9536,21 @@ DvcAdvReadPCIConfigByte( pciData.offset = offset; pciData.type = pci_scan_method; return asc_get_cfg_byte(&pciData); +#else /* ASC_CONFIG_PCI */ + return 0; +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + uchar byte_data; + pcibios_read_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info), + PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info), + ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)), + offset, &byte_data); + return byte_data; +#else /* CONFIG_PCI */ + return 0; +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ } /* @@ -9421,6 +9564,8 @@ DvcAdvWritePCIConfigByte( uchar byte_data) ) { +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93) +#ifdef ASC_CONFIG_PCI PCI_DATA pciData; pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info); @@ -9429,6 +9574,15 @@ DvcAdvWritePCIConfigByte( pciData.offset = offset; pciData.type = pci_scan_method; asc_put_cfg_byte(&pciData, byte_data); +#endif /* ASC_CONFIG_PCI */ +#else /* version >= v2.1.93 */ +#ifdef CONFIG_PCI + pcibios_write_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info), + PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info), + ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)), + offset, byte_data); +#endif /* CONFIG_PCI */ +#endif /* version >= v2.1.93 */ } /* @@ -9745,8 +9899,9 @@ asc_prt_asc_dvc_cfg(ASC_DVC_CFG *h) printk("ASC_DVC_CFG at addr %x\n", (unsigned) h); printk( -" can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, res %x,\n", - h->can_tagged_qng, h->cmd_qng_enabled, h->disc_enable, h->res); +" can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, sdtr_enable %x,\n", + h->can_tagged_qng, h->cmd_qng_enabled, h->disc_enable, + h->sdtr_enable); printk( " chip_scsi_id %d, isa_dma_speed %d, isa_dma_channel %d, chip_version %d,\n", @@ -10931,200 +11086,156 @@ AscISR( return (int_pending); } -STATIC int -AscScsiSetupCmdQ( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - uchar * buf_addr, - ulong buf_len -) -{ - ulong phy_addr; - - scsiq->r1.cntl = 0; - scsiq->r1.sg_queue_cnt = 0; - scsiq->r1.q_no = 0; - scsiq->r1.extra_bytes = 0; - scsiq->r3.scsi_stat = 0; - scsiq->r3.scsi_msg = 0; - scsiq->r3.host_stat = 0; - scsiq->r3.done_stat = 0; - scsiq->r2.vm_id = 0; - scsiq->r1.data_cnt = buf_len; - scsiq->cdbptr = (uchar *) scsiq->cdb; - scsiq->sense_ptr = (uchar *) scsiq->sense ; - scsiq->r1.sense_len = ASC_MIN_SENSE_LEN ; - scsiq->r2.tag_code = (uchar) M2_QTAG_MSG_SIMPLE; - scsiq->r2.flag = (uchar) ASC_FLAG_SCSIQ_REQ; - scsiq->r2.srb_ptr = (ulong) scsiq; - scsiq->r1.status = (uchar) QS_READY; - scsiq->r1.data_addr = 0L; - if (buf_len != 0L) { - if ((phy_addr = AscGetOnePhyAddr(asc_dvc, - (uchar *) buf_addr, scsiq->r1.data_cnt)) == 0L) { - return (ERR); - } - scsiq->r1.data_addr = phy_addr; - } - if ((phy_addr = AscGetOnePhyAddr(asc_dvc, - (uchar *) scsiq->sense_ptr, - (ulong) scsiq->r1.sense_len)) == 0L) { - return (ERR); - } - scsiq->r1.sense_addr = phy_addr ; - return (0); -} - STATIC uchar _asc_mcode_buf[] ASC_INITDATA = { 0x01, 0x03, 0x01, 0x19, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xDB, 0x0C, 0x0A, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x91, 0x10, 0x0A, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x23, 0x00, 0x23, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x23, 0x00, 0x24, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x88, 0x00, 0x00, 0x00, 0x00, 0x80, 0x73, 0x48, 0x04, 0x36, 0x00, 0x00, 0xA2, 0xC2, 0x00, 0x80, 0x73, 0x03, 0x23, 0x36, 0x40, 0xB6, 0x00, 0x36, 0x00, 0x05, 0xD6, 0x0C, 0xD2, 0x12, 0xDA, 0x00, 0xA2, 0xC2, 0x00, 0x92, 0x80, - 0x10, 0x98, 0x50, 0x00, 0xF5, 0x00, 0x3A, 0x98, 0xDF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, - 0x4F, 0x00, 0xF5, 0x00, 0x3A, 0x98, 0xEF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, 0x80, 0x62, + 0x1E, 0x98, 0x50, 0x00, 0xF5, 0x00, 0x48, 0x98, 0xDF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, + 0x4F, 0x00, 0xF5, 0x00, 0x48, 0x98, 0xEF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, 0x80, 0x62, 0x92, 0x80, 0x00, 0x46, 0x17, 0xEE, 0x13, 0xEA, 0x02, 0x01, 0x09, 0xD8, 0xCD, 0x04, 0x4D, 0x00, - 0x00, 0xA3, 0xD6, 0x00, 0x98, 0x97, 0x7F, 0x23, 0x04, 0x61, 0x84, 0x01, 0xE0, 0x84, 0xD2, 0xC1, - 0x80, 0x73, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xE2, 0x01, 0x98, 0x97, 0xCE, 0x81, 0x00, 0x33, - 0x02, 0x00, 0xB2, 0x88, 0x80, 0x73, 0x80, 0x77, 0x00, 0x01, 0x01, 0xA1, 0x02, 0x01, 0x4F, 0x00, - 0x76, 0x97, 0x07, 0xA6, 0x0C, 0x01, 0x00, 0x33, 0x03, 0x00, 0xB2, 0x88, 0x03, 0x03, 0x03, 0xDE, - 0x00, 0x33, 0x05, 0x00, 0xB2, 0x88, 0xCE, 0x00, 0x69, 0x60, 0xCE, 0x00, 0x02, 0x03, 0x4A, 0x60, + 0x00, 0xA3, 0xD6, 0x00, 0xA6, 0x97, 0x7F, 0x23, 0x04, 0x61, 0x84, 0x01, 0xE6, 0x84, 0xD2, 0xC1, + 0x80, 0x73, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xE2, 0x01, 0xA6, 0x97, 0xCE, 0x81, 0x00, 0x33, + 0x02, 0x00, 0xC0, 0x88, 0x80, 0x73, 0x80, 0x77, 0x00, 0x01, 0x01, 0xA1, 0x02, 0x01, 0x4F, 0x00, + 0x84, 0x97, 0x07, 0xA6, 0x0C, 0x01, 0x00, 0x33, 0x03, 0x00, 0xC0, 0x88, 0x03, 0x03, 0x03, 0xDE, + 0x00, 0x33, 0x05, 0x00, 0xC0, 0x88, 0xCE, 0x00, 0x69, 0x60, 0xCE, 0x00, 0x02, 0x03, 0x4A, 0x60, 0x00, 0xA2, 0x80, 0x01, 0x80, 0x63, 0x07, 0xA6, 0x2C, 0x01, 0x80, 0x81, 0x03, 0x03, 0x80, 0x63, - 0xE2, 0x00, 0x07, 0xA6, 0x3C, 0x01, 0x00, 0x33, 0x04, 0x00, 0xB2, 0x88, 0x03, 0x07, 0x02, 0x01, - 0x04, 0xCA, 0x0D, 0x23, 0x5A, 0x98, 0x4D, 0x04, 0xFE, 0x84, 0x05, 0xD8, 0x0D, 0x23, 0x5A, 0x98, - 0xCD, 0x04, 0x15, 0x23, 0xE8, 0x88, 0xFB, 0x23, 0x02, 0x61, 0x82, 0x01, 0x80, 0x63, 0x02, 0x03, - 0x06, 0xA3, 0x6A, 0x01, 0x00, 0x33, 0x0A, 0x00, 0xB2, 0x88, 0x4E, 0x00, 0x07, 0xA3, 0x76, 0x01, - 0x00, 0x33, 0x0B, 0x00, 0xB2, 0x88, 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, 0x1A, 0x00, 0xB2, 0x88, + 0xE2, 0x00, 0x07, 0xA6, 0x3C, 0x01, 0x00, 0x33, 0x04, 0x00, 0xC0, 0x88, 0x03, 0x07, 0x02, 0x01, + 0x04, 0xCA, 0x0D, 0x23, 0x68, 0x98, 0x4D, 0x04, 0x04, 0x85, 0x05, 0xD8, 0x0D, 0x23, 0x68, 0x98, + 0xCD, 0x04, 0x15, 0x23, 0xF6, 0x88, 0xFB, 0x23, 0x02, 0x61, 0x82, 0x01, 0x80, 0x63, 0x02, 0x03, + 0x06, 0xA3, 0x6A, 0x01, 0x00, 0x33, 0x0A, 0x00, 0xC0, 0x88, 0x4E, 0x00, 0x07, 0xA3, 0x76, 0x01, + 0x00, 0x33, 0x0B, 0x00, 0xC0, 0x88, 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, 0x1A, 0x00, 0xC0, 0x88, 0x50, 0x04, 0x90, 0x81, 0x06, 0xAB, 0x8A, 0x01, 0x90, 0x81, 0x4E, 0x00, 0x07, 0xA3, 0x9A, 0x01, - 0x50, 0x00, 0x00, 0xA3, 0x44, 0x01, 0x00, 0x05, 0x84, 0x81, 0x38, 0x97, 0x02, 0x01, 0x05, 0xC6, + 0x50, 0x00, 0x00, 0xA3, 0x44, 0x01, 0x00, 0x05, 0x84, 0x81, 0x46, 0x97, 0x02, 0x01, 0x05, 0xC6, 0x04, 0x23, 0xA0, 0x01, 0x15, 0x23, 0xA1, 0x01, 0xC6, 0x81, 0xFD, 0x23, 0x02, 0x61, 0x82, 0x01, 0x0A, 0xDA, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xBC, 0x01, 0x80, 0x63, 0xCD, 0x04, 0x36, 0x2D, - 0x00, 0x33, 0x1B, 0x00, 0xB2, 0x88, 0x06, 0x23, 0x5A, 0x98, 0xCD, 0x04, 0xE0, 0x84, 0x06, 0x01, - 0x00, 0xA2, 0xDC, 0x01, 0x57, 0x60, 0x00, 0xA0, 0xE2, 0x01, 0xE0, 0x84, 0x80, 0x23, 0xA0, 0x01, - 0xE0, 0x84, 0x80, 0x73, 0x4B, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x08, 0x02, 0x04, 0x01, 0x0C, 0xDE, - 0x02, 0x01, 0x03, 0xCC, 0x4F, 0x00, 0x76, 0x97, 0x04, 0x82, 0x08, 0x23, 0x02, 0x41, 0x82, 0x01, - 0x4F, 0x00, 0x54, 0x97, 0x48, 0x04, 0x84, 0x80, 0xE2, 0x97, 0x00, 0x46, 0x56, 0x00, 0x03, 0xC0, + 0x00, 0x33, 0x1B, 0x00, 0xC0, 0x88, 0x06, 0x23, 0x68, 0x98, 0xCD, 0x04, 0xE6, 0x84, 0x06, 0x01, + 0x00, 0xA2, 0xDC, 0x01, 0x57, 0x60, 0x00, 0xA0, 0xE2, 0x01, 0xE6, 0x84, 0x80, 0x23, 0xA0, 0x01, + 0xE6, 0x84, 0x80, 0x73, 0x4B, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x08, 0x02, 0x04, 0x01, 0x0C, 0xDE, + 0x02, 0x01, 0x03, 0xCC, 0x4F, 0x00, 0x84, 0x97, 0x04, 0x82, 0x08, 0x23, 0x02, 0x41, 0x82, 0x01, + 0x4F, 0x00, 0x62, 0x97, 0x48, 0x04, 0x84, 0x80, 0xF0, 0x97, 0x00, 0x46, 0x56, 0x00, 0x03, 0xC0, 0x01, 0x23, 0xE8, 0x00, 0x81, 0x73, 0x06, 0x29, 0x03, 0x42, 0x06, 0xE2, 0x03, 0xEE, 0x67, 0xEB, - 0x11, 0x23, 0xE8, 0x88, 0xF6, 0x97, 0xF4, 0x80, 0x80, 0x73, 0x80, 0x77, 0x06, 0xA6, 0x36, 0x02, - 0x00, 0x33, 0x31, 0x00, 0xB2, 0x88, 0x04, 0x01, 0x03, 0xD8, 0xA4, 0x98, 0x4C, 0x96, 0x48, 0x82, - 0xDC, 0x95, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0xB6, 0x2D, 0x02, 0xA6, 0x72, 0x02, 0x07, 0xA6, - 0x60, 0x02, 0x06, 0xA6, 0x64, 0x02, 0x03, 0xA6, 0x68, 0x02, 0x00, 0x33, 0x10, 0x00, 0xB2, 0x88, - 0x76, 0x95, 0x4A, 0x82, 0x42, 0x96, 0x4A, 0x82, 0x04, 0x23, 0xA0, 0x01, 0x14, 0x23, 0xA1, 0x01, - 0x36, 0x84, 0x04, 0x01, 0x0C, 0xDC, 0xE0, 0x23, 0x25, 0x61, 0xEF, 0x00, 0x14, 0x01, 0x4F, 0x04, - 0xA8, 0x01, 0x6F, 0x00, 0xA5, 0x01, 0x03, 0x23, 0xA4, 0x01, 0x06, 0x23, 0x9C, 0x01, 0x24, 0x2B, - 0x1C, 0x01, 0x02, 0xA6, 0xB0, 0x02, 0x07, 0xA6, 0x60, 0x02, 0x06, 0xA6, 0x64, 0x02, 0x03, 0xA6, - 0x1A, 0x04, 0x01, 0xA6, 0xBA, 0x02, 0x00, 0xA6, 0xBA, 0x02, 0x00, 0x33, 0x12, 0x00, 0xB2, 0x88, - 0x00, 0x0E, 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, 0x92, 0x02, 0x4D, 0x04, 0x04, 0x01, 0x0B, 0xDC, - 0xE7, 0x23, 0x04, 0x61, 0x84, 0x01, 0x10, 0x31, 0x12, 0x35, 0x14, 0x01, 0xEC, 0x00, 0x6C, 0x38, - 0x00, 0x3F, 0x00, 0x00, 0xF0, 0x82, 0x18, 0x23, 0x04, 0x61, 0x18, 0xA0, 0xE8, 0x02, 0x04, 0x01, - 0x9C, 0xC8, 0x00, 0x33, 0x1F, 0x00, 0xB2, 0x88, 0x08, 0x31, 0x0A, 0x35, 0x0C, 0x39, 0x0E, 0x3D, - 0x70, 0x98, 0xB6, 0x2D, 0x01, 0xA6, 0x1A, 0x03, 0x00, 0xA6, 0x1A, 0x03, 0x07, 0xA6, 0x12, 0x03, - 0x06, 0xA6, 0x16, 0x03, 0x03, 0xA6, 0x1A, 0x04, 0x02, 0xA6, 0x72, 0x02, 0x00, 0x33, 0x33, 0x00, - 0xB2, 0x88, 0x76, 0x95, 0xF4, 0x82, 0x42, 0x96, 0xF4, 0x82, 0x74, 0x98, 0x80, 0x42, 0x70, 0x98, - 0x60, 0xE4, 0x04, 0x01, 0x29, 0xC8, 0x31, 0x05, 0x07, 0x01, 0x00, 0xA2, 0x5A, 0x03, 0x00, 0x43, - 0x87, 0x01, 0x05, 0x05, 0x78, 0x98, 0x70, 0x98, 0x00, 0xA6, 0x1C, 0x03, 0x07, 0xA6, 0x52, 0x03, - 0x03, 0xA6, 0x36, 0x04, 0x06, 0xA6, 0x56, 0x03, 0x01, 0xA6, 0x1C, 0x03, 0x00, 0x33, 0x25, 0x00, - 0xB2, 0x88, 0x76, 0x95, 0x38, 0x83, 0x42, 0x96, 0x38, 0x83, 0x04, 0x01, 0x0C, 0xCE, 0x03, 0xC8, - 0x00, 0x33, 0x42, 0x00, 0xB2, 0x88, 0x00, 0x01, 0x05, 0x05, 0xFF, 0xA2, 0x78, 0x03, 0xB1, 0x01, - 0x08, 0x23, 0xB2, 0x01, 0x34, 0x83, 0x05, 0x05, 0x15, 0x01, 0x00, 0xA2, 0x98, 0x03, 0xEC, 0x00, - 0x6E, 0x00, 0x95, 0x01, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, 0x01, 0xA6, 0x94, 0x03, 0x00, 0xA6, - 0x94, 0x03, 0x0C, 0x84, 0x80, 0x42, 0x70, 0x98, 0x01, 0xA6, 0xA2, 0x03, 0x00, 0xA6, 0xBA, 0x03, - 0x0C, 0x84, 0x98, 0x98, 0x80, 0x42, 0x01, 0xA6, 0xA2, 0x03, 0x07, 0xA6, 0xB0, 0x03, 0xD2, 0x83, - 0x76, 0x95, 0xA6, 0x83, 0x00, 0x33, 0x2F, 0x00, 0xB2, 0x88, 0x98, 0x98, 0x80, 0x42, 0x00, 0xA6, - 0xBA, 0x03, 0x07, 0xA6, 0xC8, 0x03, 0xD2, 0x83, 0x76, 0x95, 0xBE, 0x83, 0x00, 0x33, 0x26, 0x00, - 0xB2, 0x88, 0x38, 0x2B, 0x80, 0x32, 0x80, 0x36, 0x04, 0x23, 0xA0, 0x01, 0x12, 0x23, 0xA1, 0x01, - 0x0C, 0x84, 0x06, 0xF0, 0x06, 0xA4, 0xF0, 0x03, 0x80, 0x6B, 0x05, 0x23, 0x83, 0x03, 0x80, 0x63, - 0x03, 0xA6, 0x0A, 0x04, 0x07, 0xA6, 0x02, 0x04, 0x06, 0xA6, 0x06, 0x04, 0x00, 0x33, 0x17, 0x00, - 0xB2, 0x88, 0x76, 0x95, 0xF0, 0x83, 0x42, 0x96, 0xF0, 0x83, 0x1A, 0x84, 0x06, 0xF0, 0x06, 0xA4, - 0x1A, 0x04, 0x80, 0x6B, 0x05, 0x23, 0x83, 0x03, 0x80, 0x63, 0xB6, 0x2D, 0x03, 0xA6, 0x36, 0x04, - 0x07, 0xA6, 0x2E, 0x04, 0x06, 0xA6, 0x32, 0x04, 0x00, 0x33, 0x30, 0x00, 0xB2, 0x88, 0x76, 0x95, - 0x1A, 0x84, 0x42, 0x96, 0x1A, 0x84, 0x1D, 0x01, 0x06, 0xCC, 0x00, 0x33, 0x00, 0x84, 0xC0, 0x20, - 0x00, 0x23, 0xEA, 0x00, 0x81, 0x62, 0xA2, 0x0D, 0x80, 0x63, 0x07, 0xA6, 0x54, 0x04, 0x00, 0x33, - 0x18, 0x00, 0xB2, 0x88, 0x03, 0x03, 0x80, 0x63, 0xA3, 0x01, 0x07, 0xA4, 0x5E, 0x04, 0x23, 0x01, - 0x00, 0xA2, 0x80, 0x04, 0x0A, 0xA0, 0x70, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1D, 0x00, 0xB2, 0x88, - 0x0B, 0xA0, 0x7C, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1E, 0x00, 0xB2, 0x88, 0x42, 0x23, 0xE8, 0x88, - 0x00, 0x23, 0x22, 0xA3, 0xE0, 0x04, 0x08, 0x23, 0x22, 0xA3, 0x9C, 0x04, 0x28, 0x23, 0x22, 0xA3, - 0xA8, 0x04, 0x02, 0x23, 0x22, 0xA3, 0xBE, 0x04, 0x42, 0x23, 0xE8, 0x88, 0x4A, 0x00, 0x06, 0x61, - 0x00, 0xA0, 0xA8, 0x04, 0x45, 0x23, 0xE8, 0x88, 0xF6, 0x97, 0x00, 0xA2, 0xBA, 0x04, 0xA4, 0x98, - 0x00, 0x33, 0x00, 0x82, 0xC0, 0x20, 0x81, 0x62, 0xF0, 0x81, 0x47, 0x23, 0xE8, 0x88, 0x04, 0x01, - 0x0B, 0xDE, 0xF6, 0x97, 0xA4, 0x98, 0x00, 0x33, 0x00, 0x81, 0xC0, 0x20, 0x81, 0x62, 0x14, 0x01, - 0x00, 0xA0, 0x08, 0x02, 0x43, 0x23, 0xE8, 0x88, 0x04, 0x23, 0xA0, 0x01, 0x44, 0x23, 0xA1, 0x01, - 0x80, 0x73, 0x4D, 0x00, 0x03, 0xA3, 0xEE, 0x04, 0x00, 0x33, 0x27, 0x00, 0xB2, 0x88, 0x04, 0x01, - 0x04, 0xDC, 0x02, 0x23, 0xA2, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xF6, 0x97, 0x20, 0x95, 0x4B, 0x00, - 0xF6, 0x00, 0x4F, 0x04, 0x4F, 0x00, 0x00, 0xA3, 0x1C, 0x05, 0x00, 0x05, 0x76, 0x00, 0x06, 0x61, - 0x00, 0xA2, 0x16, 0x05, 0x04, 0x85, 0x38, 0x97, 0xCD, 0x04, 0x1E, 0x85, 0x48, 0x04, 0x84, 0x80, - 0x02, 0x01, 0x03, 0xDA, 0x80, 0x23, 0x82, 0x01, 0x2E, 0x85, 0x02, 0x23, 0xA0, 0x01, 0x4A, 0x00, - 0x06, 0x61, 0x00, 0xA2, 0x3A, 0x05, 0x1D, 0x01, 0x04, 0xD6, 0xFF, 0x23, 0x86, 0x41, 0x4B, 0x60, - 0xCB, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x49, 0x00, 0x81, 0x01, 0x04, 0x01, 0x02, 0xC8, 0x30, 0x01, - 0x80, 0x01, 0xF7, 0x04, 0x03, 0x01, 0x49, 0x04, 0x80, 0x01, 0xC9, 0x00, 0x00, 0x05, 0x00, 0x01, - 0xFF, 0xA0, 0x5A, 0x05, 0x77, 0x04, 0x01, 0x23, 0xEA, 0x00, 0x5D, 0x00, 0xFE, 0xC7, 0x00, 0x62, - 0x00, 0x23, 0xEA, 0x00, 0x00, 0x63, 0x07, 0xA4, 0xDA, 0x05, 0x03, 0x03, 0x02, 0xA0, 0x88, 0x05, - 0xD6, 0x85, 0x00, 0x33, 0x2D, 0x00, 0xB2, 0x88, 0x04, 0xA0, 0xAE, 0x05, 0x80, 0x63, 0x4A, 0x00, - 0x06, 0x61, 0x00, 0xA2, 0x9A, 0x05, 0x1D, 0x01, 0x06, 0xD6, 0x02, 0x23, 0x02, 0x41, 0x82, 0x01, - 0x50, 0x00, 0x54, 0x97, 0xFE, 0x84, 0x04, 0x23, 0x02, 0x41, 0x82, 0x01, 0xFE, 0x84, 0x08, 0xA0, - 0xB4, 0x05, 0xD6, 0x85, 0x03, 0xA0, 0xBA, 0x05, 0xD6, 0x85, 0x01, 0xA0, 0xC4, 0x05, 0x88, 0x00, - 0x80, 0x63, 0xB2, 0x86, 0x07, 0xA0, 0xD0, 0x05, 0x06, 0x23, 0x5A, 0x98, 0x48, 0x23, 0xE8, 0x88, - 0x07, 0x23, 0x80, 0x00, 0xF8, 0x86, 0x80, 0x63, 0x76, 0x85, 0x00, 0x63, 0x4A, 0x00, 0x06, 0x61, - 0x00, 0xA2, 0x18, 0x06, 0x1D, 0x01, 0x18, 0xD4, 0xC0, 0x23, 0x07, 0x41, 0x83, 0x03, 0x80, 0x63, - 0x06, 0xA6, 0xFA, 0x05, 0x00, 0x33, 0x37, 0x00, 0xB2, 0x88, 0x1D, 0x01, 0x02, 0xD6, 0x46, 0x23, - 0xE8, 0x88, 0x63, 0x60, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x12, 0x06, 0x00, 0x33, 0x38, 0x00, - 0xB2, 0x88, 0xEF, 0x04, 0x6F, 0x00, 0x00, 0x63, 0x4B, 0x00, 0x06, 0x41, 0xCB, 0x00, 0x52, 0x00, - 0x06, 0x61, 0x00, 0xA2, 0x30, 0x06, 0x1D, 0x01, 0x03, 0xCA, 0xC0, 0x23, 0x07, 0x41, 0x00, 0x63, - 0x1D, 0x01, 0x04, 0xCC, 0x00, 0x33, 0x00, 0x83, 0xC0, 0x20, 0x81, 0x62, 0x80, 0x23, 0x07, 0x41, - 0x00, 0x63, 0x80, 0x67, 0x08, 0x23, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x06, 0xA6, 0x5E, 0x06, - 0x07, 0xA6, 0x76, 0x05, 0x02, 0xA6, 0xEC, 0x06, 0x00, 0x33, 0x39, 0x00, 0xB2, 0x88, 0x00, 0x00, - 0x01, 0xA0, 0x06, 0x07, 0xDC, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x72, 0x06, 0x07, 0xA6, - 0x76, 0x05, 0x00, 0x00, 0x01, 0xA0, 0x06, 0x07, 0x00, 0x2B, 0x40, 0x0E, 0x80, 0x63, 0x01, 0x00, - 0x06, 0xA6, 0x8E, 0x06, 0x07, 0xA6, 0x76, 0x05, 0x00, 0x33, 0x3A, 0x00, 0xB2, 0x88, 0x40, 0x0E, - 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, 0x80, 0x06, 0x06, 0xA6, 0xA6, 0x06, 0x07, 0xA6, 0x76, 0x05, - 0x00, 0x33, 0x3B, 0x00, 0xB2, 0x88, 0x80, 0x67, 0x40, 0x0E, 0x80, 0x63, 0x07, 0xA6, 0x76, 0x05, - 0x00, 0x63, 0x07, 0xA6, 0xBC, 0x06, 0x00, 0x33, 0x2A, 0x00, 0xB2, 0x88, 0x03, 0x03, 0x80, 0x63, - 0x89, 0x00, 0x0A, 0x2B, 0x07, 0xA6, 0xCE, 0x06, 0x00, 0x33, 0x29, 0x00, 0xB2, 0x88, 0x00, 0x43, - 0x00, 0xA2, 0xDA, 0x06, 0xC0, 0x0E, 0x80, 0x63, 0xC4, 0x86, 0xC0, 0x0E, 0x00, 0x33, 0x00, 0x80, - 0xC0, 0x20, 0x81, 0x62, 0x04, 0x01, 0x08, 0xDA, 0x80, 0x63, 0x76, 0x85, 0x80, 0x67, 0x00, 0x33, - 0x00, 0x40, 0xC0, 0x20, 0x81, 0x62, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x6A, 0x06, - 0x00, 0x33, 0x2C, 0x00, 0xB2, 0x88, 0x0C, 0xA2, 0x20, 0x07, 0xDC, 0x95, 0x83, 0x03, 0x80, 0x63, - 0x06, 0xA6, 0x1E, 0x07, 0x07, 0xA6, 0x76, 0x05, 0x00, 0x33, 0x3D, 0x00, 0xB2, 0x88, 0x00, 0x00, - 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x0C, 0xA0, 0x36, 0x07, 0x07, 0xA6, 0x76, 0x05, 0xBF, 0x23, - 0x04, 0x61, 0x84, 0x01, 0xE0, 0x84, 0x00, 0x63, 0xF0, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x00, 0x01, - 0xF2, 0x00, 0x01, 0x05, 0x80, 0x01, 0x72, 0x04, 0x71, 0x00, 0x81, 0x01, 0x70, 0x04, 0x80, 0x05, - 0x81, 0x05, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x70, 0x00, - 0x81, 0x01, 0x70, 0x04, 0x71, 0x00, 0x81, 0x01, 0x72, 0x00, 0x80, 0x01, 0x71, 0x04, 0x70, 0x00, - 0x80, 0x01, 0x70, 0x04, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x00, 0x01, 0xF1, 0x00, - 0x70, 0x00, 0x80, 0x01, 0x70, 0x04, 0x71, 0x00, 0x80, 0x01, 0x72, 0x00, 0x81, 0x01, 0x71, 0x04, - 0x70, 0x00, 0x81, 0x01, 0x70, 0x04, 0x00, 0x63, 0x00, 0x23, 0xB3, 0x01, 0x83, 0x05, 0xA3, 0x01, - 0xA2, 0x01, 0xA1, 0x01, 0x01, 0x23, 0xA0, 0x01, 0x00, 0x01, 0xC8, 0x00, 0x03, 0xA1, 0xB6, 0x07, - 0x00, 0x33, 0x07, 0x00, 0xB2, 0x88, 0x80, 0x05, 0x81, 0x05, 0x04, 0x01, 0x11, 0xC8, 0x48, 0x00, - 0xB0, 0x01, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x05, 0x01, 0x48, 0x04, 0x00, 0x43, 0x00, 0xA2, - 0xD6, 0x07, 0x00, 0x05, 0xCC, 0x87, 0x00, 0x01, 0xC8, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x05, 0x05, - 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x80, 0x43, 0x76, 0x08, - 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, - 0x00, 0xA0, 0x06, 0x08, 0x08, 0x88, 0x00, 0x43, 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, - 0xF3, 0x04, 0x00, 0x23, 0xF4, 0x00, 0x74, 0x00, 0x80, 0x43, 0xF4, 0x00, 0xCF, 0x40, 0x00, 0xA2, - 0x36, 0x08, 0x74, 0x04, 0x02, 0x01, 0xF7, 0xC9, 0xF6, 0xD9, 0x00, 0x01, 0x01, 0xA1, 0x16, 0x08, - 0xF6, 0x97, 0x20, 0x95, 0x16, 0x88, 0x73, 0x04, 0x00, 0x63, 0xF3, 0x04, 0x75, 0x04, 0x4C, 0x88, - 0x02, 0x01, 0x04, 0xD8, 0x38, 0x97, 0xF6, 0x97, 0x20, 0x95, 0x3C, 0x88, 0x75, 0x00, 0x00, 0xA3, - 0x56, 0x08, 0x00, 0x05, 0x40, 0x88, 0x73, 0x04, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, - 0x68, 0x08, 0x00, 0x33, 0x3E, 0x00, 0xB2, 0x88, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, - 0x38, 0x2B, 0x8E, 0x88, 0x38, 0x2B, 0x84, 0x88, 0x32, 0x09, 0x31, 0x05, 0x84, 0x98, 0x05, 0x05, - 0xB2, 0x09, 0x00, 0x63, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x3E, 0x00, 0x63, 0x80, 0x32, - 0x80, 0x36, 0x80, 0x3A, 0x80, 0x3E, 0x00, 0x63, 0x38, 0x2B, 0x40, 0x32, 0x40, 0x36, 0x40, 0x3A, - 0x40, 0x3E, 0x00, 0x63, 0x5A, 0x20, 0xC9, 0x40, 0x00, 0xA0, 0xA4, 0x08, 0x5D, 0x00, 0xFE, 0xC3, - 0x00, 0x63, 0x80, 0x73, 0xE6, 0x20, 0x02, 0x23, 0xE8, 0x00, 0x82, 0x73, 0xFF, 0xFD, 0x80, 0x73, - 0x13, 0x23, 0xE8, 0x88, 0x66, 0x20, 0xC0, 0x20, 0x04, 0x23, 0xA0, 0x01, 0xA1, 0x23, 0xA1, 0x01, - 0x81, 0x62, 0xD2, 0x88, 0x80, 0x73, 0x80, 0x77, 0x68, 0x00, 0x00, 0xA2, 0x80, 0x00, 0x03, 0xC2, - 0xF1, 0xC7, 0x41, 0x23, 0xE8, 0x88, 0x11, 0x23, 0xA1, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xE0, 0x84, - + 0x11, 0x23, 0xF6, 0x88, 0x04, 0x98, 0xF4, 0x80, 0x80, 0x73, 0x80, 0x77, 0x07, 0xA4, 0x32, 0x02, + 0x7C, 0x95, 0x06, 0xA6, 0x3C, 0x02, 0x03, 0xA6, 0x4C, 0x04, 0xC0, 0x88, 0x04, 0x01, 0x03, 0xD8, + 0xB2, 0x98, 0x6A, 0x96, 0x4E, 0x82, 0xFE, 0x95, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0xB6, 0x2D, + 0x02, 0xA6, 0x78, 0x02, 0x07, 0xA6, 0x66, 0x02, 0x06, 0xA6, 0x6A, 0x02, 0x03, 0xA6, 0x6E, 0x02, + 0x00, 0x33, 0x10, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0x50, 0x82, 0x60, 0x96, 0x50, 0x82, 0x04, 0x23, + 0xA0, 0x01, 0x14, 0x23, 0xA1, 0x01, 0x3C, 0x84, 0x04, 0x01, 0x0C, 0xDC, 0xE0, 0x23, 0x25, 0x61, + 0xEF, 0x00, 0x14, 0x01, 0x4F, 0x04, 0xA8, 0x01, 0x6F, 0x00, 0xA5, 0x01, 0x03, 0x23, 0xA4, 0x01, + 0x06, 0x23, 0x9C, 0x01, 0x24, 0x2B, 0x1C, 0x01, 0x02, 0xA6, 0xB6, 0x02, 0x07, 0xA6, 0x66, 0x02, + 0x06, 0xA6, 0x6A, 0x02, 0x03, 0xA6, 0x20, 0x04, 0x01, 0xA6, 0xC0, 0x02, 0x00, 0xA6, 0xC0, 0x02, + 0x00, 0x33, 0x12, 0x00, 0xC0, 0x88, 0x00, 0x0E, 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, 0x98, 0x02, + 0x4D, 0x04, 0x04, 0x01, 0x0B, 0xDC, 0xE7, 0x23, 0x04, 0x61, 0x84, 0x01, 0x10, 0x31, 0x12, 0x35, + 0x14, 0x01, 0xEC, 0x00, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, 0xF6, 0x82, 0x18, 0x23, 0x04, 0x61, + 0x18, 0xA0, 0xEE, 0x02, 0x04, 0x01, 0x9C, 0xC8, 0x00, 0x33, 0x1F, 0x00, 0xC0, 0x88, 0x08, 0x31, + 0x0A, 0x35, 0x0C, 0x39, 0x0E, 0x3D, 0x7E, 0x98, 0xB6, 0x2D, 0x01, 0xA6, 0x20, 0x03, 0x00, 0xA6, + 0x20, 0x03, 0x07, 0xA6, 0x18, 0x03, 0x06, 0xA6, 0x1C, 0x03, 0x03, 0xA6, 0x20, 0x04, 0x02, 0xA6, + 0x78, 0x02, 0x00, 0x33, 0x33, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0xFA, 0x82, 0x60, 0x96, 0xFA, 0x82, + 0x82, 0x98, 0x80, 0x42, 0x7E, 0x98, 0x60, 0xE4, 0x04, 0x01, 0x29, 0xC8, 0x31, 0x05, 0x07, 0x01, + 0x00, 0xA2, 0x60, 0x03, 0x00, 0x43, 0x87, 0x01, 0x05, 0x05, 0x86, 0x98, 0x7E, 0x98, 0x00, 0xA6, + 0x22, 0x03, 0x07, 0xA6, 0x58, 0x03, 0x03, 0xA6, 0x3C, 0x04, 0x06, 0xA6, 0x5C, 0x03, 0x01, 0xA6, + 0x22, 0x03, 0x00, 0x33, 0x25, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0x3E, 0x83, 0x60, 0x96, 0x3E, 0x83, + 0x04, 0x01, 0x0C, 0xCE, 0x03, 0xC8, 0x00, 0x33, 0x42, 0x00, 0xC0, 0x88, 0x00, 0x01, 0x05, 0x05, + 0xFF, 0xA2, 0x7E, 0x03, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x3A, 0x83, 0x05, 0x05, 0x15, 0x01, + 0x00, 0xA2, 0x9E, 0x03, 0xEC, 0x00, 0x6E, 0x00, 0x95, 0x01, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, + 0x01, 0xA6, 0x9A, 0x03, 0x00, 0xA6, 0x9A, 0x03, 0x12, 0x84, 0x80, 0x42, 0x7E, 0x98, 0x01, 0xA6, + 0xA8, 0x03, 0x00, 0xA6, 0xC0, 0x03, 0x12, 0x84, 0xA6, 0x98, 0x80, 0x42, 0x01, 0xA6, 0xA8, 0x03, + 0x07, 0xA6, 0xB6, 0x03, 0xD8, 0x83, 0x7C, 0x95, 0xAC, 0x83, 0x00, 0x33, 0x2F, 0x00, 0xC0, 0x88, + 0xA6, 0x98, 0x80, 0x42, 0x00, 0xA6, 0xC0, 0x03, 0x07, 0xA6, 0xCE, 0x03, 0xD8, 0x83, 0x7C, 0x95, + 0xC4, 0x83, 0x00, 0x33, 0x26, 0x00, 0xC0, 0x88, 0x38, 0x2B, 0x80, 0x32, 0x80, 0x36, 0x04, 0x23, + 0xA0, 0x01, 0x12, 0x23, 0xA1, 0x01, 0x12, 0x84, 0x06, 0xF0, 0x06, 0xA4, 0xF6, 0x03, 0x80, 0x6B, + 0x05, 0x23, 0x83, 0x03, 0x80, 0x63, 0x03, 0xA6, 0x10, 0x04, 0x07, 0xA6, 0x08, 0x04, 0x06, 0xA6, + 0x0C, 0x04, 0x00, 0x33, 0x17, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0xF6, 0x83, 0x60, 0x96, 0xF6, 0x83, + 0x20, 0x84, 0x06, 0xF0, 0x06, 0xA4, 0x20, 0x04, 0x80, 0x6B, 0x05, 0x23, 0x83, 0x03, 0x80, 0x63, + 0xB6, 0x2D, 0x03, 0xA6, 0x3C, 0x04, 0x07, 0xA6, 0x34, 0x04, 0x06, 0xA6, 0x38, 0x04, 0x00, 0x33, + 0x30, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0x20, 0x84, 0x60, 0x96, 0x20, 0x84, 0x1D, 0x01, 0x06, 0xCC, + 0x00, 0x33, 0x00, 0x84, 0xC0, 0x20, 0x00, 0x23, 0xEA, 0x00, 0x81, 0x62, 0xA2, 0x0D, 0x80, 0x63, + 0x07, 0xA6, 0x5A, 0x04, 0x00, 0x33, 0x18, 0x00, 0xC0, 0x88, 0x03, 0x03, 0x80, 0x63, 0xA3, 0x01, + 0x07, 0xA4, 0x64, 0x04, 0x23, 0x01, 0x00, 0xA2, 0x86, 0x04, 0x0A, 0xA0, 0x76, 0x04, 0xE0, 0x00, + 0x00, 0x33, 0x1D, 0x00, 0xC0, 0x88, 0x0B, 0xA0, 0x82, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1E, 0x00, + 0xC0, 0x88, 0x42, 0x23, 0xF6, 0x88, 0x00, 0x23, 0x22, 0xA3, 0xE6, 0x04, 0x08, 0x23, 0x22, 0xA3, + 0xA2, 0x04, 0x28, 0x23, 0x22, 0xA3, 0xAE, 0x04, 0x02, 0x23, 0x22, 0xA3, 0xC4, 0x04, 0x42, 0x23, + 0xF6, 0x88, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xAE, 0x04, 0x45, 0x23, 0xF6, 0x88, 0x04, 0x98, + 0x00, 0xA2, 0xC0, 0x04, 0xB2, 0x98, 0x00, 0x33, 0x00, 0x82, 0xC0, 0x20, 0x81, 0x62, 0xF0, 0x81, + 0x47, 0x23, 0xF6, 0x88, 0x04, 0x01, 0x0B, 0xDE, 0x04, 0x98, 0xB2, 0x98, 0x00, 0x33, 0x00, 0x81, + 0xC0, 0x20, 0x81, 0x62, 0x14, 0x01, 0x00, 0xA0, 0x08, 0x02, 0x43, 0x23, 0xF6, 0x88, 0x04, 0x23, + 0xA0, 0x01, 0x44, 0x23, 0xA1, 0x01, 0x80, 0x73, 0x4D, 0x00, 0x03, 0xA3, 0xF4, 0x04, 0x00, 0x33, + 0x27, 0x00, 0xC0, 0x88, 0x04, 0x01, 0x04, 0xDC, 0x02, 0x23, 0xA2, 0x01, 0x04, 0x23, 0xA0, 0x01, + 0x04, 0x98, 0x26, 0x95, 0x4B, 0x00, 0xF6, 0x00, 0x4F, 0x04, 0x4F, 0x00, 0x00, 0xA3, 0x22, 0x05, + 0x00, 0x05, 0x76, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x1C, 0x05, 0x0A, 0x85, 0x46, 0x97, 0xCD, 0x04, + 0x24, 0x85, 0x48, 0x04, 0x84, 0x80, 0x02, 0x01, 0x03, 0xDA, 0x80, 0x23, 0x82, 0x01, 0x34, 0x85, + 0x02, 0x23, 0xA0, 0x01, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x40, 0x05, 0x1D, 0x01, 0x04, 0xD6, + 0xFF, 0x23, 0x86, 0x41, 0x4B, 0x60, 0xCB, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x49, 0x00, 0x81, 0x01, + 0x04, 0x01, 0x02, 0xC8, 0x30, 0x01, 0x80, 0x01, 0xF7, 0x04, 0x03, 0x01, 0x49, 0x04, 0x80, 0x01, + 0xC9, 0x00, 0x00, 0x05, 0x00, 0x01, 0xFF, 0xA0, 0x60, 0x05, 0x77, 0x04, 0x01, 0x23, 0xEA, 0x00, + 0x5D, 0x00, 0xFE, 0xC7, 0x00, 0x62, 0x00, 0x23, 0xEA, 0x00, 0x00, 0x63, 0x07, 0xA4, 0xF8, 0x05, + 0x03, 0x03, 0x02, 0xA0, 0x8E, 0x05, 0xF4, 0x85, 0x00, 0x33, 0x2D, 0x00, 0xC0, 0x88, 0x04, 0xA0, + 0xB8, 0x05, 0x80, 0x63, 0x00, 0x23, 0xDF, 0x00, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0xA4, 0x05, + 0x1D, 0x01, 0x06, 0xD6, 0x02, 0x23, 0x02, 0x41, 0x82, 0x01, 0x50, 0x00, 0x62, 0x97, 0x04, 0x85, + 0x04, 0x23, 0x02, 0x41, 0x82, 0x01, 0x04, 0x85, 0x08, 0xA0, 0xBE, 0x05, 0xF4, 0x85, 0x03, 0xA0, + 0xC4, 0x05, 0xF4, 0x85, 0x01, 0xA0, 0xCE, 0x05, 0x88, 0x00, 0x80, 0x63, 0xCC, 0x86, 0x07, 0xA0, + 0xEE, 0x05, 0x5F, 0x00, 0x00, 0x2B, 0xDF, 0x08, 0x00, 0xA2, 0xE6, 0x05, 0x80, 0x67, 0x80, 0x63, + 0x01, 0xA2, 0x7A, 0x06, 0x7C, 0x85, 0x06, 0x23, 0x68, 0x98, 0x48, 0x23, 0xF6, 0x88, 0x07, 0x23, + 0x80, 0x00, 0x06, 0x87, 0x80, 0x63, 0x7C, 0x85, 0x00, 0x23, 0xDF, 0x00, 0x00, 0x63, 0x4A, 0x00, + 0x06, 0x61, 0x00, 0xA2, 0x36, 0x06, 0x1D, 0x01, 0x16, 0xD4, 0xC0, 0x23, 0x07, 0x41, 0x83, 0x03, + 0x80, 0x63, 0x06, 0xA6, 0x1C, 0x06, 0x00, 0x33, 0x37, 0x00, 0xC0, 0x88, 0x1D, 0x01, 0x01, 0xD6, + 0x20, 0x23, 0x63, 0x60, 0x83, 0x03, 0x80, 0x63, 0x02, 0x23, 0xDF, 0x00, 0x07, 0xA6, 0x7C, 0x05, + 0xEF, 0x04, 0x6F, 0x00, 0x00, 0x63, 0x4B, 0x00, 0x06, 0x41, 0xCB, 0x00, 0x52, 0x00, 0x06, 0x61, + 0x00, 0xA2, 0x4E, 0x06, 0x1D, 0x01, 0x03, 0xCA, 0xC0, 0x23, 0x07, 0x41, 0x00, 0x63, 0x1D, 0x01, + 0x04, 0xCC, 0x00, 0x33, 0x00, 0x83, 0xC0, 0x20, 0x81, 0x62, 0x80, 0x23, 0x07, 0x41, 0x00, 0x63, + 0x80, 0x67, 0x08, 0x23, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x01, 0x23, 0xDF, 0x00, 0x06, 0xA6, + 0x84, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x80, 0x67, 0x80, 0x63, 0x00, 0x33, 0x00, 0x40, 0xC0, 0x20, + 0x81, 0x62, 0x00, 0x63, 0x00, 0x00, 0xFE, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x94, 0x06, + 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x00, 0x01, 0xA0, 0x14, 0x07, 0x00, 0x2B, 0x40, 0x0E, 0x80, 0x63, + 0x01, 0x00, 0x06, 0xA6, 0xAA, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x40, 0x0E, 0x80, 0x63, 0x00, 0x43, + 0x00, 0xA0, 0xA2, 0x06, 0x06, 0xA6, 0xBC, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x80, 0x67, 0x40, 0x0E, + 0x80, 0x63, 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x23, 0xDF, 0x00, 0x00, 0x63, 0x07, 0xA6, 0xD6, 0x06, + 0x00, 0x33, 0x2A, 0x00, 0xC0, 0x88, 0x03, 0x03, 0x80, 0x63, 0x89, 0x00, 0x0A, 0x2B, 0x07, 0xA6, + 0xE8, 0x06, 0x00, 0x33, 0x29, 0x00, 0xC0, 0x88, 0x00, 0x43, 0x00, 0xA2, 0xF4, 0x06, 0xC0, 0x0E, + 0x80, 0x63, 0xDE, 0x86, 0xC0, 0x0E, 0x00, 0x33, 0x00, 0x80, 0xC0, 0x20, 0x81, 0x62, 0x04, 0x01, + 0x02, 0xDA, 0x80, 0x63, 0x7C, 0x85, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x8C, 0x06, 0x00, 0x33, + 0x2C, 0x00, 0xC0, 0x88, 0x0C, 0xA2, 0x2E, 0x07, 0xFE, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, + 0x2C, 0x07, 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x33, 0x3D, 0x00, 0xC0, 0x88, 0x00, 0x00, 0x80, 0x67, + 0x83, 0x03, 0x80, 0x63, 0x0C, 0xA0, 0x44, 0x07, 0x07, 0xA6, 0x7C, 0x05, 0xBF, 0x23, 0x04, 0x61, + 0x84, 0x01, 0xE6, 0x84, 0x00, 0x63, 0xF0, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x00, 0x01, 0xF2, 0x00, + 0x01, 0x05, 0x80, 0x01, 0x72, 0x04, 0x71, 0x00, 0x81, 0x01, 0x70, 0x04, 0x80, 0x05, 0x81, 0x05, + 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x70, 0x00, 0x81, 0x01, + 0x70, 0x04, 0x71, 0x00, 0x81, 0x01, 0x72, 0x00, 0x80, 0x01, 0x71, 0x04, 0x70, 0x00, 0x80, 0x01, + 0x70, 0x04, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x00, 0x01, 0xF1, 0x00, 0x70, 0x00, + 0x80, 0x01, 0x70, 0x04, 0x71, 0x00, 0x80, 0x01, 0x72, 0x00, 0x81, 0x01, 0x71, 0x04, 0x70, 0x00, + 0x81, 0x01, 0x70, 0x04, 0x00, 0x63, 0x00, 0x23, 0xB3, 0x01, 0x83, 0x05, 0xA3, 0x01, 0xA2, 0x01, + 0xA1, 0x01, 0x01, 0x23, 0xA0, 0x01, 0x00, 0x01, 0xC8, 0x00, 0x03, 0xA1, 0xC4, 0x07, 0x00, 0x33, + 0x07, 0x00, 0xC0, 0x88, 0x80, 0x05, 0x81, 0x05, 0x04, 0x01, 0x11, 0xC8, 0x48, 0x00, 0xB0, 0x01, + 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x05, 0x01, 0x48, 0x04, 0x00, 0x43, 0x00, 0xA2, 0xE4, 0x07, + 0x00, 0x05, 0xDA, 0x87, 0x00, 0x01, 0xC8, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x05, 0x05, 0x00, 0x63, + 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x80, 0x43, 0x76, 0x08, 0x80, 0x02, + 0x77, 0x04, 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x00, 0xA0, + 0x14, 0x08, 0x16, 0x88, 0x00, 0x43, 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF3, 0x04, + 0x00, 0x23, 0xF4, 0x00, 0x74, 0x00, 0x80, 0x43, 0xF4, 0x00, 0xCF, 0x40, 0x00, 0xA2, 0x44, 0x08, + 0x74, 0x04, 0x02, 0x01, 0xF7, 0xC9, 0xF6, 0xD9, 0x00, 0x01, 0x01, 0xA1, 0x24, 0x08, 0x04, 0x98, + 0x26, 0x95, 0x24, 0x88, 0x73, 0x04, 0x00, 0x63, 0xF3, 0x04, 0x75, 0x04, 0x5A, 0x88, 0x02, 0x01, + 0x04, 0xD8, 0x46, 0x97, 0x04, 0x98, 0x26, 0x95, 0x4A, 0x88, 0x75, 0x00, 0x00, 0xA3, 0x64, 0x08, + 0x00, 0x05, 0x4E, 0x88, 0x73, 0x04, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x76, 0x08, + 0x00, 0x33, 0x3E, 0x00, 0xC0, 0x88, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x38, 0x2B, + 0x9C, 0x88, 0x38, 0x2B, 0x92, 0x88, 0x32, 0x09, 0x31, 0x05, 0x92, 0x98, 0x05, 0x05, 0xB2, 0x09, + 0x00, 0x63, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x3E, 0x00, 0x63, 0x80, 0x32, 0x80, 0x36, + 0x80, 0x3A, 0x80, 0x3E, 0x00, 0x63, 0x38, 0x2B, 0x40, 0x32, 0x40, 0x36, 0x40, 0x3A, 0x40, 0x3E, + 0x00, 0x63, 0x5A, 0x20, 0xC9, 0x40, 0x00, 0xA0, 0xB2, 0x08, 0x5D, 0x00, 0xFE, 0xC3, 0x00, 0x63, + 0x80, 0x73, 0xE6, 0x20, 0x02, 0x23, 0xE8, 0x00, 0x82, 0x73, 0xFF, 0xFD, 0x80, 0x73, 0x13, 0x23, + 0xF6, 0x88, 0x66, 0x20, 0xC0, 0x20, 0x04, 0x23, 0xA0, 0x01, 0xA1, 0x23, 0xA1, 0x01, 0x81, 0x62, + 0xE0, 0x88, 0x80, 0x73, 0x80, 0x77, 0x68, 0x00, 0x00, 0xA2, 0x80, 0x00, 0x03, 0xC2, 0xF1, 0xC7, + 0x41, 0x23, 0xF6, 0x88, 0x11, 0x23, 0xA1, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xE6, 0x84, }; STATIC ushort _asc_mcode_size ASC_INITDATA = sizeof(_asc_mcode_buf); -STATIC ulong _asc_mcode_chksum ASC_INITDATA = 0x012A727FUL ; +STATIC ulong _asc_mcode_chksum ASC_INITDATA = 0x012B5442UL; #define ASC_SYN_OFFSET_ONE_DISABLE_LIST 16 STATIC uchar _syn_offset_one_disable_cmd[ASC_SYN_OFFSET_ONE_DISABLE_LIST] = @@ -12232,7 +12343,7 @@ AscWaitTixISRDone( if ((cur_req = asc_dvc->cur_dvc_qng[tid_no]) == 0) { break; } - DvcSleepMilliSecond(100L); + DvcSleepMilliSecond(1000L); if (asc_dvc->cur_dvc_qng[tid_no] == cur_req) { break; } @@ -12850,11 +12961,12 @@ AscInitAscDvcVar( asc_dvc->cfg->can_tagged_qng = 0 ; asc_dvc->cfg->cmd_qng_enabled = 0; asc_dvc->dvc_cntl = ASC_DEF_DVC_CNTL; - asc_dvc->init_sdtr = ASC_SCSI_WIDTH_BIT_SET; + asc_dvc->init_sdtr = 0; asc_dvc->max_total_qng = ASC_DEF_MAX_TOTAL_QNG; asc_dvc->scsi_reset_wait = 3; asc_dvc->start_motor = ASC_SCSI_WIDTH_BIT_SET; asc_dvc->max_dma_count = AscGetMaxDmaCount(asc_dvc->bus_type); + asc_dvc->cfg->sdtr_enable = ASC_SCSI_WIDTH_BIT_SET; asc_dvc->cfg->disc_enable = ASC_SCSI_WIDTH_BIT_SET; asc_dvc->cfg->chip_scsi_id = ASC_DEF_CHIP_SCSI_ID; asc_dvc->cfg->lib_serial_no = ASC_LIB_SERIAL_NUMBER; @@ -13009,7 +13121,7 @@ AscInitFromEEP( warn_code |= ASC_WARN_EEPROM_CHKSUM ; } } - asc_dvc->init_sdtr = eep_config->init_sdtr; + asc_dvc->cfg->sdtr_enable = eep_config->init_sdtr ; asc_dvc->cfg->disc_enable = eep_config->disc_enable; asc_dvc->cfg->cmd_qng_enabled = eep_config->use_cmd_qng; asc_dvc->cfg->isa_dma_speed = eep_config->isa_dma_speed; @@ -13127,47 +13239,6 @@ AscInitMicroCodeVar( return (warn_code); } -STATIC void -AscInitPollIsrCallBack( - ASC_DVC_VAR asc_ptr_type * asc_dvc, - ASC_QDONE_INFO * scsi_done_q -) -{ - ASC_SCSI_REQ_Q *scsiq_req; - ASC_ISR_CALLBACK asc_isr_callback; - uchar cp_sen_len; - uchar i; - - ASC_DBG(1, "AscInitPollIsrCallBack: begin\n"); - if ((scsi_done_q->d2.flag & ASC_FLAG_SCSIQ_REQ) != 0) { - scsiq_req = (ASC_SCSI_REQ_Q *) scsi_done_q->d2.srb_ptr; - scsiq_req->r3.done_stat = scsi_done_q->d3.done_stat; - scsiq_req->r3.host_stat = scsi_done_q->d3.host_stat; - scsiq_req->r3.scsi_stat = scsi_done_q->d3.scsi_stat; - scsiq_req->r3.scsi_msg = scsi_done_q->d3.scsi_msg; - ASC_DBG4(1, "AscInitPollIsrCallBack: done_stat %x, host_stat %x, scsi_stat %x, scsi_msg %x\n", - scsi_done_q->d3.done_stat, scsi_done_q->d3.host_stat, - scsi_done_q->d3.scsi_stat, scsi_done_q->d3.scsi_msg); - if ((scsi_done_q->d3.scsi_stat == SS_CHK_CONDITION) && - (scsi_done_q->d3.host_stat == 0)) { - cp_sen_len = (uchar) ASC_MIN_SENSE_LEN; - if (scsiq_req->r1.sense_len < ASC_MIN_SENSE_LEN) { - cp_sen_len = (uchar) scsiq_req->r1.sense_len; - } - for (i = 0; i < cp_sen_len; i++) { - scsiq_req->sense[i] = scsiq_req->sense_ptr[i]; - } - } - } else { - if (asc_dvc->isr_callback != 0) { - asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback; - (*asc_isr_callback) (asc_dvc, scsi_done_q); - } - } - ASC_DBG(1, "AscInitPollIsrCallBack: end\n"); - return; -} - ASC_INITFUNC( STATIC int AscTestExternalLram( @@ -13437,413 +13508,124 @@ AscSetEEPConfig( return (n_error); } -STATIC int -AscInitPollBegin( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc -) -{ - PortAddr iop_base; - - iop_base = asc_dvc->iop_base; - AscDisableInterrupt(iop_base); - asc_dvc->init_state |= ASC_INIT_STATE_BEG_INQUIRY; - AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B, 0x00); - asc_dvc->use_tagged_qng = 0; - asc_dvc->cfg->can_tagged_qng = 0; - asc_dvc->saved_ptr2func = (ulong) asc_dvc->isr_callback; - asc_dvc->isr_callback = ASC_GET_PTR2FUNC(AscInitPollIsrCallBack); - return (0); -} - -STATIC int -AscInitPollEnd( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc -) -{ - PortAddr iop_base; - rint i; - - iop_base = asc_dvc->iop_base; - asc_dvc->isr_callback = (Ptr2Func) asc_dvc->saved_ptr2func; - AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B, - asc_dvc->cfg->disc_enable); - AscWriteLramByte(iop_base, ASCV_USE_TAGGED_QNG_B, - asc_dvc->use_tagged_qng); - AscWriteLramByte(iop_base, ASCV_CAN_TAGGED_QNG_B, - asc_dvc->cfg->can_tagged_qng); - for (i = 0; i <= ASC_MAX_TID; i++) { - AscWriteLramByte(iop_base, - (ushort) ((ushort) ASCV_MAX_DVC_QNG_BEG + (ushort) i), - asc_dvc->max_dvc_qng[i]); - } - AscAckInterrupt(iop_base); - AscEnableInterrupt(iop_base); - asc_dvc->init_state |= ASC_INIT_STATE_END_INQUIRY; - return (0); -} - -STATIC int -AscInitPollTarget( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - REG ASC_SCSI_INQUIRY * inq, - REG ASC_CAP_INFO * cap_info -) +STATIC void +AscAsyncFix( + ASC_DVC_VAR asc_ptr_type *asc_dvc, + uchar tid_no, + ASC_SCSI_INQUIRY *inq) { - uchar tid_no, lun; - uchar dvc_type; + uchar dvc_type; ASC_SCSI_BIT_ID_TYPE tid_bits; - int dvc_found; - int support_read_cap; - int tmp_disable_init_sdtr; - int sta; - dvc_found = 0; - tmp_disable_init_sdtr = FALSE; - tid_bits = scsiq->r1.target_id; - lun = scsiq->r1.target_lun; - tid_no = ASC_TIX_TO_TID(scsiq->r2.target_ix); - if (((asc_dvc->init_sdtr & tid_bits) != 0) && - ((asc_dvc->sdtr_done & tid_bits) == 0)) { - asc_dvc->init_sdtr &= ~tid_bits; - tmp_disable_init_sdtr = TRUE; - } - ASC_DBG(1, "AscInitPollTarget: before PollScsiInquiry\n"); - if (PollScsiInquiry(asc_dvc, scsiq, (uchar *) inq, - sizeof (ASC_SCSI_INQUIRY)) == 1) { - dvc_found = 1; - dvc_type = inq->byte0.peri_dvc_type; - if (dvc_type != SCSI_TYPE_UNKNOWN) { - support_read_cap = TRUE; - if ((dvc_type != SCSI_TYPE_DASD) - && (dvc_type != SCSI_TYPE_WORM) - && (dvc_type != SCSI_TYPE_CDROM) - && (dvc_type != SCSI_TYPE_OPTMEM)) { - asc_dvc->start_motor &= ~tid_bits; - support_read_cap = FALSE; + dvc_type = inq->byte0.peri_dvc_type; + tid_bits = ASC_TIX_TO_TARGET_ID(tid_no); + + if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ASYN_USE_SYN) { + if (!(asc_dvc->init_sdtr & tid_bits)) { + if ((dvc_type == SCSI_TYPE_CDROM) && + (AscCompareString((uchar *) inq->vendor_id, + (uchar *) "HP ", 3) == 0)) { + asc_dvc->pci_fix_asyn_xfer_always |= tid_bits; } - if (lun == 0) { - if ((inq->byte3.rsp_data_fmt >= 2) || - (inq->byte2.ansi_apr_ver >= 2)) { - if (inq->byte7.CmdQue) { - asc_dvc->cfg->can_tagged_qng |= tid_bits; - if (asc_dvc->cfg->cmd_qng_enabled & tid_bits) { - asc_dvc->use_tagged_qng |= tid_bits; - asc_dvc->max_dvc_qng[tid_no] = asc_dvc->cfg->max_tag_qng[tid_no]; - } - } - if (!inq->byte7.Sync) { - asc_dvc->init_sdtr &= ~tid_bits; - asc_dvc->sdtr_done &= ~tid_bits; - } else if (tmp_disable_init_sdtr) { - asc_dvc->init_sdtr |= tid_bits; - } - } else { - asc_dvc->init_sdtr &= ~tid_bits; - asc_dvc->sdtr_done &= ~tid_bits; - asc_dvc->use_tagged_qng &= ~tid_bits; - } + asc_dvc->pci_fix_asyn_xfer |= tid_bits; + if ((dvc_type == SCSI_TYPE_PROC) || + (dvc_type == SCSI_TYPE_SCANNER)) { + asc_dvc->pci_fix_asyn_xfer &= ~tid_bits; } - if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ASYN_USE_SYN) { - if (!(asc_dvc->init_sdtr & tid_bits)) { - if ((dvc_type == SCSI_TYPE_CDROM) && - (AscCompareString((uchar *) inq->vendor_id, - (uchar *) "HP ", 3) == 0)) { - asc_dvc->pci_fix_asyn_xfer_always |= tid_bits; - } - asc_dvc->pci_fix_asyn_xfer |= tid_bits; - if ((dvc_type == SCSI_TYPE_PROC) || (dvc_type == SCSI_TYPE_SCANNER)) { - asc_dvc->pci_fix_asyn_xfer &= ~tid_bits; - } - if ((dvc_type == SCSI_TYPE_SASD) && - (AscCompareString((uchar *) inq->vendor_id, - (uchar *) "TANDBERG", 8) == 0) && - (AscCompareString((uchar *) inq->product_id, - (uchar *) " TDC 36", 7) == 0)) { - asc_dvc->pci_fix_asyn_xfer &= ~tid_bits; - } - if ((dvc_type == SCSI_TYPE_SASD) && - (AscCompareString((uchar *) inq->vendor_id, - (uchar *) "WANGTEK ", 8) == 0)) { - asc_dvc->pci_fix_asyn_xfer &= ~tid_bits; - } - - if ((dvc_type == SCSI_TYPE_CDROM) && - (AscCompareString((uchar *)inq->vendor_id, - (uchar *)"NEC ", 8) == 0) && - (AscCompareString((uchar *)inq->product_id, - (uchar *)"CD-ROM DRIVE ", 16) == 0)) { - asc_dvc->pci_fix_asyn_xfer &= ~tid_bits ; - } - if ((dvc_type == SCSI_TYPE_CDROM) && - (AscCompareString((uchar *) inq->vendor_id, - (uchar *) "YAMAHA", 6) == 0) && - (AscCompareString((uchar *) inq->product_id, - (uchar *) "CDR400", 6) == 0)) - { - asc_dvc->pci_fix_asyn_xfer &= ~tid_bits ; - } - - if (asc_dvc->pci_fix_asyn_xfer & tid_bits) { - AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no, - ASYN_SDTR_DATA_FIX_PCI_REV_AB); - } - } + if ((dvc_type == SCSI_TYPE_SASD) && + (AscCompareString((uchar *) inq->vendor_id, + (uchar *) "TANDBERG", 8) == 0) && + (AscCompareString((uchar *) inq->product_id, + (uchar *) " TDC 36", 7) == 0)) { + asc_dvc->pci_fix_asyn_xfer &= ~tid_bits; } - sta = 1; - ASC_DBG(1, "AscInitPollTarget: before InitTestUnitReady\n"); - sta = InitTestUnitReady(asc_dvc, scsiq); - if (sta == 1) { - if ((cap_info != 0L) && support_read_cap) { - ASC_DBG(1, - "AscInitPollTarget: before PollScsiReadCapacity\n"); - if (PollScsiReadCapacity(asc_dvc, scsiq, - cap_info) != 1) { - cap_info->lba = 0L; - cap_info->blk_size = 0x0000; - } else { - } - } + if ((dvc_type == SCSI_TYPE_SASD) && + (AscCompareString((uchar *) inq->vendor_id, + (uchar *) "WANGTEK ", 8) == 0)) { + asc_dvc->pci_fix_asyn_xfer &= ~tid_bits; } - } else { - asc_dvc->start_motor &= ~tid_bits; - } - } else if (tmp_disable_init_sdtr) { - asc_dvc->init_sdtr |= tid_bits; - } - ASC_DBG1(1, "AscInitPollTarget: dvc_found %d\n", dvc_found); - return (dvc_found); -} -STATIC int -PollQueueDone( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - int timeout_sec -) -{ - int status; - int retry = 0; + if ((dvc_type == SCSI_TYPE_CDROM) && + (AscCompareString((uchar *) inq->vendor_id, + (uchar *) "NEC ", 8) == 0) && + (AscCompareString((uchar *) inq->product_id, + (uchar *) "CD-ROM DRIVE ", 16) == 0)) { + asc_dvc->pci_fix_asyn_xfer &= ~tid_bits; + } - ASC_DBG1(1, "PollQueueDone: timeout_sec %d\n", timeout_sec); - do { - ASC_DBG(1, "PollQueueDone: before AscExeScsiQueue\n"); - if ((status = AscExeScsiQueue(asc_dvc, - (ASC_SCSI_Q *) scsiq)) == 1) { - ASC_DBG(1, "PollQueueDone: before AscPollQDone\n"); - if ((status = AscPollQDone(asc_dvc, scsiq, - timeout_sec)) != 1) { - ASC_DBG1(1, "PollQueueDone: status %x\n", status); - if (status == 0x80) { - if (retry++ > ASC_MAX_INIT_BUSY_RETRY) { - break; - } - scsiq->r3.done_stat = 0; - scsiq->r3.host_stat = 0; - scsiq->r3.scsi_stat = 0; - scsiq->r3.scsi_msg = 0; - DvcSleepMilliSecond(1000); - continue; - } - scsiq->r3.done_stat = 0; - scsiq->r3.host_stat = 0; - scsiq->r3.scsi_stat = 0; - scsiq->r3.scsi_msg = 0; - ASC_DBG(1, "PollQueueDone: before AscAbortSRB()\n"); - AscAbortSRB(asc_dvc, (ulong) scsiq); + if ((dvc_type == SCSI_TYPE_CDROM) && + (AscCompareString((uchar *) inq->vendor_id, + (uchar *) "YAMAHA", 6) == 0) && + (AscCompareString((uchar *) inq->product_id, + (uchar *) "CDR400", 6) == 0)) { + asc_dvc->pci_fix_asyn_xfer &= ~tid_bits; + } + if (asc_dvc->pci_fix_asyn_xfer & tid_bits) { + AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no, + ASYN_SDTR_DATA_FIX_PCI_REV_AB); } - ASC_DBG1(1, "PollQueueDone: status %x\n", status); - ASC_DBG1(1, "PollQueueDone: done_stat %x\n", scsiq->r3.done_stat); - return (scsiq->r3.done_stat); } - ASC_DBG1(1, "PollQueueDone: status %x\n", status); - DvcSleepMilliSecond(5); - } while (((status == 0) || (status == 0x80)) && - retry++ < ASC_MAX_INIT_BUSY_RETRY); - ASC_DBG1(1, "PollQueueDone: status %x\n", status); - ASC_DBG(1, "PollQueueDone: done_stat QD_WITH_ERROR\n"); - return (scsiq->r3.done_stat = QD_WITH_ERROR); -} - -STATIC int -PollScsiInquiry( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - uchar * buf, - int buf_len -) -{ - if (AscScsiInquiry(asc_dvc, scsiq, buf, buf_len) == ERR) { - return (scsiq->r3.done_stat = QD_WITH_ERROR); } - return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 4)); -} - -STATIC int -PollScsiStartUnit( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq -) -{ - if (AscScsiStartStopUnit(asc_dvc, scsiq, 1) == ERR) { - return (scsiq->r3.done_stat = QD_WITH_ERROR); - } - return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 40)); + return; } STATIC int -PollScsiReadCapacity( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - REG ASC_CAP_INFO * cap_info -) +AscTagQueuingSafe(ASC_SCSI_INQUIRY *inq) { - ASC_CAP_INFO scsi_cap_info; - int status; - - if (AscScsiReadCapacity(asc_dvc, scsiq, - (uchar *) & scsi_cap_info) == ERR) { - return (scsiq->r3.done_stat = QD_WITH_ERROR); - } - status = PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 8); - if (status == 1) { - cap_info->lba = (ulong) * swapfarbuf4((uchar *) & scsi_cap_info.lba); - cap_info->blk_size = (ulong) * swapfarbuf4((uchar *) & scsi_cap_info.blk_size); - return (scsiq->r3.done_stat); + if ((inq->add_len >= 32) && + (AscCompareString((uchar *) inq->vendor_id, + (uchar *) "QUANTUM XP34301", 15) == 0) && + (AscCompareString((uchar *) inq->product_rev_level, + (uchar *) "1071", 4) == 0)) + { + return 0; } - return (scsiq->r3.done_stat = QD_WITH_ERROR); + return 1; } -STATIC ulong * -swapfarbuf4( - uchar *buf -) +STATIC void +AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *asc_dvc, + uchar tid_no, ASC_SCSI_INQUIRY *inq) { - uchar tmp; - - tmp = buf[3]; - buf[3] = buf[0]; - buf[0] = tmp; + ASC_SCSI_BIT_ID_TYPE tid_bit = ASC_TIX_TO_TARGET_ID(tid_no); + ASC_SCSI_BIT_ID_TYPE orig_init_sdtr, orig_use_tagged_qng; - tmp = buf[1]; - buf[1] = buf[2]; - buf[2] = tmp; + orig_init_sdtr = asc_dvc->init_sdtr; + orig_use_tagged_qng = asc_dvc->use_tagged_qng; - return ((ulong *) buf); -} + asc_dvc->init_sdtr &= ~tid_bit; + asc_dvc->cfg->can_tagged_qng &= ~tid_bit; + asc_dvc->use_tagged_qng &= ~tid_bit; -STATIC int -PollScsiTestUnitReady( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq -) -{ - if (AscScsiTestUnitReady(asc_dvc, scsiq) == ERR) { - return (scsiq->r3.done_stat = QD_WITH_ERROR); - } - return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 12)); -} - -STATIC int -InitTestUnitReady( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq -) -{ - ASC_SCSI_BIT_ID_TYPE tid_bits; - int retry; - ASC_REQ_SENSE *sen; - - retry = 0; - tid_bits = scsiq->r1.target_id; - while (retry++ < 4) { - PollScsiTestUnitReady(asc_dvc, scsiq); - if (scsiq->r3.done_stat == 0x01) { - return (1); - } else if (scsiq->r3.done_stat == QD_WITH_ERROR) { - sen = (ASC_REQ_SENSE *) scsiq->sense_ptr; - if ((scsiq->r3.scsi_stat == SS_CHK_CONDITION) && - ((sen->err_code & 0x70) != 0)) { - if (sen->sense_key == SCSI_SENKEY_NOT_READY) { - if (sen->asc == SCSI_ASC_NOMEDIA) - { - break; - } - if (asc_dvc->start_motor & tid_bits) { - if (PollScsiStartUnit(asc_dvc, scsiq) == 1) { - DvcSleepMilliSecond(250); - continue; - } else { - asc_dvc->start_motor &= ~tid_bits; - break; - } - } else { - DvcSleepMilliSecond(250); - } - } else if (sen->sense_key == SCSI_SENKEY_ATTENTION) { - DvcSleepMilliSecond(250); - } else { - break; - } - } else { - break; + if (inq->byte3.rsp_data_fmt >= 2 || inq->byte2.ansi_apr_ver >= 2) { + if ((asc_dvc->cfg->sdtr_enable & tid_bit) && inq->byte7.Sync) { + asc_dvc->init_sdtr |= tid_bit; + } + if ((asc_dvc->cfg->cmd_qng_enabled & tid_bit) && inq->byte7.CmdQue) { + if (AscTagQueuingSafe(inq)) { + asc_dvc->use_tagged_qng |= tid_bit; + asc_dvc->cfg->can_tagged_qng |= tid_bit; } - } else if (scsiq->r3.done_stat == QD_ABORTED_BY_HOST) { - break; - } else { - break; } } - return (0); -} - -STATIC int -AscPollQDone( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - int timeout_sec -) -{ - int loop, loop_end; - int sta; - PortAddr iop_base; + if (orig_use_tagged_qng != asc_dvc->use_tagged_qng) { + AscWriteLramByte(asc_dvc->iop_base, ASCV_DISC_ENABLE_B, + asc_dvc->cfg->disc_enable); + AscWriteLramByte(asc_dvc->iop_base, ASCV_USE_TAGGED_QNG_B, + asc_dvc->use_tagged_qng); + AscWriteLramByte(asc_dvc->iop_base, ASCV_CAN_TAGGED_QNG_B, + asc_dvc->cfg->can_tagged_qng); - iop_base = asc_dvc->iop_base; - loop = 0; - loop_end = timeout_sec * 100; - sta = 1; - while (TRUE) { - if (asc_dvc->err_code != 0) { - scsiq->r3.done_stat = QD_WITH_ERROR; - ASC_DBG1(1, "AscPollQDone: err_code %x\n", asc_dvc->err_code); - sta = ERR; - break; - } - if (scsiq->r3.done_stat != QD_IN_PROGRESS) { - if ((scsiq->r3.done_stat == QD_WITH_ERROR) && - (scsiq->r3.scsi_stat == SS_TARGET_BUSY)) { - sta = 0x80; - } - break; - } - DvcSleepMilliSecond(10); - if (loop++ > loop_end) { - ASC_DBG(1, "AscPollQDone: loop finished\n"); - sta = 0; - break; - } - if (AscIsChipHalted(iop_base)) { - ASC_DBG(1, "AscPollQDone: AscIsChipHalted()\n"); - AscISR(asc_dvc); - loop = 0; - } else { - if (AscIsIntPending(iop_base)) { - ASC_DBG(1, "AscPollQDone: AscIsIntPending()\n"); - AscISR(asc_dvc); - } - } + asc_dvc->max_dvc_qng[tid_no] = + asc_dvc->cfg->max_tag_qng[tid_no]; + AscWriteLramByte(asc_dvc->iop_base, + (ushort) (ASCV_MAX_DVC_QNG_BEG + tid_no), + asc_dvc->max_dvc_qng[tid_no]); } - return (sta); + if (orig_init_sdtr != asc_dvc->init_sdtr) { + AscAsyncFix(asc_dvc, tid_no, inq); + } + return; } STATIC int @@ -14040,105 +13822,16 @@ AscMemWordSetLram( } -STATIC int -AscScsiInquiry( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - uchar * buf, - int buf_len -) -{ - if (AscScsiSetupCmdQ(asc_dvc, scsiq, buf, - (ulong) buf_len) == ERR) { - return (scsiq->r3.done_stat = QD_WITH_ERROR); - } - scsiq->cdb[0] = (uchar) SCSICMD_Inquiry; - scsiq->cdb[1] = scsiq->r1.target_lun << 5; - scsiq->cdb[2] = 0; - scsiq->cdb[3] = 0; - scsiq->cdb[4] = buf_len; - scsiq->cdb[5] = 0; - scsiq->r2.cdb_len = 6; - return (0); -} - -STATIC int -AscScsiReadCapacity( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - uchar * info -) -{ - if (AscScsiSetupCmdQ(asc_dvc, scsiq, info, 8L) == ERR) { - return (scsiq->r3.done_stat = QD_WITH_ERROR); - } - scsiq->cdb[0] = (uchar) SCSICMD_ReadCapacity; - scsiq->cdb[1] = scsiq->r1.target_lun << 5; - scsiq->cdb[2] = 0; - scsiq->cdb[3] = 0; - scsiq->cdb[4] = 0; - scsiq->cdb[5] = 0; - scsiq->cdb[6] = 0; - scsiq->cdb[7] = 0; - scsiq->cdb[8] = 0; - scsiq->cdb[9] = 0; - scsiq->r2.cdb_len = 10; - return (0); -} - -STATIC int -AscScsiTestUnitReady( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq -) -{ - if (AscScsiSetupCmdQ(asc_dvc, scsiq, FNULLPTR, - (ulong) 0L) == ERR) { - return (scsiq->r3.done_stat = QD_WITH_ERROR); - } - scsiq->r1.cntl = (uchar) ASC_SCSIDIR_NODATA; - scsiq->cdb[0] = (uchar) SCSICMD_TestUnitReady; - scsiq->cdb[1] = scsiq->r1.target_lun << 5; - scsiq->cdb[2] = 0; - scsiq->cdb[3] = 0; - scsiq->cdb[4] = 0; - scsiq->cdb[5] = 0; - scsiq->r2.cdb_len = 6; - return (0); -} - -STATIC int -AscScsiStartStopUnit( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_REQ_Q * scsiq, - uchar op_mode -) -{ - if (AscScsiSetupCmdQ(asc_dvc, scsiq, FNULLPTR, (ulong) 0L) == ERR) { - return (scsiq->r3.done_stat = QD_WITH_ERROR); - } - scsiq->r1.cntl = (uchar) ASC_SCSIDIR_NODATA; - scsiq->cdb[0] = (uchar) SCSICMD_StartStopUnit; - scsiq->cdb[1] = scsiq->r1.target_lun << 5; - scsiq->cdb[2] = 0; - scsiq->cdb[3] = 0; - scsiq->cdb[4] = op_mode; - scsiq->cdb[5] = 0; - scsiq->r2.cdb_len = 6; - return (0); -} - - /* * --- Adv Library Functions */ /* a_qswap.h */ STATIC unsigned char _adv_mcode_buf[] ASC_INITDATA = { - 0x9C, 0xF0, 0x80, 0x01, 0x00, 0xF0, 0x40, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9C, 0xF0, 0x80, 0x01, 0x00, 0xF0, 0x44, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x01, 0xD2, 0x11, 0x00, 0x00, 0x70, 0x01, - 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x0F, 0x22, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x01, 0xD6, 0x11, 0x00, 0x00, 0x70, 0x01, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x10, 0x2D, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -14158,273 +13851,274 @@ STATIC unsigned char _adv_mcode_buf[] ASC_INITDATA = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0xF7, 0x70, 0x01, 0x0C, 0x1C, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF2, 0xD2, 0x0A, + 0x00, 0x00, 0x04, 0xF7, 0x70, 0x01, 0x0C, 0x1C, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF2, 0xD6, 0x0A, 0x04, 0xF7, 0x70, 0x01, 0x06, 0xF7, 0x02, 0x00, 0x3E, 0x57, 0x3C, 0x56, 0x0C, 0x1C, 0x00, 0xFC, - 0xA6, 0x00, 0x01, 0x58, 0xAA, 0x13, 0x20, 0xF0, 0x9A, 0x03, 0x06, 0xEC, 0xB9, 0x00, 0x0E, 0x47, + 0xA6, 0x00, 0x01, 0x58, 0xAA, 0x13, 0x20, 0xF0, 0xA6, 0x03, 0x06, 0xEC, 0xB9, 0x00, 0x0E, 0x47, 0x03, 0xE6, 0x10, 0x00, 0xCE, 0x45, 0x02, 0x13, 0x3E, 0x57, 0x06, 0xEA, 0xB9, 0x00, 0x47, 0x4B, - 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x01, 0x48, 0x4E, 0x12, 0x03, 0xF6, 0xC0, 0x00, - 0x00, 0xF2, 0x64, 0x0A, 0x41, 0x58, 0x03, 0xF6, 0xD0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x49, 0x44, - 0x59, 0xF0, 0x0A, 0x02, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x44, 0x58, 0x00, 0xF2, - 0xDE, 0x0D, 0x02, 0xCC, 0x4A, 0xE4, 0x01, 0x00, 0x55, 0xF0, 0x08, 0x03, 0x45, 0xF4, 0x02, 0x00, - 0x83, 0x5A, 0x04, 0xCC, 0x01, 0x4A, 0x12, 0x12, 0x00, 0xF2, 0xDE, 0x0D, 0x00, 0xCD, 0x48, 0xE4, - 0x01, 0x00, 0xE9, 0x13, 0x00, 0xF2, 0xC2, 0x0F, 0xFA, 0x10, 0x0E, 0x47, 0x03, 0xE6, 0x10, 0x00, + 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x4E, 0x12, 0x03, 0xF6, 0xC0, 0x00, + 0x00, 0xF2, 0x68, 0x0A, 0x41, 0x58, 0x03, 0xF6, 0xD0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x49, 0x44, + 0x59, 0xF0, 0x0A, 0x02, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x44, 0x58, 0x00, 0xF2, + 0xE2, 0x0D, 0x02, 0xCC, 0x4A, 0xE4, 0x01, 0x00, 0x55, 0xF0, 0x08, 0x03, 0x45, 0xF4, 0x02, 0x00, + 0x83, 0x5A, 0x04, 0xCC, 0x01, 0x4A, 0x12, 0x12, 0x00, 0xF2, 0xE2, 0x0D, 0x00, 0xCD, 0x48, 0xE4, + 0x01, 0x00, 0xE9, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0xFA, 0x10, 0x0E, 0x47, 0x03, 0xE6, 0x10, 0x00, 0xCE, 0x45, 0x02, 0x13, 0x3E, 0x57, 0xCE, 0x47, 0x97, 0x13, 0x04, 0xEC, 0xB4, 0x00, 0x00, 0xF2, - 0xDE, 0x0D, 0x00, 0xCD, 0x48, 0xE4, 0x00, 0x00, 0x12, 0x12, 0x3E, 0x57, 0x06, 0xCC, 0x45, 0xF4, + 0xE2, 0x0D, 0x00, 0xCD, 0x48, 0xE4, 0x00, 0x00, 0x12, 0x12, 0x3E, 0x57, 0x06, 0xCC, 0x45, 0xF4, 0x02, 0x00, 0x83, 0x5A, 0x00, 0xCC, 0x00, 0xEA, 0xB4, 0x00, 0x92, 0x10, 0x00, 0xF0, 0x8C, 0x01, 0x43, 0xF0, 0x5C, 0x02, 0x44, 0xF0, 0x60, 0x02, 0x45, 0xF0, 0x64, 0x02, 0x46, 0xF0, 0x68, 0x02, 0x47, 0xF0, 0x6E, 0x02, 0x48, 0xF0, 0x9E, 0x02, 0xB9, 0x54, 0x62, 0x10, 0x00, 0x1C, 0x5A, 0x10, - 0x02, 0x1C, 0x56, 0x10, 0x1E, 0x1C, 0x52, 0x10, 0x00, 0xF2, 0x1A, 0x11, 0x50, 0x10, 0x06, 0xFC, - 0xA8, 0x00, 0x03, 0xF6, 0xBE, 0x00, 0x00, 0xF2, 0x4A, 0x0A, 0x8C, 0x10, 0x01, 0xF6, 0x01, 0x00, - 0x01, 0xFA, 0xA8, 0x00, 0x00, 0xF2, 0x28, 0x0B, 0x06, 0x10, 0xB9, 0x54, 0x01, 0xFA, 0xA8, 0x00, - 0x03, 0xF6, 0xBE, 0x00, 0x00, 0xF2, 0x54, 0x0A, 0x01, 0xFC, 0xA8, 0x00, 0x20, 0x10, 0x58, 0x1C, - 0x00, 0xF2, 0x18, 0x0B, 0x5A, 0x1C, 0x01, 0xF6, 0x01, 0x00, 0x38, 0x54, 0x00, 0xFA, 0xA6, 0x00, + 0x02, 0x1C, 0x56, 0x10, 0x1E, 0x1C, 0x52, 0x10, 0x00, 0xF2, 0x1E, 0x11, 0x50, 0x10, 0x06, 0xFC, + 0xA8, 0x00, 0x03, 0xF6, 0xBE, 0x00, 0x00, 0xF2, 0x4E, 0x0A, 0x8C, 0x10, 0x01, 0xF6, 0x01, 0x00, + 0x01, 0xFA, 0xA8, 0x00, 0x00, 0xF2, 0x2C, 0x0B, 0x06, 0x10, 0xB9, 0x54, 0x01, 0xFA, 0xA8, 0x00, + 0x03, 0xF6, 0xBE, 0x00, 0x00, 0xF2, 0x58, 0x0A, 0x01, 0xFC, 0xA8, 0x00, 0x20, 0x10, 0x58, 0x1C, + 0x00, 0xF2, 0x1C, 0x0B, 0x5A, 0x1C, 0x01, 0xF6, 0x01, 0x00, 0x38, 0x54, 0x00, 0xFA, 0xA6, 0x00, 0x01, 0xFA, 0xA8, 0x00, 0x20, 0x1C, 0x00, 0xF0, 0x72, 0x01, 0x01, 0xF6, 0x01, 0x00, 0x38, 0x54, 0x00, 0xFA, 0xA6, 0x00, 0x01, 0xFA, 0xA8, 0x00, 0x20, 0x1C, 0x00, 0xF0, 0x80, 0x01, 0x03, 0xF6, - 0xE0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x01, 0x48, 0x0A, 0x13, 0x00, 0xF2, 0x34, 0x10, 0x00, 0xF2, - 0x50, 0x0F, 0x24, 0x10, 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x02, 0xF6, 0xD0, 0x00, - 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x49, 0x44, 0x5B, 0xF0, 0x04, 0x03, 0x00, 0xF2, 0x98, 0x0F, - 0x00, 0xF0, 0x80, 0x01, 0x00, 0xF2, 0x10, 0x10, 0x0C, 0x1C, 0x02, 0x4B, 0xBF, 0x57, 0x9E, 0x43, - 0x77, 0x57, 0x07, 0x4B, 0x20, 0xF0, 0x9A, 0x03, 0x40, 0x1C, 0x1E, 0xF0, 0x30, 0x03, 0x26, 0xF0, - 0x2C, 0x03, 0xA0, 0xF0, 0x1A, 0x03, 0x11, 0xF0, 0x9A, 0x03, 0x12, 0x10, 0x9F, 0xF0, 0x3E, 0x03, - 0x46, 0x1C, 0x82, 0xE7, 0x05, 0x00, 0x9E, 0xE7, 0x11, 0x00, 0x00, 0xF0, 0x02, 0x0A, 0x0C, 0x1C, + 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x0A, 0x13, 0x00, 0xF2, 0x38, 0x10, 0x00, 0xF2, + 0x54, 0x0F, 0x24, 0x10, 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x02, 0xF6, 0xD0, 0x00, + 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x49, 0x44, 0x5B, 0xF0, 0x04, 0x03, 0x00, 0xF2, 0x9C, 0x0F, + 0x00, 0xF0, 0x80, 0x01, 0x00, 0xF2, 0x14, 0x10, 0x0C, 0x1C, 0x02, 0x4B, 0xBF, 0x57, 0x9E, 0x43, + 0x77, 0x57, 0x07, 0x4B, 0x20, 0xF0, 0xA6, 0x03, 0x40, 0x1C, 0x1E, 0xF0, 0x30, 0x03, 0x26, 0xF0, + 0x2C, 0x03, 0xA0, 0xF0, 0x1A, 0x03, 0x11, 0xF0, 0xA6, 0x03, 0x12, 0x10, 0x9F, 0xF0, 0x3E, 0x03, + 0x46, 0x1C, 0x82, 0xE7, 0x05, 0x00, 0x9E, 0xE7, 0x11, 0x00, 0x00, 0xF0, 0x06, 0x0A, 0x0C, 0x1C, 0x48, 0x1C, 0x46, 0x1C, 0x38, 0x54, 0x00, 0xEC, 0xBA, 0x00, 0x08, 0x44, 0x00, 0xEA, 0xBA, 0x00, - 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x08, 0x44, 0x00, 0x4C, 0x82, 0xE7, 0x02, 0x00, - 0x00, 0xF2, 0x0E, 0x11, 0x00, 0xF2, 0x0E, 0x11, 0x06, 0xF0, 0x74, 0x03, 0x1E, 0xF0, 0xF8, 0x09, - 0x00, 0xF0, 0xFE, 0x09, 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0x55, 0xF0, 0xA0, 0x04, 0x01, 0xE6, - 0x0C, 0x00, 0x00, 0xF2, 0x4A, 0x0D, 0x00, 0xF2, 0x0E, 0x11, 0x00, 0xF2, 0xB8, 0x11, 0x00, 0xF2, - 0xC4, 0x11, 0x01, 0xF0, 0x7C, 0x02, 0x00, 0xF0, 0x8A, 0x02, 0x46, 0x1C, 0x0C, 0x1C, 0x67, 0x1B, - 0xBF, 0x57, 0x77, 0x57, 0x02, 0x4B, 0x48, 0x1C, 0x32, 0x1C, 0x00, 0xF2, 0x8E, 0x0D, 0x30, 0x1C, - 0x96, 0xF0, 0xB0, 0x03, 0xB1, 0xF0, 0xB4, 0x03, 0x1E, 0xF0, 0xF8, 0x09, 0x85, 0xF0, 0xFE, 0x09, - 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0x14, 0x12, 0x01, 0xE6, 0x0C, 0x00, 0x00, 0xF2, 0x4A, 0x0D, - 0x00, 0xF2, 0x0E, 0x11, 0x01, 0xF0, 0x7C, 0x02, 0x00, 0xF0, 0x8A, 0x02, 0x03, 0xF6, 0xE0, 0x00, - 0x00, 0xF2, 0x64, 0x0A, 0x01, 0x48, 0x55, 0xF0, 0x8C, 0x04, 0x03, 0x82, 0x03, 0xFC, 0xA0, 0x00, - 0x9B, 0x57, 0x40, 0x12, 0x69, 0x18, 0x00, 0xF2, 0x0E, 0x11, 0x85, 0xF0, 0x36, 0x04, 0x69, 0x08, - 0x00, 0xF2, 0x0E, 0x11, 0x85, 0xF0, 0xFE, 0x09, 0x68, 0x08, 0x4C, 0x44, 0x28, 0x12, 0x44, 0x48, - 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x45, 0x58, 0x00, 0xF2, 0xF2, 0x0D, 0x00, 0xCC, - 0x01, 0x48, 0x55, 0xF0, 0x8C, 0x04, 0x4C, 0x44, 0xEF, 0x13, 0x00, 0xF2, 0xC2, 0x0F, 0x00, 0xF2, - 0x10, 0x10, 0x08, 0x10, 0x68, 0x18, 0x45, 0x5A, 0x00, 0xF2, 0xF2, 0x0D, 0x04, 0x80, 0x18, 0xE4, - 0x10, 0x00, 0x28, 0x12, 0x01, 0xE6, 0x06, 0x00, 0x04, 0x80, 0x18, 0xE4, 0x01, 0x00, 0x04, 0x12, - 0x01, 0xE6, 0x0D, 0x00, 0x00, 0xF2, 0x4A, 0x0D, 0x00, 0xF2, 0x0E, 0x11, 0x04, 0xE6, 0x02, 0x00, - 0x9E, 0xE7, 0x15, 0x00, 0x01, 0xF0, 0x18, 0x0A, 0x00, 0xF0, 0xFE, 0x09, 0x69, 0x08, 0x05, 0x80, - 0x48, 0xE4, 0x00, 0x00, 0x0C, 0x12, 0x00, 0xE6, 0x11, 0x00, 0x00, 0xEA, 0xB8, 0x00, 0x00, 0xF2, - 0xB2, 0x10, 0x82, 0xE7, 0x02, 0x00, 0x1C, 0x90, 0x40, 0x5C, 0x00, 0x16, 0x01, 0xE6, 0x06, 0x00, - 0x00, 0xF2, 0x4A, 0x0D, 0x01, 0xF0, 0x80, 0x01, 0x1E, 0xF0, 0x80, 0x01, 0x00, 0xF0, 0x94, 0x04, - 0x42, 0x5B, 0x06, 0xF7, 0x03, 0x00, 0x46, 0x59, 0xBF, 0x57, 0x77, 0x57, 0x01, 0xE6, 0x80, 0x00, - 0x07, 0x80, 0x31, 0x44, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x5E, 0x13, 0x20, 0x80, 0x48, 0xE4, - 0x03, 0x00, 0x56, 0x12, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x4C, 0x13, 0x00, 0xFC, 0xA2, 0x00, - 0x98, 0x57, 0x55, 0xF0, 0x18, 0x05, 0x31, 0xE4, 0x40, 0x00, 0x00, 0xFC, 0xA0, 0x00, 0x98, 0x57, - 0x36, 0x12, 0x4C, 0x1C, 0x00, 0xF2, 0x0E, 0x11, 0x89, 0x48, 0x00, 0xF2, 0x0E, 0x11, 0x86, 0xF0, - 0x2A, 0x05, 0x82, 0xE7, 0x06, 0x00, 0x1B, 0x80, 0x48, 0xE4, 0x22, 0x00, 0x5B, 0xF0, 0x08, 0x05, - 0x48, 0xE4, 0x20, 0x00, 0x59, 0xF0, 0x0C, 0x05, 0x00, 0xE6, 0x20, 0x00, 0x09, 0x48, 0x00, 0xF2, - 0x0E, 0x11, 0x86, 0xF0, 0x2A, 0x05, 0x83, 0x80, 0x04, 0x10, 0x00, 0xF2, 0x9E, 0x0D, 0x00, 0xE6, - 0x01, 0x00, 0x00, 0xEA, 0x26, 0x01, 0x01, 0xEA, 0x27, 0x01, 0x04, 0x80, 0x18, 0xE4, 0x10, 0x00, - 0x36, 0x12, 0xB9, 0x54, 0x00, 0xF2, 0xF2, 0x0E, 0x01, 0xE6, 0x06, 0x00, 0x04, 0x80, 0x18, 0xE4, - 0x01, 0x00, 0x04, 0x12, 0x01, 0xE6, 0x0D, 0x00, 0x00, 0xF2, 0x4A, 0x0D, 0x00, 0xF2, 0x0E, 0x11, - 0x00, 0xF2, 0xB8, 0x11, 0x00, 0xF2, 0xC4, 0x11, 0x04, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00, - 0x01, 0xF0, 0x18, 0x0A, 0x00, 0xF0, 0xFE, 0x09, 0x00, 0xFC, 0x20, 0x01, 0x98, 0x57, 0x34, 0x12, - 0x00, 0xFC, 0x24, 0x01, 0x98, 0x57, 0x2C, 0x13, 0xB9, 0x54, 0x00, 0xF2, 0xF2, 0x0E, 0x86, 0xF0, - 0xA4, 0x05, 0x03, 0xF6, 0x01, 0x00, 0x00, 0xF2, 0x88, 0x0E, 0x85, 0xF0, 0x9A, 0x05, 0x82, 0xE7, - 0x03, 0x00, 0x00, 0xF2, 0x5C, 0x0B, 0x82, 0xE7, 0x02, 0x00, 0x00, 0xFC, 0x24, 0x01, 0xB0, 0x57, - 0x00, 0xFA, 0x24, 0x01, 0x00, 0xFC, 0x9E, 0x00, 0x98, 0x57, 0x5A, 0x12, 0x00, 0xFC, 0xB6, 0x00, - 0x98, 0x57, 0x52, 0x13, 0x03, 0xE6, 0x0C, 0x00, 0x00, 0xFC, 0x9C, 0x00, 0x98, 0x57, 0x04, 0x13, - 0x03, 0xE6, 0x19, 0x00, 0x05, 0xE6, 0x08, 0x00, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, - 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, 0x00, 0x80, 0x04, 0x13, 0x05, 0xE6, 0x0F, 0x00, 0xB9, 0x54, - 0x00, 0xF2, 0xF2, 0x0E, 0x86, 0xF0, 0x06, 0x06, 0x00, 0xF2, 0xB6, 0x0E, 0x85, 0xF0, 0xFC, 0x05, - 0x82, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x5C, 0x0B, 0x82, 0xE7, 0x02, 0x00, 0x00, 0xFC, 0xB6, 0x00, - 0xB0, 0x57, 0x00, 0xFA, 0xB6, 0x00, 0x01, 0xF6, 0x01, 0x00, 0x00, 0xF2, 0xF2, 0x0E, 0x9C, 0x32, - 0x4E, 0x1C, 0x32, 0x1C, 0x00, 0xF2, 0x8E, 0x0D, 0x30, 0x1C, 0x82, 0xE7, 0x04, 0x00, 0xB1, 0xF0, - 0x1E, 0x06, 0x0A, 0xF0, 0x3A, 0x06, 0x05, 0xF0, 0xD2, 0x06, 0x06, 0xF0, 0xD8, 0x06, 0x09, 0xF0, - 0x20, 0x09, 0x1E, 0xF0, 0xF8, 0x09, 0x00, 0xF0, 0xFE, 0x09, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, - 0x30, 0x12, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x21, 0x80, 0x18, 0xE4, 0xE0, 0x00, - 0x09, 0x48, 0x00, 0xF2, 0x0E, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x09, 0xE7, - 0x00, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x99, 0xA4, 0x00, 0xF2, 0x0E, 0x11, 0x09, 0xE7, 0x00, 0x00, - 0x9A, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x34, 0x12, 0x09, 0xE7, 0x1B, 0x00, 0x00, 0xF2, - 0x0E, 0x11, 0x21, 0x80, 0x18, 0xE4, 0xE0, 0x00, 0x09, 0x48, 0x00, 0xF2, 0x0E, 0x11, 0x09, 0xE7, - 0x00, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x09, 0xE7, - 0x01, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF0, 0x08, 0x09, 0xBB, 0x55, - 0x9A, 0x81, 0x03, 0xF7, 0x20, 0x00, 0x09, 0x6F, 0x93, 0x45, 0x55, 0xF0, 0xDE, 0x06, 0xB1, 0xF0, - 0xBE, 0x06, 0x0A, 0xF0, 0xB6, 0x06, 0x09, 0xF0, 0x20, 0x09, 0x1E, 0xF0, 0xF8, 0x09, 0x00, 0xF0, - 0xFE, 0x09, 0x00, 0xF2, 0x5C, 0x0B, 0x47, 0x10, 0x09, 0xE7, 0x08, 0x00, 0x41, 0x10, 0x05, 0x80, - 0x48, 0xE4, 0x00, 0x00, 0x1E, 0x12, 0x00, 0xE6, 0x11, 0x00, 0x00, 0xEA, 0xB8, 0x00, 0x00, 0xF2, - 0xB2, 0x10, 0x2C, 0x90, 0xAE, 0x90, 0x08, 0x50, 0x8A, 0x50, 0x38, 0x54, 0x1F, 0x40, 0x00, 0xF2, - 0xB0, 0x0D, 0x08, 0x10, 0x08, 0x90, 0x8A, 0x90, 0x30, 0x50, 0xB2, 0x50, 0x9C, 0x32, 0x0C, 0x92, - 0x8E, 0x92, 0x38, 0x54, 0x04, 0x80, 0x30, 0xE4, 0x08, 0x00, 0x04, 0x40, 0x0C, 0x1C, 0x00, 0xF6, - 0x03, 0x00, 0xB1, 0xF0, 0x22, 0x07, 0x9E, 0xF0, 0x36, 0x07, 0x01, 0x48, 0x55, 0xF0, 0xF8, 0x09, - 0x0C, 0x1C, 0x10, 0x44, 0xED, 0x10, 0x0B, 0xF0, 0x5A, 0x07, 0x0C, 0xF0, 0x5E, 0x07, 0x05, 0xF0, - 0x4E, 0x07, 0x06, 0xF0, 0x54, 0x07, 0x09, 0xF0, 0x20, 0x09, 0x00, 0xF0, 0xFE, 0x09, 0x00, 0xF2, - 0x5C, 0x0B, 0xCF, 0x10, 0x09, 0xE7, 0x08, 0x00, 0xC9, 0x10, 0x2E, 0x1C, 0x02, 0x10, 0x2C, 0x1C, - 0xAA, 0xF0, 0x60, 0x07, 0xAC, 0xF0, 0x6E, 0x07, 0x40, 0x10, 0x34, 0x1C, 0xF3, 0x10, 0xAD, 0xF0, - 0x78, 0x07, 0xC8, 0x10, 0x36, 0x1C, 0xE9, 0x10, 0x2B, 0xF0, 0x7E, 0x08, 0x6B, 0x18, 0x18, 0xF4, - 0x00, 0xFE, 0x20, 0x12, 0x01, 0x58, 0xD2, 0xF0, 0x7E, 0x08, 0x76, 0x18, 0x18, 0xF4, 0x03, 0x00, - 0xEC, 0x12, 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0xE2, 0x12, 0x0B, 0xF0, 0x60, 0x07, - 0x0C, 0xF0, 0x60, 0x07, 0x36, 0x1C, 0x34, 0x1C, 0xB7, 0x10, 0x38, 0x54, 0xB9, 0x54, 0x84, 0x80, - 0x19, 0xE4, 0x20, 0x00, 0xB2, 0x13, 0x85, 0x80, 0x81, 0x48, 0x66, 0x12, 0x04, 0x80, 0x18, 0xE4, - 0x08, 0x00, 0x58, 0x13, 0x1F, 0x80, 0x08, 0x44, 0xC8, 0x44, 0x9F, 0x12, 0x1F, 0x40, 0x34, 0x91, - 0xB6, 0x91, 0x44, 0x55, 0xE5, 0x55, 0x02, 0xEC, 0xB8, 0x00, 0x02, 0x49, 0xBB, 0x55, 0x82, 0x81, - 0xC0, 0x55, 0x48, 0xF4, 0x0F, 0x00, 0x5A, 0xF0, 0x16, 0x08, 0x4A, 0xE4, 0x17, 0x00, 0xD5, 0xF0, - 0xF6, 0x07, 0x02, 0xF6, 0x0F, 0x00, 0x02, 0xF4, 0x02, 0x00, 0x02, 0xEA, 0xB8, 0x00, 0x04, 0x91, - 0x86, 0x91, 0x02, 0x4B, 0x2C, 0x90, 0x08, 0x50, 0x2E, 0x90, 0x0A, 0x50, 0x2C, 0x51, 0xAE, 0x51, - 0x00, 0xF2, 0xB2, 0x10, 0x38, 0x54, 0x00, 0xF2, 0xB0, 0x0D, 0x56, 0x10, 0x34, 0x91, 0xB6, 0x91, - 0x0C, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x08, 0x00, 0x41, 0x12, 0x0C, 0x91, 0x8E, 0x91, 0x04, 0x80, - 0x18, 0xE4, 0xF7, 0x00, 0x04, 0x40, 0x30, 0x90, 0xB2, 0x90, 0x36, 0x10, 0x02, 0x80, 0x48, 0xE4, - 0x10, 0x00, 0x31, 0x12, 0x82, 0xE7, 0x10, 0x00, 0x84, 0x80, 0x19, 0xE4, 0x20, 0x00, 0x10, 0x13, - 0x0C, 0x90, 0x8E, 0x90, 0x5D, 0xF0, 0x74, 0x07, 0x0C, 0x58, 0x8D, 0x58, 0x00, 0xF0, 0x60, 0x07, - 0x38, 0x54, 0xB9, 0x54, 0x19, 0x80, 0xF1, 0x10, 0x3A, 0x55, 0x19, 0x81, 0xBB, 0x55, 0x10, 0x90, - 0x92, 0x90, 0x10, 0x58, 0x91, 0x58, 0x14, 0x59, 0x95, 0x59, 0x00, 0xF0, 0x60, 0x07, 0x04, 0x80, - 0x18, 0xE4, 0x20, 0x00, 0x06, 0x12, 0x6C, 0x19, 0x19, 0x41, 0x7C, 0x10, 0x6C, 0x19, 0x0C, 0x51, - 0xED, 0x19, 0x8E, 0x51, 0x6B, 0x18, 0x18, 0xF4, 0x00, 0xFF, 0x02, 0x13, 0x6A, 0x10, 0x01, 0x58, - 0xD2, 0xF0, 0xBC, 0x08, 0x76, 0x18, 0x18, 0xF4, 0x03, 0x00, 0x0A, 0x12, 0x00, 0xFC, 0x22, 0x01, - 0x18, 0xF4, 0x01, 0x00, 0x06, 0x13, 0x9E, 0xE7, 0x16, 0x00, 0x4C, 0x10, 0xD1, 0xF0, 0xC6, 0x08, - 0x9E, 0xE7, 0x17, 0x00, 0x42, 0x10, 0xD0, 0xF0, 0xD0, 0x08, 0x9E, 0xE7, 0x19, 0x00, 0x38, 0x10, - 0xCF, 0xF0, 0xDA, 0x08, 0x9E, 0xE7, 0x20, 0x00, 0x2E, 0x10, 0xCE, 0xF0, 0xE4, 0x08, 0x9E, 0xE7, - 0x21, 0x00, 0x24, 0x10, 0xCD, 0xF0, 0xEE, 0x08, 0x9E, 0xE7, 0x22, 0x00, 0x1A, 0x10, 0xCC, 0xF0, - 0x00, 0x09, 0x84, 0x80, 0x19, 0xE4, 0x04, 0x00, 0x06, 0x12, 0x9E, 0xE7, 0x12, 0x00, 0x08, 0x10, - 0xCB, 0xF0, 0x08, 0x09, 0x9E, 0xE7, 0x24, 0x00, 0xB1, 0xF0, 0x08, 0x09, 0x05, 0xF0, 0x1A, 0x09, - 0x09, 0xF0, 0x20, 0x09, 0x1E, 0xF0, 0xF8, 0x09, 0xE4, 0x10, 0x00, 0xF2, 0x5C, 0x0B, 0xE9, 0x10, - 0x9C, 0x32, 0x82, 0xE7, 0x20, 0x00, 0x32, 0x1C, 0xE9, 0x09, 0x00, 0xF2, 0x0E, 0x11, 0x85, 0xF0, - 0xFE, 0x09, 0x69, 0x08, 0x01, 0xF0, 0x40, 0x09, 0x1E, 0xF0, 0xF8, 0x09, 0x00, 0xF0, 0x34, 0x09, - 0x30, 0x44, 0x06, 0x12, 0x9E, 0xE7, 0x42, 0x00, 0xB8, 0x10, 0x04, 0xF6, 0x01, 0x00, 0xB3, 0x45, - 0x74, 0x12, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x22, 0x13, 0x4B, 0xE4, 0x02, 0x00, 0x36, 0x12, - 0x4B, 0xE4, 0x28, 0x00, 0xAC, 0x13, 0x00, 0xF2, 0xB8, 0x11, 0x00, 0xF2, 0xC4, 0x11, 0x03, 0xF6, - 0xD0, 0x00, 0xFA, 0x14, 0x82, 0xE7, 0x01, 0x00, 0x00, 0xF0, 0x80, 0x01, 0x9E, 0xE7, 0x44, 0x00, - 0x4B, 0xE4, 0x02, 0x00, 0x06, 0x12, 0x03, 0xE6, 0x02, 0x00, 0x76, 0x10, 0x00, 0xF2, 0x9E, 0x0D, - 0x03, 0xE6, 0x02, 0x00, 0x6C, 0x10, 0x00, 0xF2, 0x9E, 0x0D, 0x19, 0x82, 0x34, 0x46, 0x0A, 0x13, - 0x03, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x43, 0x00, 0x68, 0x10, 0x04, 0x80, 0x30, 0xE4, 0x20, 0x00, - 0x04, 0x40, 0x00, 0xF2, 0xB8, 0x11, 0x00, 0xF2, 0xC4, 0x11, 0x82, 0xE7, 0x01, 0x00, 0x06, 0xF7, - 0x02, 0x00, 0x00, 0xF0, 0x08, 0x03, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x06, 0x12, 0x03, 0xE6, - 0x02, 0x00, 0x3E, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x3A, 0x12, 0x04, 0x80, 0x18, 0xE4, - 0xFD, 0x00, 0x04, 0x40, 0x1C, 0x1C, 0x9D, 0xF0, 0xE6, 0x09, 0x1C, 0x1C, 0x9D, 0xF0, 0xEC, 0x09, - 0xC1, 0x10, 0x9E, 0xE7, 0x13, 0x00, 0x0A, 0x10, 0x9E, 0xE7, 0x41, 0x00, 0x04, 0x10, 0x9E, 0xE7, - 0x24, 0x00, 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0xD5, 0xF0, 0x8A, 0x02, 0x04, 0xE6, 0x04, 0x00, - 0x06, 0x10, 0x04, 0xE6, 0x04, 0x00, 0x9D, 0x41, 0x1C, 0x42, 0x9F, 0xE7, 0x00, 0x00, 0x06, 0xF7, - 0x02, 0x00, 0x03, 0xF6, 0xE0, 0x00, 0x3C, 0x14, 0x44, 0x58, 0x45, 0x58, 0x00, 0xF2, 0xF2, 0x0D, - 0x00, 0xF2, 0x7A, 0x10, 0x00, 0xF2, 0xC2, 0x0F, 0x3C, 0x14, 0x1E, 0x1C, 0x00, 0xF0, 0x80, 0x01, - 0x12, 0x1C, 0x22, 0x1C, 0xD2, 0x14, 0x00, 0xF0, 0x72, 0x01, 0x83, 0x59, 0x03, 0xDC, 0x73, 0x57, - 0x80, 0x5D, 0x00, 0x16, 0x83, 0x59, 0x03, 0xDC, 0x38, 0x54, 0x70, 0x57, 0x33, 0x54, 0x3B, 0x54, - 0x80, 0x5D, 0x00, 0x16, 0x03, 0x57, 0x83, 0x59, 0x38, 0x54, 0x00, 0xCC, 0x00, 0x16, 0x03, 0x57, - 0x83, 0x59, 0x00, 0x4C, 0x00, 0x16, 0x02, 0x80, 0x48, 0xE4, 0x01, 0x00, 0x0E, 0x12, 0x48, 0xE4, - 0x05, 0x00, 0x08, 0x12, 0x00, 0xF2, 0xB8, 0x11, 0x00, 0xF2, 0xC4, 0x11, 0xC1, 0x5A, 0x3A, 0x55, - 0x02, 0xEC, 0xB5, 0x00, 0x45, 0x59, 0x00, 0xF2, 0xF2, 0x0D, 0x83, 0x58, 0x30, 0xE7, 0x00, 0x00, - 0x10, 0x4D, 0x30, 0xE7, 0x40, 0x00, 0x10, 0x4F, 0x38, 0x90, 0xBA, 0x90, 0x10, 0x5C, 0x80, 0x5C, - 0x83, 0x5A, 0x10, 0x4E, 0x04, 0xEA, 0xB5, 0x00, 0x43, 0x5B, 0x03, 0xF4, 0xE0, 0x00, 0x83, 0x59, - 0x04, 0xCC, 0x01, 0x4A, 0x0A, 0x12, 0x45, 0x5A, 0x00, 0xF2, 0xF2, 0x0D, 0x00, 0xF2, 0x34, 0x10, - 0x00, 0x16, 0x08, 0x1C, 0x00, 0xFC, 0xAC, 0x00, 0x06, 0x58, 0x67, 0x18, 0x18, 0xF4, 0x8F, 0xE1, - 0x01, 0xFC, 0xAE, 0x00, 0x19, 0xF4, 0x70, 0x1E, 0xB0, 0x54, 0x07, 0x58, 0x00, 0xFC, 0xB0, 0x00, - 0x08, 0x58, 0x00, 0xFC, 0xB2, 0x00, 0x09, 0x58, 0x0A, 0x1C, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xEA, - 0xB9, 0x00, 0x38, 0x54, 0x00, 0xFA, 0x24, 0x01, 0x00, 0xFA, 0xB6, 0x00, 0x18, 0x1C, 0x14, 0x1C, - 0x10, 0x1C, 0x32, 0x1C, 0x12, 0x1C, 0x00, 0x16, 0x3E, 0x57, 0x0C, 0x14, 0x0E, 0x47, 0x07, 0xE6, - 0x10, 0x00, 0xCE, 0x47, 0xF5, 0x13, 0x00, 0x16, 0x00, 0xF2, 0x9E, 0x0D, 0x02, 0x4B, 0x03, 0xF6, - 0xE0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x01, 0x48, 0x20, 0x12, 0x44, 0x58, 0x45, 0x58, 0x9E, 0xE7, - 0x15, 0x00, 0x9C, 0xE7, 0x04, 0x00, 0x00, 0xF2, 0xF2, 0x0D, 0x00, 0xF2, 0x7A, 0x10, 0x00, 0xF2, - 0xC2, 0x0F, 0x00, 0xF2, 0x76, 0x0A, 0x1E, 0x1C, 0xD5, 0x10, 0x00, 0x16, 0x69, 0x08, 0x48, 0xE4, - 0x04, 0x00, 0x64, 0x12, 0x48, 0xE4, 0x02, 0x00, 0x20, 0x12, 0x48, 0xE4, 0x03, 0x00, 0x1A, 0x12, - 0x48, 0xE4, 0x08, 0x00, 0x14, 0x12, 0x48, 0xE4, 0x01, 0x00, 0xF0, 0x12, 0x48, 0xE4, 0x07, 0x00, - 0x12, 0x12, 0x01, 0xE6, 0x07, 0x00, 0x00, 0xF2, 0x4A, 0x0D, 0x00, 0xF2, 0x0E, 0x11, 0x05, 0xF0, - 0x5C, 0x0B, 0x00, 0x16, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xEA, 0x99, 0x00, 0x02, 0x80, 0x48, 0xE4, - 0x03, 0x00, 0xB9, 0x12, 0x48, 0xE4, 0x06, 0x00, 0xB3, 0x12, 0x01, 0xE6, 0x06, 0x00, 0x00, 0xF2, - 0x4A, 0x0D, 0x00, 0xF2, 0x0E, 0x11, 0x04, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00, 0x01, 0xF0, - 0x18, 0x0A, 0x00, 0xF0, 0xFE, 0x09, 0x00, 0x16, 0x02, 0x80, 0x48, 0xE4, 0x10, 0x00, 0x1C, 0x12, - 0x82, 0xE7, 0x08, 0x00, 0x3C, 0x56, 0x03, 0x82, 0x00, 0xF2, 0xDE, 0x0D, 0x30, 0xE7, 0x08, 0x00, - 0x04, 0xF7, 0x70, 0x01, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF0, 0x80, 0x01, 0x6C, 0x19, 0xED, 0x19, - 0x5D, 0xF0, 0xD0, 0x0B, 0x44, 0x55, 0xE5, 0x55, 0x59, 0xF0, 0x4E, 0x0C, 0x04, 0x55, 0xA5, 0x55, - 0x1F, 0x80, 0x01, 0xEC, 0xB8, 0x00, 0x82, 0x48, 0x82, 0x80, 0x49, 0x44, 0x2E, 0x13, 0x01, 0xEC, - 0xB8, 0x00, 0x41, 0xE4, 0x02, 0x00, 0x01, 0xEA, 0xB8, 0x00, 0x49, 0xE4, 0x11, 0x00, 0x59, 0xF0, - 0x2A, 0x0C, 0x01, 0xE6, 0x17, 0x00, 0x01, 0xEA, 0xB8, 0x00, 0x02, 0x4B, 0x88, 0x90, 0xAC, 0x50, - 0x8A, 0x90, 0xAE, 0x50, 0x01, 0xEC, 0xB8, 0x00, 0x82, 0x48, 0x82, 0x80, 0x10, 0x44, 0x02, 0x4B, - 0x1F, 0x40, 0xC0, 0x44, 0x00, 0xF2, 0xB0, 0x0D, 0x04, 0x55, 0xA5, 0x55, 0x9F, 0x10, 0x0C, 0x51, - 0x8E, 0x51, 0x30, 0x90, 0xB2, 0x90, 0x00, 0x56, 0xA1, 0x56, 0x30, 0x50, 0xB2, 0x50, 0x34, 0x90, - 0xB6, 0x90, 0x40, 0x56, 0xE1, 0x56, 0x34, 0x50, 0xB6, 0x50, 0x65, 0x10, 0xB1, 0xF0, 0x6C, 0x0C, - 0x85, 0xF0, 0xC6, 0x0B, 0xE9, 0x09, 0x4B, 0xE4, 0x03, 0x00, 0x78, 0x12, 0x4B, 0xE4, 0x02, 0x00, - 0x01, 0x13, 0xB1, 0xF0, 0x82, 0x0C, 0x85, 0xF0, 0xC6, 0x0B, 0x69, 0x08, 0x48, 0xE4, 0x03, 0x00, - 0xD5, 0xF0, 0x82, 0x0B, 0x00, 0xF2, 0x0E, 0x11, 0x85, 0xF0, 0xC6, 0x0B, 0xE8, 0x09, 0x3C, 0x56, - 0x00, 0xFC, 0x20, 0x01, 0x98, 0x57, 0x02, 0x13, 0xBB, 0x45, 0x4B, 0xE4, 0x00, 0x00, 0x08, 0x12, - 0x03, 0xE6, 0x01, 0x00, 0x04, 0xF6, 0x00, 0x80, 0xA8, 0x14, 0xD2, 0x14, 0x30, 0x1C, 0x02, 0x80, - 0x48, 0xE4, 0x03, 0x00, 0x10, 0x13, 0x00, 0xFC, 0xB6, 0x00, 0x98, 0x57, 0x02, 0x13, 0x4C, 0x1C, - 0x3E, 0x1C, 0x00, 0xF0, 0x8A, 0x0B, 0x00, 0xFC, 0x24, 0x01, 0xB0, 0x57, 0x00, 0xFA, 0x24, 0x01, - 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF2, 0x0E, 0x11, 0x86, 0xF0, 0x8A, 0x0B, 0x00, 0xF2, 0x88, 0x0E, - 0x00, 0xF0, 0x8A, 0x0B, 0xB1, 0xF0, 0xF4, 0x0C, 0x85, 0xF0, 0x82, 0x0B, 0x69, 0x08, 0x48, 0xE4, - 0x01, 0x00, 0xD5, 0xF0, 0x82, 0x0B, 0xFC, 0x14, 0x42, 0x58, 0x6C, 0x14, 0x80, 0x14, 0x30, 0x1C, - 0x4A, 0xF4, 0x02, 0x00, 0x55, 0xF0, 0x82, 0x0B, 0x4A, 0xF4, 0x01, 0x00, 0x0E, 0x12, 0x02, 0x80, - 0x48, 0xE4, 0x03, 0x00, 0x06, 0x13, 0x3E, 0x1C, 0x00, 0xF0, 0x8A, 0x0B, 0x00, 0xFC, 0xB6, 0x00, - 0xB0, 0x57, 0x00, 0xFA, 0xB6, 0x00, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF2, 0x0E, 0x11, 0x86, 0xF0, - 0x8A, 0x0B, 0x00, 0xF2, 0xB6, 0x0E, 0x00, 0xF0, 0x8A, 0x0B, 0x4C, 0x1C, 0xB1, 0xF0, 0x4C, 0x0D, - 0x85, 0xF0, 0x58, 0x0D, 0x69, 0x08, 0xF3, 0x10, 0x86, 0xF0, 0x60, 0x0D, 0x4E, 0x1C, 0x89, 0x48, - 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, - 0xFF, 0x7F, 0x30, 0x56, 0x00, 0x5C, 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, - 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, 0x00, 0x80, 0x30, 0x56, 0x00, 0x5C, 0x00, 0x16, 0x00, 0xF6, - 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x0B, 0x58, 0x00, 0x16, 0x03, 0xF6, - 0x24, 0x01, 0x00, 0xF2, 0x54, 0x0A, 0x03, 0xF6, 0xB6, 0x00, 0x00, 0xF2, 0x54, 0x0A, 0x00, 0x16, - 0x02, 0xEC, 0xB8, 0x00, 0x02, 0x49, 0x18, 0xF4, 0xFF, 0x00, 0x00, 0x54, 0x00, 0x54, 0x00, 0x54, - 0x00, 0xF4, 0x08, 0x00, 0xE1, 0x18, 0x80, 0x54, 0x03, 0x58, 0x00, 0xDD, 0x01, 0xDD, 0x02, 0xDD, - 0x03, 0xDC, 0x02, 0x4B, 0x30, 0x50, 0xB2, 0x50, 0x34, 0x51, 0xB6, 0x51, 0x00, 0x16, 0x45, 0x5A, - 0x1D, 0xF4, 0xFF, 0x00, 0x85, 0x56, 0x85, 0x56, 0x85, 0x56, 0x05, 0xF4, 0x02, 0x12, 0x83, 0x5A, - 0x00, 0x16, 0x1D, 0xF4, 0xFF, 0x00, 0x85, 0x56, 0x85, 0x56, 0x85, 0x56, 0x05, 0xF4, 0x00, 0x12, - 0x83, 0x5A, 0x00, 0x16, 0x38, 0x54, 0xBB, 0x55, 0x3C, 0x56, 0xBD, 0x56, 0x00, 0xF2, 0x0E, 0x11, - 0x85, 0xF0, 0x7E, 0x0E, 0xE9, 0x09, 0xC1, 0x59, 0x00, 0xF2, 0x0E, 0x11, 0x85, 0xF0, 0x7E, 0x0E, - 0xE8, 0x0A, 0x83, 0x55, 0x83, 0x55, 0x4B, 0xF4, 0x90, 0x01, 0x5C, 0xF0, 0x32, 0x0E, 0xBD, 0x56, - 0x40, 0x10, 0x4B, 0xF4, 0x30, 0x00, 0x59, 0xF0, 0x44, 0x0E, 0x01, 0xF6, 0x0C, 0x00, 0x00, 0xF6, - 0x01, 0x00, 0x2E, 0x10, 0x02, 0xFC, 0x9C, 0x00, 0x9A, 0x57, 0x14, 0x13, 0x4B, 0xF4, 0x64, 0x00, - 0x59, 0xF0, 0x60, 0x0E, 0x03, 0xF6, 0x64, 0x00, 0x01, 0xF6, 0x19, 0x00, 0x00, 0xF6, 0x01, 0x00, - 0x43, 0xF4, 0x33, 0x00, 0x56, 0xF0, 0x72, 0x0E, 0x04, 0xF4, 0x00, 0x01, 0x43, 0xF4, 0x19, 0x00, - 0xF3, 0x10, 0xB4, 0x56, 0xC3, 0x58, 0x02, 0xFC, 0x9E, 0x00, 0x9A, 0x57, 0x08, 0x13, 0x3C, 0x56, - 0x00, 0xF6, 0x02, 0x00, 0x00, 0x16, 0x00, 0x16, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, 0x0E, 0x11, - 0x86, 0xF0, 0xB4, 0x0E, 0x09, 0xE7, 0x02, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x86, 0xF0, 0xB4, 0x0E, - 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x86, 0xF0, 0xB4, 0x0E, 0x4E, 0x1C, 0x89, 0x49, - 0x00, 0xF2, 0x0E, 0x11, 0x00, 0x16, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x86, 0xF0, - 0xEE, 0x0E, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x86, 0xF0, 0xEE, 0x0E, 0x09, 0xE7, - 0x01, 0x00, 0x00, 0xF2, 0x0E, 0x11, 0x86, 0xF0, 0xEE, 0x0E, 0x89, 0x49, 0x00, 0xF2, 0x0E, 0x11, - 0x86, 0xF0, 0xEE, 0x0E, 0x4E, 0x1C, 0x89, 0x4A, 0x00, 0xF2, 0x0E, 0x11, 0x00, 0x16, 0x3C, 0x56, - 0x00, 0x16, 0x00, 0xEC, 0x26, 0x01, 0x48, 0xE4, 0x01, 0x00, 0x1E, 0x13, 0x38, 0x44, 0x00, 0xEA, - 0x26, 0x01, 0x49, 0xF4, 0x00, 0x00, 0x04, 0x12, 0x4E, 0x1C, 0x02, 0x10, 0x4C, 0x1C, 0x01, 0xEC, - 0x27, 0x01, 0x89, 0x48, 0x00, 0xF2, 0x0E, 0x11, 0x02, 0x14, 0x00, 0x16, 0x85, 0xF0, 0x4E, 0x0F, - 0x38, 0x54, 0x00, 0xEA, 0x99, 0x00, 0x00, 0xF2, 0x5C, 0x0B, 0x02, 0x80, 0x48, 0xE4, 0x06, 0x00, - 0x1C, 0x13, 0x00, 0xEC, 0x99, 0x00, 0x48, 0xE4, 0x01, 0x00, 0x0A, 0x12, 0x04, 0x80, 0x30, 0xE4, - 0x01, 0x00, 0x04, 0x40, 0x08, 0x10, 0x04, 0x80, 0x18, 0xE4, 0xFE, 0x00, 0x04, 0x40, 0x00, 0x16, - 0x02, 0xF6, 0xE0, 0x00, 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x81, 0x48, 0x22, 0x12, 0x00, 0x4E, - 0x83, 0x5A, 0x90, 0x4C, 0x20, 0xE7, 0x00, 0x00, 0xC3, 0x58, 0x1B, 0xF4, 0xFF, 0x00, 0x83, 0x55, - 0x83, 0x55, 0x83, 0x55, 0x03, 0xF4, 0x00, 0x12, 0x8B, 0x55, 0x83, 0x59, 0x00, 0x4E, 0x00, 0x16, - 0x00, 0x4E, 0x02, 0xF6, 0xF0, 0x00, 0x02, 0x57, 0x03, 0x59, 0x00, 0x4E, 0x83, 0x5A, 0x30, 0xE7, - 0x00, 0x00, 0x20, 0xE7, 0x00, 0x00, 0x00, 0x16, 0x02, 0xF6, 0xF0, 0x00, 0x02, 0x57, 0x03, 0x59, - 0x01, 0xCC, 0x00, 0x4E, 0x83, 0x5A, 0x30, 0xE7, 0x00, 0x00, 0x80, 0x4C, 0xC3, 0x58, 0x1B, 0xF4, - 0xFF, 0x00, 0x83, 0x55, 0x83, 0x55, 0x83, 0x55, 0x03, 0xF4, 0x00, 0x12, 0x83, 0x59, 0x00, 0x4E, - 0x00, 0x16, 0x03, 0xF6, 0xE0, 0x00, 0x03, 0x57, 0x83, 0x59, 0x3A, 0x55, 0x02, 0xCC, 0x45, 0x5A, - 0x00, 0xF2, 0xF2, 0x0D, 0xC0, 0x5A, 0x40, 0x5C, 0x38, 0x54, 0x00, 0xCD, 0x01, 0xCC, 0x4A, 0x46, - 0x0A, 0x13, 0x83, 0x59, 0x00, 0x4C, 0x01, 0x48, 0x16, 0x13, 0x0C, 0x10, 0xC5, 0x58, 0x00, 0xF2, - 0xF2, 0x0D, 0x00, 0x4C, 0x01, 0x48, 0x08, 0x13, 0x05, 0xF6, 0xF0, 0x00, 0x05, 0x57, 0x08, 0x10, - 0x45, 0x58, 0x00, 0xF2, 0xF2, 0x0D, 0x8D, 0x56, 0x83, 0x5A, 0x80, 0x4C, 0x05, 0x17, 0x00, 0x16, - 0x02, 0x4B, 0x06, 0xF7, 0x04, 0x00, 0x62, 0x0B, 0x03, 0x82, 0x00, 0xF2, 0xDE, 0x0D, 0x02, 0x80, - 0x00, 0x4C, 0x45, 0xF4, 0x02, 0x00, 0x52, 0x14, 0x06, 0xF7, 0x02, 0x00, 0x06, 0x14, 0x00, 0xF2, - 0x50, 0x0F, 0x00, 0x16, 0x02, 0x4B, 0x01, 0xF6, 0xFF, 0x00, 0x38, 0x1C, 0x05, 0xF4, 0x04, 0x00, - 0x83, 0x5A, 0x18, 0xDF, 0x19, 0xDF, 0x1D, 0xF7, 0x3C, 0x00, 0xB8, 0xF0, 0x4A, 0x10, 0x9C, 0x14, - 0x01, 0x48, 0x1C, 0x13, 0x0E, 0xF7, 0x3C, 0x00, 0x03, 0xF7, 0x04, 0x00, 0xAF, 0x19, 0x03, 0x42, - 0x45, 0xF4, 0x02, 0x00, 0x83, 0x5A, 0x02, 0xCC, 0x02, 0x41, 0x45, 0xF4, 0x02, 0x00, 0x00, 0x16, - 0x91, 0x44, 0xD5, 0xF0, 0x3A, 0x10, 0x00, 0xF0, 0x9E, 0x02, 0x01, 0xF6, 0xFF, 0x00, 0x38, 0x1C, - 0x05, 0xF4, 0x04, 0x00, 0x83, 0x5A, 0x18, 0xDF, 0x19, 0xDF, 0x0E, 0xF7, 0x3C, 0x00, 0x03, 0xF7, - 0x04, 0x00, 0x0F, 0x79, 0x1C, 0xF7, 0x3C, 0x00, 0xB8, 0xF0, 0x98, 0x10, 0x4E, 0x14, 0x01, 0x48, - 0x06, 0x13, 0x45, 0xF4, 0x04, 0x00, 0x00, 0x16, 0x91, 0x44, 0xD5, 0xF0, 0x7E, 0x10, 0x00, 0xF0, - 0x9E, 0x02, 0x02, 0xF6, 0xFF, 0x00, 0x38, 0x1C, 0x2C, 0xBC, 0xAE, 0xBC, 0xE2, 0x08, 0x00, 0xEC, - 0xB8, 0x00, 0x02, 0x48, 0x1D, 0xF7, 0x80, 0x00, 0xB8, 0xF0, 0xC8, 0x10, 0x1E, 0x14, 0x01, 0x48, - 0x0E, 0x13, 0x0E, 0xF7, 0x80, 0x00, 0x38, 0x54, 0x03, 0x58, 0xAF, 0x19, 0x82, 0x48, 0x00, 0x16, - 0x82, 0x48, 0x12, 0x45, 0xD5, 0xF0, 0xB6, 0x10, 0x00, 0xF0, 0x9E, 0x02, 0x39, 0xF0, 0xF4, 0x10, - 0x38, 0x44, 0x00, 0x16, 0x7E, 0x18, 0x18, 0xF4, 0x03, 0x00, 0x04, 0x13, 0x61, 0x18, 0x00, 0x16, - 0x38, 0x1C, 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0xF1, 0x12, 0xE3, 0x10, 0x30, 0x44, - 0x30, 0x44, 0x30, 0x44, 0xB1, 0xF0, 0x14, 0x11, 0x00, 0x16, 0x3E, 0x57, 0x03, 0xF6, 0xE0, 0x00, - 0x03, 0x57, 0x83, 0x59, 0x04, 0xCC, 0x01, 0x4A, 0x6A, 0x12, 0x45, 0x5A, 0x00, 0xF2, 0xF2, 0x0D, - 0x02, 0x4B, 0x70, 0x14, 0x34, 0x13, 0x02, 0x80, 0x48, 0xE4, 0x08, 0x00, 0x18, 0x12, 0x9C, 0xE7, - 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00, 0x00, 0xF2, 0xC2, 0x0F, 0x00, 0xF2, 0x76, 0x0A, 0x1E, 0x1C, - 0x01, 0xF6, 0x01, 0x00, 0x00, 0x16, 0x30, 0xE4, 0x10, 0x00, 0x04, 0x40, 0x00, 0xF2, 0xDE, 0x0D, - 0x20, 0xE7, 0x01, 0x00, 0x01, 0xF6, 0x01, 0x00, 0x00, 0x16, 0x04, 0xDC, 0x01, 0x4A, 0x24, 0x12, - 0x45, 0x5A, 0x00, 0xF2, 0xF2, 0x0D, 0x43, 0x5B, 0x06, 0xEC, 0x98, 0x00, 0x00, 0xF2, 0x34, 0x10, - 0xC6, 0x59, 0x20, 0x14, 0x0A, 0x13, 0x00, 0xF2, 0xC2, 0x0F, 0x00, 0xF2, 0x10, 0x10, 0xA7, 0x10, - 0x83, 0x5A, 0xD7, 0x10, 0x0E, 0x47, 0x07, 0xE6, 0x10, 0x00, 0xCE, 0x47, 0x5A, 0xF0, 0x1C, 0x11, - 0xB9, 0x54, 0x00, 0x16, 0x14, 0x90, 0x96, 0x90, 0x02, 0xFC, 0xA8, 0x00, 0x03, 0xFC, 0xAA, 0x00, - 0x48, 0x55, 0x02, 0x13, 0xC9, 0x55, 0x00, 0x16, 0x00, 0xEC, 0xBA, 0x00, 0x10, 0x44, 0x00, 0xEA, - 0xBA, 0x00, 0x00, 0x16, 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x64, 0x0A, 0x10, 0x44, 0x00, 0x4C, - 0x00, 0x16 }; + 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x08, 0x44, 0x00, 0x4C, 0x82, 0xE7, 0x02, 0x00, + 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x70, 0x03, 0x00, 0xF2, 0x60, 0x0B, + 0x06, 0xF0, 0x80, 0x03, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A, + 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0x55, 0xF0, 0xAC, 0x04, 0x01, 0xE6, 0x0C, 0x00, 0x00, 0xF2, + 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x01, 0xF0, + 0x7C, 0x02, 0x00, 0xF0, 0x8A, 0x02, 0x46, 0x1C, 0x0C, 0x1C, 0x67, 0x1B, 0xBF, 0x57, 0x77, 0x57, + 0x02, 0x4B, 0x48, 0x1C, 0x32, 0x1C, 0x00, 0xF2, 0x92, 0x0D, 0x30, 0x1C, 0x96, 0xF0, 0xBC, 0x03, + 0xB1, 0xF0, 0xC0, 0x03, 0x1E, 0xF0, 0xFC, 0x09, 0x85, 0xF0, 0x02, 0x0A, 0x00, 0xFC, 0xBE, 0x00, + 0x98, 0x57, 0x14, 0x12, 0x01, 0xE6, 0x0C, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, + 0x01, 0xF0, 0x7C, 0x02, 0x00, 0xF0, 0x8A, 0x02, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, + 0x01, 0x48, 0x55, 0xF0, 0x98, 0x04, 0x03, 0x82, 0x03, 0xFC, 0xA0, 0x00, 0x9B, 0x57, 0x40, 0x12, + 0x69, 0x18, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x42, 0x04, 0x69, 0x08, 0x00, 0xF2, 0x12, 0x11, + 0x85, 0xF0, 0x02, 0x0A, 0x68, 0x08, 0x4C, 0x44, 0x28, 0x12, 0x44, 0x48, 0x03, 0xF6, 0xE0, 0x00, + 0x00, 0xF2, 0x68, 0x0A, 0x45, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xCC, 0x01, 0x48, 0x55, 0xF0, + 0x98, 0x04, 0x4C, 0x44, 0xEF, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, 0x14, 0x10, 0x08, 0x10, + 0x68, 0x18, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0x04, 0x80, 0x18, 0xE4, 0x10, 0x00, 0x28, 0x12, + 0x01, 0xE6, 0x06, 0x00, 0x04, 0x80, 0x18, 0xE4, 0x01, 0x00, 0x04, 0x12, 0x01, 0xE6, 0x0D, 0x00, + 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x04, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00, + 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x69, 0x08, 0x05, 0x80, 0x48, 0xE4, 0x00, 0x00, + 0x0C, 0x12, 0x00, 0xE6, 0x11, 0x00, 0x00, 0xEA, 0xB8, 0x00, 0x00, 0xF2, 0xB6, 0x10, 0x82, 0xE7, + 0x02, 0x00, 0x1C, 0x90, 0x40, 0x5C, 0x00, 0x16, 0x01, 0xE6, 0x06, 0x00, 0x00, 0xF2, 0x4E, 0x0D, + 0x01, 0xF0, 0x80, 0x01, 0x1E, 0xF0, 0x80, 0x01, 0x00, 0xF0, 0xA0, 0x04, 0x42, 0x5B, 0x06, 0xF7, + 0x03, 0x00, 0x46, 0x59, 0xBF, 0x57, 0x77, 0x57, 0x01, 0xE6, 0x80, 0x00, 0x07, 0x80, 0x31, 0x44, + 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x56, 0x13, 0x20, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x4E, 0x12, + 0x00, 0xFC, 0xA2, 0x00, 0x98, 0x57, 0x55, 0xF0, 0x1C, 0x05, 0x31, 0xE4, 0x40, 0x00, 0x00, 0xFC, + 0xA0, 0x00, 0x98, 0x57, 0x36, 0x12, 0x4C, 0x1C, 0x00, 0xF2, 0x12, 0x11, 0x89, 0x48, 0x00, 0xF2, + 0x12, 0x11, 0x86, 0xF0, 0x2E, 0x05, 0x82, 0xE7, 0x06, 0x00, 0x1B, 0x80, 0x48, 0xE4, 0x22, 0x00, + 0x5B, 0xF0, 0x0C, 0x05, 0x48, 0xE4, 0x20, 0x00, 0x59, 0xF0, 0x10, 0x05, 0x00, 0xE6, 0x20, 0x00, + 0x09, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0x2E, 0x05, 0x83, 0x80, 0x04, 0x10, 0x00, 0xF2, + 0xA2, 0x0D, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xEA, 0x26, 0x01, 0x01, 0xEA, 0x27, 0x01, 0x04, 0x80, + 0x18, 0xE4, 0x10, 0x00, 0x36, 0x12, 0xB9, 0x54, 0x00, 0xF2, 0xF6, 0x0E, 0x01, 0xE6, 0x06, 0x00, + 0x04, 0x80, 0x18, 0xE4, 0x01, 0x00, 0x04, 0x12, 0x01, 0xE6, 0x0D, 0x00, 0x00, 0xF2, 0x4E, 0x0D, + 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x04, 0xE6, 0x02, 0x00, + 0x9E, 0xE7, 0x15, 0x00, 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0xFC, 0x20, 0x01, + 0x98, 0x57, 0x34, 0x12, 0x00, 0xFC, 0x24, 0x01, 0x98, 0x57, 0x2C, 0x13, 0xB9, 0x54, 0x00, 0xF2, + 0xF6, 0x0E, 0x86, 0xF0, 0xA8, 0x05, 0x03, 0xF6, 0x01, 0x00, 0x00, 0xF2, 0x8C, 0x0E, 0x85, 0xF0, + 0x9E, 0x05, 0x82, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x82, 0xE7, 0x02, 0x00, 0x00, 0xFC, + 0x24, 0x01, 0xB0, 0x57, 0x00, 0xFA, 0x24, 0x01, 0x00, 0xFC, 0x9E, 0x00, 0x98, 0x57, 0x5A, 0x12, + 0x00, 0xFC, 0xB6, 0x00, 0x98, 0x57, 0x52, 0x13, 0x03, 0xE6, 0x0C, 0x00, 0x00, 0xFC, 0x9C, 0x00, + 0x98, 0x57, 0x04, 0x13, 0x03, 0xE6, 0x19, 0x00, 0x05, 0xE6, 0x08, 0x00, 0x00, 0xF6, 0x00, 0x01, + 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, 0x00, 0x80, 0x04, 0x13, 0x05, 0xE6, + 0x0F, 0x00, 0xB9, 0x54, 0x00, 0xF2, 0xF6, 0x0E, 0x86, 0xF0, 0x0A, 0x06, 0x00, 0xF2, 0xBA, 0x0E, + 0x85, 0xF0, 0x00, 0x06, 0x82, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x82, 0xE7, 0x02, 0x00, + 0x00, 0xFC, 0xB6, 0x00, 0xB0, 0x57, 0x00, 0xFA, 0xB6, 0x00, 0x01, 0xF6, 0x01, 0x00, 0x00, 0xF2, + 0xF6, 0x0E, 0x9C, 0x32, 0x4E, 0x1C, 0x32, 0x1C, 0x00, 0xF2, 0x92, 0x0D, 0x30, 0x1C, 0x82, 0xE7, + 0x04, 0x00, 0xB1, 0xF0, 0x22, 0x06, 0x0A, 0xF0, 0x3E, 0x06, 0x05, 0xF0, 0xD6, 0x06, 0x06, 0xF0, + 0xDC, 0x06, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A, 0x04, 0x80, + 0x18, 0xE4, 0x20, 0x00, 0x30, 0x12, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x21, 0x80, + 0x18, 0xE4, 0xE0, 0x00, 0x09, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, + 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x99, 0xA4, 0x00, 0xF2, 0x12, 0x11, + 0x09, 0xE7, 0x00, 0x00, 0x9A, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x34, 0x12, 0x09, 0xE7, + 0x1B, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x21, 0x80, 0x18, 0xE4, 0xE0, 0x00, 0x09, 0x48, 0x00, 0xF2, + 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, + 0x12, 0x11, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF0, + 0x0C, 0x09, 0xBB, 0x55, 0x9A, 0x81, 0x03, 0xF7, 0x20, 0x00, 0x09, 0x6F, 0x93, 0x45, 0x55, 0xF0, + 0xE2, 0x06, 0xB1, 0xF0, 0xC2, 0x06, 0x0A, 0xF0, 0xBA, 0x06, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, + 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0xF2, 0x60, 0x0B, 0x47, 0x10, 0x09, 0xE7, 0x08, 0x00, + 0x41, 0x10, 0x05, 0x80, 0x48, 0xE4, 0x00, 0x00, 0x1E, 0x12, 0x00, 0xE6, 0x11, 0x00, 0x00, 0xEA, + 0xB8, 0x00, 0x00, 0xF2, 0xB6, 0x10, 0x2C, 0x90, 0xAE, 0x90, 0x08, 0x50, 0x8A, 0x50, 0x38, 0x54, + 0x1F, 0x40, 0x00, 0xF2, 0xB4, 0x0D, 0x08, 0x10, 0x08, 0x90, 0x8A, 0x90, 0x30, 0x50, 0xB2, 0x50, + 0x9C, 0x32, 0x0C, 0x92, 0x8E, 0x92, 0x38, 0x54, 0x04, 0x80, 0x30, 0xE4, 0x08, 0x00, 0x04, 0x40, + 0x0C, 0x1C, 0x00, 0xF6, 0x03, 0x00, 0xB1, 0xF0, 0x26, 0x07, 0x9E, 0xF0, 0x3A, 0x07, 0x01, 0x48, + 0x55, 0xF0, 0xFC, 0x09, 0x0C, 0x1C, 0x10, 0x44, 0xED, 0x10, 0x0B, 0xF0, 0x5E, 0x07, 0x0C, 0xF0, + 0x62, 0x07, 0x05, 0xF0, 0x52, 0x07, 0x06, 0xF0, 0x58, 0x07, 0x09, 0xF0, 0x24, 0x09, 0x00, 0xF0, + 0x02, 0x0A, 0x00, 0xF2, 0x60, 0x0B, 0xCF, 0x10, 0x09, 0xE7, 0x08, 0x00, 0xC9, 0x10, 0x2E, 0x1C, + 0x02, 0x10, 0x2C, 0x1C, 0xAA, 0xF0, 0x64, 0x07, 0xAC, 0xF0, 0x72, 0x07, 0x40, 0x10, 0x34, 0x1C, + 0xF3, 0x10, 0xAD, 0xF0, 0x7C, 0x07, 0xC8, 0x10, 0x36, 0x1C, 0xE9, 0x10, 0x2B, 0xF0, 0x82, 0x08, + 0x6B, 0x18, 0x18, 0xF4, 0x00, 0xFE, 0x20, 0x12, 0x01, 0x58, 0xD2, 0xF0, 0x82, 0x08, 0x76, 0x18, + 0x18, 0xF4, 0x03, 0x00, 0xEC, 0x12, 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0xE2, 0x12, + 0x0B, 0xF0, 0x64, 0x07, 0x0C, 0xF0, 0x64, 0x07, 0x36, 0x1C, 0x34, 0x1C, 0xB7, 0x10, 0x38, 0x54, + 0xB9, 0x54, 0x84, 0x80, 0x19, 0xE4, 0x20, 0x00, 0xB2, 0x13, 0x85, 0x80, 0x81, 0x48, 0x66, 0x12, + 0x04, 0x80, 0x18, 0xE4, 0x08, 0x00, 0x58, 0x13, 0x1F, 0x80, 0x08, 0x44, 0xC8, 0x44, 0x9F, 0x12, + 0x1F, 0x40, 0x34, 0x91, 0xB6, 0x91, 0x44, 0x55, 0xE5, 0x55, 0x02, 0xEC, 0xB8, 0x00, 0x02, 0x49, + 0xBB, 0x55, 0x82, 0x81, 0xC0, 0x55, 0x48, 0xF4, 0x0F, 0x00, 0x5A, 0xF0, 0x1A, 0x08, 0x4A, 0xE4, + 0x17, 0x00, 0xD5, 0xF0, 0xFA, 0x07, 0x02, 0xF6, 0x0F, 0x00, 0x02, 0xF4, 0x02, 0x00, 0x02, 0xEA, + 0xB8, 0x00, 0x04, 0x91, 0x86, 0x91, 0x02, 0x4B, 0x2C, 0x90, 0x08, 0x50, 0x2E, 0x90, 0x0A, 0x50, + 0x2C, 0x51, 0xAE, 0x51, 0x00, 0xF2, 0xB6, 0x10, 0x38, 0x54, 0x00, 0xF2, 0xB4, 0x0D, 0x56, 0x10, + 0x34, 0x91, 0xB6, 0x91, 0x0C, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x08, 0x00, 0x41, 0x12, 0x0C, 0x91, + 0x8E, 0x91, 0x04, 0x80, 0x18, 0xE4, 0xF7, 0x00, 0x04, 0x40, 0x30, 0x90, 0xB2, 0x90, 0x36, 0x10, + 0x02, 0x80, 0x48, 0xE4, 0x10, 0x00, 0x31, 0x12, 0x82, 0xE7, 0x10, 0x00, 0x84, 0x80, 0x19, 0xE4, + 0x20, 0x00, 0x10, 0x13, 0x0C, 0x90, 0x8E, 0x90, 0x5D, 0xF0, 0x78, 0x07, 0x0C, 0x58, 0x8D, 0x58, + 0x00, 0xF0, 0x64, 0x07, 0x38, 0x54, 0xB9, 0x54, 0x19, 0x80, 0xF1, 0x10, 0x3A, 0x55, 0x19, 0x81, + 0xBB, 0x55, 0x10, 0x90, 0x92, 0x90, 0x10, 0x58, 0x91, 0x58, 0x14, 0x59, 0x95, 0x59, 0x00, 0xF0, + 0x64, 0x07, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x06, 0x12, 0x6C, 0x19, 0x19, 0x41, 0x7C, 0x10, + 0x6C, 0x19, 0x0C, 0x51, 0xED, 0x19, 0x8E, 0x51, 0x6B, 0x18, 0x18, 0xF4, 0x00, 0xFF, 0x02, 0x13, + 0x6A, 0x10, 0x01, 0x58, 0xD2, 0xF0, 0xC0, 0x08, 0x76, 0x18, 0x18, 0xF4, 0x03, 0x00, 0x0A, 0x12, + 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0x06, 0x13, 0x9E, 0xE7, 0x16, 0x00, 0x4C, 0x10, + 0xD1, 0xF0, 0xCA, 0x08, 0x9E, 0xE7, 0x17, 0x00, 0x42, 0x10, 0xD0, 0xF0, 0xD4, 0x08, 0x9E, 0xE7, + 0x19, 0x00, 0x38, 0x10, 0xCF, 0xF0, 0xDE, 0x08, 0x9E, 0xE7, 0x20, 0x00, 0x2E, 0x10, 0xCE, 0xF0, + 0xE8, 0x08, 0x9E, 0xE7, 0x21, 0x00, 0x24, 0x10, 0xCD, 0xF0, 0xF2, 0x08, 0x9E, 0xE7, 0x22, 0x00, + 0x1A, 0x10, 0xCC, 0xF0, 0x04, 0x09, 0x84, 0x80, 0x19, 0xE4, 0x04, 0x00, 0x06, 0x12, 0x9E, 0xE7, + 0x12, 0x00, 0x08, 0x10, 0xCB, 0xF0, 0x0C, 0x09, 0x9E, 0xE7, 0x24, 0x00, 0xB1, 0xF0, 0x0C, 0x09, + 0x05, 0xF0, 0x1E, 0x09, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0xE4, 0x10, 0x00, 0xF2, + 0x60, 0x0B, 0xE9, 0x10, 0x9C, 0x32, 0x82, 0xE7, 0x20, 0x00, 0x32, 0x1C, 0xE9, 0x09, 0x00, 0xF2, + 0x12, 0x11, 0x85, 0xF0, 0x02, 0x0A, 0x69, 0x08, 0x01, 0xF0, 0x44, 0x09, 0x1E, 0xF0, 0xFC, 0x09, + 0x00, 0xF0, 0x38, 0x09, 0x30, 0x44, 0x06, 0x12, 0x9E, 0xE7, 0x42, 0x00, 0xB8, 0x10, 0x04, 0xF6, + 0x01, 0x00, 0xB3, 0x45, 0x74, 0x12, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x22, 0x13, 0x4B, 0xE4, + 0x02, 0x00, 0x36, 0x12, 0x4B, 0xE4, 0x28, 0x00, 0xAC, 0x13, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, + 0xC8, 0x11, 0x03, 0xF6, 0xD0, 0x00, 0xFA, 0x14, 0x82, 0xE7, 0x01, 0x00, 0x00, 0xF0, 0x80, 0x01, + 0x9E, 0xE7, 0x44, 0x00, 0x4B, 0xE4, 0x02, 0x00, 0x06, 0x12, 0x03, 0xE6, 0x02, 0x00, 0x76, 0x10, + 0x00, 0xF2, 0xA2, 0x0D, 0x03, 0xE6, 0x02, 0x00, 0x6C, 0x10, 0x00, 0xF2, 0xA2, 0x0D, 0x19, 0x82, + 0x34, 0x46, 0x0A, 0x13, 0x03, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x43, 0x00, 0x68, 0x10, 0x04, 0x80, + 0x30, 0xE4, 0x20, 0x00, 0x04, 0x40, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x82, 0xE7, + 0x01, 0x00, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF0, 0x08, 0x03, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, + 0x06, 0x12, 0x03, 0xE6, 0x02, 0x00, 0x3E, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x3A, 0x12, + 0x04, 0x80, 0x18, 0xE4, 0xFD, 0x00, 0x04, 0x40, 0x1C, 0x1C, 0x9D, 0xF0, 0xEA, 0x09, 0x1C, 0x1C, + 0x9D, 0xF0, 0xF0, 0x09, 0xC1, 0x10, 0x9E, 0xE7, 0x13, 0x00, 0x0A, 0x10, 0x9E, 0xE7, 0x41, 0x00, + 0x04, 0x10, 0x9E, 0xE7, 0x24, 0x00, 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0xD5, 0xF0, 0x8A, 0x02, + 0x04, 0xE6, 0x04, 0x00, 0x06, 0x10, 0x04, 0xE6, 0x04, 0x00, 0x9D, 0x41, 0x1C, 0x42, 0x9F, 0xE7, + 0x00, 0x00, 0x06, 0xF7, 0x02, 0x00, 0x03, 0xF6, 0xE0, 0x00, 0x3C, 0x14, 0x44, 0x58, 0x45, 0x58, + 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xF2, 0x7E, 0x10, 0x00, 0xF2, 0xC6, 0x0F, 0x3C, 0x14, 0x1E, 0x1C, + 0x00, 0xF0, 0x80, 0x01, 0x12, 0x1C, 0x22, 0x1C, 0xD2, 0x14, 0x00, 0xF0, 0x72, 0x01, 0x83, 0x59, + 0x03, 0xDC, 0x73, 0x57, 0x80, 0x5D, 0x00, 0x16, 0x83, 0x59, 0x03, 0xDC, 0x38, 0x54, 0x70, 0x57, + 0x33, 0x54, 0x3B, 0x54, 0x80, 0x5D, 0x00, 0x16, 0x03, 0x57, 0x83, 0x59, 0x38, 0x54, 0x00, 0xCC, + 0x00, 0x16, 0x03, 0x57, 0x83, 0x59, 0x00, 0x4C, 0x00, 0x16, 0x02, 0x80, 0x48, 0xE4, 0x01, 0x00, + 0x0E, 0x12, 0x48, 0xE4, 0x05, 0x00, 0x08, 0x12, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, + 0xC1, 0x5A, 0x3A, 0x55, 0x02, 0xEC, 0xB5, 0x00, 0x45, 0x59, 0x00, 0xF2, 0xF6, 0x0D, 0x83, 0x58, + 0x30, 0xE7, 0x00, 0x00, 0x10, 0x4D, 0x30, 0xE7, 0x40, 0x00, 0x10, 0x4F, 0x38, 0x90, 0xBA, 0x90, + 0x10, 0x5C, 0x80, 0x5C, 0x83, 0x5A, 0x10, 0x4E, 0x04, 0xEA, 0xB5, 0x00, 0x43, 0x5B, 0x03, 0xF4, + 0xE0, 0x00, 0x83, 0x59, 0x04, 0xCC, 0x01, 0x4A, 0x0A, 0x12, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, + 0x00, 0xF2, 0x38, 0x10, 0x00, 0x16, 0x08, 0x1C, 0x00, 0xFC, 0xAC, 0x00, 0x06, 0x58, 0x67, 0x18, + 0x18, 0xF4, 0x8F, 0xE1, 0x01, 0xFC, 0xAE, 0x00, 0x19, 0xF4, 0x70, 0x1E, 0xB0, 0x54, 0x07, 0x58, + 0x00, 0xFC, 0xB0, 0x00, 0x08, 0x58, 0x00, 0xFC, 0xB2, 0x00, 0x09, 0x58, 0x0A, 0x1C, 0x00, 0xE6, + 0x0F, 0x00, 0x00, 0xEA, 0xB9, 0x00, 0x38, 0x54, 0x00, 0xFA, 0x24, 0x01, 0x00, 0xFA, 0xB6, 0x00, + 0x18, 0x1C, 0x14, 0x1C, 0x10, 0x1C, 0x32, 0x1C, 0x12, 0x1C, 0x00, 0x16, 0x3E, 0x57, 0x0C, 0x14, + 0x0E, 0x47, 0x07, 0xE6, 0x10, 0x00, 0xCE, 0x47, 0xF5, 0x13, 0x00, 0x16, 0x00, 0xF2, 0xA2, 0x0D, + 0x02, 0x4B, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x20, 0x12, 0x44, 0x58, + 0x45, 0x58, 0x9E, 0xE7, 0x15, 0x00, 0x9C, 0xE7, 0x04, 0x00, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xF2, + 0x7E, 0x10, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, 0x7A, 0x0A, 0x1E, 0x1C, 0xD5, 0x10, 0x00, 0x16, + 0x69, 0x08, 0x48, 0xE4, 0x04, 0x00, 0x64, 0x12, 0x48, 0xE4, 0x02, 0x00, 0x20, 0x12, 0x48, 0xE4, + 0x03, 0x00, 0x1A, 0x12, 0x48, 0xE4, 0x08, 0x00, 0x14, 0x12, 0x48, 0xE4, 0x01, 0x00, 0xF0, 0x12, + 0x48, 0xE4, 0x07, 0x00, 0x12, 0x12, 0x01, 0xE6, 0x07, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, + 0x12, 0x11, 0x05, 0xF0, 0x60, 0x0B, 0x00, 0x16, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xEA, 0x99, 0x00, + 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0xE7, 0x12, 0x48, 0xE4, 0x06, 0x00, 0xE1, 0x12, 0x01, 0xE6, + 0x06, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x04, 0xE6, 0x02, 0x00, 0x9E, 0xE7, + 0x15, 0x00, 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0x16, 0x02, 0x80, 0x48, 0xE4, + 0x10, 0x00, 0x1C, 0x12, 0x82, 0xE7, 0x08, 0x00, 0x3C, 0x56, 0x03, 0x82, 0x00, 0xF2, 0xE2, 0x0D, + 0x30, 0xE7, 0x08, 0x00, 0x04, 0xF7, 0x70, 0x01, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF0, 0x80, 0x01, + 0x6C, 0x19, 0xED, 0x19, 0x5D, 0xF0, 0xD4, 0x0B, 0x44, 0x55, 0xE5, 0x55, 0x59, 0xF0, 0x52, 0x0C, + 0x04, 0x55, 0xA5, 0x55, 0x1F, 0x80, 0x01, 0xEC, 0xB8, 0x00, 0x82, 0x48, 0x82, 0x80, 0x49, 0x44, + 0x2E, 0x13, 0x01, 0xEC, 0xB8, 0x00, 0x41, 0xE4, 0x02, 0x00, 0x01, 0xEA, 0xB8, 0x00, 0x49, 0xE4, + 0x11, 0x00, 0x59, 0xF0, 0x2E, 0x0C, 0x01, 0xE6, 0x17, 0x00, 0x01, 0xEA, 0xB8, 0x00, 0x02, 0x4B, + 0x88, 0x90, 0xAC, 0x50, 0x8A, 0x90, 0xAE, 0x50, 0x01, 0xEC, 0xB8, 0x00, 0x82, 0x48, 0x82, 0x80, + 0x10, 0x44, 0x02, 0x4B, 0x1F, 0x40, 0xC0, 0x44, 0x00, 0xF2, 0xB4, 0x0D, 0x04, 0x55, 0xA5, 0x55, + 0x9F, 0x10, 0x0C, 0x51, 0x8E, 0x51, 0x30, 0x90, 0xB2, 0x90, 0x00, 0x56, 0xA1, 0x56, 0x30, 0x50, + 0xB2, 0x50, 0x34, 0x90, 0xB6, 0x90, 0x40, 0x56, 0xE1, 0x56, 0x34, 0x50, 0xB6, 0x50, 0x65, 0x10, + 0xB1, 0xF0, 0x70, 0x0C, 0x85, 0xF0, 0xCA, 0x0B, 0xE9, 0x09, 0x4B, 0xE4, 0x03, 0x00, 0x78, 0x12, + 0x4B, 0xE4, 0x02, 0x00, 0x01, 0x13, 0xB1, 0xF0, 0x86, 0x0C, 0x85, 0xF0, 0xCA, 0x0B, 0x69, 0x08, + 0x48, 0xE4, 0x03, 0x00, 0xD5, 0xF0, 0x86, 0x0B, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0xCA, 0x0B, + 0xE8, 0x09, 0x3C, 0x56, 0x00, 0xFC, 0x20, 0x01, 0x98, 0x57, 0x02, 0x13, 0xBB, 0x45, 0x4B, 0xE4, + 0x00, 0x00, 0x08, 0x12, 0x03, 0xE6, 0x01, 0x00, 0x04, 0xF6, 0x00, 0x80, 0xA8, 0x14, 0xD2, 0x14, + 0x30, 0x1C, 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x10, 0x13, 0x00, 0xFC, 0xB6, 0x00, 0x98, 0x57, + 0x02, 0x13, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF0, 0x8E, 0x0B, 0x00, 0xFC, 0x24, 0x01, 0xB0, 0x57, + 0x00, 0xFA, 0x24, 0x01, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0x8E, 0x0B, + 0x00, 0xF2, 0x8C, 0x0E, 0x00, 0xF0, 0x8E, 0x0B, 0xB1, 0xF0, 0xF8, 0x0C, 0x85, 0xF0, 0x86, 0x0B, + 0x69, 0x08, 0x48, 0xE4, 0x01, 0x00, 0xD5, 0xF0, 0x86, 0x0B, 0xFC, 0x14, 0x42, 0x58, 0x6C, 0x14, + 0x80, 0x14, 0x30, 0x1C, 0x4A, 0xF4, 0x02, 0x00, 0x55, 0xF0, 0x86, 0x0B, 0x4A, 0xF4, 0x01, 0x00, + 0x0E, 0x12, 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x06, 0x13, 0x3E, 0x1C, 0x00, 0xF0, 0x8E, 0x0B, + 0x00, 0xFC, 0xB6, 0x00, 0xB0, 0x57, 0x00, 0xFA, 0xB6, 0x00, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF2, + 0x12, 0x11, 0x86, 0xF0, 0x8E, 0x0B, 0x00, 0xF2, 0xBA, 0x0E, 0x00, 0xF0, 0x8E, 0x0B, 0x4C, 0x1C, + 0xB1, 0xF0, 0x50, 0x0D, 0x85, 0xF0, 0x5C, 0x0D, 0x69, 0x08, 0xF3, 0x10, 0x86, 0xF0, 0x64, 0x0D, + 0x4E, 0x1C, 0x89, 0x48, 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, + 0x00, 0xDC, 0x18, 0xF4, 0xFF, 0x7F, 0x30, 0x56, 0x00, 0x5C, 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, + 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, 0x00, 0x80, 0x30, 0x56, 0x00, 0x5C, + 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x0B, 0x58, + 0x00, 0x16, 0x03, 0xF6, 0x24, 0x01, 0x00, 0xF2, 0x58, 0x0A, 0x03, 0xF6, 0xB6, 0x00, 0x00, 0xF2, + 0x58, 0x0A, 0x00, 0x16, 0x02, 0xEC, 0xB8, 0x00, 0x02, 0x49, 0x18, 0xF4, 0xFF, 0x00, 0x00, 0x54, + 0x00, 0x54, 0x00, 0x54, 0x00, 0xF4, 0x08, 0x00, 0xE1, 0x18, 0x80, 0x54, 0x03, 0x58, 0x00, 0xDD, + 0x01, 0xDD, 0x02, 0xDD, 0x03, 0xDC, 0x02, 0x4B, 0x30, 0x50, 0xB2, 0x50, 0x34, 0x51, 0xB6, 0x51, + 0x00, 0x16, 0x45, 0x5A, 0x1D, 0xF4, 0xFF, 0x00, 0x85, 0x56, 0x85, 0x56, 0x85, 0x56, 0x05, 0xF4, + 0x02, 0x12, 0x83, 0x5A, 0x00, 0x16, 0x1D, 0xF4, 0xFF, 0x00, 0x85, 0x56, 0x85, 0x56, 0x85, 0x56, + 0x05, 0xF4, 0x00, 0x12, 0x83, 0x5A, 0x00, 0x16, 0x38, 0x54, 0xBB, 0x55, 0x3C, 0x56, 0xBD, 0x56, + 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x82, 0x0E, 0xE9, 0x09, 0xC1, 0x59, 0x00, 0xF2, 0x12, 0x11, + 0x85, 0xF0, 0x82, 0x0E, 0xE8, 0x0A, 0x83, 0x55, 0x83, 0x55, 0x4B, 0xF4, 0x90, 0x01, 0x5C, 0xF0, + 0x36, 0x0E, 0xBD, 0x56, 0x40, 0x10, 0x4B, 0xF4, 0x30, 0x00, 0x59, 0xF0, 0x48, 0x0E, 0x01, 0xF6, + 0x0C, 0x00, 0x00, 0xF6, 0x01, 0x00, 0x2E, 0x10, 0x02, 0xFC, 0x9C, 0x00, 0x9A, 0x57, 0x14, 0x13, + 0x4B, 0xF4, 0x64, 0x00, 0x59, 0xF0, 0x64, 0x0E, 0x03, 0xF6, 0x64, 0x00, 0x01, 0xF6, 0x19, 0x00, + 0x00, 0xF6, 0x01, 0x00, 0x43, 0xF4, 0x33, 0x00, 0x56, 0xF0, 0x76, 0x0E, 0x04, 0xF4, 0x00, 0x01, + 0x43, 0xF4, 0x19, 0x00, 0xF3, 0x10, 0xB4, 0x56, 0xC3, 0x58, 0x02, 0xFC, 0x9E, 0x00, 0x9A, 0x57, + 0x08, 0x13, 0x3C, 0x56, 0x00, 0xF6, 0x02, 0x00, 0x00, 0x16, 0x00, 0x16, 0x09, 0xE7, 0x01, 0x00, + 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xB8, 0x0E, 0x09, 0xE7, 0x02, 0x00, 0x00, 0xF2, 0x12, 0x11, + 0x86, 0xF0, 0xB8, 0x0E, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xB8, 0x0E, + 0x4E, 0x1C, 0x89, 0x49, 0x00, 0xF2, 0x12, 0x11, 0x00, 0x16, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, + 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, + 0xF2, 0x0E, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x89, 0x49, + 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x4E, 0x1C, 0x89, 0x4A, 0x00, 0xF2, 0x12, 0x11, + 0x00, 0x16, 0x3C, 0x56, 0x00, 0x16, 0x00, 0xEC, 0x26, 0x01, 0x48, 0xE4, 0x01, 0x00, 0x1E, 0x13, + 0x38, 0x44, 0x00, 0xEA, 0x26, 0x01, 0x49, 0xF4, 0x00, 0x00, 0x04, 0x12, 0x4E, 0x1C, 0x02, 0x10, + 0x4C, 0x1C, 0x01, 0xEC, 0x27, 0x01, 0x89, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x02, 0x14, 0x00, 0x16, + 0x85, 0xF0, 0x52, 0x0F, 0x38, 0x54, 0x00, 0xEA, 0x99, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x02, 0x80, + 0x48, 0xE4, 0x06, 0x00, 0x1C, 0x13, 0x00, 0xEC, 0x99, 0x00, 0x48, 0xE4, 0x01, 0x00, 0x0A, 0x12, + 0x04, 0x80, 0x30, 0xE4, 0x01, 0x00, 0x04, 0x40, 0x08, 0x10, 0x04, 0x80, 0x18, 0xE4, 0xFE, 0x00, + 0x04, 0x40, 0x00, 0x16, 0x02, 0xF6, 0xE0, 0x00, 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x81, 0x48, + 0x22, 0x12, 0x00, 0x4E, 0x83, 0x5A, 0x90, 0x4C, 0x20, 0xE7, 0x00, 0x00, 0xC3, 0x58, 0x1B, 0xF4, + 0xFF, 0x00, 0x83, 0x55, 0x83, 0x55, 0x83, 0x55, 0x03, 0xF4, 0x00, 0x12, 0x8B, 0x55, 0x83, 0x59, + 0x00, 0x4E, 0x00, 0x16, 0x00, 0x4E, 0x02, 0xF6, 0xF0, 0x00, 0x02, 0x57, 0x03, 0x59, 0x00, 0x4E, + 0x83, 0x5A, 0x30, 0xE7, 0x00, 0x00, 0x20, 0xE7, 0x00, 0x00, 0x00, 0x16, 0x02, 0xF6, 0xF0, 0x00, + 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x00, 0x4E, 0x83, 0x5A, 0x30, 0xE7, 0x00, 0x00, 0x80, 0x4C, + 0xC3, 0x58, 0x1B, 0xF4, 0xFF, 0x00, 0x83, 0x55, 0x83, 0x55, 0x83, 0x55, 0x03, 0xF4, 0x00, 0x12, + 0x83, 0x59, 0x00, 0x4E, 0x00, 0x16, 0x03, 0xF6, 0xE0, 0x00, 0x03, 0x57, 0x83, 0x59, 0x3A, 0x55, + 0x02, 0xCC, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0xC0, 0x5A, 0x40, 0x5C, 0x38, 0x54, 0x00, 0xCD, + 0x01, 0xCC, 0x4A, 0x46, 0x0A, 0x13, 0x83, 0x59, 0x00, 0x4C, 0x01, 0x48, 0x16, 0x13, 0x0C, 0x10, + 0xC5, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0x4C, 0x01, 0x48, 0x08, 0x13, 0x05, 0xF6, 0xF0, 0x00, + 0x05, 0x57, 0x08, 0x10, 0x45, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x8D, 0x56, 0x83, 0x5A, 0x80, 0x4C, + 0x05, 0x17, 0x00, 0x16, 0x02, 0x4B, 0x06, 0xF7, 0x04, 0x00, 0x62, 0x0B, 0x03, 0x82, 0x00, 0xF2, + 0xE2, 0x0D, 0x02, 0x80, 0x00, 0x4C, 0x45, 0xF4, 0x02, 0x00, 0x52, 0x14, 0x06, 0xF7, 0x02, 0x00, + 0x06, 0x14, 0x00, 0xF2, 0x54, 0x0F, 0x00, 0x16, 0x02, 0x4B, 0x01, 0xF6, 0xFF, 0x00, 0x38, 0x1C, + 0x05, 0xF4, 0x04, 0x00, 0x83, 0x5A, 0x18, 0xDF, 0x19, 0xDF, 0x1D, 0xF7, 0x3C, 0x00, 0xB8, 0xF0, + 0x4E, 0x10, 0x9C, 0x14, 0x01, 0x48, 0x1C, 0x13, 0x0E, 0xF7, 0x3C, 0x00, 0x03, 0xF7, 0x04, 0x00, + 0xAF, 0x19, 0x03, 0x42, 0x45, 0xF4, 0x02, 0x00, 0x83, 0x5A, 0x02, 0xCC, 0x02, 0x41, 0x45, 0xF4, + 0x02, 0x00, 0x00, 0x16, 0x91, 0x44, 0xD5, 0xF0, 0x3E, 0x10, 0x00, 0xF0, 0x9E, 0x02, 0x01, 0xF6, + 0xFF, 0x00, 0x38, 0x1C, 0x05, 0xF4, 0x04, 0x00, 0x83, 0x5A, 0x18, 0xDF, 0x19, 0xDF, 0x0E, 0xF7, + 0x3C, 0x00, 0x03, 0xF7, 0x04, 0x00, 0x0F, 0x79, 0x1C, 0xF7, 0x3C, 0x00, 0xB8, 0xF0, 0x9C, 0x10, + 0x4E, 0x14, 0x01, 0x48, 0x06, 0x13, 0x45, 0xF4, 0x04, 0x00, 0x00, 0x16, 0x91, 0x44, 0xD5, 0xF0, + 0x82, 0x10, 0x00, 0xF0, 0x9E, 0x02, 0x02, 0xF6, 0xFF, 0x00, 0x38, 0x1C, 0x2C, 0xBC, 0xAE, 0xBC, + 0xE2, 0x08, 0x00, 0xEC, 0xB8, 0x00, 0x02, 0x48, 0x1D, 0xF7, 0x80, 0x00, 0xB8, 0xF0, 0xCC, 0x10, + 0x1E, 0x14, 0x01, 0x48, 0x0E, 0x13, 0x0E, 0xF7, 0x80, 0x00, 0x38, 0x54, 0x03, 0x58, 0xAF, 0x19, + 0x82, 0x48, 0x00, 0x16, 0x82, 0x48, 0x12, 0x45, 0xD5, 0xF0, 0xBA, 0x10, 0x00, 0xF0, 0x9E, 0x02, + 0x39, 0xF0, 0xF8, 0x10, 0x38, 0x44, 0x00, 0x16, 0x7E, 0x18, 0x18, 0xF4, 0x03, 0x00, 0x04, 0x13, + 0x61, 0x18, 0x00, 0x16, 0x38, 0x1C, 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0xF1, 0x12, + 0xE3, 0x10, 0x30, 0x44, 0x30, 0x44, 0x30, 0x44, 0xB1, 0xF0, 0x18, 0x11, 0x00, 0x16, 0x3E, 0x57, + 0x03, 0xF6, 0xE0, 0x00, 0x03, 0x57, 0x83, 0x59, 0x04, 0xCC, 0x01, 0x4A, 0x6A, 0x12, 0x45, 0x5A, + 0x00, 0xF2, 0xF6, 0x0D, 0x02, 0x4B, 0x70, 0x14, 0x34, 0x13, 0x02, 0x80, 0x48, 0xE4, 0x08, 0x00, + 0x18, 0x12, 0x9C, 0xE7, 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, + 0x7A, 0x0A, 0x1E, 0x1C, 0x01, 0xF6, 0x01, 0x00, 0x00, 0x16, 0x30, 0xE4, 0x10, 0x00, 0x04, 0x40, + 0x00, 0xF2, 0xE2, 0x0D, 0x20, 0xE7, 0x01, 0x00, 0x01, 0xF6, 0x01, 0x00, 0x00, 0x16, 0x04, 0xDC, + 0x01, 0x4A, 0x24, 0x12, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0x43, 0x5B, 0x06, 0xEC, 0x98, 0x00, + 0x00, 0xF2, 0x38, 0x10, 0xC6, 0x59, 0x20, 0x14, 0x0A, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, + 0x14, 0x10, 0xA7, 0x10, 0x83, 0x5A, 0xD7, 0x10, 0x0E, 0x47, 0x07, 0xE6, 0x10, 0x00, 0xCE, 0x47, + 0x5A, 0xF0, 0x20, 0x11, 0xB9, 0x54, 0x00, 0x16, 0x14, 0x90, 0x96, 0x90, 0x02, 0xFC, 0xA8, 0x00, + 0x03, 0xFC, 0xAA, 0x00, 0x48, 0x55, 0x02, 0x13, 0xC9, 0x55, 0x00, 0x16, 0x00, 0xEC, 0xBA, 0x00, + 0x10, 0x44, 0x00, 0xEA, 0xBA, 0x00, 0x00, 0x16, 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A, + 0x10, 0x44, 0x00, 0x4C, 0x00, 0x16 +}; unsigned short _adv_mcode_size ASC_INITDATA = - sizeof(_adv_mcode_buf); /* 0x11D2 */ -unsigned long _adv_mcode_chksum ASC_INITDATA = 0x0347D07AUL; + sizeof(_adv_mcode_buf); /* 0x11D6 */ +unsigned long _adv_mcode_chksum ASC_INITDATA = 0x03494981UL; /* a_init.c */ /* diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index 32ff539f2afe..5a9a0fa5b125 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -609,11 +609,12 @@ static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun) prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble) ; - if(ptr) + if(ptr){ if(prev) prev->host_scribble = ptr->host_scribble; else *SC= (Scsi_Cmnd *) ptr->host_scribble; + } return ptr; } @@ -1724,7 +1725,7 @@ void aha152x_intr(int irqno, void *dev_id, struct pt_regs * regs) /* we are waiting for the result of a selection attempt */ if(CURRENT_SC->SCp.phase & in_selection) { - if(TESTLO(SSTAT1, SELTO)) + if(TESTLO(SSTAT1, SELTO)) { /* no timeout */ if(TESTHI(SSTAT0, SELDO)) { /* clear BUS FREE interrupt */ @@ -1800,7 +1801,7 @@ void aha152x_intr(int irqno, void *dev_id, struct pt_regs * regs) return; } else aha152x_panic(shpnt, "neither timeout nor selection\007"); - else { + } else { #if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES) if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases)) printk("SELTO, "); diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c index dec616c3f766..21eaff620bb0 100644 --- a/drivers/scsi/aha1542.c +++ b/drivers/scsi/aha1542.c @@ -809,7 +809,7 @@ static int aha1542_mbenable(int base) mbenable_cmd[1]=0; mbenable_cmd[2]=mbenable_result[1]; - if(mbenable_result[1] & 0x03) retval = BIOS_TRANSLATION_25563; + if((mbenable_result[0] & 0x08) && (mbenable_result[1] & 0x03)) retval = BIOS_TRANSLATION_25563; aha1542_out(base,mbenable_cmd,3); WAIT(INTRFLAGS(base),INTRMASK,HACC,0); diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c index 779aebf247ec..1c1684d5d35b 100644 --- a/drivers/scsi/fdomain.c +++ b/drivers/scsi/fdomain.c @@ -1002,7 +1002,7 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt ) Write_FIFO_port = port_base + Write_FIFO; Write_SCSI_Data_port = port_base + Write_SCSI_Data; - fdomain_16x0_reset( NULL ); + fdomain_16x0_reset( NULL, 0 ); if (fdomain_test_loopback()) { #if DEBUG_DETECT @@ -1887,7 +1887,7 @@ int fdomain_16x0_abort( Scsi_Cmnd *SCpnt) return SCSI_ABORT_SUCCESS; } -int fdomain_16x0_reset( Scsi_Cmnd *SCpnt ) +int fdomain_16x0_reset( Scsi_Cmnd *SCpnt, unsigned int flags ) { #if DEBUG_RESET static int called_once = 0; diff --git a/drivers/scsi/fdomain.h b/drivers/scsi/fdomain.h index 429db819e268..c15255819575 100644 --- a/drivers/scsi/fdomain.h +++ b/drivers/scsi/fdomain.h @@ -29,7 +29,7 @@ int fdomain_16x0_detect( Scsi_Host_Template * ); int fdomain_16x0_command( Scsi_Cmnd * ); int fdomain_16x0_abort( Scsi_Cmnd * ); const char *fdomain_16x0_info( struct Scsi_Host * ); -int fdomain_16x0_reset( Scsi_Cmnd * ); +int fdomain_16x0_reset( Scsi_Cmnd *, unsigned int ); int fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) ); int fdomain_16x0_biosparam( Disk *, kdev_t, int * ); int fdomain_16x0_proc_info( char *buffer, char **start, off_t offset, diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 93bc46c2f33a..4bb75865b34b 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -358,7 +358,7 @@ scsi_unregister(struct Scsi_Host * sh){ /* If we are removing the last host registered, it is safe to reuse * its host number (this avoids "holes" at boot time) (DB) */ - if (max_scsi_hosts == next_scsi_host && !scsi_loadable_module_flag) + if (max_scsi_hosts == next_scsi_host) max_scsi_hosts--; next_scsi_host--; diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c index a08ff1e62d8f..bd964204a312 100644 --- a/drivers/scsi/pas16.c +++ b/drivers/scsi/pas16.c @@ -510,7 +510,7 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst, register unsigned char *d = dst; register unsigned short reg = (unsigned short) (instance->io_port + P_DATA_REG_OFFSET); - register i = len; + register int i = len; int ii = 0; while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) ) @@ -546,7 +546,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src int len) { register unsigned char *s = src; register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET); - register i = len; + register int i = len; int ii = 0; while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) ) diff --git a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c index a3784e9264c5..b5cb9dd0aad9 100644 --- a/drivers/scsi/qlogicfas.c +++ b/drivers/scsi/qlogicfas.c @@ -656,7 +656,7 @@ int qlogicfas_abort(Scsi_Cmnd * cmd) /*----------------------------------------------------------------*/ /* reset SCSI bus */ -int qlogicfas_reset(Scsi_Cmnd * cmd) +int qlogicfas_reset(Scsi_Cmnd * cmd, unsigned int flags) { qabort = 2; ql_zap(); diff --git a/drivers/scsi/qlogicfas.h b/drivers/scsi/qlogicfas.h index df2bb73ae309..5a1dfdbf7d43 100644 --- a/drivers/scsi/qlogicfas.h +++ b/drivers/scsi/qlogicfas.h @@ -6,7 +6,7 @@ const char * qlogicfas_info(struct Scsi_Host *); int qlogicfas_command(Scsi_Cmnd *); int qlogicfas_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); int qlogicfas_abort(Scsi_Cmnd *); -int qlogicfas_reset(Scsi_Cmnd *); +int qlogicfas_reset(Scsi_Cmnd *, unsigned int flags); int qlogicfas_biosparam(Disk *, kdev_t, int[]); #ifndef NULL diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c0a8ae894170..e503dd8498ec 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -214,6 +214,7 @@ static void scsi_dump_status(void); #define BLIST_SINGLELUN 0x10 #define BLIST_NOTQ 0x20 #define BLIST_SPARSELUN 0x40 +#define BLIST_MAX5LUN 0x80 struct dev_info{ const char * vendor; @@ -281,7 +282,9 @@ static struct dev_info device_list[] = {"INSITE","I325VM","*", BLIST_KEY}, {"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN}, +{"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN}, {"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN}, +{"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN}, @@ -813,6 +816,15 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun, *max_dev_lun = 8; return 1; } + + /* + * REGAL CDC-4X: avoid hang after LUN 4 + */ + if (bflags & BLIST_MAX5LUN) { + *max_dev_lun = 5; + return 1; + } + /* * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1 @@ -874,7 +886,8 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt) scsi_reset (SCpnt, SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET); return; - case (IN_ABORT | IN_RESET | IN_RESET2): + case IN_RESET2: + case (IN_ABORT | IN_RESET2): /* Obviously the bus reset didn't work. * Let's try even harder and call for an HBA reset. * Maybe the HBA itself crashed and this will shake it loose. diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index 495a99b8ff6d..c3dfcd45e926 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -521,8 +521,23 @@ static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors req->nr_sectors -= bh->b_size >> 9; req->sector += bh->b_size >> 9; bh->b_reqnext = NULL; - mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); + /* + * This is our 'MD IO has finished' event handler. + * note that b_state should be cached in a register + * anyways, so the overhead if this checking is almost + * zero. But anyways .. we never get OO for free :) + */ + if (test_bit(BH_MD, &bh->b_state)) { + struct md_personality * pers=(struct md_personality *)bh->personality; + pers->end_request(bh,uptodate); + } + /* + * the normal (nonmirrored and no RAID5) case: + */ + else { + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + } sectors -= bh->b_size >> 9; if ((bh = req->bh) != NULL) { req->current_nr_sectors = bh->b_size >> 9; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index b73b3c8b1e19..8e0da4b81670 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -76,7 +76,7 @@ extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long); static int check_scsidisk_media_change(kdev_t); static int fop_revalidate_scsidisk(kdev_t); -static sd_init_onedisk(int); +static int sd_init_onedisk(int); static void requeue_sd_request (Scsi_Cmnd * SCpnt); diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c index fe5fa79bec9d..06c6d203b4f6 100644 --- a/drivers/scsi/t128.c +++ b/drivers/scsi/t128.c @@ -324,7 +324,7 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst, int len) { register unsigned char *reg = (unsigned char *) (instance->base + T_DATA_REG_OFFSET), *d = dst; - register i = len; + register int i = len; #if 0 @@ -368,7 +368,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src int len) { register unsigned char *reg = (unsigned char *) (instance->base + T_DATA_REG_OFFSET), *s = src; - register i = len; + register int i = len; #if 0 for (; i; --i) { diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c index 0e7797780d0d..9f9560a27f0b 100644 --- a/drivers/scsi/wd7000.c +++ b/drivers/scsi/wd7000.c @@ -508,7 +508,7 @@ typedef struct icbRevLvl { typedef struct icbUnsMask { /* I'm totally guessing here */ unchar op; volatile unchar mask[14]; /* mask bits */ -#ifdef 0 +#if 0 unchar rsvd[12]; /* reserved */ #endif volatile unchar vue; /* vendor-unique error code */ @@ -1686,7 +1686,7 @@ int wd7000_abort (Scsi_Cmnd *SCpnt) /* * I also have no idea how to do a reset... */ -int wd7000_reset (Scsi_Cmnd *SCpnt) +int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int flags) { return (SCSI_RESET_PUNT); } diff --git a/drivers/scsi/wd7000.h b/drivers/scsi/wd7000.h index d26fcdcadc00..2469efb5ce25 100644 --- a/drivers/scsi/wd7000.h +++ b/drivers/scsi/wd7000.h @@ -20,7 +20,7 @@ int wd7000_detect (Scsi_Host_Template *); int wd7000_command (Scsi_Cmnd *); int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int wd7000_abort (Scsi_Cmnd *); -int wd7000_reset (Scsi_Cmnd *); +int wd7000_reset (Scsi_Cmnd *, unsigned int); int wd7000_biosparam (Disk *, kdev_t, int *); #ifndef NULL diff --git a/fs/Config.in b/fs/Config.in index 92fb802d2fb6..f6b1c90cdb1b 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -10,7 +10,7 @@ tristate 'Extended fs support' CONFIG_EXT_FS tristate 'Second extended fs support' CONFIG_EXT2_FS tristate 'xiafs filesystem support' CONFIG_XIA_FS -tristate 'Native language support (Unicode, codepages)' CONFIG_NLS +tristate 'Native language support (Needed for FAT and ISO9660)' CONFIG_NLS if [ "$CONFIG_NLS" = "y" -o "$CONFIG_NLS" = "m" ]; then dep_tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS $CONFIG_NLS diff --git a/fs/buffer.c b/fs/buffer.c index 7d1aec49fde0..fdb022a8bff6 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -53,8 +53,8 @@ static struct buffer_head * lru_list[NR_LIST] = {NULL, }; static struct buffer_head * free_list[NR_SIZES] = {NULL, }; static struct buffer_head * unused_list = NULL; -static struct buffer_head * reuse_list = NULL; -static struct wait_queue * buffer_wait = NULL; +static struct buffer_head * reuse_list = NULL; +struct wait_queue * buffer_wait = NULL; static int nr_buffers = 0; static int nr_buffers_type[NR_LIST] = {0,}; @@ -462,6 +462,11 @@ static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size) return NULL; } +struct buffer_head *efind_buffer(kdev_t dev, int block, int size) +{ + return find_buffer(dev, block, size); +} + /* * Why like this, I hear you say... The reason is race-conditions. * As we don't lock buffers (unless we are reading them, that is), @@ -650,6 +655,9 @@ static void refill_freelist(int size) /* If there are too many dirty buffers, we wake up the update process now so as to ensure that there are still clean buffers available for user processes to use (and dirty) */ + + if (nr_buffers_type[BUF_DIRTY] > nr_buffers * bdf_prm.b_un.nfract/100) + wakeup_bdflush(1); /* We are going to try to locate this much memory */ needed = bdf_prm.b_un.nrefill * size; @@ -1247,7 +1255,9 @@ void unlock_buffer(struct buffer_head * bh) struct buffer_head *tmp; struct page *page; - clear_bit(BH_Lock, &bh->b_state); + if (!clear_bit(BH_Lock, &bh->b_state)) + printk ("unlock_buffer: already unlocked on %s\n", + kdevname(bh->b_dev)); wake_up(&bh->b_wait); if (waitqueue_active(&buffer_wait)) wake_up(&buffer_wait); @@ -1594,7 +1604,7 @@ asmlinkage int sync_old_buffers(void) if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount); printk("Wrote %d/%d buffers\n", nwritten, ndirty); #endif - + run_task_queue(&tq_disk); return 0; } diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d7e2fc621c8b..902d011dfa9d 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -597,7 +597,7 @@ int isofs_bmap(struct inode * inode,int block) * If we are beyond the end of this file, don't give out any * blocks. */ - if( b_off > inode->i_size ) + if( b_off >= inode->i_size ) { off_t max_legal_read_offset; diff --git a/fs/proc/array.c b/fs/proc/array.c index bd929e4ad7c0..514adfdef032 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -409,7 +409,7 @@ static int get_arg(int pid, char * buffer) return get_array(p, (*p)->mm->arg_start, (*p)->mm->arg_end, buffer); } -static unsigned long get_wchan(struct task_struct *p) +unsigned long get_wchan(struct task_struct *p) { if (!p || p == current || p->state == TASK_RUNNING) return 0; diff --git a/fs/super.c b/fs/super.c index f9cdecb9e5ed..bbfbd20e0205 100644 --- a/fs/super.c +++ b/fs/super.c @@ -47,6 +47,7 @@ extern void wait_for_keypress(void); extern struct file_operations * get_blkfops(unsigned int major); extern void blkdev_release (struct inode *); +extern void rd_load_secondary(void); extern int root_mountflags; diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 680d99213936..71f08018f834 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -165,7 +165,6 @@ static struct super_block * detected_xenix (struct super_block *sb, struct buffe sb->sv_sb_flc_blocks = &sbd1->s_free[0]; sb->sv_sb_total_free_blocks = &sbd2->s_tfree; sb->sv_sb_time = &sbd2->s_time; - sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd1->s_isize; sb->sv_nzones = sbd1->s_fsize; @@ -223,7 +222,6 @@ static struct super_block * detected_sysv4 (struct super_block *sb, struct buffe sb->sv_sb_total_free_blocks = &sbd->s_tfree; sb->sv_sb_time = &sbd->s_time; sb->sv_sb_state = &sbd->s_state; - sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd->s_isize; sb->sv_nzones = sbd->s_fsize; @@ -281,7 +279,6 @@ static struct super_block * detected_sysv2 (struct super_block *sb, struct buffe sb->sv_sb_total_free_blocks = &sbd->s_tfree; sb->sv_sb_time = &sbd->s_time; sb->sv_sb_state = &sbd->s_state; - sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd->s_isize; sb->sv_nzones = sbd->s_fsize; @@ -328,7 +325,6 @@ static struct super_block * detected_coherent (struct super_block *sb, struct bu sb->sv_sb_flc_blocks = &sbd->s_free[0]; sb->sv_sb_total_free_blocks = &sbd->s_tfree; sb->sv_sb_time = &sbd->s_time; - sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd->s_isize; sb->sv_nzones = from_coh_ulong(sbd->s_fsize); @@ -355,6 +351,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, MOD_INC_USE_COUNT; lock_super(sb); set_blocksize(dev,BLOCK_SIZE); + sb->sv_block_base = 0; /* Try to read Xenix superblock */ if ((bh = bread(dev, 1, BLOCK_SIZE)) != NULL) { diff --git a/include/asm-i386/ptrace.h b/include/asm-i386/ptrace.h index 8e4aa52f683c..1b1b93bdb77e 100644 --- a/include/asm-i386/ptrace.h +++ b/include/asm-i386/ptrace.h @@ -43,6 +43,12 @@ struct pt_regs { unsigned short ss, __ssu; }; +/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 +#define PTRACE_GETFPREGS 14 +#define PTRACE_SETFPREGS 15 + #ifdef __KERNEL__ #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs)) #define instruction_pointer(regs) ((regs)->eip) diff --git a/include/linux/blk.h b/include/linux/blk.h index 4c5a25b69b70..92634d7a1b65 100644 --- a/include/linux/blk.h +++ b/include/linux/blk.h @@ -3,7 +3,9 @@ #include #include +#include #include +#include /* * NR_REQUEST is the number of entries in the request-queue. @@ -400,8 +402,24 @@ static void end_request(int uptodate) { if ((bh = req->bh) != NULL) { req->bh = bh->b_reqnext; bh->b_reqnext = NULL; - mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); + + /* + * This is our 'MD IO has finished' event handler. + * note that b_state should be cached in a register + * anyways, so the overhead if this checking is almost + * zero. But anyways .. we never get OO for free :) + */ + if (test_bit(BH_MD, &bh->b_state)) { + struct md_personality * pers=(struct md_personality *)bh->personality; + pers->end_request(bh,uptodate); + } + /* + * the normal (nonmirrored and no RAID5) case: + */ + else { + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + } if ((bh = req->bh) != NULL) { req->current_nr_sectors = bh->b_size >> 9; if (req->nr_sectors < req->current_nr_sectors) { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e0f578f9b481..5bfc84ed7d68 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -50,9 +50,12 @@ extern struct blk_dev_struct blk_dev[MAX_BLKDEV]; extern struct wait_queue * wait_for_request; extern void resetup_one_dev(struct gendisk *dev, int drive); extern void unplug_device(void * data); +extern void make_request(int major,int rw, struct buffer_head * bh); /* md needs this function to remap requests */ extern int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size); +extern int md_make_request (int minor, int rw, struct buffer_head * bh); +extern int md_error (kdev_t mddev, kdev_t rdev); extern int * blk_size[MAX_BLKDEV]; diff --git a/include/linux/fs.h b/include/linux/fs.h index 8492c3657a97..37ffba76b5f4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -141,14 +141,15 @@ extern unsigned long name_cache_init(unsigned long start, unsigned long end); typedef char buffer_block[BLOCK_SIZE]; /* bh state bits */ -#define BH_Uptodate 0 /* 1 if the buffer contains valid data */ -#define BH_Dirty 1 /* 1 if the buffer is dirty */ -#define BH_Lock 2 /* 1 if the buffer is locked */ -#define BH_Req 3 /* 0 if the buffer has been invalidated */ -#define BH_Touched 4 /* 1 if the buffer has been touched (aging) */ -#define BH_Has_aged 5 /* 1 if the buffer has been aged (aging) */ -#define BH_Protected 6 /* 1 if the buffer is protected */ -#define BH_FreeOnIO 7 /* 1 to discard the buffer_head after IO */ +#define BH_Uptodate 0 /* 1 if the buffer contains valid data */ +#define BH_Dirty 1 /* 1 if the buffer is dirty */ +#define BH_Lock 2 /* 1 if the buffer is locked */ +#define BH_Req 3 /* 0 if the buffer has been invalidated */ +#define BH_Touched 4 /* 1 if the buffer has been touched (aging) */ +#define BH_Has_aged 5 /* 1 if the buffer has been aged (aging) */ +#define BH_Protected 6 /* 1 if the buffer is protected */ +#define BH_FreeOnIO 7 /* 1 to discard the buffer_head after IO */ +#define BH_MD 8 /* 1 if the buffer is an MD request */ /* * Try to keep the most commonly used fields in single cache lines (16 @@ -188,6 +189,13 @@ struct buffer_head { struct buffer_head * b_prev; /* doubly linked list of hash-queue */ struct buffer_head * b_prev_free; /* doubly linked list of buffers */ struct buffer_head * b_reqnext; /* request queue */ + +/* + * Some MD stuff like RAID5 needs special event handlers and + * special private buffer_head fields: + */ + void * personality; + void * private_bh; }; static inline int buffer_uptodate(struct buffer_head * bh) diff --git a/include/linux/md.h b/include/linux/md.h index 5a5c9035934a..1c5680a0caf2 100644 --- a/include/linux/md.h +++ b/include/linux/md.h @@ -1,4 +1,3 @@ - /* md.h : Multiple Devices driver for Linux Copyright (C) 1994-96 Marc ZYNGIER @@ -20,10 +19,17 @@ #include #include -#include #include +#include -#define MD_VERSION "0.35" +/* + * Different major versions are not compatible. + * Different minor versions are only downward compatible. + * Different patchlevel versions are downward and upward compatible. + */ +#define MD_MAJOR_VERSION 0 +#define MD_MINOR_VERSION 36 +#define MD_PATCHLEVEL_VERSION 3 /* ioctls */ #define REGISTER_DEV _IO (MD_MAJOR, 1) @@ -43,9 +49,9 @@ #define FAULT_SHIFT 8 #define PERSONALITY_SHIFT 16 -#define FACTOR_MASK 0xFFUL -#define FAULT_MASK 0xFF00UL -#define PERSONALITY_MASK 0xFF0000UL +#define FACTOR_MASK 0x000000FFUL +#define FAULT_MASK 0x0000FF00UL +#define PERSONALITY_MASK 0x00FF0000UL #define MD_RESERVED 0 /* Not used by now */ #define LINEAR (1UL << PERSONALITY_SHIFT) @@ -55,14 +61,147 @@ #define RAID5 (4UL << PERSONALITY_SHIFT) #define MAX_PERSONALITY 5 +/* + * MD superblock. + * + * The MD superblock maintains some statistics on each MD configuration. + * Each real device in the MD set contains it near the end of the device. + * Some of the ideas are copied from the ext2fs implementation. + * + * We currently use 4096 bytes as follows: + * + * word offset function + * + * 0 - 31 Constant generic MD device information. + * 32 - 63 Generic state information. + * 64 - 127 Personality specific information. + * 128 - 511 12 32-words descriptors of the disks in the raid set. + * 512 - 911 Reserved. + * 912 - 1023 Disk specific descriptor. + */ + +/* + * If x is the real device size in bytes, we return an apparent size of: + * + * y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES + * + * and place the 4kB superblock at offset y. + */ +#define MD_RESERVED_BYTES (64 * 1024) +#define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) +#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE) + +#define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS) +#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS) + +#define MD_SB_BYTES 4096 +#define MD_SB_WORDS (MD_SB_BYTES / 4) +#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE) +#define MD_SB_SECTORS (MD_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define MD_SB_GENERIC_OFFSET 0 +#define MD_SB_PERSONALITY_OFFSET 64 +#define MD_SB_DISKS_OFFSET 128 +#define MD_SB_DESCRIPTOR_OFFSET 992 + +#define MD_SB_GENERIC_CONSTANT_WORDS 32 +#define MD_SB_GENERIC_STATE_WORDS 32 +#define MD_SB_GENERIC_WORDS (MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS) +#define MD_SB_PERSONALITY_WORDS 64 +#define MD_SB_DISKS_WORDS 384 +#define MD_SB_DESCRIPTOR_WORDS 32 +#define MD_SB_RESERVED_WORDS (1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS) +#define MD_SB_EQUAL_WORDS (MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS) +#define MD_SB_DISKS (MD_SB_DISKS_WORDS / MD_SB_DESCRIPTOR_WORDS) + +/* + * Device "operational" state bits + */ +#define MD_FAULTY_DEVICE 0 /* Device is faulty / operational */ +#define MD_ACTIVE_DEVICE 1 /* Device is a part or the raid set / spare disk */ +#define MD_SYNC_DEVICE 2 /* Device is in sync with the raid set */ + +typedef struct md_device_descriptor_s { + __u32 number; /* 0 Device number in the entire set */ + __u32 major; /* 1 Device major number */ + __u32 minor; /* 2 Device minor number */ + __u32 raid_disk; /* 3 The role of the device in the raid set */ + __u32 state; /* 4 Operational state */ + __u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5]; +} md_descriptor_t; + +#define MD_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define MD_SB_CLEAN 0 +#define MD_SB_ERRORS 1 + +typedef struct md_superblock_s { + + /* + * Constant generic information + */ + __u32 md_magic; /* 0 MD identifier */ + __u32 major_version; /* 1 major version to which the set conforms */ + __u32 minor_version; /* 2 minor version to which the set conforms */ + __u32 patch_version; /* 3 patchlevel version to which the set conforms */ + __u32 gvalid_words; /* 4 Number of non-reserved words in this section */ + __u32 set_magic; /* 5 Raid set identifier */ + __u32 ctime; /* 6 Creation time */ + __u32 level; /* 7 Raid personality (mirroring, raid5, ...) */ + __u32 size; /* 8 Apparent size of each individual disk, in kB */ + __u32 nr_disks; /* 9 Number of total disks in the raid set */ + __u32 raid_disks; /* 10 Number of disks in a fully functional raid set */ + __u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 11]; + + /* + * Generic state information + */ + __u32 utime; /* 0 Superblock update time */ + __u32 state; /* 1 State bits (clean, ...) */ + __u32 active_disks; /* 2 Number of currently active disks (some non-faulty disks might not be in sync) */ + __u32 working_disks; /* 3 Number of working disks */ + __u32 failed_disks; /* 4 Number of failed disks */ + __u32 spare_disks; /* 5 Number of spare disks */ + __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 6]; + + /* + * Personality information + */ + __u32 parity_algorithm; + __u32 chunk_size; + __u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 2]; + + /* + * Disks information + */ + md_descriptor_t disks[MD_SB_DISKS]; + + /* + * Reserved + */ + __u32 reserved[MD_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + md_descriptor_t descriptor; +} md_superblock_t; + #ifdef __KERNEL__ -#include +#include #include #include #define MAX_REAL 8 /* Max number of physical dev per md dev */ #define MAX_MD_DEV 4 /* Max number of md dev */ +#define MAX_MD_THREADS 2 /* Max number of kernel threads */ #define FACTOR(a) ((a)->repartition & FACTOR_MASK) #define MAX_FAULT(a) (((a)->repartition & FAULT_MASK)>>8) @@ -77,6 +216,8 @@ struct real_dev int offset; /* Real device offset (in blocks) in md dev (only used in linear mode) */ struct inode *inode; /* Lock inode */ + md_superblock_t *sb; + u32 sb_offset; }; struct md_dev; @@ -85,25 +226,39 @@ struct md_personality { char *name; int (*map)(struct md_dev *md_dev, kdev_t *rdev, - unsigned long *rsector, unsigned long size); + unsigned long *rsector, unsigned long size); + int (*make_request)(struct md_dev *md_dev, int rw, struct buffer_head * bh); + void (*end_request)(struct buffer_head * bh, int uptodate); int (*run)(int minor, struct md_dev *md_dev); int (*stop)(int minor, struct md_dev *md_dev); int (*status)(char *page, int minor, struct md_dev *md_dev); int (*ioctl)(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); int max_invalid_dev; + int (*error_handler)(struct md_dev *md_dev, kdev_t dev); }; struct md_dev { struct real_dev devices[MAX_REAL]; struct md_personality *pers; + md_superblock_t *sb; + int sb_dirty; int repartition; int busy; int nb_dev; void *private; }; +struct md_thread { + void (*run) (void *data); + void *data; + struct wait_queue *wqueue; + __u32 flags; +}; + +#define THREAD_WAKEUP 0 + extern struct md_dev md_dev[MAX_MD_DEV]; extern int md_size[MAX_MD_DEV]; @@ -111,6 +266,10 @@ extern char *partition_name (kdev_t dev); extern int register_md_personality (int p_num, struct md_personality *p); extern int unregister_md_personality (int p_num); +extern struct md_thread *md_register_thread (void (*run) (void *data), void *data); +extern void md_unregister_thread (struct md_thread *thread); +extern void md_wakeup_thread(struct md_thread *thread); +extern int md_update_sb (int minor); #endif __KERNEL__ #endif _MD_H diff --git a/include/linux/pg.h b/include/linux/pg.h new file mode 100644 index 000000000000..c752a97caf80 --- /dev/null +++ b/include/linux/pg.h @@ -0,0 +1,63 @@ +/* pg.h (c) 1998 Grant R. Guenther + Under the terms of the GNU public license + + + pg.h defines the user interface to the generic ATAPI packet + command driver for parallel port ATAPI devices (pg). The + driver is loosely modelled after the generic SCSI driver, sg, + although the actual interface is different. + + The pg driver provides a simple character device interface for + sending ATAPI commands to a device. With the exception of the + ATAPI reset operation, all operations are performed by a pair + of read and write operations to the appropriate /dev/pgN device. + A write operation delivers a command and any outbound data in + a single buffer. Normally, the write will succeed unless the + device is offline or malfunctioning, or there is already another + command pending. If the write succeeds, it should be followed + immediately by a read operation, to obtain any returned data and + status information. A read will fail if there is no operation + in progress. + + As a special case, the device can be reset with a write operation, + and in this case, no following read is expected, or permitted. + + There are no ioctl() operations. Any single operation + may transfer at most PG_MAX_DATA bytes. Note that the driver must + copy the data through an internal buffer. In keeping with all + current ATAPI devices, command packets are assumed to be exactly + 12 bytes in length. + + To permit future changes to this interface, the headers in the + read and write buffers contain a single character "magic" flag. + Currently this flag must be the character "P". + +*/ + +#define PG_MAGIC 'P' +#define PG_RESET 'Z' +#define PG_COMMAND 'C' + +#define PG_MAX_DATA 32768 + +struct pg_write_hdr { + + char magic; /* == PG_MAGIC */ + char func; /* PG_RESET or PG_COMMAND */ + int dlen; /* number of bytes expected to transfer */ + int timeout; /* number of seconds before timeout */ + char packet[12]; /* packet command */ + +}; + +struct pg_read_hdr { + + char magic; /* == PG_MAGIC */ + char scsi; /* "scsi" status == sense key */ + int dlen; /* size of device transfer request */ + int duration; /* time in seconds command took */ + char pad[12]; /* not used */ + +}; + +/* end of pg.h */ diff --git a/include/linux/raid1.h b/include/linux/raid1.h new file mode 100644 index 000000000000..c17adce33e8b --- /dev/null +++ b/include/linux/raid1.h @@ -0,0 +1,41 @@ +#ifndef _RAID1_H +#define _RAID1_H + +#include + +struct mirror_info { + int number; + int raid_disk; + kdev_t dev; + int operational; + int next; + int sect_limit; +}; + +struct raid1_data { + struct md_dev *mddev; + struct mirror_info mirrors[MD_SB_DISKS]; /* RAID1 devices, 2 to MD_SB_DISKS */ + int raid_disks; + int working_disks; /* Number of working disks */ + int last_used; + unsigned long next_sect; + int sect_count; +}; + +/* + * this is our 'private' 'collective' RAID1 buffer head. + * it contains information about what kind of IO operations were started + * for this RAID5 operation, and about their status: + */ + +struct raid1_bh { + unsigned int remaining; + unsigned int state; + int cmd; + struct md_dev *mddev; /* we could use bh->personality? */ + struct buffer_head *master_bh; + struct buffer_head *mirror_bh [MD_SB_DISKS]; + struct buffer_head *next_retry; +}; + +#endif diff --git a/include/linux/raid5.h b/include/linux/raid5.h new file mode 100644 index 000000000000..cb26e45f1e74 --- /dev/null +++ b/include/linux/raid5.h @@ -0,0 +1,34 @@ +#ifndef _RAID5_H +#define _RAID5_H + +#include +#include + +struct disk_info { + kdev_t dev; + int operational; + int number; + int raid_disk; +}; + +struct raid5_data { + struct md_dev *mddev; + struct md_thread *thread; + struct disk_info disks[MD_SB_DISKS]; + int buffer_size; + int chunk_size, level, algorithm; + int raid_disks, working_disks, failed_disks; + int sector_count; + unsigned long next_sector; + atomic_t nr_handle; +}; + +/* + * Our supported algorithms + */ +#define ALGORITHM_LEFT_ASYMMETRIC 0 +#define ALGORITHM_RIGHT_ASYMMETRIC 1 +#define ALGORITHM_LEFT_SYMMETRIC 2 +#define ALGORITHM_RIGHT_SYMMETRIC 3 + +#endif diff --git a/include/linux/socket.h b/include/linux/socket.h index 9a0fa860a26a..49858f58612a 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -64,6 +64,7 @@ struct msghdr #define AF_INET6 10 /* IP version 6 */ #endif #define AF_MAX 12 /* For now.. */ +#define AF_PACKET 17 /* Forward compat hook */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -80,7 +81,7 @@ struct msghdr #define PF_INET6 AF_INET6 #endif #define PF_MAX AF_MAX - +#define PF_PACKET AF_PACKET /* Maximum queue length specifiable by listen. */ #define SOMAXCONN 128 diff --git a/init/main.c b/init/main.c index f94032497688..c1c25ff857e1 100644 --- a/init/main.c +++ b/init/main.c @@ -182,6 +182,21 @@ extern void specialix_setup(char *str, int *ints); extern void baycom_setup(char *str, int *ints); #endif +#ifdef CONFIG_PARIDE_PD +extern void pd_setup(char *str, int *ints); +#endif +#ifdef CONFIG_PARIDE_PF +extern void pf_setup(char *str, int *ints); +#endif +#ifdef CONFIG_PARIDE_PT +extern void pt_setup(char *str, int *ints); +#endif +#ifdef CONFIG_PARIDE_PG +extern void pg_setup(char *str, int *ints); +#endif +#ifdef CONFIG_PARIDE_PCD +extern void pcd_setup(char *str, int *ints); +#endif #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD) extern void ipc_init(void); @@ -255,10 +270,12 @@ static void profile_setup(char *str, int *ints) #endif } -struct { +struct kernel_param { const char *str; void (*setup_func)(char *, int *); -} bootsetups[] = { +} ; + +struct kernel_param bootsetups[] = { { "reserve=", reserve_setup }, { "profile=", profile_setup }, #ifdef CONFIG_BLK_DEV_RAM @@ -444,6 +461,27 @@ struct { { 0, 0 } }; +static struct kernel_param raw_params[] = { + +#ifdef CONFIG_PARIDE_PD + { "pd.", pd_setup }, +#endif +#ifdef CONFIG_PARIDE_PCD + { "pcd.", pcd_setup }, +#endif +#ifdef CONFIG_PARIDE_PF + { "pf.", pf_setup }, +#endif +#ifdef CONFIG_PARIDE_PT + { "pt.", pt_setup }, +#endif +#ifdef CONFIG_PARIDE_PG + { "pg.", pg_setup }, +#endif + { 0, 0 } +} ; + + #ifdef CONFIG_BLK_DEV_RAM static void ramdisk_start_setup(char *str, int *ints) { @@ -491,6 +529,15 @@ static int checksetup(char *line) } i++; } + + for (i=0; raw_params[i].str; i++) { + int n = strlen(raw_params[i].str); + if (!strncmp(line,raw_params[i].str,n)) { + raw_params[i].setup_func(line+n, NULL); + return 1; + } + } + return 0; } @@ -599,6 +646,18 @@ static void parse_root_dev(char * line) { "gscd", 0x1000 }, { "sbpcd", 0x1900 }, { "sonycd", 0x1800 }, +#ifdef CONFIG_PARIDE_PD + { "pda", 0x2d00 }, + { "pdb", 0x2d10 }, + { "pdc", 0x2d20 }, + { "pdd", 0x2d30 }, +#endif +#ifdef CONFIG_PARIDE_PCD + { "pcd", 0x2e00 }, +#endif +#ifdef CONFIG_PARIDE_PF + { "pf", 0x2f00 }, +#endif { NULL, 0 } }; diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 1116e915fe7e..557c07d8fdfb 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -203,6 +203,9 @@ struct symbol_table symbol_table = { X(blkdev_release), X(gendisk_head), X(resetup_one_dev), + X(unplug_device), + X(make_request), + X(tq_disk), #ifdef CONFIG_SERIAL /* Module creation of serial units */ diff --git a/kernel/sched.c b/kernel/sched.c index efcfc99daf68..6e3e862f251d 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -87,6 +87,7 @@ unsigned long prof_shift = 0; #define _S(nr) (1<<((nr)-1)) extern void mem_use(void); +extern unsigned long get_wchan(struct task_struct *); static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, }; unsigned long init_user_stack[1024] = { STACK_MAGIC, }; @@ -1681,11 +1682,13 @@ static void show_task(int nr,struct task_struct * p) printk(" current "); else printk(" %08lX ", thread_saved_pc(&p->tss)); + printk("%08lX ", get_wchan(p)); #else if (p == current) printk(" current task "); else printk(" %016lx ", thread_saved_pc(&p->tss)); + printk("%08lX ", get_wchan(p) & 0xffffffffL); #endif for (free = 1; free < PAGE_SIZE/sizeof(long) ; free++) { if (((unsigned long *)p->kernel_stack_page)[free]) @@ -1712,12 +1715,12 @@ void show_state(void) #if ((~0UL) == 0xffffffff) printk("\n" - " free sibling\n"); - printk(" task PC stack pid father child younger older\n"); + " free sibling\n"); + printk(" task PC wchan stack pid father child younger older\n"); #else printk("\n" - " free sibling\n"); - printk(" task PC stack pid father child younger older\n"); + " free sibling\n"); + printk(" task PC wchan stack pid father child younger older\n"); #endif for (i=0 ; i */ #include @@ -43,6 +51,12 @@ #include #include +/* + * Do we assume the IP ToS is entirely for its intended purpose + */ + +#define TOS_VALID_MASK(x) ((x)&0x3F) + /* * Policy code extracted so it's now separate */ @@ -764,7 +778,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, */ newsk->ip_ttl=sk->ip_ttl; - newsk->ip_tos=skb->ip_hdr->tos; + newsk->ip_tos=TOS_VALID_MASK(skb->ip_hdr->tos); /* * Use 512 or whatever user asked for @@ -1024,7 +1038,7 @@ static int tcp_conn_request_fake(struct sock *sk, struct sk_buff *skb, */ newsk->ip_ttl=sk->ip_ttl; - newsk->ip_tos=skb->ip_hdr->tos; + newsk->ip_tos=TOS_VALID_MASK(skb->ip_hdr->tos); rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0, sk->bound_device); @@ -2388,6 +2402,14 @@ retry_search: if(sk->state==TCP_LISTEN) { + /* Don't start connections with illegal address + ranges. Trying to talk TCP to a broken dhcp host + isnt good on a lan with broken SunOS 4.x boxes + who think its a broadcast */ + + if ((saddr | daddr) == 0) + goto discard_it; + if (th->ack) { /* These use the socket TOS.. might want to be the received TOS */ #ifdef CONFIG_SYN_COOKIES if (!th->syn && !th->rst) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 2cd03603eaa5..24f983ec6ee0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -34,6 +34,8 @@ * refer to the old obsolete destination. * Elliot Poger : Added support for SO_BINDTODEVICE. * Juan Jose Ciarlante : Added sock dynamic source address rewriting + * Alan Cox : Clear reserved fields - bug reported by + * J Hadi Salim */ #include @@ -837,6 +839,8 @@ void tcp_send_synack_probe(unsigned long saddr, unsigned long daddr, struct tcph t1->psh = 0; t1->fin = 0; /* In case someone sent us a SYN|FIN frame! */ t1->doff = sizeof(*t1)/4; + t1->res1 = 0; /* RFC requires this, we upset ECN without it */ + t1->res2 = 0; tcp_send_check(t1, saddr, daddr, sizeof(*t1), buff); prot->queue_xmit(NULL, ndev, buff, 1); @@ -1017,6 +1021,8 @@ void tcp_send_synack(struct sock * newsk, struct sock * sk, struct sk_buff * skb t1->psh = 0; t1->ack_seq = htonl(newsk->acked_seq); t1->doff = sizeof(*t1)/4+1; + t1->res1 = 0; + t1->res2 = 0; ptr = skb_put(buff,4); ptr[0] = 2; ptr[1] = 4; diff --git a/net/socket.c b/net/socket.c index 9225e10315f8..ce40e9af4349 100644 --- a/net/socket.c +++ b/net/socket.c @@ -532,6 +532,12 @@ asmlinkage int sys_socket(int family, int type, int protocol) struct socket *sock; struct proto_ops *ops; + if(family==AF_PACKET) + { + family=AF_INET; + type=SOCK_PACKET; + } + /* Locate the correct protocol family. */ i = find_protocol_family(family); -- 2.39.5