home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1998-07-13 | 2.0 MB | 68,257 lines
Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
diff -u --recursive --new-file v2.0.34/linux/COPYING linux/COPYING --- v2.0.34/linux/COPYING Wed Dec 1 04:44:15 1993 +++ linux/COPYING Mon Jul 13 13:47:25 1998 @@ -3,7 +3,7 @@ services by normal system calls - this is merely considered normal use of the kernel, and does *not* fall under the heading of "derived work". Also note that the GPL below is copyrighted by the Free Software - Foundation, but the instance of code that it refers to (the linux + Foundation, but the instance of code that it refers to (the Linux kernel) is copyrighted by me and others who actually wrote it. Linus Torvalds diff -u --recursive --new-file v2.0.34/linux/CREDITS linux/CREDITS --- v2.0.34/linux/CREDITS Mon Jul 13 13:46:24 1998 +++ linux/CREDITS Mon Jul 13 13:47:25 1998 @@ -1594,11 +1594,11 @@ 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 -u --recursive --new-file v2.0.34/linux/Documentation/00-INDEX linux/Documentation/00-INDEX --- v2.0.34/linux/Documentation/00-INDEX Thu Jun 6 04:57:43 1996 +++ linux/Documentation/00-INDEX Mon Jul 13 13:47:25 1998 @@ -14,6 +14,8 @@ - how the boss likes the C code in the kernel to look. Configure.help - text file that is used for help when you run "make config" +IO-mapping.txt + - how to access I/O mapped memory from within device drivers. SMP.txt - notes, and "To Fix" list for multi-processor Linux. (see smp.tex) cdrom/ @@ -38,10 +40,16 @@ - info on the in-kernel binary support for Java(tm) locks.txt - info on file locking implementations, flock() vs. fcntl(), etc. +logo.gif + - GIF image of penguin as a linux logo (see logo.txt) +logo.txt + - the who where and how of the penguin linux logo. magic-number.txt - list of magic numbers used to mark/protect kernel data structures. mandatory.txt - info on the linux implementation of Sys V mandatory file locking. +memory-tuning.txt + - info on setting the /proc/sys/vm parameters to match your system. modules.txt - short guide on how to make kernel parts into loadable modules networking/ @@ -50,6 +58,8 @@ - short guide on setting up a diskless box with NFS root filesystem oops-tracing.txt - how to decode those nasty internal kernel error dump messages. +paride.txt + - info on parallel port IDE devices (tapes, floppies, CD-ROMS, etc) ramdisk.txt - short guide on how to set up and use the RAM disk. riscom8.txt @@ -60,10 +70,14 @@ - short blurb on using SCSI support as a module. smp.tex - TeX document describing implementation of Multiprocessor Linux +specialix.txt + - info on hardware/driver for specialix IO8+ multiport serial card. svga.txt - short guide on selecting video modes at boot via VGA BIOS. unicode.txt - info on the Unicode character/font mapping used in Linux. watchdog.txt - how to auto-reboot Linux if it has "fallen and can't get up". ;-) +xterm-linux.xpm + - XPM format image/icon of penguin on an xterm (see logo.txt) diff -u --recursive --new-file v2.0.34/linux/Documentation/CodingStyle linux/Documentation/CodingStyle --- v2.0.34/linux/Documentation/CodingStyle Wed Jan 3 21:43:40 1996 +++ linux/Documentation/CodingStyle Mon Jul 13 13:47:25 1998 @@ -132,7 +132,7 @@ complexity and indentation level of that function. So, if you have a conceptually simple function that is just one long (but simple) case-statement, where you have to do lots of small things for a lot of -different cases, it's ok to have a longer function. +different cases, it's OK to have a longer function. However, if you have a complex function, and you suspect that a less-than-gifted first-year high-school student might not even @@ -169,7 +169,7 @@ Chapter 6: You've made a mess of it -That's ok, we all do. You've probably been told by your long-time unix +That's OK, we all do. You've probably been told by your long-time Unix user helper that "GNU emacs" automatically formats the C sources for you, and you've noticed that yes, it does do that, but the defaults it uses are less than desirable (in fact, they are worse than random diff -u --recursive --new-file v2.0.34/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.0.34/linux/Documentation/Configure.help Mon Jul 13 13:46:24 1998 +++ linux/Documentation/Configure.help Mon Jul 13 13:47:25 1998 @@ -320,6 +320,220 @@ 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 @@ 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 @@ -1013,7 +1252,7 @@ PC/TCP compatibility mode CONFIG_INET_PCTCP - If you have been having difficulties telneting to your Linux machine + If you have been having difficulties telnetting to your Linux machine from a DOS system that uses (broken) PC/TCP networking software (all versions up to OnNet 2.0) over your local ethernet try enabling this option. Everyone else says N. @@ -2045,18 +2284,112 @@ running kernel whenever you want), say M here and read Documentation/modules.txt. -BAYCOM ser12 and par96 kiss emulation driver for AX.25 +BAYCOM ser12 and par96 driver for AX.25 CONFIG_BAYCOM This is an experimental driver for Baycom style simple amateur radio modems that connect to either a serial interface or a parallel interface. The driver supports the ser12 and par96 designs. To - configure the driver, use the setbaycom utility available from - http://www.ife.ee.ethz.ch/~sailer/ham/ham.html#lnxbay. For - information on the modems, see http://www.baycom.de and - drivers/char/README.baycom. If you want to compile this as a module - ( = code which can be inserted in and removed from the running - kernel whenever you want), say M here and read - Documentation/modules.txt. This is recommended. + configure the driver, use the sethdlc utility available + in the standard ax25 utilities package. For information on the modems, + see http://www.baycom.de and drivers/net/README.baycom. + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. This is recommended. + +Sound card modem driver for AX.25 +CONFIG_SOUNDMODEM + This experimental driver allows a standard SoundBlaster or + WindowsSoundSystem compatible sound card to be used as a packet radio + modem (NOT as a telephone modem!), to send digital traffic over + amateur radio. + + To configure the driver, use the sethdlc, smdiag and smmixer + utilities available in the standard ax25 utilities package. For + information on how to key the transmitter, see + http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html (to browse + the WWW, you need to have access to a machine on the Internet that + has a program like lynx or netscape) and + Documentation/networking/soundmodem.txt. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. This is recommended. + The module will be called soundmodem.o. + +Sound card modem support for SoundBlaster and compatible cards +CONFIG_SOUNDMODEM_SBC + This option enables the soundmodem driver to use SoundBlaster and + compatible cards. If you have a dual mode card (i.e. a WSS cards + with a SoundBlaster emulation) you should say N here and Y to + "Sound card modem support for WSS and Crystal cards", below, because + this usually results in better performance. This option also supports + SB16/32/64 in full duplex mode. + +Sound card modem support for WSS and Crystal cards +CONFIG_SOUNDMODEM_WSS + This option enables the soundmodem driver to use WindowsSoundSystem + compatible cards. These cards feature a codec chip from either + Analog Devices (such as AD1848, AD1845, AD1812) or Crystal + Semiconductors (such as CS4248, CS423x). This option also supports + the WSS full duplex operation which currently works with Crystal + CS423x chips. If you don't need full duplex operation, do not enable + it to save performance. + +Sound card modem support for 1200 baud AFSK modulation +CONFIG_SOUNDMODEM_AFSK1200 + This option enables the soundmodem driver 1200 baud AFSK modem, + compatible to popular modems using TCM3105 or AM7911. The demodulator + requires about 12% of the CPU power of a Pentium 75 CPU per channel. + +Sound card modem support for 2400 baud AFSK modulation (7.3728MHz crystal) +CONFIG_SOUNDMODEM_AFSK2400_7 + This option enables the soundmodem driver 2400 baud AFSK modem, + compatible to TCM3105 modems (over-)clocked with a 7.3728MHz + crystal. Note that the availability of this driver does _not_ imply + that I recommend building such links. It is only here since users + especially in eastern Europe have asked me to do so. In fact this + modulation scheme has many disadvantages, mainly its incompatibility + with many transceiver designs and the fact that the TCM3105 (if + used) is operated widely outside its specifications. + +Sound card modem support for 2400 baud AFSK modulation (8MHz crystal) +CONFIG_SOUNDMODEM_AFSK2400_8 + This option enables the soundmodem driver 2400 baud AFSK modem, + compatible to TCM3105 modems (over-)clocked with an 8MHz crystal. + Note that the availability of this driver does _not_ imply that I + recommend building such links. It is only here since users + especially in eastern Europe have asked me to do so. In fact this + modulation scheme has many disadvantages, mainly its incompatibility + with many transceiver designs and the fact that the TCM3105 (if + used) is operated widely outside its specifications. + +Sound card modem support for 2666 baud AFSK modulation +CONFIG_SOUNDMODEM_AFSK2666 + This option enables the soundmodem driver 2666 baud AFSK modem. + This modem is experimental, and not compatible to anything + else I know of. + +Sound card modem support for 4800 baud 8PSK modulation +CONFIG_SOUNDMODEM_PSK4800 + This option enables the soundmodem driver 4800 baud 8PSK modem. + This modem is experimental, and not compatible to anything + else I know of. + +Sound card modem support for 4800 baud HAPN-1 modulation +CONFIG_SOUNDMODEM_HAPN4800 + This option enables the soundmodem driver 4800 baud HAPN-1 + compatible modem. This modulation seems to be widely used 'down + under' and in the Netherlands. Here, nobody uses it, so I could not + test if it works. It is compatible to itself, however :-) + +Sound card modem support for 9600 baud FSK G3RUH modulation +CONFIG_SOUNDMODEM_FSK9600 + This option enables the soundmodem driver 9600 baud FSK modem, + compatible to the G3RUH standard. The demodulator requires about 4% + of the CPU power of a Pentium 75 CPU per channel. You can say Y to + both 1200 baud AFSK and 9600 baud FSK if you want (but obviously you + can only use one protocol at a time, depending on what the other end + can understand). PLIP (parallel port) support CONFIG_PLIP @@ -4324,10 +4657,10 @@ # LocalWords: ipppd syncppp RFC MPP VJ downloaded icn NICCY Creatix shmem ufr # LocalWords: ibp md ARCnet ether encap NDIS arcether ODI Amigas AmiTCP NetBSD # LocalWords: initrd tue util DES funet des OnNet BIOSP smc Travan Iomega CMS -# LocalWords: FC DC dc PPA IOMEGA's ppa RNFS FMV Fujitsu ARPD arpd loran layes +# LocalWords: FC DC dc PPA ppa RNFS FMV Fujitsu ARPD arpd loran layes # LocalWords: FRAD indiana framerelay DLCI DCLIs Sangoma SDLA mrouted sync sec # LocalWords: Starmode Metricom MosquitoNet mosquitonet kbit nfsroot Digiboard -# LocalWords: DIGI Xe Xeve digiboard UMISC touchscreens mtu ethernets HBAs MEX +# LocalWords: DIGI Xe Xeve digiboard UMISC touchscreens mtu HBAs MEX # LocalWords: Shifflett netcom js jshiffle WIC DECchip ELCP EtherPower dst RTC # LocalWords: rtc SMP lp Digi Intl RightSwitch DGRS dgrs AFFS Amiga UFS SDL AP # LocalWords: Solaris RISCom riscom syncPPP PCBIT pcbit sparc anu au artoo ufs @@ -4336,7 +4669,7 @@ # LocalWords: Bernd informatik rwth aachen uae affs multihosting bytecode java # LocalWords: applets applet JDK ncsa cabi SNI Alphatronix readme LANs scarab # LocalWords: winsock RNIS caltech OSPF honour Honouring Mbit Localtalk DEFRAG -# LocalWords: localtalk download Packetwin Baycom baycom interwork ascii JNT +# LocalWords: download Packetwin Baycom baycom interwork ascii JNT # LocalWords: Camtec proxying indyramp defragment defragmented UDP FAS FASXX # LocalWords: FastSCSI SIO FDC qlogicfas QLogic qlogicisp setbaycom ife ee LJ # LocalWords: ethz ch Travelmates ProAudioSpectrum ProAudio SoundMan SB SBPro diff -u --recursive --new-file v2.0.34/linux/Documentation/IO-mapping.txt linux/Documentation/IO-mapping.txt --- v2.0.34/linux/Documentation/IO-mapping.txt Mon Sep 30 07:05:45 1996 +++ linux/Documentation/IO-mapping.txt Mon Jul 13 13:47:25 1998 @@ -56,13 +56,13 @@ where all the addresses actually point to the same thing, it's just seen through different translations.. -Similarly, on the alpha, the normal translation is +Similarly, on the Alpha, the normal translation is physical address: 0 virtual address: 0xfffffc0000000000 bus address: 0x40000000 -(but there are also alpha's where the physical address and the bus address +(but there are also Alphas where the physical address and the bus address are the same). Anyway, the way to look up all these translations, you do @@ -169,7 +169,7 @@ Ok, that just about covers the basics of accessing IO portably. Questions? Comments? You may think that all the above is overly complex, but one day you -might find yourself with a 500MHz alpha in front of you, and then you'll be +might find yourself with a 500 MHz Alpha in front of you, and then you'll be happy that your driver works ;) Note that kernel versions 2.0.x (and earlier) mistakenly called the diff -u --recursive --new-file v2.0.34/linux/Documentation/SMP.txt linux/Documentation/SMP.txt --- v2.0.34/linux/Documentation/SMP.txt Mon May 6 02:26:01 1996 +++ linux/Documentation/SMP.txt Mon Jul 13 13:47:25 1998 @@ -19,7 +19,7 @@ o Clean up warnings/volatiles. o Fix load_TR() for non contiguous processor ids o Iterate over the slave timer requests if one is lost (keep a count per cpu) -o Distribute irq's (locking present just needs the 82489 to be asked +o Distribute IRQs (locking present just needs the 82489 to be asked nicely). o 486 startup code. o How to handle mixed FPU/non FPU processors. diff -u --recursive --new-file v2.0.34/linux/Documentation/cdrom/sonycd535 linux/Documentation/cdrom/sonycd535 --- v2.0.34/linux/Documentation/cdrom/sonycd535 Sat Jul 1 09:05:58 1995 +++ linux/Documentation/cdrom/sonycd535 Mon Jul 13 13:47:25 1998 @@ -1,7 +1,7 @@ README FOR LINUX SONY CDU-535/531 DRIVER ======================================== -This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux. +This is the Sony CDU-535 (and 531) driver version 0.7 for Linux. I do not think I have the documentation to add features like DMA support so if anyone else wants to pursue it or help me with it, please do. (I need to see what was done for the CDU-31A driver -- perhaps I can diff -u --recursive --new-file v2.0.34/linux/Documentation/devices.txt linux/Documentation/devices.txt --- v2.0.34/linux/Documentation/devices.txt Mon Jul 13 13:46:24 1998 +++ linux/Documentation/devices.txt Mon Jul 13 13:47:25 1998 @@ -261,7 +261,7 @@ ... The metadisk driver is used to span a - filesystem across multiple physical disks. + file system across multiple physical disks. 10 char Non-serial mice, misc features 0 = /dev/logibm Logitech bus mouse @@ -345,11 +345,11 @@ 33 = /dev/patmgr1 Sequencer patch manager 34 = /dev/midi02 Third MIDI port 50 = /dev/midi03 Fourth MIDI port - block BIOS harddrive callback support - 0 = /dev/dos_hda First BIOS harddrive whole disk - 64 = /dev/dos_hdb Second BIOS harddrive whole disk - 128 = /dev/dos_hdc Third BIOS harddrive whole disk - 192 = /dev/dos_hdd Fourth BIOS harddrive whole disk + block BIOS hard drive callback support + 0 = /dev/dos_hda First BIOS hard drive whole disk + 64 = /dev/dos_hdb Second BIOS hard drive whole disk + 128 = /dev/dos_hdc Third BIOS hard drive whole disk + 192 = /dev/dos_hdd Fourth BIOS hard drive whole disk Partitions are handled in the same way as IDE disks (see major number 3). @@ -854,7 +854,7 @@ /dev/cdwriter CD-writer symbolic Current CD-writer device /dev/scanner scanner symbolic Current scanner device /dev/modem modem port symbolic Current dialout device -/dev/root root device symbolic Current root filesystem +/dev/root root device symbolic Current root file system /dev/swap swap device symbolic Current swap device /dev/modem should not be used for a modem which supports dialin as diff -u --recursive --new-file v2.0.34/linux/Documentation/filesystems/affs.txt linux/Documentation/filesystems/affs.txt --- v2.0.34/linux/Documentation/filesystems/affs.txt Wed Jul 24 23:08:28 1996 +++ linux/Documentation/filesystems/affs.txt Mon Jul 13 13:47:25 1998 @@ -121,7 +121,7 @@ Although the Amiga and Linux file systems resemble each other, there are some, not always subtle, differences. One of them becomes apparent with symbolic links. While Linux has a file system with exactly one -root directory, the Amiga has a seperate root directory for each +root directory, the Amiga has a separate root directory for each file system (i. e. partition, floppy disk, ...). With the Amiga, these entities are called "volumes". They have symbolic names which can be used to access them. Thus, symbolic links can point to a diff -u --recursive --new-file v2.0.34/linux/Documentation/ide.txt linux/Documentation/ide.txt --- v2.0.34/linux/Documentation/ide.txt Mon Aug 4 11:45:55 1997 +++ linux/Documentation/ide.txt Mon Jul 13 13:47:25 1998 @@ -135,7 +135,7 @@ Note that the first parameter reserves 8 contiguous ioports, whereas the second value denotes a single ioport. If in doubt, do a 'cat /proc/ioports'. -In all probability the device uses these ports and irqs if it is attached +In all probability the device uses these ports and IRQs if it is attached to the appropriate ide channel. Pass the parameter for the correct ide channel to the kernel, as explained above. @@ -180,7 +180,7 @@ Courtesy of Scott Snyder, the driver supports ATAPI cdrom drives such as the NEC-260 and the new MITSUMI triple/quad speed drives. -Such drives will be identified at boot time, just like a harddisk. +Such drives will be identified at boot time, just like a hard disk. If for some reason your cdrom drive is *not* found at boot time, you can force the probe to look harder by supplying a kernel command line parameter @@ -190,7 +190,7 @@ or hdd=cdrom /* hdd = "slave" on second interface */ -For example, a GW2000 system might have a harddrive on the primary +For example, a GW2000 system might have a hard drive on the primary interface (/dev/hda) and an IDE cdrom drive on the secondary interface (/dev/hdc). To mount a CD in the cdrom drive, one would use something like: @@ -258,6 +258,7 @@ older/odd IDE drives. "hdx=slow" : insert a huge pause after each access to the data port. Should be used only as a last resort. + "hdx=ide-scsi" : use the ide-scsi driver for hdx "idebus=xx" : inform IDE driver of VESA/PCI bus speed in Mhz, where "xx" is between 20 and 66 inclusive, @@ -428,7 +429,7 @@ must reside within the first 1024 cylinders of the drive. If your linux root partition is *not* completely within the first 1024 cyls (quite common), then you can use LILO to boot linux from files on your DOS partition -by doing the following after installing slackware (or whatever): +by doing the following after installing Slackware (or whatever): 0. Boot from the "boot floppy" created during the installation 1. Mount your DOS partition as /dos (and stick it in /etc/fstab) diff -u --recursive --new-file v2.0.34/linux/Documentation/isdn/README linux/Documentation/isdn/README --- v2.0.34/linux/Documentation/isdn/README Mon Aug 4 17:33:59 1997 +++ linux/Documentation/isdn/README Mon Jul 13 13:47:25 1998 @@ -353,7 +353,7 @@ command "isdnctrl addphone <InterfaceName> in <caller-id>" Euro-ISDN does not transmit the leading '0' of the caller-id for an incoming call, therefore you should configure it accordingly. - If the real number for the dialout e.g. is "09311234567" the the number + If the real number for the dialout e.g. is "09311234567" the number to configure here is "9311234567". The pattern-match function works similar to the shell mechanism. @@ -374,7 +374,7 @@ Returns the EAZ of an interface. "isdnctrl delphone <InterfaceName> in|out <number>" - Deletes a number from one of the the access-lists of the interface. + Deletes a number from one of the access-lists of the interface. "isdnctrl delif <InterfaceName>" Removes the interface (and possible slaves) from the kernel. diff -u --recursive --new-file v2.0.34/linux/Documentation/isdn/README.sc linux/Documentation/isdn/README.sc --- v2.0.34/linux/Documentation/isdn/README.sc Mon Aug 4 17:33:59 1997 +++ linux/Documentation/isdn/README.sc Mon Jul 13 13:47:25 1998 @@ -42,7 +42,7 @@ --------------- The revision 2 Linux driver for SpellCaster ISA ISDN adapters is built -upon ISDN4Linux available seperately or as included in Linux 2.0 and later. +upon ISDN4Linux available separately or as included in Linux 2.0 and later. The driver will support a maximum of 4 adapters in any one system of any type including DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI for a maximum of 92 channels for host. The driver is supplied as a module in @@ -81,7 +81,7 @@ the driver and at the same time doubled the number of I/O ports probed increasing the likelyhood of finding an adapter. - We now support all ISA adapter models with a single driver instead - of seperate drivers for each model. The revision 2 driver supports + of separate drivers for each model. The revision 2 driver supports the DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI in any combination up to a maximum of four adapters per system. - On board PPP protocol support has been removed in favour of the diff -u --recursive --new-file v2.0.34/linux/Documentation/memory-tuning.txt linux/Documentation/memory-tuning.txt --- v2.0.34/linux/Documentation/memory-tuning.txt Mon Aug 18 20:16:47 1997 +++ linux/Documentation/memory-tuning.txt Mon Jul 13 13:47:25 1998 @@ -21,8 +21,8 @@ The values selected as boot defaults are the following: For a machine with n>=8 Megabytes of memory, set min_free_pages = n*2, -free_pages_low = n*3 and free_pages_high = n*4. Machines with less -than 8 Megabytes or less as if they had 8 Megabytes. +free_pages_low = n*3 and free_pages_high = n*4. Machines with +8 Megabytes or less behave as if they had 8 Megabytes. If "out of memory" errors sometimes occur, or if your machine does lots of networking, increasing min_free_pages to 64 or more may be a good diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/00-INDEX linux/Documentation/networking/00-INDEX --- v2.0.34/linux/Documentation/networking/00-INDEX Tue Apr 8 08:47:45 1997 +++ linux/Documentation/networking/00-INDEX Mon Jul 13 13:47:26 1998 @@ -12,8 +12,14 @@ - info on the using the arcnet driver itself. ax25.txt - info on using AX.25 and NET/ROM code for Linux +depca.txt + - the Digital DEPCA/EtherWORKS DE1?? and DE2?? LANCE Ethernet driver framerelay.txt - info on using Frame Relay/Data Link Connection Identifier (DLCI). +ip_dynaddr.txt + - info on using dynamic IP addressing for diald and IP masquerading. +ipx.txt + - info on the two possible modes of kernel IPX network support. masquerading.txt - using IP masquerading, multiple machines using a single IP address. ncsa-telnet @@ -22,10 +28,16 @@ - info and "insmod" parameters for all network driver modules. ppp.txt - info on what software you should use to run PPP. +so_bindtodevice.txt + - info on binding a socket to a specific device/interface. tcp.txt - short blurb on how TCP output takes place. +tlan.FAQ + - freq. asked questions on the TLAN (Compaq Netelligent) driver. +tlan.README + - usage and supported hardware info for the Thunderlan driver. tulip.txt - - info on using DEC 21040/21041/21140 based PCI ethernet cards. + - info on using DEC 21040/21041/21140 based PCI Ethernet cards. vortex.txt - info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) e'net cards. z8530drv.txt diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/arcnet-hardware.txt linux/Documentation/networking/arcnet-hardware.txt --- v2.0.34/linux/Documentation/networking/arcnet-hardware.txt Mon May 6 02:26:01 1996 +++ linux/Documentation/networking/arcnet-hardware.txt Mon Jul 13 13:47:26 1998 @@ -17,19 +17,19 @@ INTRODUCTION TO ARCNET ---------------------- -ARCnet is a network type which works in a way similar to popular "ethernet" +ARCnet is a network type which works in a way similar to popular Ethernet networks but which is also different in some very important ways. -First of all, you can get ARCnet cards in at least two speeds: 2.5Mbps -(slower than ethernet) and 100Mbps (faster than normal ethernet). In fact, +First of all, you can get ARCnet cards in at least two speeds: 2.5 Mbps +(slower than Ethernet) and 100 Mbps (faster than normal Ethernet). In fact, there are others as well, but these are less common. The different hardware types, as far as I'm aware, are not compatible and so you cannot wire a -100Mbps card to a 2.5Mbps card, and so on. From what I hear, my driver does -work with 100Mbps cards, but I haven't been able to verify this myself, -since I only have the 2.5Mbps variety. It is probably not going to saturate -your 100Mbps card. Stop complaining :) +100 Mbps card to a 2.5 Mbps card, and so on. From what I hear, my driver does +work with 100 Mbps cards, but I haven't been able to verify this myself, +since I only have the 2.5 Mbps variety. It is probably not going to saturate +your 100 Mbps card. Stop complaining :) -You also cannot connect an ARCnet card to any kind of ethernet card and +You also cannot connect an ARCnet card to any kind of Ethernet card and expect it to work. There are two "types" of ARCnet - STAR topology and BUS topology. This @@ -41,7 +41,7 @@ Once you get past these little stumbling blocks, ARCnet is actually quite a well-designed standard. It uses something called "modified token passing" which makes it completely incompatible with so-called "Token Ring" cards, -but which makes transfers much more reliable than ethernet does. In fact, +but which makes transfers much more reliable than Ethernet does. In fact, ARCnet will guarantee that a packet arrives safely at the destination, and even if it can't possibly be delivered properly (ie. because of a cable break, or because the destination computer does not exist) it will at least @@ -53,9 +53,9 @@ In addition, all known ARCnet cards have an (almost) identical programming interface. This means that with one "arcnet" driver you can support any -card; whereas, with ethernet, each manufacturer uses what is sometimes a +card; whereas, with Ethernet, each manufacturer uses what is sometimes a completely different programming interface, leading to a lot of different, -sometimes very similar, ethernet drivers. Of course, always using the same +sometimes very similar, Ethernet drivers. Of course, always using the same programming interface also means that when high-performance hardware facilities like PCI busmastering DMA appear, it's hard to take advantage of them. Let's not go into that. @@ -63,10 +63,10 @@ One thing that makes ARCnet cards difficult to program for, however, is the limit on their packet sizes; standard ARCnet can only send packets that are up to 508 bytes in length. This is smaller than the internet "bare minimum" -of 576 bytes, let alone the ethernet MTU of 1500. To compensate, an extra +of 576 bytes, let alone the Ethernet MTU of 1500. To compensate, an extra level of encapsulation is defined by RFC1201, which I call "packet splitting," that allows "virtual packets" to grow as large as 64K each, -although they are generally kept down to the ethernet-style 1500 bytes. +although they are generally kept down to the Ethernet-style 1500 bytes. For more information on the advantages and disadvantages (mostly the advantages) of ARCnet networks, you might try the "ARCnet Trade Association" @@ -319,7 +319,7 @@ - Avery's favourite: 0xD0000 - the station address: Every ARCnet card has its own "unique" network - address from 0 to 255. Unlike ethernet, you can set this address + address from 0 to 255. Unlike Ethernet, you can set this address yourself with a jumper or switch (or on some cards, with special software). Since it's only 8 bits, you can only have 254 ARCnet cards on a network. DON'T use 0 or 255, since these are reserved (although @@ -1169,7 +1169,7 @@ DIP Switches: - The dipswitches accessible on the accessible end of the card while + The DIP switches accessible on the accessible end of the card while it is installed, is used to set the arcnet address. There are 8 switches. Use an address from 1 to 254. @@ -1184,7 +1184,7 @@ 11111110 1 11111111 0 (Don't use this!) - There is another dipswitch array of 8 switches at the top of the + There is another array of eight DIP switches at the top of the card. There are five labelled MS0-MS4 which seem to control the memory address, and another three labelled IO0-IO2 which seem to control the base I/O address of the card. diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt --- v2.0.34/linux/Documentation/networking/arcnet.txt Wed Sep 11 07:57:12 1996 +++ linux/Documentation/networking/arcnet.txt Mon Jul 13 13:47:26 1998 @@ -147,10 +147,10 @@ Go read the NET-2-HOWTO and ETHERNET-HOWTO for Linux; they should be available where you picked up this driver. Think of your ARCnet as a -souped-up (or down, as the case may be) ethernet card. +souped-up (or down, as the case may be) Ethernet card. By the way, be sure to change all references from "eth0" to "arc0" in the -HOWTOs. Remember that ARCnet isn't a "true" ethernet, and the device name +HOWTOs. Remember that ARCnet isn't a "true" Ethernet, and the device name is DIFFERENT. @@ -170,7 +170,7 @@ How do I get it to work with...? -------------------------------- -NFS: Should be fine linux->linux, just pretend you're using ethernet cards. +NFS: Should be fine Linux->Linux, just pretend you're using Ethernet cards. oak.oakland.edu:/simtel/msdos/nfs has some nice DOS clients. There is also a DOS-based NFS server called SOSS. It doesn't multitask quite the way Linux does (actually, it doesn't multitask AT ALL) but @@ -196,7 +196,7 @@ LAN Manager and Windows for Workgroups: These programs use protocols that are incompatible with the internet standard. They try to pretend - the cards are ethernet, and confuse everyone else on the network. + the cards are Ethernet, and confuse everyone else on the network. However, v2.00 and higher of the Linux ARCnet driver supports this protocol via the 'arc0e' device. See the section on "Multiprotocol diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/ax25.txt linux/Documentation/networking/ax25.txt --- v2.0.34/linux/Documentation/networking/ax25.txt Wed Jul 5 03:06:27 1995 +++ linux/Documentation/networking/ax25.txt Mon Jul 13 13:47:26 1998 @@ -1,50 +1,16 @@ -This is version 029 of the new AX.25 and NET/ROM code for Linux. It -incorporates many enhancements since the last release, notably the rewriting -of the connected mode IP code and the IP over NET/ROM code. The opportunity -has been taken to add the G8BPQ NET/ROM extensions and to add BPQ Ethernet -support. The latter has been much eased by the use of the new variable -length header code by Alan Cox. +To use the amateur radio protocols within Linux you will need to get a +suitable copy of the AX.25 Utilities. More detailed information about these +and associated programs can be found on http://www.cs.nott.ac.uk/~jsn/. + +For more information about the AX.25, NET/ROM and ROSE protocol stacks, see +the AX25-HOWTO written by Terry Dawson <terry@perf.no.itg.telstra.com.au> +who is also the AX.25 Utilities maintainer. + +There is an active mailing list for discussing Linux amateur radio matters +called linux-hams. To subscribe to it, send a message to +Majordomo@vger.rutgers.edu with the words "subscribe linux-hams" in the body +of the message, the subject field is ignored. -To use the BPQ Ethernet option, first up the ethernet interface in the usual -manner, the IP address of the interface is not that important but it will -be required for the ARP table. Next create an ARP entry in the ARP table of -type ax25 for the interface binding it to an AX.25 callsign, this callsign -will be the callsign of that interface. By default BPQ Ethernet uses a -multi-cast address, this implementation does not, instead the standard -ethernet broadcast address is used. Therefore the NET.CFG file for the -ODI driver should look similar to this: +Jonathan G4KLX ------------------------------- cut here ------------------------------------ - -LINK SUPPORT - - MAX STACKS 1 - MAX BOARDS 1 - -LINK DRIVER E2000 ; or other MLID to suit your card - - INT 10 ; - PORT 300 ; to suit your card - - FRAME ETHERNET_II - - PROTOCOL BPQ 8FF ETHERNET_II ; required for BPQ - can change PID - -BPQPARMS ; optional - only needed if you want - ; to override the default target addr - - ETH_ADDR FF:FF:FF:FF:FF:FF ; Target address - ------------------------------ cut here ------------------------------------- - -The above configuration assumes that only BPQ Ethernet is being used. - -It is not possible to run IP over AX.25 on the BPQ Ethernet port. To simply -route IP frames to (say) eth0 would create standard ethernet IP frames and -completely bypass the AX.25 code. However it is possible to use IP over -NET/ROM across a BPQ Ethernet link, the performance of such a system is -very acceptable indeed. - -Jonathan Naylor G4KLX - -g4klx@amsat.org +jsn@cs.nott.ac.uk diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/baycom.txt linux/Documentation/networking/baycom.txt --- v2.0.34/linux/Documentation/networking/baycom.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/baycom.txt Mon Jul 13 13:47:26 1998 @@ -0,0 +1,131 @@ + LINUX DRIVER FOR BAYCOM MODEMS + + Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch> + +This document describes the Linux Kernel Driver for simple Baycom style +amateur radio modems. The driver supports the following modems: + +ser12: This is a very simple 1200 baud AFSK modem. The modem consists only + of a modulator/demodulator chip, usually a TI TCM3105. The computer + is responsible for regenerating the receiver bit clock, as well as + for handling the HDLC protocol. The modem connects to a serial port, + hence the name. Since the serial port is not used as an async serial + port, the kernel driver for serial ports cannot be used, and this + driver only supports standard serial hardware (8250, 16450, 16550) + +par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. + The modem does all the filtering and regenerates the receiver clock. + Data is transferred from and to the PC via a shift register. + The shift register is filled with 16 bits and an interrupt is signalled. + The PC then empties the shift register in a burst. This modem connects + to the parallel port, hence the name. The modem leaves the + implementation of the HDLC protocol and the scrambler polynomial to + the PC. + +picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem + is protocol compatible to par96, but uses only three low power ICs + and can therefore be fed from the parallel port and does not require + an additional power supply. Furthermore, it incorporates a carrier + detect circuitry. + +All of the above modems only support half duplex communications. However, +the driver supports the KISS (see below) fullduplex command. It then simply +starts to send as soon as there's a packet to transmit and does not care +about DCD, i.e. it starts to send even if there's someone else on the channel. +This command is required by some implementations of the DAMA channel +access protocol. + + +The Interface of the driver + +Unlike previous drivers, the driver is no longer a character device, +but it is now a true kernel network interface. Installation is therefore +simple. Once installed, four interfaces named bc[0-3] are available. +sethdlc from the ax25 utilities may be used to set driver states etc. +Users of userland AX.25 stacks may use the net2kiss utility (also available +in the ax25 utilities package) to converts packets of a network interface +to a KISS stream on a pseudo tty. There's also a patch available from +me for WAMPES which allows attaching a kernel network interface directly. + + +Configuring the driver + +Every time the driver is inserted into the kernel, it has to know which +modems it should access at which ports. This can be done with the setbaycom +utility. If you are only using one modem, you can also configure the +driver from the insmod command line (or by means of an option line in +/etc/conf.modules). + +Examples: (use either method, not both) + insmod baycom mode="ser12*" iobase=0x3f8 irq=4 + sethdlc -i bc0 -p type "ser12*" io 0x3f8 irq 4 + +Both lines configure the first port to drive a ser12 modem at the first +serial port (COM1 under DOS). The star ('*') instructs the driver to use +the software DCD algorithm (see below). + + insmod baycom mode="par96*" iobase=0x378 irq=7 + sethdlc -i bc0 -p type "par96*" io 0x378 irq 7 + +Both lines configure the first port to drive a par96 or par97 modem at the +first parallel port (LPT1 under DOS). options=1 instructs the driver to use +the software DCD algorithm (see below). + + insmod baycom mode="par96" iobase=0x278 irq=5 + sethdlc -i bc0 -p type "par96" io 0x278 irq 5 + +Both lines configure the first port to drive a picpar modem at the +second parallel port (LPT2 under DOS). The driver uses the hardware DCD +signalled by the picpar modem (see below). + +The channel access parameters can be set with sethdlc -a or kissparms. +Note that both utilities interpret the values slightly different. + + + +Hardware DCD versus Software DCD + +To avoid collisions on the air, the driver must know when the channel is +busy. This is the task of the DCD circuitry/software. The driver may either +utilise a software DCD algorithm (a star after the mode string) or use a DCD +signal from the hardware (no star). + +ser12: if software DCD is utilised, the radio's squelch should always be + open. It is highly recommended to use the software DCD algorithm, + as it is much faster than most hardware squelch circuitry. The + disadvantage is a slightly higher load on the system. + +par96: the software DCD algorithm for this type of modem is rather poor. + The modem simply does not provide enough information to implement + a reasonable DCD algorithm in software. Therefore, if your radio + feeds the DCD input of the par96 modem, the use of the hardware + DCD circuitry is recommended. + +picpar: the picpar modem features a builtin DCD hardware, which is highly + recommended. + + + +Compatibility with the rest of the Linux kernel + +The serial driver, the line printer (lp) driver and the baycom driver compete +for the same hardware resources. Of course only one driver can access a given +interface at a time. The serial driver grabs all interfaces it can find at +startup time. Therefore the baycom driver subsequently won't be able to +access a serial port. You might therefore find it necessary to release +a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where +# is the number of the interface. The baycom driver does not reserve any +port at startup, unless one is specified on the 'insmod' command line. Another +method to solve the problem is to compile all three drivers as modules and +leave it to kerneld to load the correct driver depending on the application. + + +Further reading + +Please take a look at http://www.ife.ee.ethz.ch/~sailer/ham/linux/hdlc.html +for further informations on the driver. + + +vy 73s de +Tom Sailer, sailer@ife.ee.ethz.ch +Packet Radio: hb9jnx@hb9w.che.eu diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/soundmodem.txt linux/Documentation/networking/soundmodem.txt --- v2.0.34/linux/Documentation/networking/soundmodem.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/soundmodem.txt Mon Jul 13 13:47:26 1998 @@ -0,0 +1,96 @@ + LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS + + Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch> + +This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss) +compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK +AX.25 packet radio modems. Only half duplex operation is supported by +the SoundBlaster driver. Fullduplex is supported for WSS chipsets capable +of addressing two DMA channels. The driver needs some processing power! +A 486DX/2 66MHz is a minimum requirement, otherwise interactive performance +of the computer may become sluggish. + + +The Interface of the driver + +The driver provides a kernel network drivers named sm[0-3]. sethdlc +from the ax25 utilities may be used to set driver states etc. Users +of userland AX.25 stacks may use the net2kiss utility (also available +in the ax25 utilities package) to converts packets of a network interface +to a KISS stream on a pseudo tty. There's also a patch available from +me for WAMPES which allows attaching a kernel network interface directly. + + +Configuring the driver + +Some sound cards need to be initialized before they operate in either +SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this; +you may use the standard linux sound driver to initialize the soundcard; +compile it as a module, and do + insmod sound + rmmod sound +The soundcard should then be initialized correctly. isapnptools is another +package that can configure PnP soundcards. If your soundcard contains +a Crystal CS423x chip, you may also try the setcrystal utility in the +ax25-utilities package. + +Every time the driver is inserted into the kernel, it has to know which +modems it should access at which ports. This can be done with the sethdlc +utility. If you are only using one modem, you can also configure the +driver from the insmod command line (or by means of an option line in +/etc/conf.modules). + +Examples: (use either method, not both) + insmod soundmodem mode="sbc:afsk1200" iobase=0x220 irq=5 dma=1 + sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1 + +Both lines configure the first port to drive a soundblaster card +in 1200 baud AFSK mode. + +The channel access parameters can be set with sethdlc -a or kissparms. +Note that both utilities interpret the values slightly different. + + +Input and output levels + +It is important that the input and output levels are adjusted properly. +There are two utilities, available in the ax25 utilities distribution, +to facilitate this: smmixer and smdiag. smdiag allows you to display +the input signal in an oscilloscope like display or an eye diagram. +smmixer allows you to adjust input/output levels. See the respective +man pages. These two utilities have two counterparts: xfsmmixer and +xfsmdiag. They both require the FORMS to compile, and provide a nicer +user interface. Functionality is the same, though. + + +Transmitter keying + +Since soundcards do not have a DC coupled output; PTT keying options include +the following: +* VOX circuitry +* Serial port pin +* Parallel port pin +* MPU401 MIDI output via a retriggerable monoflop. +Circuit schematics may be found at +http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html. + + +Compatibility with the rest of the Linux kernel + +The sound driver and the soundcard modem driver compete for the same +hardware resources. Of course only one driver can access a given +interface at a time. Worse yet, the sound driver grabs the soundcard +at startup time. Therefore the soundcard modem driver subsequently won't +be able to access the soundcard. You might therefore find it necessary to +unload the sound driver before using the soundcard modem driver. + + +Further reading + +Please take a look at http://www.ife.ee.ethz.ch/~sailer/ham/linux/hdlc.html +for further informations on the driver. + + +vy 73s de +Tom Sailer, sailer@ife.ee.ethz.ch +Packet Radio: hb9jnx@hb9w.che.eu diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/tlan.README linux/Documentation/networking/tlan.README --- v2.0.34/linux/Documentation/networking/tlan.README Mon Jul 13 13:46:24 1998 +++ linux/Documentation/networking/tlan.README Mon Jul 13 13:47:26 1998 @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 -u --recursive --new-file v2.0.34/linux/Documentation/networking/tulip.txt linux/Documentation/networking/tulip.txt --- v2.0.34/linux/Documentation/networking/tulip.txt Mon Apr 22 01:27:41 1996 +++ linux/Documentation/networking/tulip.txt Mon Jul 13 13:47:26 1998 @@ -1,7 +1,7 @@ - Tulip ethernet card driver + Tulip Ethernet Card Driver The Tulip driver is developed by Donald Becker and changed by -Takashi Manabe. This driver is designed to work with PCI ethernet +Takashi Manabe. This driver is designed to work with PCI Ethernet cards which use the DECchip DC21x4x family. This driver hopefully works with all of 1.2.x and 1.3.x kernels, but I tested only with 1.2.13, 1.3.39, 1.3.49, 1.3.52, 1.3.57 and later. diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/z8530drv.txt linux/Documentation/networking/z8530drv.txt --- v2.0.34/linux/Documentation/networking/z8530drv.txt Tue Aug 12 14:47:03 1997 +++ linux/Documentation/networking/z8530drv.txt Mon Jul 13 13:47:26 1998 @@ -238,7 +238,7 @@ clock dpll # clock source: # dpll = normal halfduplex operation # external = MODEM provides own Rx/Tx clock - # divider = use fullduplex divider if + # divider = use full duplex divider if # installed (1) mode nrzi # HDLC encoding mode # nrzi = 1k2 MODEM, G3RUH 9k6 MODEM @@ -463,7 +463,7 @@ Example: sccparam /dev/scc3 speed 9600 txdelay: - The delay (in units of 10ms) after keying of the + The delay (in units of 10 ms) after keying of the transmitter, until the first byte is sent. This is usually called "TXDELAY" in a TNC. When 0 is specified, the driver will just wait until the CTS signal is asserted. This @@ -485,7 +485,7 @@ slottime: This is the time between samples of the channel. It is - expressed in units of 10ms. About 200-300 ms (value 20-30) + expressed in units of 10 ms. About 200-300 ms (value 20-30) seems to be a good value. Example: sccparam /dev/scc0 slot 20 @@ -497,7 +497,7 @@ SCC before the transmitter is keyed down. The value depends on the baudrate selected. A few character times should be sufficient, e.g. 40ms at 1200 baud. (value 4) - The value of this parameter is in 10ms units. + The value of this parameter is in 10 ms units. Example: sccparam /dev/scc2 4 @@ -520,9 +520,9 @@ wait: The initial waittime before any transmit attempt, after the frame has been queue for transmit. This is the length of - the first slot in CSMA mode. In fullduplex modes it is + the first slot in CSMA mode. In full duplex modes it is set to 0 for maximum performance. - The value of this parameter is in 10ms units. + The value of this parameter is in 10 ms units. Example: sccparam /dev/scc1 wait 4 @@ -547,7 +547,7 @@ Example: sccparam /dev/scc3 min 10 idle - This parameter specifies the maximum idle time in fullduplex + This parameter specifies the maximum idle time in full duplex 2 mode, in seconds. When no frames have been sent for this time, the transmitter will be keyed down. A value of 0 is has same result as the fullduplex mode 1. This parameter diff -u --recursive --new-file v2.0.34/linux/Documentation/nfsroot.txt linux/Documentation/nfsroot.txt --- v2.0.34/linux/Documentation/nfsroot.txt Mon May 6 02:26:01 1996 +++ linux/Documentation/nfsroot.txt Mon Jul 13 13:47:26 1998 @@ -109,7 +109,7 @@ less a value has been received by BOOTP. <hostname> Name of the client. If empty, the client IP address is - used in ASCII-notation, or the value received by BOOTP. + used in ASCII notation, or the value received by BOOTP. <device> Name of network device to use. If this is empty, all devices are used for RARP requests, and the first one diff -u --recursive --new-file v2.0.34/linux/Documentation/paride.txt linux/Documentation/paride.txt --- v2.0.34/linux/Documentation/paride.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/paride.txt Mon Jul 13 13:47:26 1998 @@ -0,0 +1,360 @@ + + Linux and parallel port IDE devices + + +PARIDE-2.0.35 (c) 1997-8 Grant Guenther <grant@torque.net> + +************************************************************************* + +Special notes for the 2.0.35 version: + +(i) This is the paride from 2.1.107 retrofitted to work with 2.0.34. + +(ii) PARPORT is _not_ supported. If you obtain the PARPORT patches + for 2.0 and try to use them, it might work. I have not tried + it. + +(iii) There is no guarantee of any ongoing support or future development + for this special retrofit. + +(iv) I have not built or tested PARIDE with SMP support in 2.0.35, + use it at your own risk. + +************************************************************************* + +1. Introduction + +Owing to the simplicity and near universality of the parallel port interface +to personal computers, many external devices such as portable hard-disk, +CD-ROM, LS-120 and tape drives use the parallel port to connect to their +host computer. While some devices (notably scanners) use ad-hoc methods +to pass commands and data through the parallel port interface, most +external devices are actually identical to an internal model, but with +a parallel-port adapter chip added in. Some of the original parallel port +adapters were little more than mechanisms for multiplexing a SCSI bus. +(The Iomega PPA-3 adapter used in the ZIP drives is an example of this +approach). Most current designs, however, take a different approach. +The adapter chip reproduces a small ISA or IDE bus in the external device +and the communication protocol provides operations for reading and writing +device registers, as well as data block transfer functions. Sometimes, +the device being addressed via the parallel cable is a standard SCSI +controller like an NCR 5380. The "ditto" family of external tape +drives use the ISA replicator to interface a floppy disk controller, +which is then connected to a floppy-tape mechanism. The vast majority +of external parallel port devices, however, are now based on standard +IDE type devices, which require no intermediate controller. If one +were to open up a parallel port CD-ROM drive, for instance, one would +find a standard ATAPI CD-ROM drive, a power supply, and a single adapter +that interconnected a standard PC parallel port cable and a standard +IDE cable. It is usually possible to exchange the CD-ROM device with +any other device using the IDE interface. + +The document describes the support in Linux for parallel port IDE +devices. It does not cover parallel port SCSI devices, "ditto" tape +drives or scanners. Many different devices are supported by the +parallel port IDE subsystem, including: + + MicroSolutions backpack CD-ROM + MicroSolutions backpack PD/CD + MicroSolutions backpack hard-drives + MicroSolutions backpack 8000t tape drive + SyQuest EZ-135, EZ-230 & SparQ drives + Avatar Shark + Imation Superdisk LS-120 + FreeCom Power CD + Hewlett-Packard 5GB tape drive + Hewlett-Packard 7100 and 7200 CD-RW drives + +as well as most of the clone and no-name products on the market. + +To support such a wide range of devices, PARIDE, the parallel port IDE +subsystem, is actually structured in three parts. There is a base +paride module which provides a registry and some common methods for +accessing the parallel ports. The second component is a set of +high-level drivers for each of the different type of supported device: + + pd IDE disk + pcd ATAPI CD-ROM + pf ATAPI disk + pt ATAPI tape + pg ATAPI generic + +(Currently, the pg driver is only used with CD-R drives). + +The high-level drivers function according to the relevant standards. +The third component of PARIDE is a set of low-level protocol drivers +for each of the parallel port IDE adapter chips. Thanks to the interest +and encouragement of Linux users from many parts of the world, +support is available for almost all known adapter protocols: + + aten ATEN EH-100 (HK) + bpck Microsolutions backpack (US) + comm DataStor (old-type) "commuter" adapter (TW) + dstr DataStor EP-2000 (TW) + epat Shuttle EPAT (UK) + epia Shuttle EPIA (UK) + fit2 FIT TD-2000 (US) + fit3 FIT TD-3000 (US) + frpw Freecom Power (DE) + kbic KingByte KBIC-951A and KBIC-971A (TW) + ktti KT Technology PHd adapter (SG) + on20 OnSpec 90c20 (US) + on26 OnSpec 90c26 (US) + + +2. Using the PARIDE subsystem + +While configuring the Linux kernel, you may choose either to build +the PARIDE drivers into your kernel, or to build them as modules. + +In either case, you will need to select "Parallel port IDE device support" +as well as at least one of the high-level drivers and at least one +of the parallel port communication protocols. If you do not know +what kind of parallel port adapter is used in your drive, you could +begin by checking the file names and any text files on your DOS +installation floppy. Alternatively, you can look at the markings on +the adapter chip itself. That's usually sufficient to identify the +correct device. + +You can actually select all the protocol modules, and allow the PARIDE +subsystem to try them all for you. + +For the "brand-name" products listed above, here are the protocol +and high-level drivers that you would use: + + Manufacturer Model Driver Protocol + + MicroSolutions CD-ROM pcd bpck + MicroSolutions PD drive pf bpck + MicroSolutions hard-drive pd bpck + MicroSolutions 8000t tape pt bpck + SyQuest EZ, SparQ pd epat + Imation Superdisk pf epat + Avatar Shark pd epat + FreeCom CD-ROM pcd frpw + Hewlett-Packard 5GB Tape pt epat + Hewlett-Packard 7100/7200 pg epat + +2.1 Configuring built-in drivers + +We recommend that you get to know how the drivers work and how to +configure them as loadable modules, before attempting to compile a +kernel with the drivers built-in. + +If you built all of your PARIDE support directly into your kernel, +and you have just a single parallel port IDE device, your kernel should +locate it automatically for you. If you have more than one device, +you may need to give some command line options to your bootloader +(eg: LILO), how to do that is beyond the scope of this document. + +The high-level drivers accept a number of command line parameters, all +of which are documented in the source files in linux/drivers/block/paride. +By default, each driver will automatically try all parallel ports it +can find, and all protocol types that have been installed, until it finds +a parallel port IDE adapter. Once it finds one, the probe stops. So, +if you have more than one device, you will need to tell the drivers +how to identify them. This requires specifying the port address, the +protocol identification number and, for some devices, the drive's +chain ID. While your system is booting, a number of messages are +displayed on the console. Like all such messages, they can be +reviewed with the 'dmesg' command. Among those messages will be +some lines like: + + paride: bpck registered as protocol 0 + paride: epat registered as protocol 1 + +The numbers will always be the same until you build a new kernel with +different protocol selections. You should note these numbers as you +will need them to identify the devices. + +If you happen to be using a MicroSolutions backpack device, you will +also need to know the unit ID number for each drive. This is usually +the last two digits of the drive's serial number (but read MicroSolutions' +documentation about this). + +As an example, let's assume that you have a MicroSolutions PD/CD drive +with unit ID number 36 connected to the parallel port at 0x378, a SyQuest +EZ-135 connected to the chained port on the PD/CD drive and also an +Imation Superdisk connected to port 0x278. You could give the following +options on your boot command: + + pd.drive0=0x378,1 pf.drive0=0x278,1 pf.drive1=0x378,0,36 + +In the last option, pf.drive1 configures device /dev/pf1, the 0x378 +is the parallel port base address, the 0 is the protocol registration +number and 36 is the chain ID. + +This (2.0.34) version of PARIDE does not support chained devices on the +same parallel port. + +2.2 Loading and configuring PARIDE as modules + +It is much faster and simpler to get to understand the PARIDE drivers +if you use them as loadable kernel modules. + +Note: using these drivers with the "kerneld" automatic module loading +system is not recommended, and is not documented here. + +To use PARIDE, you must begin by + + insmod paride + +this loads a base module which provides a registry for the protocols, +among other tasks. + +Then, load as many of the protocol modules as you think you might need. +As you load each module, it will register the protocols that it supports, +and print a log message to your kernel log file and your console. For +example: + + # insmod epat + paride: epat registered as protocol 0 + # insmod kbic + paride: k951 registered as protocol 1 + paride: k971 registered as protocol 2 + +Finally, you can load high-level drivers for each kind of device that +you have connected. By default, each driver will autoprobe for a single +device, but you can support up to four similar devices by giving their +individual co-ordinates when you load the driver. + +For example, if you had two no-name CD-ROM drives both using the +KingByte KBIC-951A adapter, one on port 0x378 and the other on 0x3bc +you could give the following command: + + # insmod pcd drive0=0x378,1 drive1=0x3bc,1 + +For most adapters, giving a port address and protocol number is sufficient, +but check the source files in linux/drivers/block/paride for more +information. (Hopefully someone will write some man pages one day !). + +As another example, here's what happens when PARPORT is installed, and +a SyQuest EZ-135 is attached to port 0x378: + + # insmod paride + paride: version 1.0 installed + # insmod epat + paride: epat registered as protocol 0 + # insmod pd + pd: pd version 1.0, major 45, cluster 64, nice 0 + pda: Sharing parport1 at 0x378 + pda: epat 1.0, Shuttle EPAT chip c3 at 0x378, mode 5 (EPP-32), delay 1 + pda: SyQuest EZ135A, 262144 blocks [128M], (512/16/32), removable media + pda: pda1 + +Note that the last line is the output from the generic partition table +scanner - in this case it reports that it has found a disk with one partition. + +2.3 Using a PARIDE device + +Once the drivers have been loaded, you can access PARIDE devices in the +same way as their traditional counterparts. You will probably need to +create the device "special files". Here is a simple script that you can +cut to a file and execute: + +#!/bin/bash +# +# mkd -- a script to create the device special files for the PARIDE subsystem +# +function mkdev { + mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1 +} +# +function pd { + D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) ) + mkdev pd$D b 45 $[ $1 * 16 ] + for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + do mkdev pd$D$P b 45 $[ $1 * 16 + $P ] + done +} +# +cd /dev +# +for u in 0 1 2 3 ; do pd $u ; done +for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done +for u in 0 1 2 3 ; do mkdev pf$u b 47 $u ; done +for u in 0 1 2 3 ; do mkdev pt$u c 96 $u ; done +for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done +for u in 0 1 2 3 ; do mkdev pg$u c 97 $u ; done +# +# end of mkd + +With the device files and drivers in place, you can access PARIDE devices +like any other Linux device. For example, to mount a CD-ROM in pcd0, use: + + mount /dev/pcd0 /cdrom + +If you have a fresh Avatar Shark cartridge, and the drive is pda, you +might do something like: + + fdisk /dev/pda -- make a new partition table with + partition 1 of type 83 + + mke2fs /dev/pda1 -- to build the file system + + mkdir /shark -- make a place to mount the disk + + mount /dev/pda1 /shark + +Devices like the Imation superdisk work in the same way, except that +they do not have a partition table. For example to make a 120MB +floppy that you could share with a DOS system: + + mkdosfs /dev/pf0 + mount /dev/pf0 /mnt + +2.4 Using the pg driver + +The pg driver can be used in conjunction with the cdrecord program +to create CD-ROMs. For more information, and the required patches +to cdrecord, please visit http://www.torque.net/parport/cdr.html . + +3. Troubleshooting + +While a lot of testing has gone into these drivers to make them work +as smoothly as possible, problems will arise. If you do have problems, +please check all the obvious things first: does the drive work in +DOS with the manufacturer's drivers ? If that doesn't yield any useful +clues, then please make sure that only one drive is hooked to your system, +and that no other device driver is using your parallel port (check in +/proc/ioports). Then, load the appropriate drivers (you can load several +protocol modules if you want) as in: + + # insmod paride + # insmod epat + # insmod bpck + # insmod kbic + ... + # insmod pd verbose=1 + +(using the correct driver for the type of device you have, of course). +The verbose=1 parameter will cause the drivers to log a trace of their +activity as they attempt to locate your drive. + +Use 'dmesg' to capture a log of all the PARIDE messages (any messages +beginning with paride:, a protocol module's name or a driver's name) and +include that with your bug report. You can submit a bug report in one +of two ways. Either send it directly to the author of the PARIDE suite, +by e-mail to grant@torque.net, or join the linux-parport mailing list +and post your report there. + +You can join the linux-parport mailing list by sending a mail message +to + linux-parport-request@torque.net + +with the single word + + subscribe + +in the body of the mail message (not in the subject line). Please be +sure that your mail program is correctly set up when you do this, as +the list manager is a robot that will subscribe you using the reply +address in your mail headers. REMOVE any anti-spam gimmicks you may +have in your mail headers, when sending mail to the list server. + +You might also find some useful information on the linux-parport +web pages (although they are not always up to date) at + + http://www.torque.net/parport/ + + diff -u --recursive --new-file v2.0.34/linux/Documentation/rtc.txt linux/Documentation/rtc.txt --- v2.0.34/linux/Documentation/rtc.txt Mon Jul 13 13:46:24 1998 +++ linux/Documentation/rtc.txt Mon Jul 13 13:47:26 1998 @@ -158,7 +158,7 @@ fflush(stderr); for (i=1; i<6; i++) { struct timeval tv = {5, 0}; /* 5 second timeout on select */ - struct fd_set readfds; + fd_set readfds; FD_ZERO(&readfds); FD_SET(fd, &readfds); diff -u --recursive --new-file v2.0.34/linux/Documentation/smp.tex linux/Documentation/smp.tex --- v2.0.34/linux/Documentation/smp.tex Thu Jun 6 04:57:43 1996 +++ linux/Documentation/smp.tex Mon Jul 13 13:47:26 1998 @@ -24,8 +24,8 @@ \hfill Alan Cox, 1995 -The author wishes to thank Caldera Inc ( http://www.caldera.com ) -whose donation of an ASUS dual pentium board made this project possible, +The author wishes to thank Caldera Inc. ( http://www.caldera.com ) +whose donation of an ASUS dual Pentium board made this project possible, and Thomas Radke, whose initial work on multiprocessor Linux formed the backbone of this project. @@ -35,7 +35,7 @@ specification places much of the onus for hard work on the chipset and hardware rather than the operating system. -The Intel pentium processors have a wide variety of inbuilt facilities for +The Intel Pentium processors have a wide variety of built-in facilities for supporting multiprocessing, including hardware cache coherency, built in interprocessor interrupt handling and a set of atomic test and set, exchange and similar operations. The cache coherency in particular makes the @@ -176,7 +176,7 @@ The memory management core of the existing Linux system functions adequately within the multiprocessor framework providing the locking is used. Certain processor specific areas do need changing, in particular -invalidate() must invalidate the TLB's of all processors before it returns. +invalidate() must invalidate the TLBs of all processors before it returns. \subsubsection{Miscellaneous Functions} @@ -210,7 +210,7 @@ extensions to standard kernel facilities to cope with multiple processors. \subsubsection{Initialisation} -The intel MP architecture captures all the processors except for a single +The Intel MP architecture captures all the processors except for a single processor known as the 'boot processor' in the BIOS at boot time. Thus a single processor enters the kernel bootup code. The first processor executes the bootstrap code, loads and uncompresses the kernel. Having @@ -266,8 +266,8 @@ appropriately. From then on the real APIC logical identity register is read. -Message passing is accomplished using a pair of IPI's on interrupt 13 -(unused by the 80486 FPU's in SMP mode) and interrupt 16. Two are used in +Message passing is accomplished using a pair of IPIs on interrupt 13 +(unused by the 80486 FPUs in SMP mode) and interrupt 16. Two are used in order to separate messages that cannot be processed until the receiver obtains the kernel spinlock from messages that can be processed immediately. In effect IRQ 13 is a fast IRQ handler that does not obtain diff -u --recursive --new-file v2.0.34/linux/MAINTAINERS linux/MAINTAINERS --- v2.0.34/linux/MAINTAINERS Mon Jul 13 13:46:24 1998 +++ linux/MAINTAINERS Mon Jul 13 13:47:26 1998 @@ -389,6 +389,12 @@ 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 -u --recursive --new-file v2.0.34/linux/Makefile linux/Makefile --- v2.0.34/linux/Makefile Mon Jul 13 13:46:24 1998 +++ linux/Makefile Mon Jul 13 13:47:26 1998 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 0 -SUBLEVEL = 34 +SUBLEVEL = 35 ARCH = i386 @@ -146,6 +146,10 @@ 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 @@ -323,6 +327,10 @@ rm -f include/linux/autoconf.h include/linux/version.h rm -f drivers/sound/local.h drivers/sound/.defines rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash + rm -f drivers/net/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h + rm -f drivers/net/soundmodem/sm_tbl_{hapn4800,psk4800}.h + rm -f drivers/net/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h + rm -f drivers/net/soundmodem/gentbl rm -f .version .config* config.in config.old rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog diff -u --recursive --new-file v2.0.34/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c --- v2.0.34/linux/arch/alpha/kernel/bios32.c Mon Jul 13 13:46:25 1998 +++ linux/arch/alpha/kernel/bios32.c Mon Jul 13 13:47:26 1998 @@ -345,13 +345,13 @@ * Alpha implementation of the PCI interface: * * In sparse memory address space, the first - * octant (16MB) of every 128MB segment is - * aliased to the the very first 16MB of the + * octant (16 MB) of every 128 MB segment is + * aliased to the very first 16 MB of the * address space (i.e., it aliases the ISA * memory address space). Thus, we try to * avoid allocating PCI devices in that range. * Can be allocated in 2nd-7th octant only. - * Devices that need more than 112MB of + * Devices that need more than 112 MB of * address space must be accessed through * dense memory space only! */ diff -u --recursive --new-file v2.0.34/linux/arch/alpha/math-emu/ieee-math.c linux/arch/alpha/math-emu/ieee-math.c --- v2.0.34/linux/arch/alpha/math-emu/ieee-math.c Mon Jul 13 13:46:25 1998 +++ linux/arch/alpha/math-emu/ieee-math.c Mon Jul 13 13:47:26 1998 @@ -733,19 +733,23 @@ * FPCR_INV if invalid operation occurred, etc. */ unsigned long -ieee_CVTTQ (int f, unsigned long a, unsigned long *b) +ieee_CVTTQ (int f, unsigned long a, unsigned long *pb) { unsigned int midway; - unsigned long ov, uv, res = 0; + unsigned long ov, uv, res, b; fpclass_t a_type; EXTENDED temp; - *b = 0; a_type = extend_ieee(a, &temp, DOUBLE); + + b = 0x7fffffffffffffff; + res = FPCR_INV; if (a_type == NaN || a_type == INFTY) - return FPCR_INV; + goto out; + + res = 0; if (a_type == QNaN) - return 0; + goto out; if (temp.e > 0) { ov = 0; @@ -757,7 +761,7 @@ if (ov || (temp.f[1] & 0xffc0000000000000)) res |= FPCR_IOV | FPCR_INE; } - if (temp.e < 0) { + else if (temp.e < 0) { while (temp.e < 0) { ++temp.e; uv = temp.f[0] & 1; /* save sticky bit */ @@ -765,7 +769,8 @@ temp.f[0] |= uv; } } - *b = ((temp.f[1] << 9) | (temp.f[0] >> 55)) & 0x7fffffffffffffff; + b = (temp.f[1] << 9) | (temp.f[0] >> 55); + /* * Notice: the fraction is only 52 bits long. Thus, rounding * cannot possibly result in an integer overflow. @@ -781,12 +786,12 @@ break; case ROUND_PINF: - if ((temp.f[0] & 0x003fffffffffffff) != 0) + if ((temp.f[0] & 0x007fffffffffffff) != 0) ++b; break; case ROUND_NINF: - if ((temp.f[0] & 0x003fffffffffffff) != 0) + if ((temp.f[0] & 0x007fffffffffffff) != 0) --b; break; @@ -794,12 +799,15 @@ /* no action needed */ break; } - if ((temp.f[0] & 0x003fffffffffffff) != 0) + if ((temp.f[0] & 0x007fffffffffffff) != 0) res |= FPCR_INE; if (temp.s) { - *b = -*b; + b = -b; } + +out: + *pb = b; return res; } diff -u --recursive --new-file v2.0.34/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.0.34/linux/arch/i386/defconfig Mon Sep 22 13:44:01 1997 +++ linux/arch/i386/defconfig Mon Jul 13 13:47:26 1998 @@ -58,6 +58,7 @@ # CONFIG_BLK_DEV_MD is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set # CONFIG_BLK_DEV_HD is not set # @@ -110,9 +111,10 @@ # CONFIG_EL1 is not set # CONFIG_EL2 is not set CONFIG_EL3=y +# CONFIG_3C515 is not set # CONFIG_VORTEX is not set -# CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_PCI is not set # CONFIG_NET_ISA is not set # CONFIG_NET_EISA is not set # CONFIG_NET_POCKET is not set @@ -138,15 +140,42 @@ # CONFIG_EXT_FS is not set CONFIG_EXT2_FS=y # CONFIG_XIA_FS is not set +CONFIG_NLS=y +CONFIG_ISO9660_FS=y CONFIG_FAT_FS=y CONFIG_MSDOS_FS=y -# CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y # CONFIG_ROOT_NFS is not set # CONFIG_SMB_FS is not set -CONFIG_ISO9660_FS=y # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- v2.0.34/linux/arch/i386/kernel/entry.S Mon Jul 13 13:46:25 1998 +++ linux/arch/i386/kernel/entry.S Mon Jul 13 13:47:27 1998 @@ -374,6 +374,7 @@ jne signal_return 2: RESTORE_ALL ALIGN + .globl signal_return signal_return: movl %esp,%ecx pushl %ecx diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- v2.0.34/linux/arch/i386/kernel/head.S Mon Jul 13 13:46:25 1998 +++ linux/arch/i386/kernel/head.S Mon Jul 13 13:47:27 1998 @@ -103,16 +103,17 @@ 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 @@ 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 @@ 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 -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/ksyms.c linux/arch/i386/kernel/ksyms.c --- v2.0.34/linux/arch/i386/kernel/ksyms.c Tue Sep 16 14:40:50 1997 +++ linux/arch/i386/kernel/ksyms.c Mon Jul 13 13:47:27 1998 @@ -5,6 +5,7 @@ #include <linux/delay.h> #include <asm/semaphore.h> +#include <asm/processor.h> extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -12,6 +13,7 @@ static struct symbol_table arch_symbol_table = { #include <linux/symtab_begin.h> /* platform dependent support */ + X(x86_capability), X(dump_thread), X(dump_fpu), XNOVERS(__do_delay), diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v2.0.34/linux/arch/i386/kernel/ptrace.c Mon Jul 13 13:46:25 1998 +++ linux/arch/i386/kernel/ptrace.c Mon Jul 13 13:47:27 1998 @@ -323,41 +323,11 @@ } #endif /* defined(CONFIG_MATH_EMULATION) */ -/* Put a word to the part of the user structure containing - * floating point registers +/* * Floating point support added to ptrace by Ramon Garcia, * ramon@juguete.quim.ucm.es */ -static int put_fpreg_word (struct task_struct *child, - unsigned long addr, long data) -{ - struct user *dummy = NULL; - if (addr < (long) (&dummy->i387.st_space)) - return -EIO; - addr -= (long) (&dummy->i387.st_space); - - if (!hard_math) { -#ifdef CONFIG_MATH_EMULATION - write_emulator_word(child, addr, data); -#else - return 0; -#endif - } - else -#ifndef __SMP__ - if (last_task_used_math == child) { - clts(); - __asm__("fsave %0; fwait":"=m" (child->tss.i387)); - last_task_used_math = current; - stts(); - } -#endif - *(long *) - ((char *) (child->tss.i387.hard.st_space) + addr) = data; - return 0; -} - #ifdef CONFIG_MATH_EMULATION static unsigned long get_emulator_word(struct task_struct *child, @@ -386,35 +356,54 @@ } #endif /* defined(CONFIG_MATH_EMULATION) */ -/* Get a word from the part of the user structure containing - * floating point registers - */ -static unsigned long get_fpreg_word(struct task_struct *child, - unsigned long addr) + +static int putreg(struct task_struct *child, + unsigned long regno, unsigned long value) { - struct user *dummy = NULL; - unsigned long tmp; - addr -= (long) (&dummy->i387.st_space); - if (!hard_math) { -#ifdef CONFIG_MATH_EMULATION - tmp = get_emulator_word(child, addr); -#else - tmp = 0; -#endif /* !defined(CONFIG_MATH_EMULATION) */ - } else { -#ifndef __SMP__ - if (last_task_used_math == child) { - clts(); - __asm__("fsave %0; fwait":"=m" (child->tss.i387)); - last_task_used_math = current; - stts(); - } -#endif - tmp = *(long *) - ((char *) (child->tss.i387.hard.st_space) + - addr); + 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; } - return tmp; + 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) @@ -490,39 +479,23 @@ 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 +506,18 @@ 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) - 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) + if ((addr & 3) || addr < 0 + || addr > sizeof(struct user) - 3) 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]){ @@ -665,6 +606,108 @@ put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); 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 -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.0.34/linux/arch/i386/kernel/setup.c Mon Jul 13 13:46:25 1998 +++ linux/arch/i386/kernel/setup.c Mon Jul 13 13:47:27 1998 @@ -32,6 +32,7 @@ #include <asm/segment.h> #include <asm/system.h> #include <asm/smp.h> +#include <asm/io.h> /* * Tell us the machine setup.. @@ -49,7 +50,7 @@ unsigned char Cx86_step = 0; static const char *Cx86_type[] = { - "unknown", "1.3", "1.4", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2" + "unknown", "1.3", "1.4", "1.5", "1.6", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2" }; char ignore_irq13 = 0; /* set if exception 16 works */ @@ -224,7 +225,9 @@ static const char * i586model(unsigned int nr) { static const char *model[] = { - "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83" + "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83", + "Pentium MMX", NULL, NULL, "Mobile Pentium 75+", + "Mobile Pentium MMX" }; if (nr < sizeof(model)/sizeof(char *)) return model[nr]; @@ -235,7 +238,7 @@ { unsigned char nr6x86 = 0; static const char *model[] = { - "unknown", "6x86", "6x86L", "6x86MX", "6x86MXi" + "unknown", "6x86", "6x86L", "6x86MX", "MII" }; switch (x86) { case 5: @@ -247,6 +250,10 @@ 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 */ @@ -254,20 +261,26 @@ 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; @@ -285,12 +298,44 @@ return NULL; } +struct cpu_model_info { + int x86; + char *model_names[16]; +}; + +static struct cpu_model_info amd_models[] = { + { 4, + { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4", + "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }}, + { 5, + { "K5/SSA5 (PR-75, PR-90, PR-100)", "K5 (PR-120, PR-133)", + "K5 (PR-166)", "K5 (PR-200)", NULL, NULL, + "K6 (166 - 266)", "K6 (166 - 300)", "K6-2 (200 - 450)", + "K6-3D-Plus (200 - 450)", NULL, NULL, NULL, NULL, NULL, NULL }}, +}; + +static const char * AMDmodel(void) +{ + const char *p=NULL; + int i; + + if (x86_model < 16) + for (i=0; i<sizeof(amd_models)/sizeof(struct cpu_model_info); i++) + if (amd_models[i].x86 == x86) { + p = amd_models[i].model_names[(int)x86_model]; + break; + } + return p; +} + static const char * getmodel(int x86, int model) { const char *p = NULL; static char nbuf[12]; if (strncmp(x86_vendor_id, "Cyrix", 5) == 0) p = Cx86model(); + else if(strcmp(x86_vendor_id, "AuthenticAMD")==0) + p = AMDmodel(); else { switch (x86) { case 4: diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- v2.0.34/linux/arch/i386/kernel/traps.c Tue Mar 10 13:19:08 1998 +++ linux/arch/i386/kernel/traps.c Mon Jul 13 13:47:27 1998 @@ -192,13 +192,36 @@ DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) DO_ERROR(18, SIGSEGV, "reserved", reserved, current) +/* signal_return is directly after ret_from_sys_call in entry.S */ +asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); +asmlinkage void signal_return(void) __asm__("signal_return"); + asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { if (regs->eflags & VM_MASK) { handle_vm86_fault((struct vm86_regs *) regs, error_code); return; } - die_if_kernel("general protection",regs,error_code); + + /* + * HACK HACK HACK :) Fixing the segment invalid on syscall return + * barfage for 2.0 has been put into the too-hard basket but having + * a user producing endless GPFs is unacceptable as well. - Paul G. + */ + if ((regs->cs & 3) != 3) { + if (regs->eip >= (unsigned long)ret_from_sys_call && + regs->eip < (unsigned long)signal_return) { + static int moancount = 0; + if (moancount < 5) { + printk(KERN_INFO "Ignoring GPF attempt from program \"%s\" (pid %d).\n", + current->comm, current->pid); + moancount++; + } + do_exit(SIGSEGV); + } + else + die_if_kernel("general protection",regs,error_code); + } current->tss.error_code = error_code; current->tss.trap_no = 13; force_sig(SIGSEGV, current); diff -u --recursive --new-file v2.0.34/linux/arch/m68k/atari/stram.c linux/arch/m68k/atari/stram.c --- v2.0.34/linux/arch/m68k/atari/stram.c Wed Dec 27 12:44:28 1995 +++ linux/arch/m68k/atari/stram.c Mon Jul 13 13:47:27 1998 @@ -153,7 +153,7 @@ /* ++roman: * * New version of ST-Ram buffer allocation. Instead of using the - * 1 MB - 4 KB that remain when the the ST-Ram chunk starts at $1000 + * 1 MB - 4 KB that remain when the ST-Ram chunk starts at $1000 * (1 MB granularity!), such buffers are reserved like this: * * - If the kernel resides in ST-Ram anyway, we can take the buffer diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/res_func.S linux/arch/m68k/fpsp040/res_func.S --- v2.0.34/linux/arch/m68k/fpsp040/res_func.S Fri Apr 26 02:12:35 1996 +++ linux/arch/m68k/fpsp040/res_func.S Mon Jul 13 13:47:27 1998 @@ -1889,7 +1889,7 @@ | | Input: a0 points to a normalized number in internal extended format | d0 is the round precision (=1 for sgl; =2 for dbl) -| d1 is the the single precision or double precision +| d1 is the single precision or double precision | denorm threshold | | Output: (In the format for dest_sgl or dest_dbl) diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/slogn.S linux/arch/m68k/fpsp040/slogn.S --- v2.0.34/linux/arch/m68k/fpsp040/slogn.S Fri Apr 26 02:12:35 1996 +++ linux/arch/m68k/fpsp040/slogn.S Mon Jul 13 13:47:27 1998 @@ -17,7 +17,7 @@ | result is provably monotonic in double precision. | | Speed: The program slogn takes approximately 190 cycles for input -| argument X such that |X-1| >= 1/16, which is the the usual +| argument X such that |X-1| >= 1/16, which is the usual | situation. For those arguments, slognp1 takes approximately | 210 cycles. For the less common arguments, the program will | run no worse than 10% slower. diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/ssin.S linux/arch/m68k/fpsp040/ssin.S --- v2.0.34/linux/arch/m68k/fpsp040/ssin.S Fri Apr 26 02:12:35 1996 +++ linux/arch/m68k/fpsp040/ssin.S Mon Jul 13 13:47:27 1998 @@ -21,7 +21,7 @@ | result is provably monotonic in double precision. | | Speed: The programs sSIN and sCOS take approximately 150 cycles for -| input argument X such that |X| < 15Pi, which is the the usual +| input argument X such that |X| < 15Pi, which is the usual | situation. The speed for sSINCOS is approximately 190 cycles. | | Algorithm: diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/stan.S linux/arch/m68k/fpsp040/stan.S --- v2.0.34/linux/arch/m68k/fpsp040/stan.S Fri Apr 26 02:12:35 1996 +++ linux/arch/m68k/fpsp040/stan.S Mon Jul 13 13:47:27 1998 @@ -16,7 +16,7 @@ | result is provably monotonic in double precision. | | Speed: The program sTAN takes approximately 170 cycles for -| input argument X such that |X| < 15Pi, which is the the usual +| input argument X such that |X| < 15Pi, which is the usual | situation. | | Algorithm: diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/sto_res.S linux/arch/m68k/fpsp040/sto_res.S --- v2.0.34/linux/arch/m68k/fpsp040/sto_res.S Tue Apr 23 21:31:24 1996 +++ linux/arch/m68k/fpsp040/sto_res.S Mon Jul 13 13:47:27 1998 @@ -3,7 +3,7 @@ | | Takes the result and puts it in where the user expects it. | Library functions return result in fp0. If fp0 is not the -| users destination register then fp0 is moved to the the +| user's destination register then fp0 is moved to the | correct floating-point destination register. fp0 and fp1 | are then restored to the original contents. | diff -u --recursive --new-file v2.0.34/linux/arch/m68k/mm/memory.c linux/arch/m68k/mm/memory.c --- v2.0.34/linux/arch/m68k/mm/memory.c Wed May 15 23:05:10 1996 +++ linux/arch/m68k/mm/memory.c Mon Jul 13 13:47:27 1998 @@ -138,7 +138,7 @@ return; } else { /* - * move this descriptor the the front of the list, since + * move this descriptor to the front of the list, since * it has one or more free tables. */ save_flags(flags); diff -u --recursive --new-file v2.0.34/linux/arch/sparc/config.in linux/arch/sparc/config.in --- v2.0.34/linux/arch/sparc/config.in Thu Apr 25 03:22:05 1996 +++ linux/arch/sparc/config.in Mon Jul 13 13:47:27 1998 @@ -40,6 +40,8 @@ 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 -u --recursive --new-file v2.0.34/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S --- v2.0.34/linux/arch/sparc/kernel/head.S Mon May 6 02:26:03 1996 +++ linux/arch/sparc/kernel/head.S Mon Jul 13 13:47:27 1998 @@ -549,7 +549,7 @@ bnz srmmu_nviking ! is in mbus mode nop - rd %psr, %g3 ! DONT TOUCH %g3 + rd %psr, %g3 ! DO NOT TOUCH %g3 andn %g3, PSR_ET, %g2 wr %g2, 0x0, %psr WRITE_PAUSE @@ -560,10 +560,10 @@ set AC_M_CTPR, %g4 lda [%g4] ASI_M_MMUREGS, %g4 sll %g4, 0x4, %g4 ! We use this below - ! DONT TOUCH %g4 + ! DO NOT TOUCH %g4 /* Set the AC bit in the Viking's MMU control reg. */ - lda [%g0] ASI_M_MMUREGS, %g5 ! DONT TOUCH %g5 + lda [%g0] ASI_M_MMUREGS, %g5 ! DO NOT TOUCH %g5 set 0x8000, %g6 ! AC bit mask or %g5, %g6, %g6 ! Or it in... sta %g6, [%g0] ASI_M_MMUREGS ! Close your eyes... @@ -886,7 +886,7 @@ * No, it doesn't work, have to play the save/readCWP/restore trick. */ - wr %g0, 0x0, %wim ! so we dont get a trap + wr %g0, 0x0, %wim ! so we do not get a trap WRITE_PAUSE save diff -u --recursive --new-file v2.0.34/linux/drivers/block/Config.in linux/drivers/block/Config.in --- v2.0.34/linux/drivers/block/Config.in Mon Aug 4 11:45:55 1997 +++ linux/drivers/block/Config.in Mon Jul 13 13:47:27 1998 @@ -45,6 +45,8 @@ 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 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/block/Makefile linux/drivers/block/Makefile --- v2.0.34/linux/drivers/block/Makefile Mon Aug 4 11:45:55 1997 +++ linux/drivers/block/Makefile Mon Jul 13 13:47:27 1998 @@ -109,6 +109,15 @@ 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 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/block/hd.c linux/drivers/block/hd.c --- v2.0.34/linux/drivers/block/hd.c Thu Feb 29 21:50:39 1996 +++ linux/drivers/block/hd.c Mon Jul 13 13:47:27 1998 @@ -1064,7 +1064,7 @@ #define DEVICE_BUSY busy[target] #define USAGE access_count[target] #define CAPACITY (bios_info[target].head*bios_info[target].sect*bios_info[target].cyl) -/* We assume that the the bios parameters do not change, so the disk capacity +/* We assume that the BIOS parameters do not change, so the disk capacity will not change */ #undef MAYBE_REINIT #define GENDISK_STRUCT hd_gendisk diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v2.0.34/linux/drivers/block/ide-cd.c Mon Jul 13 13:46:26 1998 +++ linux/drivers/block/ide-cd.c Mon Jul 13 13:47:27 1998 @@ -2414,7 +2414,6 @@ case CDROMREADMODE2: { struct cdrom_msf msf; int blocksize, format, stat, lba; - struct atapi_toc *toc; char *buf; if (cmd == CDROMREADMODE1) { diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c --- v2.0.34/linux/drivers/block/ide-floppy.c Mon Aug 4 11:45:55 1997 +++ linux/drivers/block/ide-floppy.c Mon Jul 13 13:47:27 1998 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide-floppy.c Version 0.7 - ALPHA Aug 4, 1997 + * linux/drivers/block/ide-floppy.c Version 0.71 - ALPHA May 21, 1998 * * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il> */ @@ -23,6 +23,11 @@ * Fix potential null dereferencing with DEBUG_LOG. * Ver 0.6 Jul 3 97 Limit max sectors per read/write command to 64. * Ver 0.7 Aug 4 97 Increase irq timeout from 10 to 100 seconds. + * Ver 0.71 May 21 98 Disallow opening a write protected media for writing + * Limit max sectors only on IOMEGA ZIP 21.D + * Issue START cmd only if TEST_UNIT_READY fails + * Add CDROMEJECT ioctl + * Clean up error messages a bit */ #include <linux/config.h> @@ -41,6 +46,7 @@ #include <linux/hdreg.h> #include <linux/genhd.h> #include <linux/malloc.h> +#include <linux/cdrom.h> #include <asm/byteorder.h> #include <asm/irq.h> @@ -80,10 +86,7 @@ */ #define IDEFLOPPY_PC_STACK (10 + IDEFLOPPY_MAX_PC_RETRIES) -/* - * Some drives fail read/write requests with 64 or more sectors. - */ -#define IDEFLOPPY_MAX_SECTORS 64 +#define IDEFLOPPY_MAX_SECTORS 256 /* * Some drives require a longer irq timeout. @@ -203,6 +206,8 @@ int blocks, block_size, bs_factor; /* Current format */ idefloppy_capacity_descriptor_t capacity; /* Last format capacity */ idefloppy_flexible_disk_page_t flexible_disk_page; /* Copy of the flexible disk page */ + int wp; + int max_sectors; unsigned int flags; /* Status/Action flags */ } idefloppy_floppy_t; @@ -950,10 +955,16 @@ pc->c[4] = start; } +static void idefloppy_create_test_unit_ready_cmd (idefloppy_pc_t *pc) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_TEST_UNIT_READY_CMD; +} + static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector) { int block = sector / floppy->bs_factor; - int blocks = IDEFLOPPY_MIN(rq->nr_sectors / floppy->bs_factor, IDEFLOPPY_MAX_SECTORS); + int blocks = IDEFLOPPY_MIN(rq->nr_sectors, floppy->max_sectors) / floppy->bs_factor; #if IDEFLOPPY_DEBUG_LOG printk ("create_rw1%d_cmd: block == %d, blocks == %d\n", @@ -1057,6 +1068,7 @@ return 1; } header = (idefloppy_mode_parameter_header_t *) pc.buffer; + floppy->wp = header->wp; page = (idefloppy_flexible_disk_page_t *) (header + 1); page->transfer_rate = ntohs (page->transfer_rate); @@ -1075,8 +1087,10 @@ drive->bios_sect = page->sectors; lba_capacity = floppy->blocks * floppy->block_size; if (capacity != lba_capacity) { - printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n", - drive->name, capacity, lba_capacity); + if (!lba_capacity) + printk(KERN_NOTICE "%s: no media in the drive\n", drive->name); + else printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n", + drive->name, capacity, lba_capacity); capacity = IDEFLOPPY_MIN(capacity, lba_capacity); floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0; } @@ -1143,6 +1157,17 @@ int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + idefloppy_pc_t pc; + + if (cmd == CDROMEJECT) { + if (drive->usage > 1) + return -EBUSY; + idefloppy_create_prevent_cmd (&pc, 0); + (void) idefloppy_queue_pc_tail (drive, &pc); + idefloppy_create_start_stop_cmd (&pc, 2); + (void) idefloppy_queue_pc_tail (drive, &pc); + return 0; + } return -EIO; } @@ -1160,13 +1185,21 @@ MOD_INC_USE_COUNT; if (drive->usage == 1) { - idefloppy_create_start_stop_cmd (&pc, 1); - (void) idefloppy_queue_pc_tail (drive, &pc); + idefloppy_create_test_unit_ready_cmd(&pc); + if (idefloppy_queue_pc_tail(drive, &pc)) { + idefloppy_create_start_stop_cmd (&pc, 1); + (void) idefloppy_queue_pc_tail (drive, &pc); + } if (idefloppy_get_capacity (drive)) { drive->usage--; MOD_DEC_USE_COUNT; return -EIO; } + if (floppy->wp && (filp->f_mode & 2)) { + drive->usage--; + MOD_DEC_USE_COUNT; + return -EROFS; + } set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); idefloppy_create_prevent_cmd (&pc, 1); (void) idefloppy_queue_pc_tail (drive, &pc); @@ -1328,46 +1361,6 @@ } /* - * idefloppy_get_capabilities asks the floppy about its various - * parameters. - */ -static void idefloppy_get_capabilities (ide_drive_t *drive) -{ - idefloppy_pc_t pc; - idefloppy_mode_parameter_header_t *header; - idefloppy_capabilities_page_t *capabilities; - - idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, MODE_SENSE_CURRENT); - if (idefloppy_queue_pc_tail (drive,&pc)) { - printk (KERN_ERR "ide-floppy: Can't get drive capabilities\n"); - return; - } - header = (idefloppy_mode_parameter_header_t *) pc.buffer; - capabilities = (idefloppy_capabilities_page_t *) (header + 1); - - if (!capabilities->sflp) - printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name); - -#if IDEFLOPPY_DEBUG_INFO - printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n"); - printk (KERN_INFO "Mode Parameter Header:\n"); - printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length); - printk (KERN_INFO "Medium Type - %d\n",header->medium_type); - printk (KERN_INFO "WP - %d\n",header->wp); - - printk (KERN_INFO "Capabilities Page:\n"); - printk (KERN_INFO "Page code - %d\n",capabilities->page_code); - printk (KERN_INFO "Page length - %d\n",capabilities->page_length); - printk (KERN_INFO "PS - %d\n",capabilities->ps); - printk (KERN_INFO "System Floppy Type device - %s\n",capabilities->sflp ? "Yes":"No"); - printk (KERN_INFO "Supports Reporting progress of Format - %s\n",capabilities->srfp ? "Yes":"No"); - printk (KERN_INFO "Non CD Optical device - %s\n",capabilities->ncd ? "Yes":"No"); - printk (KERN_INFO "Multiple LUN support - %s\n",capabilities->sml ? "Yes":"No"); - printk (KERN_INFO "Total LUN supported - %s\n",capabilities->tlun ? "Yes":"No"); -#endif /* IDEFLOPPY_DEBUG_INFO */ -} - -/* * Driver initialization. */ void idefloppy_setup (ide_drive_t *drive) @@ -1382,7 +1375,10 @@ floppy->pc = floppy->pc_stack; if (gcw.drq_type == 1) set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags); - - idefloppy_get_capabilities (drive); + if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0 && + strcmp(drive->id->fw_rev, "21.D") == 0) + floppy->max_sectors = 64; + else + floppy->max_sectors = IDEFLOPPY_MAX_SECTORS; (void) idefloppy_get_capacity (drive); } diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c --- v2.0.34/linux/drivers/block/ide-tape.c Wed Nov 13 23:34:52 1996 +++ linux/drivers/block/ide-tape.c Mon Jul 13 13:47:28 1998 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide-tape.c Version 1.9 - ALPHA Nov 5, 1996 + * linux/drivers/block/ide-tape.c Version 1.91 May 21, 1998 * * Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il> * @@ -195,15 +195,8 @@ * MTTELL was sometimes returning incorrect results. * Return the real block size in the MTIOCGET ioctl. * Some error recovery bug fixes. - * - * We are currently in an *alpha* stage. The driver is not complete and not - * much tested. I would strongly suggest to: - * - * 1. Connect the tape to a separate interface and irq. - * 2. Be truly prepared for a kernel crash and the resulting data loss. - * 3. Don't rely too much on the resulting backups. - * - * Other than that, enjoy ! + * Ver 1.91 May 21 98 Add support for INTERRUPT DRQ devices. + * Add "speed == 0" work-around for HP COLORADO 5GB * * Here are some words from the first releases of hd.c, which are quoted * in ide.c and apply here as well: @@ -1157,11 +1150,6 @@ printk ("ide-tape: The removable flag is not set\n");support=0; } - if (gcw.drq_type != 2) { - printk ("ide-tape: Sorry, DRQ types other than Accelerated DRQ\n"); - printk ("ide-tape: are still not supported by the driver\n");support=0; - } - if (gcw.packet_size != 0) { printk ("ide-tape: Packet size is not 12 bytes long\n"); if (gcw.packet_size == 1) @@ -1233,6 +1221,8 @@ { idetape_tape_t *tape=&(drive->tape); unsigned int allocation_length; + struct idetape_id_gcw gcw; + #if IDETAPE_ANTICIPATE_READ_WRITE_DSC ide_hwif_t *hwif = HWIF(drive); unsigned long t1, tmid, tn; @@ -1260,7 +1250,10 @@ tape->chrdev_direction=idetape_direction_none; tape->reset_issued=0; tape->pc=&(tape->pc_stack [0]); - + + *((unsigned short *) &gcw) = drive->id->config; + tape->drq_interrupt = (gcw.drq_type == 1) ? 1 : 0; + #if IDETAPE_PIPELINE tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES; #else @@ -1397,6 +1390,15 @@ capabilities->speed=idetape_swap_short (capabilities->speed); capabilities->buffer_size=idetape_swap_short (capabilities->buffer_size); + if (!capabilities->speed) { + printk("ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name); + capabilities->speed = 650; + } + if (!capabilities->max_speed) { + printk("ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name); + capabilities->max_speed = 650; + } + tape->capabilities=*capabilities; /* Save us a copy */ tape->tape_block_size=capabilities->blk512 ? 512:1024; @@ -1441,6 +1443,26 @@ #endif /* IDETAPE_DEBUG_LOG */ } +static void idetape_transfer_pc (ide_drive_t *drive) +{ + idetape_tape_t *tape=&(drive->tape); + idetape_packet_command_t *pc = tape->pc; + idetape_ireason_reg_t ireason; + + if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n"); + return; + } + ireason.all=IN_BYTE (IDE_IREASON_REG); + if (!ireason.b.cod || ireason.b.io) { + printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n"); + ide_do_reset (drive); + return; + } + ide_set_handler (drive, &idetape_pc_intr, WAIT_CMD); /* Set the interrupt routine */ + ide_output_data (drive,pc->c,12/4); /* Send the actual packet */ +} + /* * Packet Command Interface * @@ -1489,7 +1511,6 @@ { idetape_tape_t *tape; idetape_bcount_reg_t bcount; - idetape_ireason_reg_t ireason; int dma_ok=0; tape=&(drive->tape); @@ -1562,37 +1583,21 @@ OUT_BYTE (bcount.b.high,IDETAPE_BCOUNTH_REG); OUT_BYTE (bcount.b.low,IDETAPE_BCOUNTL_REG); OUT_BYTE (drive->select.all,IDETAPE_DRIVESEL_REG); - - ide_set_handler (drive,handler,WAIT_CMD); /* Set the interrupt routine */ - OUT_BYTE (WIN_PACKETCMD,IDETAPE_ATACOMMAND_REG); /* Issue the packet command */ - if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { /* Wait for DRQ to be ready - Assuming Accelerated DRQ */ - /* - * We currently only support tape drives which report - * accelerated DRQ assertion. For this case, specs - * allow up to 50us. We really shouldn't get here. - * - * ??? Still needs to think what to do if we reach - * here anyway. - */ - - printk ("ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n"); - return; - } - - ireason.all=IN_BYTE (IDETAPE_IREASON_REG); - if (!ireason.b.cod || ireason.b.io) { - printk ("ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n"); - ide_do_reset (drive); - return; - } - - ide_output_data (drive,pc->c,12/4); /* Send the actual packet */ + #ifdef CONFIG_BLK_DEV_TRITON if ((pc->dma_in_progress=dma_ok)) { /* Begin DMA, if necessary */ pc->dma_error=0; (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); } #endif /* CONFIG_BLK_DEV_TRITON */ + + if (tape->drq_interrupt) { + ide_set_handler (drive, &idetape_transfer_pc, WAIT_CMD); + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ + } else { + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); + idetape_transfer_pc (drive); + } } /* diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide-tape.h linux/drivers/block/ide-tape.h --- v2.0.34/linux/drivers/block/ide-tape.h Wed Nov 6 04:48:32 1996 +++ linux/drivers/block/ide-tape.h Mon Jul 13 13:47:28 1998 @@ -511,6 +511,8 @@ idetape_pipeline_stage_t *last_stage; /* New requests will be added to the pipeline here */ int error_in_pipeline_stage; /* Set when an error was detected in one of the pipeline stages */ + int drq_interrupt; + } idetape_tape_t; /* diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.0.34/linux/drivers/block/ide.c Mon Jul 13 13:46:26 1998 +++ linux/drivers/block/ide.c Mon Jul 13 13:47:28 1998 @@ -273,6 +273,10 @@ * add work-around for BMI drives * remove "LBA" from boot messages * Version 5.53.1 add UDMA "CRC retry" support + * Version 5.53.2 add Promise/33 auto-detection and DMA support + * fix MC_ERR handling + * fix mis-detection of NEC cdrom as floppy + * issue ATAPI reset and re-probe after "no response" * * Some additional driver compile-time options are in ide.h * @@ -1059,6 +1063,8 @@ rq->errors = ERROR_MAX; else if (err & TRK0_ERR) /* help it find track zero */ rq->errors |= ERROR_RECAL; + else if (err & MC_ERR) + drive->special.b.mc = 1; } if ((stat & DRQ_STAT) && rq->cmd != WRITE) try_to_flush_leftover_data(drive); @@ -2447,11 +2453,11 @@ return; } #endif /* CONFIG_BLK_DEV_PROMISE */ - switch (type) { + if (!drive->ide_scsi) switch (type) { case 0: if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP")) printk("cdrom or floppy?, assuming "); - if (drive->media != ide_cdrom) { + if (drive->media != ide_cdrom && !strstr(id->model, "CD-ROM")) { #ifdef CONFIG_BLK_DEV_IDEFLOPPY printk("FLOPPY drive\n"); drive->media = ide_floppy; @@ -2728,6 +2734,7 @@ { int rc; ide_hwif_t *hwif = HWIF(drive); + unsigned long timeout; #ifdef CONFIG_BLK_DEV_IDEATAPI if (drive->present) { /* avoid waiting for inappropriate probes */ if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY)) @@ -2752,6 +2759,17 @@ { if ((rc = try_to_identify(drive,cmd))) /* send cmd and wait */ rc = try_to_identify(drive,cmd); /* failed: try again */ + if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) { + printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT()); + delay_50ms(); + OUT_BYTE (drive->select.all, IDE_SELECT_REG); + delay_50ms(); + OUT_BYTE(WIN_SRST, IDE_COMMAND_REG); + timeout = jiffies; + while ((GET_STAT() & BUSY_STAT) && jiffies < timeout + WAIT_WORSTCASE) + delay_50ms(); + rc = try_to_identify(drive, cmd); + } if (rc == 1) printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT()); (void) GET_STAT(); /* ensure drive irq is clear */ @@ -3085,7 +3103,7 @@ if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) { const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom", "serialize", "autotune", "noautotune", - "slow", NULL}; + "slow", "ide-scsi", NULL}; unit = s[2] - 'a'; hw = unit / MAX_DRIVES; unit = unit % MAX_DRIVES; @@ -3118,6 +3136,9 @@ case -8: /* "slow" */ drive->slow = 1; goto done; + case -9: /* "ide-scsi" */ + drive->ide_scsi = 1; + goto done; case 3: /* cyl,head,sect */ drive->media = ide_disk; drive->cyl = drive->bios_cyl = vals[0]; @@ -3514,6 +3535,43 @@ } #endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */ + +static void ide_probe_promise_20246(void) +{ + byte fn, bus; + unsigned short io[6], count = 0; + unsigned int reg, tmp, i; + ide_hwif_t *hwif; + + memset(io, 0, 6 * sizeof(unsigned short)); + if (pcibios_find_device(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, 0, &bus, &fn)) + return; + printk("ide: Promise Technology IDE Ultra-DMA 33 on PCI bus %d function %d\n", bus, fn); + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + pcibios_read_config_dword(bus, fn, reg, &tmp); + if (tmp & PCI_BASE_ADDRESS_SPACE_IO) + io[count++] = tmp & PCI_BASE_ADDRESS_IO_MASK; + } + for (i = 2; i < 4; i++) { + hwif = ide_hwifs + i; + if (hwif->chipset == ide_generic) { + printk("ide%d: overridden with command line parameter\n", i); + return; + } + tmp = (i - 2) * 2; + if (!io[tmp] || !io[tmp + 1]) { + printk("ide%d: invalid port address %x, %x -- aborting\n", i, io[tmp], io[tmp + 1]); + return; + } + hwif->io_base = io[tmp]; + hwif->ctl_port = io[tmp + 1] + 2; + hwif->noprobe = 0; + } +#ifdef CONFIG_BLK_DEV_TRITON + ide_init_promise (bus, fn, &ide_hwifs[2], &ide_hwifs[3], io[4]); +#endif /* CONFIG_BLK_DEV_TRITON */ +} + #endif /* CONFIG_PCI */ /* @@ -3545,6 +3603,7 @@ ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0); ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0); #endif /* CONFIG_BLK_DEV_TRITON */ + ide_probe_promise_20246(); } #endif /* CONFIG_PCI */ #ifdef CONFIG_BLK_DEV_CMD640 diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v2.0.34/linux/drivers/block/ide.h Mon Jul 13 13:46:26 1998 +++ linux/drivers/block/ide.h Mon Jul 13 13:47:28 1998 @@ -381,6 +381,7 @@ #ifdef CONFIG_BLK_DEV_IDESCSI void *scsi; /* for ide-scsi.c */ #endif /* CONFIG_BLK_DEV_IDESCSI */ + byte ide_scsi; /* use ide-scsi driver */ } ide_drive_t; /* @@ -428,7 +429,7 @@ typedef enum { ide_unknown, ide_generic, ide_triton, ide_cmd640, ide_dtc2278, ide_ali14xx, ide_qd6580, ide_umc8672, ide_ht6560b, - ide_promise } + ide_promise, ide_promise_udma } hwif_chipset_t; typedef struct hwif_s { @@ -742,4 +743,5 @@ #ifdef CONFIG_BLK_DEV_TRITON void ide_init_triton (byte, byte); +void ide_init_promise (byte bus, byte fn, ide_hwif_t *hwif0, ide_hwif_t *hwif1, unsigned short dma); #endif /* CONFIG_BLK_DEV_TRITON */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/linear.c linux/drivers/block/linear.c --- v2.0.34/linux/drivers/block/linear.c Sun May 19 21:50:46 1996 +++ linux/drivers/block/linear.c Mon Jul 13 13:47:28 1998 @@ -161,6 +161,7 @@ sz+=sprintf (page+sz, "\n"); #endif + sz+=sprintf (page+sz, " %dk rounding", 1<<FACTOR_SHIFT(FACTOR(mddev))); return sz; } @@ -169,6 +170,8 @@ { "linear", linear_map, + NULL, + NULL, linear_run, linear_stop, linear_status, diff -u --recursive --new-file v2.0.34/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v2.0.34/linux/drivers/block/ll_rw_blk.c Wed Feb 26 11:10:15 1997 +++ linux/drivers/block/ll_rw_blk.c Mon Jul 13 13:47:28 1998 @@ -280,7 +280,30 @@ sti(); } -static void make_request(int major,int rw, struct buffer_head * bh) +#define MAX_SECTORS 244 + +static inline void attempt_merge (struct request *req) +{ + struct request *next = req->next; + + if (!next) + return; + if (req->sector + req->nr_sectors != next->sector) + return; + if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS) + return; +#if 0 + printk ("%s: merge %ld, %ld + %ld == %ld\n", kdevname(req->rq_dev), req->sector, req->nr_sectors, next->nr_sectors, req->nr_sectors + next->nr_sectors); +#endif + req->bhtail->b_reqnext = next->bh; + req->bhtail = next->bhtail; + req->nr_sectors += next->nr_sectors; + next->rq_status = RQ_INACTIVE; + req->next = next->next; + wake_up (&wait_for_request); +} + +void make_request(int major,int rw, struct buffer_head * bh) { unsigned int sector, count; struct request * req; @@ -290,8 +313,12 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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]); } } @@ -645,6 +687,9 @@ #endif #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(); diff -u --recursive --new-file v2.0.34/linux/drivers/block/md.c linux/drivers/block/md.c --- v2.0.34/linux/drivers/block/md.c Mon Jul 13 13:46:26 1998 +++ linux/drivers/block/md.c Mon Jul 13 13:47:28 1998 @@ -9,6 +9,9 @@ kerneld support by Boris Tobotras <boris@xtalk.msk.su> + RAID-1/RAID-5 extensions by: + Ingo Molnar, Miguel de Icaza, Gadi Oxman + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) @@ -31,18 +34,27 @@ #include <linux/proc_fs.h> #include <linux/blkdev.h> #include <linux/genhd.h> +#include <linux/smp_lock.h> #ifdef CONFIG_KERNELD #include <linux/kerneld.h> #endif #include <linux/errno.h> +/* + * For kernel_thread() + */ +#define __KERNEL_SYSCALLS__ +#include <linux/unistd.h> #define MAJOR_NR MD_MAJOR #define MD_DRIVER #include <linux/blk.h> +#include <asm/bitops.h> +#include <asm/atomic.h> static struct hd_struct md_hd_struct[MAX_MD_DEV]; static int md_blocksizes[MAX_MD_DEV]; +static struct md_thread md_threads[MAX_MD_THREADS]; int md_size[MAX_MD_DEV]={0, }; @@ -91,8 +103,7 @@ 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 @@ read_ahead[MD_MAJOR]=minra; } +static int legacy_raid_sb (int minor, int pnum) +{ + int i, factor; + + factor = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); + + /***** + * do size and offset calculations. + */ + for (i=0; i<md_dev[minor].nb_dev; i++) { + md_dev[minor].devices[i].size &= ~(factor - 1); + md_size[minor] += md_dev[minor].devices[i].size; + md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + + md_dev[minor].devices[i-1].size) : 0; + } + return 0; +} + +static void free_sb (struct md_dev *mddev) +{ + int i; + struct real_dev *realdev; + + if (mddev->sb) { + free_page((unsigned long) mddev->sb); + mddev->sb = NULL; + } + for (i = 0; i <mddev->nb_dev; i++) { + realdev = mddev->devices + i; + if (realdev->sb) { + free_page((unsigned long) realdev->sb); + realdev->sb = NULL; + } + } +} + +static int analyze_sb (int minor, int pnum) +{ + int i; + struct md_dev *mddev = md_dev + minor; + struct buffer_head *bh; + kdev_t dev; + struct real_dev *realdev; + u32 sb_offset, device_size; + md_superblock_t *sb = NULL; + + /* + * raid-0 and linear don't use a raid superblock + */ + if (pnum == RAID0 >> PERSONALITY_SHIFT || pnum == LINEAR >> PERSONALITY_SHIFT) + return legacy_raid_sb(minor, pnum); + + /* + * Verify the raid superblock on each real device + */ + for (i = 0; i < mddev->nb_dev; i++) { + realdev = mddev->devices + i; + dev = realdev->dev; + device_size = blk_size[MAJOR(dev)][MINOR(dev)]; + realdev->sb_offset = sb_offset = MD_NEW_SIZE_BLOCKS(device_size); + set_blocksize(dev, MD_SB_BYTES); + bh = bread(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); + if (bh) { + sb = (md_superblock_t *) bh->b_data; + if (sb->md_magic != MD_SB_MAGIC) { + printk("md: %s: invalid raid superblock magic (%x) on block %u\n", kdevname(dev), sb->md_magic, sb_offset); + goto abort; + } + if (!mddev->sb) { + mddev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL); + if (!mddev->sb) + goto abort; + memcpy(mddev->sb, sb, MD_SB_BYTES); + } + realdev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL); + if (!realdev->sb) + goto abort; + memcpy(realdev->sb, bh->b_data, MD_SB_BYTES); + + if (memcmp(mddev->sb, sb, MD_SB_GENERIC_CONSTANT_WORDS * 4)) { + printk(KERN_ERR "md: superblock inconsistenty -- run ckraid\n"); + goto abort; + } + /* + * Find the newest superblock version + */ + if (sb->utime != mddev->sb->utime) { + printk(KERN_ERR "md: superblock update time inconsistenty -- using the most recent one\n"); + if (sb->utime > mddev->sb->utime) + memcpy(mddev->sb, sb, MD_SB_BYTES); + } + realdev->size = sb->size; + } else + printk(KERN_ERR "md: disabled device %s\n", kdevname(dev)); + } + if (!mddev->sb) { + printk(KERN_ERR "md: couldn't access raid array %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + sb = mddev->sb; + + /* + * Check if we can support this raid array + */ + if (sb->major_version != MD_MAJOR_VERSION || sb->minor_version > MD_MINOR_VERSION) { + printk("md: %s: unsupported raid array version %d.%d.%d\n", kdevname(MKDEV(MD_MAJOR, minor)), + sb->major_version, sb->minor_version, sb->patch_version); + goto abort; + } + if (sb->state != (1 << MD_SB_CLEAN)) { + printk(KERN_ERR "md: %s: raid array is not clean -- run ckraid\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + switch (sb->level) { + case 1: + md_size[minor] = sb->size; + break; + case 4: + case 5: + md_size[minor] = sb->size * (sb->raid_disks - 1); + break; + default: + printk(KERN_ERR "md: %s: unsupported raid level %d\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + goto abort; + } + return 0; +abort: + free_sb(mddev); + return 1; +} + +int md_update_sb(int minor) +{ + struct md_dev *mddev = md_dev + minor; + struct buffer_head *bh; + md_superblock_t *sb = mddev->sb; + struct real_dev *realdev; + kdev_t dev; + int i; + u32 sb_offset; + + sb->utime = CURRENT_TIME; + for (i = 0; i < mddev->nb_dev; i++) { + realdev = mddev->devices + i; + if (!realdev->sb) + continue; + dev = realdev->dev; + sb_offset = realdev->sb_offset; + set_blocksize(dev, MD_SB_BYTES); + printk("md: updating raid superblock on device %s, sb_offset == %u\n", kdevname(dev), sb_offset); + bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); + if (bh) { + sb = (md_superblock_t *) bh->b_data; + memcpy(sb, mddev->sb, MD_SB_BYTES); + memcpy(&sb->descriptor, sb->disks + realdev->sb->descriptor.number, MD_SB_DESCRIPTOR_WORDS * 4); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + } else + printk(KERN_ERR "md: getblk failed for device %s\n", kdevname(dev)); + } + return 0; +} static int do_md_run (int minor, int repart) { - int pnum, i, min, current_ra, err; - + int pnum, i, min, factor, current_ra, err; + if (!md_dev[minor].nb_dev) return -EINVAL; if (md_dev[minor].pers) return -EBUSY; - + md_dev[minor].repartition=repart; - if ((pnum=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT)) + if ((pnum=PERSONALITY(&md_dev[minor]) >> (PERSONALITY_SHIFT)) >= MAX_PERSONALITY) return -EINVAL; - + + /* Only RAID-1 and RAID-5 can have MD devices as underlying devices */ + if (pnum != (RAID1 >> PERSONALITY_SHIFT) && pnum != (RAID5 >> PERSONALITY_SHIFT)){ + for (i = 0; i < md_dev [minor].nb_dev; i++) + if (MAJOR (md_dev [minor].devices [i].dev) == MD_MAJOR) + return -EINVAL; + } if (!pers[pnum]) { #ifdef CONFIG_KERNELD @@ -145,7 +329,7 @@ return -EINVAL; } - min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); + factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); for (i=0; i<md_dev[minor].nb_dev; i++) if (md_dev[minor].devices[i].size<min) @@ -154,26 +338,37 @@ partition_name (md_dev[minor].devices[i].dev), min); return -EINVAL; } + + for (i=0; i<md_dev[minor].nb_dev; i++) { + fsync_dev(md_dev[minor].devices[i].dev); + invalidate_buffers(md_dev[minor].devices[i].dev); + } /* Resize devices according to the factor. It is used to align partitions size on a given chunk size. */ md_size[minor]=0; - - for (i=0; i<md_dev[minor].nb_dev; i++) - { - md_dev[minor].devices[i].size &= ~(min - 1); - md_size[minor] += md_dev[minor].devices[i].size; - md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + md_dev[minor].devices[i-1].size) : 0; - } + + /* + * Analyze the raid superblock + */ + if (analyze_sb(minor, pnum)) + return -EINVAL; md_dev[minor].pers=pers[pnum]; if ((err=md_dev[minor].pers->run (minor, md_dev+minor))) { md_dev[minor].pers=NULL; + free_sb(md_dev + minor); return (err); } - + + if (pnum != RAID0 >> PERSONALITY_SHIFT && pnum != LINEAR >> PERSONALITY_SHIFT) + { + md_dev[minor].sb->state &= ~(1 << MD_SB_CLEAN); + md_update_sb(minor); + } + /* FIXME : We assume here we have blocks that are twice as large as sectors. THIS MAY NOT BE TRUE !!! */ @@ -191,7 +386,6 @@ 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 @@ /* The device won't exist anymore -> flush it now */ fsync_dev (inode->i_rdev); invalidate_buffers (inode->i_rdev); + if (md_dev[minor].sb) + { + md_dev[minor].sb->state |= 1 << MD_SB_CLEAN; + md_update_sb(minor); + } md_dev[minor].pers->stop (minor, md_dev+minor); } /* Remove locks. */ + if (md_dev[minor].sb) + free_sb(md_dev + minor); for (i=0; i<md_dev[minor].nb_dev; i++) clear_inode (md_dev[minor].devices[i].inode); - + md_dev[minor].nb_dev=md_size[minor]=0; md_hd_struct[minor].nr_sects=0; md_dev[minor].pers=NULL; set_ra (); /* calculate new read_ahead */ - printk ("STOP_DEV md%x\n", minor); return (0); } static int do_md_add (int minor, kdev_t dev) { - struct gendisk *gen_real; int i; - - if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL) + + if (md_dev[minor].nb_dev==MAX_REAL) return -EINVAL; if (!fs_may_mount (dev) || md_dev[minor].pers) return -EBUSY; - - if (!(gen_real=find_gendisk (dev))) - return -ENOENT; - + i=md_dev[minor].nb_dev++; md_dev[minor].devices[i].dev=dev; @@ -258,7 +454,13 @@ /* Sizes are now rounded at run time */ - md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; +/* md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; HACKHACK*/ + + if (blk_size[MAJOR(dev)][MINOR(dev)] == 0) { + printk("md_add(): zero device size, huh, bailing out.\n"); + } + + md_dev[minor].devices[i].size=blk_size[MAJOR(dev)][MINOR(dev)]; printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor); return (0); @@ -420,6 +622,27 @@ 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 @@ return; } +/* + * We run MAX_MD_THREADS from md_init() and arbitrate them in run time. + * This is not so elegant, but how can we use kernel_thread() from within + * loadable modules? + */ +struct md_thread *md_register_thread (void (*run) (void *), void *data) +{ + int i; + for (i = 0; i < MAX_MD_THREADS; i++) { + if (md_threads[i].run == NULL) { + md_threads[i].run = run; + md_threads[i].data = data; + return md_threads + i; + } + } + return NULL; +} + + +void md_unregister_thread (struct md_thread *thread) +{ + thread->run = NULL; + thread->data = NULL; + thread->flags = 0; +} + +void md_wakeup_thread(struct md_thread *thread) +{ + set_bit(THREAD_WAKEUP, &thread->flags); + wake_up(&thread->wqueue); +} + +struct buffer_head *efind_buffer(kdev_t dev, int block, int size); + static struct symbol_table md_symbol_table= { #include <linux/symtab_begin.h> @@ -435,11 +692,18 @@ X(register_md_personality), X(unregister_md_personality), X(partition_name), + X(md_dev), + X(md_error), + X(md_register_thread), + X(md_unregister_thread), + X(md_update_sb), + X(md_map), + X(md_wakeup_thread), + X(efind_buffer), #include <linux/symtab_end.h> }; - static void md_geninit (struct gendisk *gdisk) { int i; @@ -463,6 +727,17 @@ }); } +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 @@ 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 @@ if (md_dev[i].pers->max_invalid_dev) sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i)); - sz+=sprintf (page+sz, " %dk %s\n", 1<<FACTOR_SHIFT(FACTOR(md_dev+i)), - md_dev[i].pers == pers[LINEAR>>PERSONALITY_SHIFT] ? - "rounding" : "chunks"); - sz+=md_dev[i].pers->status (page+sz, i, md_dev+i); + sz+=sprintf (page+sz, "\n"); } return (sz); @@ -545,6 +821,32 @@ 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 @@ 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 @@ 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; @@ -572,6 +886,12 @@ #endif #ifdef CONFIG_MD_STRIPED raid0_init (); +#endif +#ifdef CONFIG_MD_MIRRORING + raid1_init (); +#endif +#ifdef CONFIG_MD_RAID5 + raid5_init (); #endif return (0); diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/Config.in linux/drivers/block/paride/Config.in --- v2.0.34/linux/drivers/block/paride/Config.in Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/Config.in Mon Jul 13 13:47:28 1998 @@ -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 -u --recursive --new-file v2.0.34/linux/drivers/block/paride/Makefile linux/drivers/block/paride/Makefile --- v2.0.34/linux/drivers/block/paride/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/Makefile Mon Jul 13 13:47:28 1998 @@ -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 -u --recursive --new-file v2.0.34/linux/drivers/block/paride/aten.c linux/drivers/block/paride/aten.c --- v2.0.34/linux/drivers/block/paride/aten.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/aten.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,172 @@ +/* + aten.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + aten.c is a low-level protocol driver for the ATEN EH-100 + parallel port adapter. The EH-100 supports 4-bit and 8-bit + modes only. There is also an EH-132 which supports EPP mode + transfers. The EH-132 is not yet supported. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 init_proto, release_proto + +*/ + +#define ATEN_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x20 }; + +static void aten_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont] + 0x80; + + w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc); +} + +static int aten_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont] + 0x40; + + switch (pi->mode) { + + case 0: w0(r); w2(0xe); w2(6); + w2(7); w2(6); w2(0); + a = r1(); w0(0x10); b = r1(); w2(0xc); + return j44(a,b); + + case 1: r |= 0x10; + w0(r); w2(0xe); w2(6); w0(0xff); + w2(0x27); w2(0x26); w2(0x20); + a = r0(); + w2(0x26); w2(0xc); + return a; + } + return -1; +} + +static void aten_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b, c, d; + + switch (pi->mode) { + + case 0: w0(0x48); w2(0xe); w2(6); + for (k=0;k<count/2;k++) { + w2(7); w2(6); w2(2); + a = r1(); w0(0x58); b = r1(); + w2(0); d = r1(); w0(0x48); c = r1(); + buf[2*k] = j44(c,d); + buf[2*k+1] = j44(a,b); + } + w2(0xc); + break; + + case 1: w0(0x58); w2(0xe); w2(6); + for (k=0;k<count/2;k++) { + w2(0x27); w2(0x26); w2(0x22); + a = r0(); w2(0x20); b = r0(); + buf[2*k] = b; buf[2*k+1] = a; + } + w2(0x26); w2(0xc); + break; + } +} + +static void aten_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + w0(0x88); w2(0xe); w2(6); + for (k=0;k<count/2;k++) { + w0(buf[2*k+1]); w2(0xe); w2(6); + w0(buf[2*k]); w2(7); w2(6); + } + w2(0xc); +} + +static void aten_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xc); +} + +static void aten_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void aten_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[2] = {"4-bit","8-bit"}; + + printk("%s: aten %s, ATEN EH-100 at 0x%x, ", + pi->device,ATEN_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void aten_init_proto( PIA *pi ) + +{ MOD_INC_USE_COUNT; +} + +static void aten_release_proto( PIA *pi ) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol aten = {"aten",0,2,2,1,1, + aten_write_regr, + aten_read_regr, + aten_write_block, + aten_read_block, + aten_connect, + aten_disconnect, + 0, + 0, + 0, + aten_log_adapter, + aten_init_proto, + aten_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &aten ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &aten ); +} + +#endif + +/* end of aten.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/bpck.c linux/drivers/block/paride/bpck.c --- v2.0.34/linux/drivers/block/paride/bpck.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/bpck.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,482 @@ +/* + bpck.c (c) 1996-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + bpck.c is a low-level protocol driver for the MicroSolutions + "backpack" parallel port IDE adapter. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 init_proto, release_proto, pi->delay + +*/ + +#define BPCK_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#undef r2 +#undef w2 + +#define PC pi->private +#define r2() (PC=(in_p(2) & 0xff)) +#define w2(byte) {out_p(2,byte); PC = byte;} +#define t2(pat) {PC ^= pat; out_p(2,PC);} +#define e2() {PC &= 0xfe; out_p(2,PC);} +#define o2() {PC |= 1; out_p(2,PC);} + +#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set + cont = 2 - use internal bpck register addressing +*/ + +static int cont_map[3] = { 0x40, 0x48, 0 }; + +static int bpck_read_regr( PIA *pi, int cont, int regr ) + +{ int r, l, h; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r & 0xf); w0(r); t2(2); t2(4); + l = r1(); + t2(4); + h = r1(); + return j44(l,h); + + case 1: w0(r & 0xf); w0(r); t2(2); + e2(); t2(0x20); + t2(4); h = r0(); + t2(1); t2(0x20); + return h; + + case 2: + case 3: + case 4: w0(r); w2(9); w2(0); w2(0x20); + h = r4(); + w2(0); + return h; + + } + return -1; +} + +static void bpck_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: w0(r); + t2(2); + w0(val); + o2(); t2(4); t2(1); + break; + + case 2: + case 3: + case 4: w0(r); w2(9); w2(0); + w0(val); w2(1); w2(3); w2(0); + break; + + } +} + +/* These macros access the bpck registers in native addressing */ + +#define WR(r,v) bpck_write_regr(pi,2,r,v) +#define RR(r) (bpck_read_regr(pi,2,r)) + +static void bpck_write_block( PIA *pi, char * buf, int count ) + +{ int i; + + switch (pi->mode) { + + case 0: WR(4,0x40); + w0(0x40); t2(2); t2(1); + for (i=0;i<count;i++) { w0(buf[i]); t2(4); } + WR(4,0); + break; + + case 1: WR(4,0x50); + w0(0x40); t2(2); t2(1); + for (i=0;i<count;i++) { w0(buf[i]); t2(4); } + WR(4,0x10); + break; + + case 2: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(1); + for (i=0;i<count;i++) w4(buf[i]); + w2(0); + WR(4,8); + break; + + case 3: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(1); + for (i=0;i<count/2;i++) w4w(((u16 *)buf)[i]); + w2(0); + WR(4,8); + break; + + case 4: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(1); + for (i=0;i<count/4;i++) w4l(((u32 *)buf)[i]); + w2(0); + WR(4,8); + break; + } +} + +static void bpck_read_block( PIA *pi, char * buf, int count ) + +{ int i, l, h; + + switch (pi->mode) { + + case 0: WR(4,0x40); + w0(0x40); t2(2); + for (i=0;i<count;i++) { + t2(4); l = r1(); + t2(4); h = r1(); + buf[i] = j44(l,h); + } + WR(4,0); + break; + + case 1: WR(4,0x50); + w0(0x40); t2(2); t2(0x20); + for(i=0;i<count;i++) { t2(4); buf[i] = r0(); } + t2(1); t2(0x20); + WR(4,0x10); + break; + + case 2: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(0x20); + for (i=0;i<count;i++) buf[i] = r4(); + w2(0); + WR(4,8); + break; + + case 3: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(0x20); + for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w(); + w2(0); + WR(4,8); + break; + + case 4: WR(4,0x48); + w0(0x40); w2(9); w2(0); w2(0x20); + for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l(); + w2(0); + WR(4,8); + break; + + } +} + +static int bpck_probe_unit ( PIA *pi ) + +{ int o1, o0, f7, id; + int t, s; + + id = pi->unit; + s = 0; + w2(4); w2(0xe); r2(); t2(2); + o1 = r1()&0xf8; + o0 = r0(); + w0(255-id); w2(4); w0(id); + t2(8); t2(8); t2(8); + t2(2); t = r1()&0xf8; + f7 = ((id % 8) == 7); + if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; } + if ((t == o1) && ((!f7) || (s == o1))) { + w2(0x4c); w0(o0); + return 0; + } + t2(8); w0(0); t2(2); w2(0x4c); w0(o0); + return 1; +} + +static void bpck_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + w0(0xff-pi->unit); w2(4); w0(pi->unit); + t2(8); t2(8); t2(8); + t2(2); t2(2); + + switch (pi->mode) { + + case 0: t2(8); WR(4,0); + break; + + case 1: t2(8); WR(4,0x10); + break; + + case 2: + case 3: + case 4: w2(0); WR(4,8); + break; + + } + + WR(5,8); + + if (pi->devtype == PI_PCD) { + WR(0x46,0x10); /* fiddle with ESS logic ??? */ + WR(0x4c,0x38); + WR(0x4d,0x88); + WR(0x46,0xa0); + WR(0x41,0); + WR(0x4e,8); + } +} + +static void bpck_disconnect ( PIA *pi ) + +{ w0(0); + if (pi->mode >= 2) { w2(9); w2(0); } else t2(2); + w2(0x4c); w0(pi->saved_r0); +} + +static void bpck_force_spp ( PIA *pi ) + +/* This fakes the EPP protocol to turn off EPP ... */ + +{ pi->saved_r0 = r0(); + w0(0xff-pi->unit); w2(4); w0(pi->unit); + t2(8); t2(8); t2(8); + t2(2); t2(2); + + w2(0); + w0(4); w2(9); w2(0); + w0(0); w2(1); w2(3); w2(0); + w0(0); w2(9); w2(0); + w2(0x4c); w0(pi->saved_r0); +} + +#define TEST_LEN 16 + +static int bpck_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int i, e, l, h, om; + char buf[TEST_LEN]; + + bpck_force_spp(pi); + + switch (pi->mode) { + + case 0: bpck_connect(pi); + WR(0x13,0x7f); + w0(0x13); t2(2); + for(i=0;i<TEST_LEN;i++) { + t2(4); l = r1(); + t2(4); h = r1(); + buf[i] = j44(l,h); + } + bpck_disconnect(pi); + break; + + case 1: bpck_connect(pi); + WR(0x13,0x7f); + w0(0x13); t2(2); t2(0x20); + for(i=0;i<TEST_LEN;i++) { t2(4); buf[i] = r0(); } + t2(1); t2(0x20); + bpck_disconnect(pi); + break; + + case 2: + case 3: + case 4: om = pi->mode; + pi->mode = 0; + bpck_connect(pi); + WR(7,3); + WR(4,8); + bpck_disconnect(pi); + + pi->mode = om; + bpck_connect(pi); + w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0); + + switch (pi->mode) { + case 2: for (i=0;i<TEST_LEN;i++) buf[i] = r4(); + break; + case 3: for (i=0;i<TEST_LEN/2;i++) ((u16 *)buf)[i] = r4w(); + break; + case 4: for (i=0;i<TEST_LEN/4;i++) ((u32 *)buf)[i] = r4l(); + break; + } + + w2(0); + WR(7,0); + bpck_disconnect(pi); + + break; + + } + + if (verbose) { + printk("%s: bpck: 0x%x unit %d mode %d: ", + pi->device,pi->port,pi->unit,pi->mode); + for (i=0;i<TEST_LEN;i++) printk("%3d",buf[i]); + printk("\n"); + } + + e = 0; + for (i=0;i<TEST_LEN;i++) if (buf[i] != (i+1)) e++; + return e; +} + +static void bpck_read_eeprom ( PIA *pi, char * buf ) + +{ int i,j,k,n,p,v,f, om, od; + + bpck_force_spp(pi); + + om = pi->mode; od = pi->delay; + pi->mode = 0; pi->delay = 6; + + bpck_connect(pi); + + n = 0; + WR(4,0); + for (i=0;i<64;i++) { + WR(6,8); + WR(6,0xc); + p = 0x100; + for (k=0;k<9;k++) { + f = (((i + 0x180) & p) != 0) * 2; + WR(6,f+0xc); + WR(6,f+0xd); + WR(6,f+0xc); + p = (p >> 1); + } + for (j=0;j<2;j++) { + v = 0; + for (k=0;k<8;k++) { + WR(6,0xc); + WR(6,0xd); + WR(6,0xc); + f = RR(0); + v = 2*v + (f == 0x84); + } + buf[2*i+1-j] = v; + } + } + WR(6,8); + WR(6,0); + WR(5,8); + + bpck_disconnect(pi); + + if (om >= 2) { + bpck_connect(pi); + WR(7,3); + WR(4,8); + bpck_disconnect(pi); + } + + pi->mode = om; pi->delay = od; +} + +static int bpck_test_port ( PIA *pi ) /* check for 8-bit port */ + +{ int i, r, m; + + w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i); + m = -1; + if (r == i) m = 2; + if (r == (255-i)) m = 0; + + w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i); + if (r != (255-i)) m = -1; + + if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); } + if (m == 2) { w2(0x26); w2(0xc); } + + if (m == -1) return 0; + return 5; +} + +static void bpck_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = { "4-bit","8-bit","EPP-8", + "EPP-16","EPP-32" }; + +#ifdef DUMP_EEPROM + int i; +#endif + + bpck_read_eeprom(pi,scratch); + +#ifdef DUMP_EEPROM + if (verbose) { + for(i=0;i<128;i++) + if ((scratch[i] < ' ') || (scratch[i] > '~')) + scratch[i] = '.'; + printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch); + printk("%s: %64.64s\n",pi->device,&scratch[64]); + } +#endif + + printk("%s: bpck %s, backpack %8.8s unit %d", + pi->device,BPCK_VERSION,&scratch[110],pi->unit); + printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port, + pi->mode,mode_string[pi->mode],pi->delay); +} + +static void bpck_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void bpck_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol bpck = { "bpck",0,5,2,1,256, + bpck_write_regr, + bpck_read_regr, + bpck_write_block, + bpck_read_block, + bpck_connect, + bpck_disconnect, + bpck_test_port, + bpck_probe_unit, + bpck_test_proto, + bpck_log_adapter, + bpck_init_proto, + bpck_release_proto + }; + +#ifdef MODULE + +int init_module(void) + +{ return pi_register(&bpck) - 1; +} + +void cleanup_module(void) + +{ pi_unregister(&bpck); +} + +#endif + +/* end of bpck.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/comm.c linux/drivers/block/paride/comm.c --- v2.0.34/linux/drivers/block/paride/comm.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/comm.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,228 @@ +/* + comm.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + comm.c is a low-level protocol driver for some older models + of the DataStor "Commuter" parallel to IDE adapter. Some of + the parallel port devices marketed by Arista currently + use this adapter. +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 init_proto, release_proto + +*/ + +#define COMM_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode +*/ + +#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0)) + +#define P1 w2(5);w2(0xd);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(7);w2(5);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x10 }; + +static int comm_read_regr( PIA *pi, int cont, int regr ) + +{ int l, h, r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r); P1; w0(0); + w2(6); l = r1(); w0(0x80); h = r1(); w2(4); + return j44(l,h); + + case 1: w0(r+0x20); P1; + w0(0); w2(0x26); h = r0(); w2(4); + return h; + + case 2: + case 3: + case 4: w3(r+0x20); r1(); + w2(0x24); h = r4(); w2(4); + return h; + + } + return -1; +} + +static void comm_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: w0(r); P1; w0(val); P2; + break; + + case 2: + case 3: + case 4: w3(r); r1(); w4(val); + break; + } +} + +static void comm_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); w0(0xff); w2(6); + w2(4); w0(0xaa); w2(6); + w2(4); w0(0x00); w2(6); + w2(4); w0(0x87); w2(6); + w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4); +} + +static void comm_disconnect ( PIA *pi ) + +{ w2(0); w2(0); w2(0); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void comm_read_block( PIA *pi, char * buf, int count ) + +{ int i, l, h; + + switch (pi->mode) { + + case 0: w0(0x48); P1; + for(i=0;i<count;i++) { + w0(0); w2(6); l = r1(); + w0(0x80); h = r1(); w2(4); + buf[i] = j44(l,h); + } + break; + + case 1: w0(0x68); P1; w0(0); + for(i=0;i<count;i++) { + w2(0x26); buf[i] = r0(); w2(0x24); + } + w2(4); + break; + + case 2: w3(0x68); r1(); w2(0x24); + for (i=0;i<count;i++) buf[i] = r4(); + w2(4); + break; + + case 3: w3(0x68); r1(); w2(0x24); + for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w(); + w2(4); + break; + + case 4: w3(0x68); r1(); w2(0x24); + for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l(); + w2(4); + break; + + } +} + +/* NB: Watch out for the byte swapped writes ! */ + +static void comm_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch (pi->mode) { + + case 0: + case 1: w0(0x68); P1; + for (k=0;k<count;k++) { + w2(5); w0(buf[k^1]); w2(7); + } + w2(5); w2(4); + break; + + case 2: w3(0x48); r1(); + for (k=0;k<count;k++) w4(buf[k^1]); + break; + + case 3: w3(0x48); r1(); + for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k)); + break; + + case 4: w3(0x48); r1(); + for (k=0;k<count/4;k++) w4l(pi_swab32(buf,k)); + break; + + + } +} + +static void comm_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"}; + + printk("%s: comm %s, DataStor Commuter at 0x%x, ", + pi->device,COMM_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void comm_init_proto(PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void comm_release_proto(PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol comm = {"comm",0,5,2,1,1, + comm_write_regr, + comm_read_regr, + comm_write_block, + comm_read_block, + comm_connect, + comm_disconnect, + 0, + 0, + 0, + comm_log_adapter, + comm_init_proto, + comm_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &comm ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &comm ); +} + +#endif + +/* end of comm.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/dstr.c linux/drivers/block/paride/dstr.c --- v2.0.34/linux/drivers/block/paride/dstr.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/dstr.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,243 @@ +/* + dstr.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + dstr.c is a low-level protocol driver for the + DataStor EP2000 parallel to IDE adapter chip. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + +*/ + +#define DSTR_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode + 3 EPP-16 + 4 EPP-32 +*/ + +#define j44(a,b) (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80)) + +#define P1 w2(5);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(5);w2(4); +#define P3 w2(6);w2(4);w2(6);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x20, 0x40 }; + +static int dstr_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont]; + + w0(0x81); P1; + if (pi->mode) { w0(0x11); } else { w0(1); } + P2; w0(r); P1; + + switch (pi->mode) { + + case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4); + return j44(a,b); + + case 1: w0(0); w2(0x26); a = r0(); w2(4); + return a; + + case 2: + case 3: + case 4: w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; +} + +static void dstr_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + w0(0x81); P1; + if (pi->mode >= 2) { w0(0x11); } else { w0(1); } + P2; w0(r); P1; + + switch (pi->mode) { + + case 0: + case 1: w0(val); w2(5); w2(7); w2(5); w2(4); + break; + + case 2: + case 3: + case 4: w4(val); + break; + } +} + +#define CCP(x) w0(0xff);w2(0xc);w2(4);\ + w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\ + w0(x);w2(5);w2(4); + +static void dstr_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); CCP(0xe0); w0(0xff); +} + +static void dstr_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void dstr_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + w0(0x81); P1; + if (pi->mode) { w0(0x19); } else { w0(9); } + P2; w0(0x82); P1; P3; w0(0x20); P1; + + switch (pi->mode) { + + case 0: for (k=0;k<count;k++) { + w2(6); a = r1(); w2(4); + w2(6); b = r1(); w2(4); + buf[k] = j44(a,b); + } + break; + + case 1: w0(0); + for (k=0;k<count;k++) { + w2(0x26); buf[k] = r0(); w2(0x24); + } + w2(4); + break; + + case 2: w2(0x24); + for (k=0;k<count;k++) buf[k] = r4(); + w2(4); + break; + + case 3: w2(0x24); + for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); + w2(4); + break; + + case 4: w2(0x24); + for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); + w2(4); + break; + + } +} + +static void dstr_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + w0(0x81); P1; + if (pi->mode) { w0(0x19); } else { w0(9); } + P2; w0(0x82); P1; P3; w0(0x20); P1; + + switch (pi->mode) { + + case 0: + case 1: for (k=0;k<count;k++) { + w2(5); w0(buf[k]); w2(7); + } + w2(5); w2(4); + break; + + case 2: w2(0xc5); + for (k=0;k<count;k++) w4(buf[k]); + w2(0xc4); + break; + + case 3: w2(0xc5); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + w2(0xc4); + break; + + case 4: w2(0xc5); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + w2(0xc4); + break; + + } +} + + +static void dstr_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = {"4-bit","8-bit","EPP-8", + "EPP-16","EPP-32"}; + + printk("%s: dstr %s, DataStor EP2000 at 0x%x, ", + pi->device,DSTR_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void dstr_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void dstr_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol dstr = {"dstr",0,5,2,1,1, + dstr_write_regr, + dstr_read_regr, + dstr_write_block, + dstr_read_block, + dstr_connect, + dstr_disconnect, + 0, + 0, + 0, + dstr_log_adapter, + dstr_init_proto, + dstr_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &dstr ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &dstr ); +} + +#endif + +/* end of dstr.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/epat.c linux/drivers/block/paride/epat.c --- v2.0.34/linux/drivers/block/paride/epat.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/epat.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,321 @@ +/* + epat.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + This is the low level protocol driver for the EPAT parallel + to IDE adapter from Shuttle Technologies. This adapter is + used in many popular parallel port disk products such as the + SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + +*/ + +#define EPAT_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) +#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) + +/* cont = 0 IDE register file + cont = 1 IDE control registers + cont = 2 internal EPAT registers +*/ + +static int cont_map[3] = { 0x18, 0x10, 0 }; + +static void epat_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0x60+r); w2(1); w0(val); w2(4); + break; + + case 3: + case 4: + case 5: w3(0x40+r); w4(val); + break; + + } +} + +static int epat_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r); w2(1); w2(3); + a = r1(); w2(4); b = r1(); + return j44(a,b); + + case 1: w0(0x40+r); w2(1); w2(4); + a = r1(); b = r2(); w0(0xff); + return j53(a,b); + + case 2: w0(0x20+r); w2(1); w2(0x25); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w3(r); w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; /* never gets here */ +} + +static void epat_read_block( PIA *pi, char * buf, int count ) + +{ int k, ph, a, b; + + switch (pi->mode) { + + case 0: w0(7); w2(1); w2(3); w0(0xff); + ph = 0; + for(k=0;k<count;k++) { + if (k == count-1) w0(0xfd); + w2(6+ph); a = r1(); + if (a & 8) b = a; + else { w2(4+ph); b = r1(); } + buf[k] = j44(a,b); + ph = 1 - ph; + } + w0(0); w2(4); + break; + + case 1: w0(0x47); w2(1); w2(5); w0(0xff); + ph = 0; + for(k=0;k<count;k++) { + if (k == count-1) w0(0xfd); + w2(4+ph); + a = r1(); b = r2(); + buf[k] = j53(a,b); + ph = 1 - ph; + } + w0(0); w2(4); + break; + + case 2: w0(0x27); w2(1); w2(0x25); w0(0); + ph = 0; + for(k=0;k<count-1;k++) { + w2(0x24+ph); + buf[k] = r0(); + ph = 1 - ph; + } + w2(0x26); w2(0x27); buf[count-1] = r0(); + w2(0x25); w2(4); + break; + + case 3: w3(0x80); w2(0x24); + for(k=0;k<count-1;k++) buf[k] = r4(); + w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); + w2(4); + break; + + case 4: w3(0x80); w2(0x24); + for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w(); + buf[count-2] = r4(); + w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); + w2(4); + break; + + case 5: w3(0x80); w2(0x24); + for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l(); + for(k=count-4;k<count-1;k++) buf[k] = r4(); + w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); + w2(4); + break; + + } +} + +static void epat_write_block( PIA *pi, char * buf, int count ) + +{ int ph, k; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0x67); w2(1); w2(5); + ph = 0; + for(k=0;k<count;k++) { + w0(buf[k]); + w2(4+ph); + ph = 1 - ph; + } + w2(7); w2(4); + break; + + case 3: w3(0xc0); + for(k=0;k<count;k++) w4(buf[k]); + w2(4); + break; + + case 4: w3(0xc0); + for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]); + w2(4); + break; + + case 5: w3(0xc0); + for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]); + w2(4); + break; + + } +} + +/* these macros access the EPAT registers in native addressing */ + +#define WR(r,v) epat_write_regr(pi,2,r,v) +#define RR(r) (epat_read_regr(pi,2,r)) + +/* and these access the IDE task file */ + +#define WRi(r,v) epat_write_regr(pi,0,r,v) +#define RRi(r) (epat_read_regr(pi,0,r)) + +/* FIXME: the CCP stuff should be fixed to handle multiple EPATs on a chain */ + +#define CCP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ + w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff); + +static void epat_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + CCP(0); CCP(0xe0); + w0(0); w2(1); w2(4); + if (pi->mode >= 3) { + w0(0); w2(1); w2(4); w2(0xc); + w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4); + } + WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10); +} + +static void epat_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static int epat_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int k, j, f, cc; + int e[2] = {0,0}; + + epat_connect(pi); + cc = RR(0xd); + epat_disconnect(pi); + + epat_connect(pi); + for (j=0;j<2;j++) { + WRi(6,0xa0+j*0x10); + for (k=0;k<256;k++) { + WRi(2,k^0xaa); + WRi(3,k^0x55); + if (RRi(2) != (k^0xaa)) e[j]++; + } + } + epat_disconnect(pi); + + f = 0; + epat_connect(pi); + WR(0x13,1); WR(0x13,0); WR(0xa,0x11); + epat_read_block(pi,scratch,512); + + for (k=0;k<256;k++) { + if ((scratch[2*k] & 0xff) != k) f++; + if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++; + } + epat_disconnect(pi); + + if (verbose) { + printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n", + pi->device,pi->port,pi->mode,cc,e[0],e[1],f); + } + + return (e[0] && e[1]) || f; +} + +static void epat_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ int ver; + char *mode_string[6] = + {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"}; + + epat_connect(pi); + WR(0xa,0x38); /* read the version code */ + ver = RR(0xb); + epat_disconnect(pi); + + printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ", + pi->device,EPAT_VERSION,ver,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void epat_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void epat_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol epat = {"epat",0,6,3,1,1, + epat_write_regr, + epat_read_regr, + epat_write_block, + epat_read_block, + epat_connect, + epat_disconnect, + 0, + 0, + epat_test_proto, + epat_log_adapter, + epat_init_proto, + epat_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &epat) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &epat); +} + +#endif + +/* end of epat.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/epia.c linux/drivers/block/paride/epia.c --- v2.0.34/linux/drivers/block/paride/epia.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/epia.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,326 @@ +/* + epia.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + epia.c is a low-level protocol driver for Shuttle Technologies + EPIA parallel to IDE adapter chip. This device is now obsolete + and has been replaced with the EPAT chip, which is supported + by epat.c, however, some devices based on EPIA are still + available. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + 1.02 GRG 1998.06.17 support older versions of EPIA + +*/ + +#define EPIA_VERSION "1.02" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +/* mode codes: 0 nybble reads on port 1, 8-bit writes + 1 5/3 reads on ports 1 & 2, 8-bit writes + 2 8-bit reads and writes + 3 8-bit EPP mode + 4 16-bit EPP + 5 32-bit EPP +*/ + +#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) +#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) + +/* cont = 0 IDE register file + cont = 1 IDE control registers +*/ + +static int cont_map[2] = { 0, 0x80 }; + +static int epia_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + regr += cont_map[cont]; + + switch (pi->mode) { + + case 0: r = regr^0x39; + w0(r); w2(1); w2(3); w0(r); + a = r1(); w2(1); b = r1(); w2(4); + return j44(a,b); + + case 1: r = regr^0x31; + w0(r); w2(1); w0(r&0x37); + w2(3); w2(5); w0(r|0xf0); + a = r1(); b = r2(); w2(4); + return j53(a,b); + + case 2: r = regr^0x29; + w0(r); w2(1); w2(0X21); w2(0x23); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w3(regr); w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; +} + +static void epia_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + regr += cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: r = regr^0x19; + w0(r); w2(1); w0(val); w2(3); w2(4); + break; + + case 3: + case 4: + case 5: r = regr^0x40; + w3(r); w4(val); w2(4); + break; + } +} + +#define WR(r,v) epia_write_regr(pi,0,r,v) +#define RR(r) (epia_read_regr(pi,0,r)) + +/* The use of register 0x84 is entirely unclear - it seems to control + some EPP counters ... currently we know about 3 different block + sizes: the standard 512 byte reads and writes, 12 byte writes and + 2048 byte reads (the last two being used in the CDrom drivers. +*/ + +static void epia_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0); + w2(1); w2(4); + if (pi->mode >= 3) { + w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4); + w2(0x24); w2(0x26); w2(4); + } + WR(0x86,8); +} + +static void epia_disconnect ( PIA *pi ) + +{ /* WR(0x84,0x10); */ + w0(pi->saved_r0); + w2(1); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void epia_read_block( PIA *pi, char * buf, int count ) + +{ int k, ph, a, b; + + switch (pi->mode) { + + case 0: w0(0x81); w2(1); w2(3); w0(0xc1); + ph = 1; + for (k=0;k<count;k++) { + w2(2+ph); a = r1(); + w2(4+ph); b = r1(); + buf[k] = j44(a,b); + ph = 1 - ph; + } + w0(0); w2(4); + break; + + case 1: w0(0x91); w2(1); w0(0x10); w2(3); + w0(0x51); w2(5); w0(0xd1); + ph = 1; + for (k=0;k<count;k++) { + w2(4+ph); + a = r1(); b = r2(); + buf[k] = j53(a,b); + ph = 1 - ph; + } + w0(0); w2(4); + break; + + case 2: w0(0x89); w2(1); w2(0x23); w2(0x21); + ph = 1; + for (k=0;k<count;k++) { + w2(0x24+ph); + buf[k] = r0(); + ph = 1 - ph; + } + w2(6); w2(4); + break; + + case 3: if (count > 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k<count;k++) buf[k] = r4(); + w2(4); WR(0x84,0); + break; + + case 4: if (count > 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); + w2(4); WR(0x84,0); + break; + + case 5: if (count > 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); + w2(4); WR(0x84,0); + break; + + } +} + +static void epia_write_block( PIA *pi, char * buf, int count ) + +{ int ph, k, last, d; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5); + ph = 0; last = 0x8000; + for (k=0;k<count;k++) { + d = buf[k]; + if (d != last) { last = d; w0(d); } + w2(4+ph); + ph = 1 - ph; + } + w2(7); w2(4); + break; + + case 3: if (count < 512) WR(0x84,1); + w3(0x40); + for (k=0;k<count;k++) w4(buf[k]); + if (count < 512) WR(0x84,0); + break; + + case 4: if (count < 512) WR(0x84,1); + w3(0x40); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + if (count < 512) WR(0x84,0); + break; + + case 5: if (count < 512) WR(0x84,1); + w3(0x40); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + if (count < 512) WR(0x84,0); + break; + + } + +} + +static int epia_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int j, k, f; + int e[2] = {0,0}; + + epia_connect(pi); + for (j=0;j<2;j++) { + WR(6,0xa0+j*0x10); + for (k=0;k<256;k++) { + WR(2,k^0xaa); + WR(3,k^0x55); + if (RR(2) != (k^0xaa)) e[j]++; + } + WR(2,1); WR(3,1); + } + epia_disconnect(pi); + + f = 0; + epia_connect(pi); + WR(0x84,8); + epia_read_block(pi,scratch,512); + for (k=0;k<256;k++) { + if ((scratch[2*k] & 0xff) != ((k+1) & 0xff)) f++; + if ((scratch[2*k+1] & 0xff) != ((-2-k) & 0xff)) f++; + } + WR(0x84,0); + epia_disconnect(pi); + + if (verbose) { + printk("%s: epia: port 0x%x, mode %d, test=(%d,%d,%d)\n", + pi->device,pi->port,pi->mode,e[0],e[1],f); + } + + return (e[0] && e[1]) || f; + +} + + +static void epia_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[6] = {"4-bit","5/3","8-bit", + "EPP-8","EPP-16","EPP-32"}; + + printk("%s: epia %s, Shuttle EPIA at 0x%x, ", + pi->device,EPIA_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void epia_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void epia_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol epia = {"epia",0,6,3,1,1, + epia_write_regr, + epia_read_regr, + epia_write_block, + epia_read_block, + epia_connect, + epia_disconnect, + 0, + 0, + epia_test_proto, + epia_log_adapter, + epia_init_proto, + epia_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &epia ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &epia ); +} + +#endif + +/* end of epia.c */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/fit2.c linux/drivers/block/paride/fit2.c --- v2.0.34/linux/drivers/block/paride/fit2.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/fit2.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,161 @@ +/* + fit2.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + fit2.c is a low-level protocol driver for the older version + of the Fidelity International Technology parallel port adapter. + This adapter is used in their TransDisk 2000 and older TransDisk + 3000 portable hard-drives. As far as I can tell, this device + supports 4-bit mode _only_. + + Newer models of the FIT products use an enhanced protocol. + The "fit3" protocol module should support current drives. + +*/ + +#define FIT2_VERSION "1.0" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set + +NB: The FIT adapter does not appear to use the control registers. +So, we map ALT_STATUS to STATUS and NO-OP writes to the device +control register - this means that IDE reset will not work on these +devices. + +*/ + +static void fit2_write_regr( PIA *pi, int cont, int regr, int val) + +{ if (cont == 1) return; + w2(0xc); w0(regr); w2(4); w0(val); w2(5); w0(0); w2(4); +} + +static int fit2_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + if (cont) { + if (regr != 6) return 0xff; + r = 7; + } else r = regr + 0x10; + + w2(0xc); w0(r); w2(4); w2(5); + w0(0); a = r1(); + w0(1); b = r1(); + w2(4); + + return j44(a,b); + +} + +static void fit2_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b, c, d; + + w2(0xc); w0(0x10); + + for (k=0;k<count/4;k++) { + + w2(4); w2(5); + w0(0); a = r1(); w0(1); b = r1(); + w0(3); c = r1(); w0(2); d = r1(); + buf[4*k+0] = j44(a,b); + buf[4*k+1] = j44(d,c); + + w2(4); w2(5); + a = r1(); w0(3); b = r1(); + w0(1); c = r1(); w0(0); d = r1(); + buf[4*k+2] = j44(d,c); + buf[4*k+3] = j44(a,b); + + } + + w2(4); + +} + +static void fit2_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + + w2(0xc); w0(0); + for (k=0;k<count/2;k++) { + w2(4); w0(buf[2*k]); + w2(5); w0(buf[2*k+1]); + } + w2(4); +} + +static void fit2_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xcc); +} + +static void fit2_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void fit2_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ printk("%s: fit2 %s, FIT 2000 adapter at 0x%x, delay %d\n", + pi->device,FIT2_VERSION,pi->port,pi->delay); + +} + +static void fit2_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void fit2_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol fit2 = {"fit2",0,1,2,1,1, + fit2_write_regr, + fit2_read_regr, + fit2_write_block, + fit2_read_block, + fit2_connect, + fit2_disconnect, + 0, + 0, + 0, + fit2_log_adapter, + fit2_init_proto, + fit2_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &fit2 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &fit2 ); +} + +#endif + +/* end of fit2.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/fit3.c linux/drivers/block/paride/fit3.c --- v2.0.34/linux/drivers/block/paride/fit3.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/fit3.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,221 @@ +/* + fit3.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + fit3.c is a low-level protocol driver for newer models + of the Fidelity International Technology parallel port adapter. + This adapter is used in their TransDisk 3000 portable + hard-drives, as well as CD-ROM, PD-CD and other devices. + + The TD-2000 and certain older devices use a different protocol. + Try the fit2 protocol module with them. + + NB: The FIT adapters do not appear to support the control + registers. So, we map ALT_STATUS to STATUS and NO-OP writes + to the device control register - this means that IDE reset + will not work on these devices. + +*/ + +#define FIT3_VERSION "1.0" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0)) + +#define w7(byte) {out_p(7,byte);} +#define r7() (in_p(7) & 0xff) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set + +*/ + +static void fit3_write_regr( PIA *pi, int cont, int regr, int val) + +{ if (cont == 1) return; + + switch (pi->mode) { + + case 0: + case 1: w2(0xc); w0(regr); w2(0x8); w2(0xc); + w0(val); w2(0xd); + w0(0); w2(0xc); + break; + + case 2: w2(0xc); w0(regr); w2(0x8); w2(0xc); + w4(val); w4(0); + w2(0xc); + break; + + } +} + +static int fit3_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b; + + if (cont) { + if (regr != 6) return 0xff; + regr = 7; + } + + switch (pi->mode) { + + case 0: w2(0xc); w0(regr + 0x10); w2(0x8); w2(0xc); + w2(0xd); a = r1(); + w2(0xf); b = r1(); + w2(0xc); + return j44(a,b); + + case 1: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc); + w2(0xec); w2(0xee); w2(0xef); a = r0(); + w2(0xc); + return a; + + case 2: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc); + w2(0xec); + a = r4(); b = r4(); + w2(0xc); + return a; + + } + return -1; + +} + +static void fit3_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b, c, d; + + switch (pi->mode) { + + case 0: w2(0xc); w0(0x10); w2(0x8); w2(0xc); + for (k=0;k<count/2;k++) { + w2(0xd); a = r1(); + w2(0xf); b = r1(); + w2(0xc); c = r1(); + w2(0xe); d = r1(); + buf[2*k ] = j44(a,b); + buf[2*k+1] = j44(c,d); + } + w2(0xc); + break; + + case 1: w2(0xc); w0(0x90); w2(0x8); w2(0xc); + w2(0xec); w2(0xee); + for (k=0;k<count/2;k++) { + w2(0xef); a = r0(); + w2(0xee); b = r0(); + buf[2*k ] = a; + buf[2*k+1] = b; + } + w2(0xec); + w2(0xc); + break; + + case 2: w2(0xc); w0(0x90); w2(0x8); w2(0xc); + w2(0xec); + for (k=0;k<count;k++) buf[k] = r4(); + w2(0xc); + break; + + } +} + +static void fit3_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch (pi->mode) { + + case 0: + case 1: w2(0xc); w0(0); w2(0x8); w2(0xc); + for (k=0;k<count/2;k++) { + w0(buf[2*k ]); w2(0xd); + w0(buf[2*k+1]); w2(0xc); + } + break; + + case 2: w2(0xc); w0(0); w2(0x8); w2(0xc); + for (k=0;k<count;k++) w4(buf[k]); + w2(0xc); + break; + } +} + +static void fit3_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xc); w0(0); w2(0xa); + if (pi->mode == 2) { + w2(0xc); w0(0x9); w2(0x8); w2(0xc); + } +} + +static void fit3_disconnect ( PIA *pi ) + +{ w2(0xc); w0(0xa); w2(0x8); w2(0xc); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void fit3_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[3] = {"4-bit","8-bit","EPP"}; + + printk("%s: fit3 %s, FIT 3000 adapter at 0x%x, " + "mode %d (%s), delay %d\n", + pi->device,FIT3_VERSION,pi->port, + pi->mode,mode_string[pi->mode],pi->delay); + +} + +static void fit3_init_proto(PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void fit3_release_proto(PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol fit3 = {"fit3",0,3,2,1,1, + fit3_write_regr, + fit3_read_regr, + fit3_write_block, + fit3_read_block, + fit3_connect, + fit3_disconnect, + 0, + 0, + 0, + fit3_log_adapter, + fit3_init_proto, + fit3_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &fit3 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &fit3 ); +} + +#endif + +/* end of fit3.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/frpw.c linux/drivers/block/paride/frpw.c --- v2.0.34/linux/drivers/block/paride/frpw.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/frpw.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,310 @@ +/* + frpw.c (c) 1996-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license + + frpw.c is a low-level protocol driver for the Freecom "Power" + parallel port IDE adapter. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + fix chip detect + added EPP-16 and EPP-32 + +*/ + +#define FRPW_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4); +#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x10 }; + +static int frpw_read_regr( PIA *pi, int cont, int regr ) + +{ int h,l,r; + + r = regr + cont_map[cont]; + + w2(4); + w0(r); cec4; + w2(6); l = r1(); + w2(4); h = r1(); + w2(4); + + return j44(l,h); + +} + +static void frpw_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + w2(4); w0(r); cec4; + w0(val); + w2(5);w2(7);w2(5);w2(4); +} + +static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr ) + +{ int h, l, k, ph; + + switch(pi->mode) { + + case 0: w2(4); w0(regr); cec4; + for (k=0;k<count;k++) { + w2(6); l = r1(); + w2(4); h = r1(); + buf[k] = j44(l,h); + } + w2(4); + break; + + case 1: ph = 2; + w2(4); w0(regr + 0xc0); cec4; + w0(0xff); + for (k=0;k<count;k++) { + w2(0xa4 + ph); + buf[k] = r0(); + ph = 2 - ph; + } + w2(0xac); w2(0xa4); w2(4); + break; + + case 2: w2(4); w0(regr + 0x80); cec4; + for (k=0;k<count;k++) buf[k] = r4(); + w2(0xac); w2(0xa4); + w2(4); + break; + + case 3: w2(4); w0(regr + 0x80); cec4; + for (k=0;k<count-2;k++) buf[k] = r4(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + case 4: w2(4); w0(regr + 0x80); cec4; + for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + case 5: w2(4); w0(regr + 0x80); cec4; + for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l(); + buf[count-4] = r4(); + buf[count-3] = r4(); + w2(0xac); w2(0xa4); + buf[count-2] = r4(); + buf[count-1] = r4(); + w2(4); + break; + + } +} + +static void frpw_read_block( PIA *pi, char * buf, int count) + +{ frpw_read_block_int(pi,buf,count,0x08); +} + +static void frpw_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch(pi->mode) { + + case 0: + case 1: + case 2: w2(4); w0(8); cec4; w2(5); + for (k=0;k<count;k++) { + w0(buf[k]); + w2(7);w2(5); + } + w2(4); + break; + + case 3: w2(4); w0(0xc8); cec4; w2(5); + for (k=0;k<count;k++) w4(buf[k]); + w2(4); + break; + + case 4: w2(4); w0(0xc8); cec4; w2(5); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + w2(4); + break; + + case 5: w2(4); w0(0xc8); cec4; w2(5); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + w2(4); + break; + } +} + +static void frpw_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); +} + +static void frpw_disconnect ( PIA *pi ) + +{ w2(4); w0(0x20); cec4; + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +/* Stub logic to see if PNP string is available - used to distinguish + between the Xilinx and ASIC implementations of the Freecom adapter. +*/ + +static int frpw_test_pnp ( PIA *pi ) + +/* returns chip_type: 0 = Xilinx, 1 = ASIC */ + +{ int olddelay, a, b; + + olddelay = pi->delay; + pi->delay = 10; + + pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4); w0(4); w2(6); w2(7); + a = r1() & 0xff; w2(4); b = r1() & 0xff; + w2(0xc); w2(0xe); w2(4); + + pi->delay = olddelay; + w0(pi->saved_r0); + w2(pi->saved_r2); + + return ((~a&0x40) && (b&0x40)); +} + +/* We use the pi->private to remember the result of the PNP test. + To make this work, private = port*2 + chip. Yes, I know it's + a hack :-( +*/ + +static int frpw_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int j, k, r; + int e[2] = {0,0}; + + if ((pi->private>>1) != pi->port) + pi->private = frpw_test_pnp(pi) + 2*pi->port; + + if (((pi->private%2) == 0) && (pi->mode > 2)) { + if (verbose) + printk("%s: frpw: Xilinx does not support mode %d\n", + pi->device, pi->mode); + return 1; + } + + if (((pi->private%2) == 1) && (pi->mode == 2)) { + if (verbose) + printk("%s: frpw: ASIC does not support mode 2\n", + pi->device); + return 1; + } + + frpw_connect(pi); + for (j=0;j<2;j++) { + frpw_write_regr(pi,0,6,0xa0+j*0x10); + for (k=0;k<256;k++) { + frpw_write_regr(pi,0,2,k^0xaa); + frpw_write_regr(pi,0,3,k^0x55); + if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++; + } + } + frpw_disconnect(pi); + + frpw_connect(pi); + frpw_read_block_int(pi,scratch,512,0x10); + r = 0; + for (k=0;k<128;k++) if (scratch[k] != k) r++; + frpw_disconnect(pi); + + if (verbose) { + printk("%s: frpw: port 0x%x, chip %d, mode %d, test=(%d,%d,%d)\n", + pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r); + } + + return (r || (e[0] && e[1])); +} + + +static void frpw_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[6] = {"4-bit","8-bit","EPP", + "EPP-8","EPP-16","EPP-32"}; + + printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device, + FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void frpw_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; + pi->private = 0; +} + +static void frpw_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol frpw = {"frpw",0,6,2,2,1, + frpw_write_regr, + frpw_read_regr, + frpw_write_block, + frpw_read_block, + frpw_connect, + frpw_disconnect, + 0, + 0, + frpw_test_proto, + frpw_log_adapter, + frpw_init_proto, + frpw_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &frpw ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &frpw ); +} + +#endif + +/* end of frpw.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/kbic.c linux/drivers/block/paride/kbic.c --- v2.0.34/linux/drivers/block/paride/kbic.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/kbic.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,311 @@ +/* + kbic.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + This is a low-level driver for the KBIC-951A and KBIC-971A + parallel to IDE adapter chips from KingByte Information Systems. + + The chips are almost identical, however, the wakeup code + required for the 971A interferes with the correct operation of + the 951A, so this driver registers itself twice, once for + each chip. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + +*/ + +#define KBIC_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#define r12w() (delay_p,inw(pi->port+1)&0xffff) + +#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88) +#define j53(w) (((w>>3)&0x1f)|((w>>4)&0xe0)) + + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x80, 0x40 }; + +static int kbic_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, s; + + s = cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8); + a = r1(); w0(0x28); b = r1(); w2(4); + return j44(a,b); + + case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8); + a = r12w(); w2(4); + return j53(a); + + case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr); + a = r4(); b = r4(); w2(4); w2(0); w2(4); + return a; + + } + return -1; +} + +static void kbic_write_regr( PIA *pi, int cont, int regr, int val) + +{ int s; + + s = cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(regr|0x10|s); w2(4); w2(6); w2(4); + w0(val); w2(5); w2(4); + break; + + case 3: + case 4: + case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr); + w4(val); w4(val); + w2(4); w2(0); w2(4); + break; + + } +} + +static void k951_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); +} + +static void k951_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +#define CCP(x) w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\ + w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff); + +static void k971_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + CCP(0x20); + w2(4); +} + +static void k971_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +/* counts must be congruent to 0 MOD 4, but all known applications + have this property. +*/ + +static void kbic_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + switch (pi->mode) { + + case 0: w0(0x98); w2(4); w2(6); w2(4); + for (k=0;k<count/2;k++) { + w2(1); w0(8); a = r1(); + w0(0x28); b = r1(); + buf[2*k] = j44(a,b); + w2(5); b = r1(); + w0(8); a = r1(); + buf[2*k+1] = j44(a,b); + w2(4); + } + break; + + case 1: w0(0xb8); w2(4); w2(6); w2(4); + for (k=0;k<count/4;k++) { + w0(0xb8); + w2(4); w2(5); + w0(8); buf[4*k] = j53(r12w()); + w0(0xb8); buf[4*k+1] = j53(r12w()); + w2(4); w2(5); + buf[4*k+3] = j53(r12w()); + w0(8); buf[4*k+2] = j53(r12w()); + } + w2(4); + break; + + case 2: w0(0x88); w2(4); w2(6); w2(4); + for (k=0;k<count/2;k++) { + w2(0xa0); w2(0xa1); buf[2*k] = r0(); + w2(0xa5); buf[2*k+1] = r0(); + } + w2(4); + break; + + case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for (k=0;k<count;k++) buf[k] = r4(); + w2(4); w2(0); w2(4); + break; + + case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); + w2(4); w2(0); w2(4); + break; + + case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); + w2(4); w2(0); w2(4); + break; + + + } +} + +static void kbic_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0x90); w2(4); w2(6); w2(4); + for(k=0;k<count/2;k++) { + w0(buf[2*k+1]); w2(0); w2(4); + w0(buf[2*k]); w2(5); w2(4); + } + break; + + case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for(k=0;k<count/2;k++) { + w4(buf[2*k+1]); + w4(buf[2*k]); + } + w2(4); w2(0); w2(4); + break; + + case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for(k=0;k<count/2;k++) w4w(pi_swab16(buf,k)); + w2(4); w2(0); w2(4); + break; + + case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0); + for(k=0;k<count/4;k++) w4l(pi_swab32(buf,k)); + w2(4); w2(0); w2(4); + break; + + } + +} + +static void kbic_log_adapter( PIA *pi, char * scratch, + int verbose, char * chip ) + +{ char *mode_string[6] = {"4-bit","5/3","8-bit", + "EPP-8","EPP_16","EPP-32"}; + + printk("%s: kbic %s, KingByte %s at 0x%x, ", + pi->device,KBIC_VERSION,chip,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void k951_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ kbic_log_adapter(pi,scratch,verbose,"KBIC-951A"); +} + +static void k971_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ kbic_log_adapter(pi,scratch,verbose,"KBIC-971A"); +} + +static void kbic_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void kbic_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol k951 = {"k951",0,6,3,1,1, + kbic_write_regr, + kbic_read_regr, + kbic_write_block, + kbic_read_block, + k951_connect, + k951_disconnect, + 0, + 0, + 0, + k951_log_adapter, + kbic_init_proto, + kbic_release_proto + }; + + +struct pi_protocol k971 = {"k971",0,6,3,1,1, + kbic_write_regr, + kbic_read_regr, + kbic_write_block, + kbic_read_block, + k971_connect, + k971_disconnect, + 0, + 0, + 0, + k971_log_adapter, + kbic_init_proto, + kbic_release_proto + }; + +#ifdef MODULE + +int init_module(void) + +{ int s5,s7; + + s5 = pi_register(&k951); + s7 = pi_register(&k971); + + return (s5 || s7) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &k951 ); + pi_unregister( &k971 ); +} + +#endif + +/* end of kbic.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/ktti.c linux/drivers/block/paride/ktti.c --- v2.0.34/linux/drivers/block/paride/ktti.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/ktti.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,138 @@ +/* + ktti.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + ktti.c is a low-level protocol driver for the KT Technology + parallel port adapter. This adapter is used in the "PHd" + portable hard-drives. As far as I can tell, this device + supports 4-bit mode _only_. + +*/ + +#define KTTI_VERSION "1.0" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x10, 0x08 }; + +static void ktti_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + w0(r); w2(0xb); w2(0xa); w2(3); w2(6); + w0(val); w2(3); w0(0); w2(6); w2(0xb); +} + +static int ktti_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont]; + + w0(r); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9); + a = r1(); w2(0xc); b = r1(); w2(9); w2(0xc); w2(9); + return j44(a,b); + +} + +static void ktti_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + for (k=0;k<count/2;k++) { + w0(0x10); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9); + a = r1(); w2(0xc); b = r1(); w2(9); + buf[2*k] = j44(a,b); + a = r1(); w2(0xc); b = r1(); w2(9); + buf[2*k+1] = j44(a,b); + } +} + +static void ktti_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + for (k=0;k<count/2;k++) { + w0(0x10); w2(0xb); w2(0xa); w2(3); w2(6); + w0(buf[2*k]); w2(3); + w0(buf[2*k+1]); w2(6); + w2(0xb); + } +} + +static void ktti_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xb); w2(0xa); w0(0); w2(3); w2(6); +} + +static void ktti_disconnect ( PIA *pi ) + +{ w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void ktti_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ printk("%s: ktti %s, KT adapter at 0x%x, delay %d\n", + pi->device,KTTI_VERSION,pi->port,pi->delay); + +} + +static void ktti_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void ktti_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol ktti = {"ktti",0,1,2,1,1, + ktti_write_regr, + ktti_read_regr, + ktti_write_block, + ktti_read_block, + ktti_connect, + ktti_disconnect, + 0, + 0, + 0, + ktti_log_adapter, + ktti_init_proto, + ktti_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &ktti ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &ktti ); +} + +#endif + +/* end of ktti.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/mkd linux/drivers/block/paride/mkd --- v2.0.34/linux/drivers/block/paride/mkd Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/mkd Mon Jul 13 13:47:28 1998 @@ -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 -u --recursive --new-file v2.0.34/linux/drivers/block/paride/on20.c linux/drivers/block/paride/on20.c --- v2.0.34/linux/drivers/block/paride/on20.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/on20.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,163 @@ +/* + on20.c (c) 1996-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + on20.c is a low-level protocol driver for the + Onspec 90c20 parallel to IDE adapter. +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + +*/ + +#define ON20_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +#define op(f) w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); +#define vl(v) w2(4);w0(v);w2(5);w2(7);w2(5);w2(4); + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int on20_read_regr( PIA *pi, int cont, int regr ) + +{ int h,l, r ; + + r = (regr<<2) + 1 + cont; + + op(1); vl(r); op(0); + + switch (pi->mode) { + + case 0: w2(4); w2(6); l = r1(); + w2(4); w2(6); h = r1(); + w2(4); w2(6); w2(4); w2(6); w2(4); + return j44(l,h); + + case 1: w2(4); w2(0x26); r = r0(); + w2(4); w2(0x26); w2(4); + return r; + + } + return -1; +} + +static void on20_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = (regr<<2) + 1 + cont; + + op(1); vl(r); + op(0); vl(val); + op(0); vl(val); +} + +static void on20_connect ( PIA *pi) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4); + if (pi->mode) { op(2); vl(8); op(2); vl(9); } + else { op(2); vl(0); op(2); vl(8); } +} + +static void on20_disconnect ( PIA *pi ) + +{ w2(4);w0(7);w2(4);w2(0xc);w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void on20_read_block( PIA *pi, char * buf, int count ) + +{ int k, l, h; + + op(1); vl(1); op(0); + + for (k=0;k<count;k++) + if (pi->mode) { + w2(4); w2(0x26); buf[k] = r0(); + } else { + w2(6); l = r1(); w2(4); + w2(6); h = r1(); w2(4); + buf[k] = j44(l,h); + } + w2(4); +} + +static void on20_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + op(1); vl(1); op(0); + + for (k=0;k<count;k++) { w2(5); w0(buf[k]); w2(7); } + w2(4); +} + +static void on20_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[2] = {"4-bit","8-bit"}; + + printk("%s: on20 %s, OnSpec 90c20 at 0x%x, ", + pi->device,ON20_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void on20_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void on20_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol on20 = {"on20",0,2,2,1,1, + on20_write_regr, + on20_read_regr, + on20_write_block, + on20_read_block, + on20_connect, + on20_disconnect, + 0, + 0, + 0, + on20_log_adapter, + on20_init_proto, + on20_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &on20 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &on20 ); +} + +#endif + +/* end of on20.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/on26.c linux/drivers/block/paride/on26.c --- v2.0.34/linux/drivers/block/paride/on26.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/on26.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,267 @@ +/* + on26.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + on26.c is a low-level protocol driver for the + OnSpec 90c26 parallel to IDE adapter chip. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 init_proto, release_proto + +*/ + +#define ON26_VERSION "1.01" + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> + +#include "paride.h" + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode + 3 EPP-16 + 4 EPP-32 +*/ + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +#define P1 w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(5);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int on26_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = (regr<<2) + 1 + cont; + + switch (pi->mode) { + + case 0: w0(1); P1; w0(r); P2; w0(0); P1; + w2(6); a = r1(); w2(4); + w2(6); b = r1(); w2(4); + w2(6); w2(4); w2(6); w2(4); + return j44(a,b); + + case 1: w0(1); P1; w0(r); P2; w0(0); P1; + w2(0x26); a = r0(); w2(4); w2(0x26); w2(4); + return a; + + case 2: + case 3: + case 4: w3(1); w3(1); w2(5); w4(r); w2(4); + w3(0); w3(0); w2(0x24); a = r4(); w2(4); + w2(0x24); r4(); w2(4); + return a; + + } + return -1; +} + +static void on26_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = (regr<<2) + 1 + cont; + + switch (pi->mode) { + + case 0: + case 1: w0(1); P1; w0(r); P2; w0(0); P1; + w0(val); P2; w0(val); P2; + break; + + case 2: + case 3: + case 4: w3(1); w3(1); w2(5); w4(r); w2(4); + w3(0); w3(0); + w2(5); w4(val); w2(4); + w2(5); w4(val); w2(4); + break; + } +} + +#define CCP(x) w0(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ + w0(0x87);w0(0x78);w0(x);w2(4); + +static void on26_connect ( PIA *pi ) + +{ int x; + + pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + CCP(0x20); + w2(0xcd); w2(0xcc); w0(0xff); + x = 8; if (pi->mode) x = 9; + + w0(2); P1; w0(8); P2; + w0(2); P1; w0(x); P2; +} + +static void on26_disconnect ( PIA *pi ) + +{ if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); } + else { w0(4); P1; w0(4); P1; } + CCP(0x30); + w2(0xcd); w2(0xcc); w0(0xff); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void on26_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + switch (pi->mode) { + + case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1; + udelay(10); + for (k=0;k<count;k++) { + w2(6); a = r1(); + w2(4); b = r1(); + buf[k] = j44(a,b); + } + w0(2); P1; w0(8); P2; + break; + + case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1; + udelay(10); + for (k=0;k<count/2;k++) { + w2(0x26); buf[2*k] = r0(); + w2(0x24); buf[2*k+1] = r0(); + } + w0(2); P1; w0(9); P2; + break; + + case 2: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0x24); + udelay(10); + for (k=0;k<count;k++) buf[k] = r4(); + w2(4); + break; + + case 3: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0x24); + udelay(10); + for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); + w2(4); + break; + + case 4: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0x24); + udelay(10); + for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); + w2(4); + break; + + } +} + +static void on26_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + switch (pi->mode) { + + case 0: + case 1: w0(1); P1; w0(1); P2; + w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1; + udelay(10); + for (k=0;k<count/2;k++) { + w2(5); w0(buf[2*k]); + w2(7); w0(buf[2*k+1]); + } + w2(5); w2(4); + w0(2); P1; w0(8+pi->mode); P2; + break; + + case 2: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0xc5); + udelay(10); + for (k=0;k<count;k++) w4(buf[k]); + w2(0xc4); + break; + + case 3: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0xc5); + udelay(10); + for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); + w2(0xc4); + break; + + case 4: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0xc5); + udelay(10); + for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); + w2(0xc4); + break; + + } + +} + +static void on26_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[5] = {"4-bit","8-bit","EPP-8", + "EPP-16","EPP-32"}; + + printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ", + pi->device,ON26_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void on26_init_proto( PIA *pi) + +{ MOD_INC_USE_COUNT; +} + +static void on26_release_proto( PIA *pi) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol on26 = {"on26",0,5,2,1,1, + on26_write_regr, + on26_read_regr, + on26_write_block, + on26_read_block, + on26_connect, + on26_disconnect, + 0, + 0, + 0, + on26_log_adapter, + on26_init_proto, + on26_release_proto + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &on26 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &on26 ); +} + +#endif + +/* end of on26.c */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/paride.c linux/drivers/block/paride/paride.c --- v2.0.34/linux/drivers/block/paride/paride.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/paride.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,514 @@ +/* + paride.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + This is the base module for the family of device drivers + that support parallel port IDE devices. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.03 Use spinlocks + 1.02 GRG 1998.05.05 init_proto, release_proto, ktti + +*/ + +#define PI_VERSION "1.02" + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/string.h> +#include "spinlock.h" + +#ifdef CONFIG_PARPORT_MODULE +#define CONFIG_PARPORT +#endif + +#ifdef CONFIG_PARPORT +#include <linux/parport.h> +#endif + +#include "paride.h" + +#define MAX_PROTOS 32 + +static struct pi_protocol *protocols[MAX_PROTOS]; + +/* spinlock_t pi_spinlock = SPIN_LOCK_UNLOCKED; */ + +void pi_write_regr( PIA *pi, int cont, int regr, int val) + +{ pi->proto->write_regr(pi,cont,regr,val); +} + +int pi_read_regr( PIA *pi, int cont, int regr) + +{ return pi->proto->read_regr(pi,cont,regr); +} + +void pi_write_block( PIA *pi, char * buf, int count) + +{ pi->proto->write_block(pi,buf,count); +} + +void pi_read_block( PIA *pi, char * buf, int count) + +{ pi->proto->read_block(pi,buf,count); +} + +#ifdef CONFIG_PARPORT + +static void pi_wake_up( void *p) + +{ PIA *pi = (PIA *) p; + long flags; + void (*cont)(void) = NULL; + + spin_lock_irqsave(&pi_spinlock,flags); + + if (pi->claim_cont && !parport_claim(pi->pardev)) { + cont = pi->claim_cont; + pi->claim_cont = NULL; + pi->claimed = 1; + } + + spin_unlock_irqrestore(&pi_spinlock,flags); + + wake_up(&(pi->parq)); + + if (cont) cont(); +} + +#endif + +void pi_do_claimed( PIA *pi, void(*cont)(void)) + +#ifdef CONFIG_PARPORT + +{ long flags; + + spin_lock_irqsave(&pi_spinlock,flags); + + if (!pi->pardev || !parport_claim(pi->pardev)) { + pi->claimed = 1; + spin_unlock_irqrestore(&pi_spinlock,flags); + cont(); + } else { + pi->claim_cont = cont; + spin_unlock_irqrestore(&pi_spinlock,flags); + } +} + +#else + +{ cont(); +} + +#endif + +static void pi_claim( PIA *pi) + +{ if (pi->claimed) return; + pi->claimed = 1; +#ifdef CONFIG_PARPORT + if (pi->pardev) + while (parport_claim((struct pardevice *)(pi->pardev))) + sleep_on(&(pi->parq)); +#endif +} + +static void pi_unclaim( PIA *pi) + +{ pi->claimed = 0; +#ifdef CONFIG_PARPORT + if (pi->pardev) parport_release((struct pardevice *)(pi->pardev)); +#endif +} + +void pi_connect( PIA *pi) + +{ pi_claim(pi); + pi->proto->connect(pi); +} + +void pi_disconnect( PIA *pi) + +{ pi->proto->disconnect(pi); + pi_unclaim(pi); +} + +static void pi_unregister_parport( PIA *pi) + +{ +#ifdef CONFIG_PARPORT + if (pi->pardev) { + parport_unregister_device((struct pardevice *)(pi->pardev)); + pi->pardev = NULL; + } +#endif +} + +void pi_release( PIA *pi) + +{ pi_unregister_parport(pi); + if ((!pi->pardev)&&(pi->reserved)) + release_region(pi->port,pi->reserved); + pi->proto->release_proto(pi); +} + +#define WR(r,v) pi_write_regr(pi,0,r,v) +#define RR(r) (pi_read_regr(pi,0,r)) + +static int pi_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int j, k; + int e[2] = {0,0}; + + if (pi->proto->test_proto) { + pi_claim(pi); + j = pi->proto->test_proto(pi,scratch,verbose); + pi_unclaim(pi); + return j; + } + + pi_connect(pi); + + for (j=0;j<2;j++) { + WR(6,0xa0+j*0x10); + for (k=0;k<256;k++) { + WR(2,k^0xaa); + WR(3,k^0x55); + if (RR(2) != (k^0xaa)) e[j]++; + } + } + + pi_disconnect(pi); + + if (verbose) + printk("%s: %s: port 0x%x, mode %d, test=(%d,%d)\n", + pi->device,pi->proto->name,pi->port, + pi->mode,e[0],e[1]); + + return (e[0] && e[1]); /* not here if both > 0 */ +} + +int pi_register( PIP *pr) + +{ int k; + + for (k=0;k<MAX_PROTOS;k++) + if (protocols[k] && !strcmp(pr->name,protocols[k]->name)) { + printk("paride: %s protocol already registered\n",pr->name); + return 0; + } + k = 0; + while((k<MAX_PROTOS) && (protocols[k])) k++; + if (k == MAX_PROTOS) { + printk("paride: protocol table full\n"); + return 0; + } + MOD_INC_USE_COUNT; + protocols[k] = pr; + pr->index = k; + printk("paride: %s registered as protocol %d\n",pr->name,k); + return 1; +} + +void pi_unregister( PIP *pr) + +{ if (!pr) return; + if (protocols[pr->index] != pr) { + printk("paride: %s not registered\n",pr->name); + return; + } + protocols[pr->index] = 0; + MOD_DEC_USE_COUNT; +} + +static void pi_register_parport( PIA *pi, int verbose) + +{ +#ifdef CONFIG_PARPORT + + struct parport *pp; + + pp = parport_enumerate(); + + while((pp)&&(pp->base != pi->port)) pp = pp->next; + + if (!pp) return; + + pi->pardev = (void *) parport_register_device( + pp,pi->device,NULL,pi_wake_up,NULL,0,(void *)pi); + + pi->parq = NULL; + + if (verbose) printk("%s: 0x%x is %s\n",pi->device,pi->port,pp->name); + + pi->parname = pp->name; + +#endif +} + +static int pi_probe_mode( PIA *pi, int max, char * scratch, int verbose) + +{ int best, range; + + if (pi->mode != -1) { + if (pi->mode >= max) return 0; + range = 3; + if (pi->mode >= pi->proto->epp_first) range = 8; + if ((range == 8) && (pi->port % 8)) return 0; + if ((!pi->pardev) && check_region(pi->port,range)) return 0; + pi->reserved = range; + return (!pi_test_proto(pi,scratch,verbose)); + } + best = -1; + for(pi->mode=0;pi->mode<max;pi->mode++) { + range = 3; + if (pi->mode >= pi->proto->epp_first) range = 8; + if ((range == 8) && (pi->port % 8)) break; + if ((!pi->pardev) && check_region(pi->port,range)) break; + pi->reserved = range; + if (!pi_test_proto(pi,scratch,verbose)) best = pi->mode; + } + pi->mode = best; + return (best > -1); +} + +static int pi_probe_unit( PIA *pi, int unit, char * scratch, int verbose) + +{ int max,s,e; + + s = unit; e = s+1; + + if (s == -1) { + s = 0; + e = pi->proto->max_units; + } + + pi_register_parport(pi,verbose); + + if ((!pi->pardev) && check_region(pi->port,3)) return 0; + + if (pi->proto->test_port) { + pi_claim(pi); + max = pi->proto->test_port(pi); + pi_unclaim(pi); + } + else max = pi->proto->max_mode; + + if (pi->proto->probe_unit) { + pi_claim(pi); + for (pi->unit=s;pi->unit<e;pi->unit++) + if (pi->proto->probe_unit(pi)) { + pi_unclaim(pi); + if (pi_probe_mode(pi,max,scratch,verbose)) return 1; + pi_unregister_parport(pi); + return 0; + } + pi_unclaim(pi); + pi_unregister_parport(pi); + return 0; + } + + if (!pi_probe_mode(pi,max,scratch,verbose)) { + pi_unregister_parport(pi); + return 0; + } + return 1; + +} + +int pi_init(PIA *pi, int autoprobe, int port, int mode, + int unit, int protocol, int delay, char * scratch, + int devtype, int verbose, char *device ) + +{ int p,k,s,e; + int lpts[7] = {0x3bc,0x378,0x278,0x268,0x27c,0x26c,0}; + + s = protocol; e = s+1; + + if (autoprobe) { + s = 0; + e = MAX_PROTOS; + } else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) || + (!protocols[s]) || (unit < 0) || + (unit >= protocols[s]->max_units)) { + printk("%s: Invalid parameters\n",device); + return 0; + } + + for (p=s;p<e;p++) { + if (protocols[p]) { + pi->proto = protocols[p]; + pi->private = 0; + pi->proto->init_proto(pi); + if (delay == -1) pi->delay = pi->proto->default_delay; + else pi->delay = delay; + pi->devtype = devtype; + pi->device = device; + + pi->parname = NULL; + pi->pardev = NULL; + pi->parq = NULL; + pi->claimed = 0; + pi->claim_cont = NULL; + + pi->mode = mode; + if (port != -1) { + pi->port = port; + if (pi_probe_unit(pi,unit,scratch,verbose)) break; + pi->port = 0; + } else { + k = 0; + while ((pi->port = lpts[k++])) + if (pi_probe_unit(pi,unit,scratch,verbose)) break; + if (pi->port) break; + } + pi->proto->release_proto(pi); + } + } + + if (!pi->port) { + if (autoprobe) printk("%s: Autoprobe failed\n",device); + else printk("%s: Adapter not found\n",device); + return 0; + } + + if (!pi->pardev) + request_region(pi->port,pi->reserved,pi->device); + + if (pi->parname) + printk("%s: Sharing %s at 0x%x\n",pi->device, + pi->parname,pi->port); + + pi->proto->log_adapter(pi,scratch,verbose); + + return 1; +} + +#ifdef MODULE + +int init_module(void) + +{ int k; + + for (k=0;k<MAX_PROTOS;k++) protocols[k] = 0; + printk("paride: version %s installed\n",PI_VERSION); + return 0; +} + +void cleanup_module(void) + +{ +} + +#else + +void paride_init( void ) + +{ + +#ifdef CONFIG_PARIDE_ATEN + { extern struct pi_protocol aten; + pi_register(&aten); + }; +#endif +#ifdef CONFIG_PARIDE_BPCK + { extern struct pi_protocol bpck; + pi_register(&bpck); + }; +#endif +#ifdef CONFIG_PARIDE_COMM + { extern struct pi_protocol comm; + pi_register(&comm); + }; +#endif +#ifdef CONFIG_PARIDE_DSTR + { extern struct pi_protocol dstr; + pi_register(&dstr); + }; +#endif +#ifdef CONFIG_PARIDE_EPAT + { extern struct pi_protocol epat; + pi_register(&epat); + }; +#endif +#ifdef CONFIG_PARIDE_EPIA + { extern struct pi_protocol epia; + pi_register(&epia); + }; +#endif +#ifdef CONFIG_PARIDE_FRPW + { extern struct pi_protocol frpw; + pi_register(&frpw); + }; +#endif +#ifdef CONFIG_PARIDE_FIT2 + { extern struct pi_protocol fit2; + pi_register(&fit2); + }; +#endif +#ifdef CONFIG_PARIDE_FIT3 + { extern struct pi_protocol fit3; + pi_register(&fit3); + }; +#endif +#ifdef CONFIG_PARIDE_KBIC + { extern struct pi_protocol k951; + extern struct pi_protocol k971; + pi_register(&k951); + pi_register(&k971); + }; +#endif +#ifdef CONFIG_PARIDE_KTTI + { extern struct pi_protocol ktti; + pi_register(&ktti); + }; +#endif +#ifdef CONFIG_PARIDE_ON20 + { extern struct pi_protocol on20; + pi_register(&on20); + }; +#endif +#ifdef CONFIG_PARIDE_ON26 + { extern struct pi_protocol on26; + pi_register(&on26); + }; +#endif + +#ifdef CONFIG_PARIDE_PD + { extern int pd_init(void); + pd_init(); + }; +#endif +#ifdef CONFIG_PARIDE_PCD + { extern int pcd_init(void); + pcd_init(); + }; +#endif +#ifdef CONFIG_PARIDE_PF + { extern int pf_init(void); + pf_init(); + }; +#endif +#ifdef CONFIG_PARIDE_PT + { extern int pt_init(void); + pt_init(); + }; +#endif +#ifdef CONFIG_PARIDE_PG + { extern int pg_init(void); + pg_init(); + }; +#endif +} + +#endif + +/* end of paride.c */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/paride.h linux/drivers/block/paride/paride.h --- v2.0.34/linux/drivers/block/paride/paride.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/paride.h Mon Jul 13 13:47:28 1998 @@ -0,0 +1,164 @@ +/* + paride.h (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GPL. + + This file defines the interface between the high-level parallel + IDE device drivers (pd, pf, pcd, pt) and the adapter chips. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 init_proto, release_proto +*/ + +#define PARIDE_H_VERSION "1.01" + +/* Some adapters need to know what kind of device they are in + + Values for devtype: +*/ + +#define PI_PD 0 /* IDE disk */ +#define PI_PCD 1 /* ATAPI CDrom */ +#define PI_PF 2 /* ATAPI disk */ +#define PI_PT 3 /* ATAPI tape */ +#define PI_PG 4 /* ATAPI generic */ + +/* The paride module contains no state, instead the drivers allocate + a pi_adapter data structure and pass it to paride in every operation. + +*/ + +struct pi_adapter { + + struct pi_protocol *proto; /* adapter protocol */ + int port; /* base address of parallel port */ + int mode; /* transfer mode in use */ + int delay; /* adapter delay setting */ + int devtype; /* device type: PI_PD etc. */ + char *device; /* name of driver */ + int unit; /* unit number for chained adapters */ + int saved_r0; /* saved port state */ + int saved_r2; /* saved port state */ + int reserved; /* number of ports reserved */ + int private; /* for protocol module */ + + struct wait_queue *parq; /* semaphore for parport sharing */ + void *pardev; /* pointer to pardevice */ + char *parname; /* parport name */ + int claimed; /* parport has already been claimed */ + void (*claim_cont)(void); /* continuation for parport wait */ +}; + +typedef struct pi_adapter PIA; + +/* functions exported by paride to the high level drivers */ + +extern int pi_init(PIA *pi, + int autoprobe, /* 1 to autoprobe */ + int port, /* base port address */ + int mode, /* -1 for autoprobe */ + int unit, /* unit number, if supported */ + int protocol, /* protocol to use */ + int delay, /* -1 to use adapter specific default */ + char * scratch, /* address of 512 byte buffer */ + int devtype, /* device type: PI_PD, PI_PCD, etc ... */ + int verbose, /* log verbose data while probing */ + char *device /* name of the driver */ + ); /* returns 0 on failure, 1 on success */ + +extern void pi_release(PIA *pi); + +/* registers are addressed as (cont,regr) + + cont: 0 for command register file, 1 for control register(s) + regr: 0-7 for register number. + +*/ + +extern void pi_write_regr(PIA *pi, int cont, int regr, int val); + +extern int pi_read_regr(PIA *pi, int cont, int regr); + +extern void pi_write_block(PIA *pi, char * buf, int count); + +extern void pi_read_block(PIA *pi, char * buf, int count); + +extern void pi_connect(PIA *pi); + +extern void pi_disconnect(PIA *pi); + +extern void pi_do_claimed(PIA *pi, void (*cont)(void)); + +/* macros and functions exported to the protocol modules */ + +#define delay_p (pi->delay?udelay(pi->delay):0) +#define out_p(offs,byte) outb(byte,pi->port+offs); delay_p; +#define in_p(offs) (delay_p,inb(pi->port+offs)) + +#define w0(byte) {out_p(0,byte);} +#define r0() (in_p(0) & 0xff) +#define w1(byte) {out_p(1,byte);} +#define r1() (in_p(1) & 0xff) +#define w2(byte) {out_p(2,byte);} +#define r2() (in_p(2) & 0xff) +#define w3(byte) {out_p(3,byte);} +#define w4(byte) {out_p(4,byte);} +#define r4() (in_p(4) & 0xff) +#define w4w(data) {outw(data,pi->port+4); delay_p;} +#define w4l(data) {outl(data,pi->port+4); delay_p;} +#define r4w() (delay_p,inw(pi->port+4)&0xffff) +#define r4l() (delay_p,inl(pi->port+4)&0xffffffff) + +static inline u16 pi_swab16( char *b, int k) + +{ union { u16 u; char t[2]; } r; + + r.t[0]=b[2*k+1]; r.t[1]=b[2*k]; + return r.u; +} + +static inline u32 pi_swab32( char *b, int k) + +{ union { u32 u; char f[4]; } r; + + r.f[0]=b[4*k+1]; r.f[1]=b[4*k]; + r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2]; + return r.u; +} + +struct pi_protocol { + + char name[8]; /* name for this protocol */ + int index; /* index into protocol table */ + + int max_mode; /* max mode number */ + int epp_first; /* modes >= this use 8 ports */ + + int default_delay; /* delay parameter if not specified */ + int max_units; /* max chained units probed for */ + + void (*write_regr)(PIA *,int,int,int); + int (*read_regr)(PIA *,int,int); + void (*write_block)(PIA *,char *,int); + void (*read_block)(PIA *,char *,int); + + void (*connect)(PIA *); + void (*disconnect)(PIA *); + + int (*test_port)(PIA *); + int (*probe_unit)(PIA *); + int (*test_proto)(PIA *,char *,int); + void (*log_adapter)(PIA *,char *,int); + + void (*init_proto)(PIA *); + void (*release_proto)(PIA *); +}; + +typedef struct pi_protocol PIP; + +extern int pi_register( PIP * ); +extern void pi_unregister ( PIP * ); + +/* end of paride.h */ diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c --- v2.0.34/linux/drivers/block/paride/pcd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/pcd.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,807 @@ +/* + pcd.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + + Special 2.0.34 version + + + This is high-level driver for parallel port ATAPI CDrom + drives based on chips supported by the paride module. + + By default, the driver will autoprobe for a single parallel + port ATAPI CDrom drive, but if their individual parameters are + specified, the driver can handle up to 4 drives. + + The behaviour of the pcd driver can be altered by setting + some parameters from the insmod command line. The following + parameters are adjustable: + + drive0 These four arguments can be arrays of + drive1 1-6 integers as follows: + drive2 + drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly> + + Where, + + <prt> is the base of the parallel port address for + the corresponding drive. (required) + + <pro> is the protocol number for the adapter that + supports this drive. These numbers are + logged by 'paride' when the protocol modules + are initialised. (0 if not given) + + <uni> for those adapters that support chained + devices, this is the unit selector for the + chain of devices on the given port. It should + be zero for devices that don't support chaining. + (0 if not given) + + <mod> this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + <slv> ATAPI CDroms can be jumpered to master or slave. + Set this to 0 to choose the master drive, 1 to + choose the slave, -1 (the default) to choose the + first drive found. + + <dly> some parallel ports require the driver to + go more slowly. -1 sets a default value that + should work with the chosen protocol. Otherwise, + set this to a small integer, the larger it is + the slower the port i/o. In some cases, setting + this to zero will speed up the device. (default -1) + + major You may use this parameter to overide the + default major number (46) that this driver + will use. Be sure to change the device + name as well. + + name This parameter is a character string that + contains the name the kernel will use for this + device (in /proc output, for instance). + (default "pcd") + + verbose This parameter controls the amount of logging + that is done while the driver probes for + devices. Set it to 0 for a quiet load, or 1 to + see all the progress messages. (default 0) + + nice This parameter controls the driver's use of + idle CPU time, at the expense of some speed. + + If this driver is built into the kernel, you can use kernel + the following command line parameters, with the same values + as the corresponding module parameters listed above: + + pcd.drive0 + pcd.drive1 + pcd.drive2 + pcd.drive3 + pcd.nice + + In addition, you can use the parameter pcd.disable to disable + the driver entirely. + +*/ + +/* Changes: + + 1.01 GRG 1997.01.24 Added test unit ready support + 1.02 GRG 1998.05.06 Changes to pcd_completion, ready_wait, + and loosen interpretation of ATAPI + standard for clearing error status. + Use spinlocks. Eliminate sti(). + 1.03 GRG 1998.06.16 Eliminated an Ugh + +*/ + +#define PCD_VERSION "1.03s" +#define PCD_MAJOR 46 +#define PCD_NAME "pcd" +#define PCD_UNITS 4 + +/* Here are things one can override from the insmod command. + Most are autoprobed by paride unless set here. Verbose is off + by default. + +*/ + +static int verbose = 0; +static int major = PCD_MAJOR; +static char *name = PCD_NAME; +static int nice = 0; +static int disable = 0; + +static int drive0[6] = {0,0,0,-1,-1,-1}; +static int drive1[6] = {0,0,0,-1,-1,-1}; +static int drive2[6] = {0,0,0,-1,-1,-1}; +static int drive3[6] = {0,0,0,-1,-1,-1}; + +static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3}; +static int pcd_drive_count; + +#define D_PRT 0 +#define D_PRO 1 +#define D_UNI 2 +#define D_MOD 3 +#define D_SLV 4 +#define D_DLY 5 + +#define DU (*drives[unit]) + +/* end of parameters */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/cdrom.h> + +#include <asm/segment.h> +#include "spinlock.h" + +#ifndef MODULE + +#include "setup.h" + +static STT pcd_stt[6] = {{"drive0",6,drive0}, + {"drive1",6,drive1}, + {"drive2",6,drive2}, + {"drive3",6,drive3}, + {"disable",1,&disable}, + {"nice",1,&nice}}; + +void pcd_setup( char *str, int *ints) + +{ generic_setup(pcd_stt,6,str); +} + +#endif + +#include "paride.h" + +/* set up defines for blk.h, why don't all drivers do it this way ? */ + +#define MAJOR_NR major +#define DEVICE_NAME "PCD" +#define DEVICE_REQUEST do_pcd_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include <linux/blk.h> + +#include "pseudo.h" + +#define PCD_RETRIES 5 +#define PCD_TMO 800 /* timeout in jiffies */ +#define PCD_DELAY 50 /* spin delay in uS */ +#define PCD_READY_TMO 20 + +#define PCD_SPIN (10000/PCD_DELAY)*PCD_TMO + +#define IDE_ERR 0x01 +#define IDE_DRQ 0x08 +#define IDE_READY 0x40 +#define IDE_BUSY 0x80 + +int pcd_init(void); +void cleanup_module( void ); + +static int pcd_open(struct inode *inode, struct file *file); +static void do_pcd_request(void); +static void do_pcd_read(void); +static int pcd_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg); + +static void pcd_release (struct inode *inode, struct file *file); + +static int pcd_detect(void); +static void pcd_lock(int unit); +static void pcd_unlock(int unit); +static void pcd_eject(int unit); +static int pcd_check_media(int unit); +static void do_pcd_read_drq(void); + +static int pcd_blocksizes[PCD_UNITS]; + +#define PCD_NAMELEN 8 + +struct pcd_unit { + struct pi_adapter pia; /* interface to paride layer */ + struct pi_adapter *pi; + int drive; /* master/slave */ + int last_sense; /* result of last request sense */ + int access; /* count of active opens */ + int present; /* does this unit exist ? */ + char name[PCD_NAMELEN]; /* pcd0, pcd1, etc */ + }; + +struct pcd_unit pcd[PCD_UNITS]; + +/* 'unit' must be defined in all functions - either as a local or a param */ + +#define PCD pcd[unit] +#define PI PCD.pi + +static char pcd_scratch[64]; +static char pcd_buffer[2048]; /* raw block buffer */ +static int pcd_bufblk = -1; /* block in buffer, in CD units, + -1 for nothing there. See also + pd_unit. + */ + +/* the variables below are used mainly in the I/O request engine, which + processes only one request at a time. +*/ + +static int pcd_unit = -1; /* unit of current request & bufblk */ +static int pcd_retries; /* retries on current request */ +static int pcd_busy = 0; /* request being processed ? */ +static int pcd_sector; /* address of next requested sector */ +static int pcd_count; /* number of blocks still to do */ +static char * pcd_buf; /* buffer for request in progress */ + +/* kernel glue structures */ + +static struct file_operations pcd_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + pcd_ioctl, /* ioctl */ + NULL, /* mmap */ + pcd_open, /* open */ + pcd_release, /* release */ + block_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* media change ? */ + NULL /* revalidate new media */ +}; + +static void pcd_init_units( void ) + +{ int unit, j; + + pcd_drive_count = 0; + for (unit=0;unit<PCD_UNITS;unit++) { + PCD.pi = & PCD.pia; + PCD.access = 0; + PCD.present = 0; + PCD.last_sense = 0; + j = 0; + while ((j < PCD_NAMELEN-2) && (PCD.name[j]=name[j])) j++; + PCD.name[j++] = '0' + unit; + PCD.name[j] = 0; + PCD.drive = DU[D_SLV]; + if (DU[D_PRT]) pcd_drive_count++; + } +} + +int pcd_init (void) /* preliminary initialisation */ + +{ int i; + + if (disable) return -1; + + pcd_init_units(); + + if (pcd_detect()) return -1; + + if (register_blkdev(MAJOR_NR,name,&pcd_fops)) { + printk("pcd: unable to get major number %d\n",MAJOR_NR); + return -1; + } + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ + + for (i=0;i<PCD_UNITS;i++) pcd_blocksizes[i] = 1024; + blksize_size[MAJOR_NR] = pcd_blocksizes; + + return 0; +} + +static int pcd_open (struct inode *inode, struct file *file) + +{ int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV; + + if (file->f_mode & 2) return -EROFS; /* wants to write ? */ + + MOD_INC_USE_COUNT; + + if (pcd_check_media(unit)) { + MOD_DEC_USE_COUNT; + return -ENXIO; + } + + pcd_lock(unit); + + PCD.access++; + return 0; +} + +static void do_pcd_request (void) + +{ int unit; + + if (pcd_busy) return; + while (1) { + if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + if (CURRENT->cmd == READ) { + unit = MINOR(CURRENT->rq_dev); + if (unit != pcd_unit) { + pcd_bufblk = -1; + pcd_unit = unit; + } + pcd_sector = CURRENT->sector; + pcd_count = CURRENT->nr_sectors; + pcd_buf = CURRENT->buffer; + pcd_busy = 1; + ps_set_intr(do_pcd_read,0,0,nice); + return; + } + else end_request(0); + } +} + +static int pcd_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg) + +/* we currently support only the EJECT ioctl. */ + +{ int unit = DEVICE_NR(inode->i_rdev); + if ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV; + + switch (cmd) { + case CDROMEJECT: if (PCD.access == 1) { + pcd_eject(unit); + return 0; + } + default: + return -EINVAL; + } +} + +static void pcd_release (struct inode *inode, struct file *file) + +{ kdev_t devp; + int unit; + + devp = inode->i_rdev; + unit = DEVICE_NR(devp); + + if ((unit >= PCD_UNITS) || (PCD.access <= 0)) + return; + + PCD.access--; + + if (!PCD.access) { + fsync_dev(devp); + + invalidate_inodes(devp); + + invalidate_buffers(devp); + pcd_unlock(unit); + + } + + MOD_DEC_USE_COUNT; + +} + +#ifdef MODULE + +/* Glue for modules ... */ + +int init_module(void) + +{ int err; + + err = pcd_init(); + + return err; +} + +void cleanup_module(void) + +{ int unit; + + unregister_blkdev(MAJOR_NR,name); + + for (unit=0;unit<PCD_UNITS;unit++) + if (PCD.present) pi_release(PI); +} + +#endif + +#define WR(c,r,v) pi_write_regr(PI,c,r,v) +#define RR(c,r) (pi_read_regr(PI,c,r)) + +static int pcd_wait( int unit, int go, int stop, char * fun, char * msg ) + +{ int j, r, e, s, p; + + j = 0; + while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PCD_SPIN)) + udelay(PCD_DELAY); + + if ((r&(IDE_ERR&stop))||(j>=PCD_SPIN)) { + s = RR(0,7); + e = RR(0,1); + p = RR(0,2); + if (j >= PCD_SPIN) e |= 0x100; + if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x" + " loop=%d phase=%d\n", + PCD.name,fun,msg,r,s,e,j,p); + return (s<<8)+r; + } + return 0; +} + +static int pcd_command( int unit, char * cmd, int dlen, char * fun ) + +{ pi_connect(PI); + + WR(0,6,0xa0 + 0x10*PCD.drive); + + if (pcd_wait(unit,IDE_BUSY|IDE_DRQ,0,fun,"before command")) { + pi_disconnect(PI); + return -1; + } + + WR(0,4,dlen % 256); + WR(0,5,dlen / 256); + WR(0,7,0xa0); /* ATAPI packet command */ + + if (pcd_wait(unit,IDE_BUSY,IDE_DRQ,fun,"command DRQ")) { + pi_disconnect(PI); + return -1; + } + + if (RR(0,2) != 1) { + printk("%s: %s: command phase error\n",PCD.name,fun); + pi_disconnect(PI); + return -1; + } + + pi_write_block(PI,cmd,12); + + return 0; +} + +static int pcd_completion( int unit, char * buf, char * fun ) + +{ int r, s, n; + + r = pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,fun,"completion"); + + if ((RR(0,2)&2) && (RR(0,7)&IDE_DRQ)) { + n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc); + pi_read_block(PI,buf,n); + } + + s = pcd_wait(unit,IDE_BUSY,IDE_READY|IDE_ERR,fun,"data done"); + + pi_disconnect(PI); + + return (r?r:s); +} + +static void pcd_req_sense( int unit, int quiet ) + +{ char rs_cmd[12] = { 0x03,0,0,0,16,0,0,0,0,0,0,0 }; + char buf[16]; + int r; + + r = pcd_command(unit,rs_cmd,16,"Request sense"); + udelay(1000); + if (!r) pcd_completion(unit,buf,"Request sense"); + + PCD.last_sense = -1; + if (!r) { + if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n", + PCD.name,buf[2]&0xf,buf[12],buf[13]); + PCD.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8) + | ((buf[13]&0xff)<<16) ; + } +} + +static int pcd_atapi( int unit, char * cmd, int dlen, char * buf, char * fun ) + +{ int r; + + r = pcd_command(unit,cmd,dlen,fun); + udelay(1000); + if (!r) r = pcd_completion(unit,buf,fun); + if (r) pcd_req_sense(unit,!fun); + + return r; +} + +#define DBMSG(msg) NULL + +static void pcd_lock(int unit) + +{ char lo_cmd[12] = { 0x1e,0,0,0,1,0,0,0,0,0,0,0 }; + char cl_cmd[12] = { 0x1b,0,0,0,3,0,0,0,0,0,0,0 }; + + pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd1")); + pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd2")); + pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd3")); + pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd4")); + pcd_atapi(unit,cl_cmd,0,pcd_scratch,"close door"); + + pcd_atapi(unit,lo_cmd,0,pcd_scratch,DBMSG("ld")); + pcd_atapi(unit,lo_cmd,0,pcd_scratch,"lock door"); +} + +static void pcd_unlock( int unit ) + +{ char un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 }; + + pcd_atapi(unit,un_cmd,0,pcd_scratch,"unlock door"); +} + +static void pcd_eject( int unit) + +{ char ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 }; + + pcd_unlock(unit); + pcd_atapi(unit,ej_cmd,0,pcd_scratch,"eject"); +} + +#define PCD_RESET_TMO 30 /* in tenths of a second */ + +static void pcd_sleep( int cs ) + +{ current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + cs; + schedule(); +} + +static int pcd_reset( int unit ) + +/* the ATAPI standard actually specifies the contents of all 7 registers + after a reset, but the specification is ambiguous concerning the last + two bytes, and different drives interpret the standard differently. +*/ + +{ int i, k, flg; + int expect[5] = {1,1,1,0x14,0xeb}; + + pi_connect(PI); + WR(0,6,0xa0 + 0x10*PCD.drive); + WR(0,7,8); + + pcd_sleep(2); /* delay a bit*/ + + k = 0; + while ((k++ < PCD_RESET_TMO) && (RR(1,6)&IDE_BUSY)) + pcd_sleep(10); + + flg = 1; + for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]); + + if (verbose) { + printk("%s: Reset (%d) signature = ",PCD.name,k); + for (i=0;i<5;i++) printk("%3x",RR(0,i+1)); + if (!flg) printk(" (incorrect)"); + printk("\n"); + } + + pi_disconnect(PI); + return flg-1; +} + +static int pcd_ready_wait( int unit, int tmo ) + +{ char tr_cmd[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + int k, p; + + k = 0; + while (k < tmo) { + PCD.last_sense = 0; + pcd_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready")); + p = PCD.last_sense; + if (!p) return 0; + if (!(((p & 0xffff) == 0x0402)||((p & 0xff) == 6))) return p; + k++; + pcd_sleep(100); + } + return 0x000020; /* timeout */ +} + +static int pcd_check_media( int unit ) + +{ char rc_cmd[12] = { 0x25,0,0,0,0,0,0,0,0,0,0,0}; + + pcd_ready_wait(unit,PCD_READY_TMO); + return (pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("check media"))); +} + +static int pcd_identify( int unit, char * id ) + +{ int k, s; + char id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0}; + + pcd_bufblk = -1; + + s = pcd_atapi(unit,id_cmd,36,pcd_buffer,"identify"); + + if (s) return -1; + if ((pcd_buffer[0] & 0x1f) != 5) { + if (verbose) printk("%s: %s is not a CDrom\n", + PCD.name,PCD.drive?"Slave":"Master"); + return -1; + } + for (k=0;k<16;k++) id[k] = pcd_buffer[16+k]; id[16] = 0; + k = 16; while ((k >= 0) && (id[k] <= 0x20)) { id[k] = 0; k--; } + + printk("%s: %s: %s\n",PCD.name,PCD.drive?"Slave":"Master",id); + + return 0; +} + +static int pcd_probe( int unit, int ms, char * id ) + +/* returns 0, with id set if drive is detected + -1, if drive detection failed +*/ + +{ if (ms == -1) { + for (PCD.drive=0;PCD.drive<=1;PCD.drive++) + if (!pcd_reset(unit) && !pcd_identify(unit,id)) + return 0; + } else { + PCD.drive = ms; + if (!pcd_reset(unit) && !pcd_identify(unit,id)) + return 0; + } + return -1; +} + +static int pcd_detect( void ) + +{ char id[18]; + int k, unit; + + printk("%s: %s version %s, major %d, nice %d\n", + name,name,PCD_VERSION,major,nice); + + k = 0; + if (pcd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */ + unit = 0; + if (pi_init(PI,1,-1,-1,-1,-1,-1,pcd_buffer, + PI_PCD,verbose,PCD.name)) { + if (!pcd_probe(unit,-1,id)) { + PCD.present = 1; + k++; + } else pi_release(PI); + } + + } else for (unit=0;unit<PCD_UNITS;unit++) if (DU[D_PRT]) + if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI], + DU[D_PRO],DU[D_DLY],pcd_buffer,PI_PCD,verbose, + PCD.name)) { + if (!pcd_probe(unit,DU[D_SLV],id)) { + PCD.present = 1; + k++; + } else pi_release(PI); + } + + if (k) return 0; + + printk("%s: No CDrom drive found\n",name); + return -1; +} + +/* I/O request processing */ + +static int pcd_ready( void ) + +{ int unit = pcd_unit; + + return (((RR(1,6)&(IDE_BUSY|IDE_DRQ))==IDE_DRQ)) ; +} + +static void pcd_transfer( void ) + +{ int k, o; + + while (pcd_count && (pcd_sector/4 == pcd_bufblk)) { + o = (pcd_sector % 4) * 512; + for(k=0;k<512;k++) pcd_buf[k] = pcd_buffer[o+k]; + pcd_count--; + pcd_buf += 512; + pcd_sector++; + } +} + +static void pcd_start( void ) + +{ int unit = pcd_unit; + int b, i; + char rd_cmd[12] = {0xa8,0,0,0,0,0,0,0,0,1,0,0}; + long saved_flags; + + pcd_bufblk = pcd_sector / 4; + b = pcd_bufblk; + for(i=0;i<4;i++) { + rd_cmd[5-i] = b & 0xff; + b = b >> 8; + } + + if (pcd_command(unit,rd_cmd,2048,"read block")) { + pcd_bufblk = -1; + spin_lock_irqsave(&io_request_lock,saved_flags); + pcd_busy = 0; + end_request(0); + do_pcd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + + udelay(1000); + + ps_set_intr(do_pcd_read_drq,pcd_ready,PCD_TMO,nice); + +} + +static void do_pcd_read( void ) + + +{ int unit = pcd_unit; + long saved_flags; + + pcd_busy = 1; + pcd_retries = 0; + pcd_transfer(); + if (!pcd_count) { + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pcd_busy = 0; + do_pcd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + + pi_do_claimed(PI,pcd_start); +} + +static void do_pcd_read_drq( void ) + +{ int unit = pcd_unit; + long saved_flags; + + if (pcd_completion(unit,pcd_buffer,"read block")) { + if (pcd_retries < PCD_RETRIES) { + udelay(1000); + pcd_retries++; + pi_do_claimed(PI,pcd_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + pcd_busy = 0; + pcd_bufblk = -1; + end_request(0); + do_pcd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + + do_pcd_read(); + spin_lock_irqsave(&io_request_lock,saved_flags); + do_pcd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +/* end of pcd.c */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c --- v2.0.34/linux/drivers/block/paride/pd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/pd.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,1107 @@ +/* + pd.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + + Special 2.0.34 version. + + + + This is the high-level driver for parallel port IDE hard + drives based on chips supported by the paride module. + + By default, the driver will autoprobe for a single parallel + port IDE drive, but if their individual parameters are + specified, the driver can handle up to 4 drives. + + The behaviour of the pd driver can be altered by setting + some parameters from the insmod command line. The following + parameters are adjustable: + + drive0 These four arguments can be arrays of + drive1 1-8 integers as follows: + drive2 + drive3 <prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<dly>,<slv> + + Where, + + <prt> is the base of the parallel port address for + the corresponding drive. (required) + + <pro> is the protocol number for the adapter that + supports this drive. These numbers are + logged by 'paride' when the protocol modules + are initialised. (0 if not given) + + <uni> for those adapters that support chained + devices, this is the unit selector for the + chain of devices on the given port. It should + be zero for devices that don't support chaining. + (0 if not given) + + <mod> this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + <geo> this defaults to 0 to indicate that the driver + should use the CHS geometry provided by the drive + itself. If set to 1, the driver will provide + a logical geometry with 64 heads and 32 sectors + per track, to be consistent with most SCSI + drivers. (0 if not given) + + <sby> set this to zero to disable the power saving + standby mode, if needed. (1 if not given) + + <dly> some parallel ports require the driver to + go more slowly. -1 sets a default value that + should work with the chosen protocol. Otherwise, + set this to a small integer, the larger it is + the slower the port i/o. In some cases, setting + this to zero will speed up the device. (default -1) + + <slv> IDE disks can be jumpered to master or slave. + Set this to 0 to choose the master drive, 1 to + choose the slave, -1 (the default) to choose the + first drive found. + + + major You may use this parameter to overide the + default major number (45) that this driver + will use. Be sure to change the device + name as well. + + name This parameter is a character string that + contains the name the kernel will use for this + device (in /proc output, for instance). + (default "pd") + + cluster The driver will attempt to aggregate requests + for adjacent blocks into larger multi-block + clusters. The maximum cluster size (in 512 + byte sectors) is set with this parameter. + (default 64) + + verbose This parameter controls the amount of logging + that is done while the driver probes for + devices. Set it to 0 for a quiet load, or to 1 + see all the progress messages. (default 0) + + nice This parameter controls the driver's use of + idle CPU time, at the expense of some speed. + + If this driver is built into the kernel, you can use kernel + the following command line parameters, with the same values + as the corresponding module parameters listed above: + + pd.drive0 + pd.drive1 + pd.drive2 + pd.drive3 + pd.cluster + pd.nice + + In addition, you can use the parameter pd.disable to disable + the driver entirely. + +*/ + +/* Changes: + + 1.01 GRG 1997.01.24 Restored pd_reset() + Added eject ioctl + 1.02 GRG 1998.05.06 SMP spinlock changes, + Added slave support + 1.03 GRG 1998.06.16 Eliminate an Ugh. + +*/ + +#define PD_VERSION "1.03s" +#define PD_MAJOR 45 +#define PD_NAME "pd" +#define PD_UNITS 4 + +/* Here are things one can override from the insmod command. + Most are autoprobed by paride unless set here. Verbose is off + by default. + +*/ + +static int verbose = 0; +static int major = PD_MAJOR; +static char *name = PD_NAME; +static int cluster = 64; +static int nice = 0; +static int disable = 0; + +static int drive0[8] = {0,0,0,-1,0,1,-1,-1}; +static int drive1[8] = {0,0,0,-1,0,1,-1,-1}; +static int drive2[8] = {0,0,0,-1,0,1,-1,-1}; +static int drive3[8] = {0,0,0,-1,0,1,-1,-1}; + +static int (*drives[4])[8] = {&drive0,&drive1,&drive2,&drive3}; +static int pd_drive_count; + +#define D_PRT 0 +#define D_PRO 1 +#define D_UNI 2 +#define D_MOD 3 +#define D_GEO 4 +#define D_SBY 5 +#define D_DLY 6 +#define D_SLV 7 + +#define DU (*drives[unit]) + +/* end of parameters */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/genhd.h> +#include <linux/hdreg.h> +#include <linux/cdrom.h> /* for the eject ioctl */ + +#include "spinlock.h" +#include <asm/segment.h> + +#ifndef MODULE + +#include "setup.h" + +static STT pd_stt[7] = {{"drive0",8,drive0}, + {"drive1",8,drive1}, + {"drive2",8,drive2}, + {"drive3",8,drive3}, + {"disable",1,&disable}, + {"cluster",1,&cluster}, + {"nice",1,&nice}}; + +void pd_setup( char *str, int *ints) + +{ generic_setup(pd_stt,7,str); +} + +#endif + +#include "paride.h" + +#define PD_BITS 4 + +/* set up defines for blk.h, why don't all drivers do it this way ? */ + +#define MAJOR_NR major +#define DEVICE_NAME "PD" +#define DEVICE_REQUEST do_pd_request +#define DEVICE_NR(device) (MINOR(device)>>PD_BITS) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include <linux/blk.h> + +#include "pseudo.h" + +#define PD_PARTNS (1<<PD_BITS) +#define PD_DEVS PD_PARTNS*PD_UNITS + +/* numbers for "SCSI" geometry */ + +#define PD_LOG_HEADS 64 +#define PD_LOG_SECTS 32 + +#define PD_ID_OFF 54 +#define PD_ID_LEN 14 + +#define PD_MAX_RETRIES 5 +#define PD_TMO 800 /* interrupt timeout in jiffies */ +#define PD_SPIN_DEL 50 /* spin delay in micro-seconds */ + +#define PD_SPIN (10000/PD_SPIN_DEL)*PD_TMO + +#define STAT_ERR 0x00001 +#define STAT_INDEX 0x00002 +#define STAT_ECC 0x00004 +#define STAT_DRQ 0x00008 +#define STAT_SEEK 0x00010 +#define STAT_WRERR 0x00020 +#define STAT_READY 0x00040 +#define STAT_BUSY 0x00080 + +#define ERR_AMNF 0x00100 +#define ERR_TK0NF 0x00200 +#define ERR_ABRT 0x00400 +#define ERR_MCR 0x00800 +#define ERR_IDNF 0x01000 +#define ERR_MC 0x02000 +#define ERR_UNC 0x04000 +#define ERR_TMO 0x10000 + +#define IDE_READ 0x20 +#define IDE_WRITE 0x30 +#define IDE_READ_VRFY 0x40 +#define IDE_INIT_DEV_PARMS 0x91 +#define IDE_STANDBY 0x96 +#define IDE_ACKCHANGE 0xdb +#define IDE_DOORLOCK 0xde +#define IDE_DOORUNLOCK 0xdf +#define IDE_IDENTIFY 0xec +#define IDE_EJECT 0xed + +int pd_init(void); +void pd_setup(char * str, int * ints); +#ifdef MODULE +void cleanup_module( void ); +#endif +static void pd_geninit(struct gendisk *ignored); +static int pd_open(struct inode *inode, struct file *file); +static void do_pd_request(void); +static int pd_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg); +static void pd_release (struct inode *inode, struct file *file); +static int pd_revalidate(kdev_t dev); +static int pd_detect(void); +static void do_pd_read(void); +static void do_pd_read_start(void); +static void do_pd_write(void); +static void do_pd_write_start(void); +static void do_pd_read_drq( void ); +static void do_pd_write_done( void ); + +static int pd_identify (int unit); +static void pd_media_check(int unit); +static void pd_doorlock(int unit, int func); +static int pd_check_media(kdev_t dev); +static void pd_eject( int unit); + +static struct hd_struct pd_hd[PD_DEVS]; +static int pd_sizes[PD_DEVS]; +static int pd_blocksizes[PD_DEVS]; + +#define PD_NAMELEN 8 + +struct pd_unit { + struct pi_adapter pia; /* interface to paride layer */ + struct pi_adapter *pi; + int access; /* count of active opens ... */ + int capacity; /* Size of this volume in sectors */ + int heads; /* physical geometry */ + int sectors; + int cylinders; + int drive; /* master=0 slave=1 */ + int changed; /* Have we seen a disk change ? */ + int removable; /* removable media device ? */ + int standby; + int alt_geom; + int present; + char name[PD_NAMELEN]; /* pda, pdb, etc ... */ + }; + +struct pd_unit pd[PD_UNITS]; + +/* 'unit' must be defined in all functions - either as a local or a param */ + +#define PD pd[unit] +#define PI PD.pi + +static int pd_valid = 1; /* serialise partition checks */ +static char pd_scratch[512]; /* scratch block buffer */ + +/* the variables below are used mainly in the I/O request engine, which + processes only one request at a time. +*/ + +static int pd_retries = 0; /* i/o error retry count */ +static int pd_busy = 0; /* request being processed ? */ +static int pd_block; /* address of next requested block */ +static int pd_count; /* number of blocks still to do */ +static int pd_run; /* sectors in current cluster */ +static int pd_cmd; /* current command READ/WRITE */ +static int pd_unit; /* unit of current request */ +static int pd_dev; /* minor of current request */ +static int pd_poffs; /* partition offset of current minor */ +static char * pd_buf; /* buffer for request in progress */ + +static struct wait_queue *pd_wait_open = NULL; + +static char *pd_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR", + "READY","BUSY","AMNF","TK0NF","ABRT","MCR", + "IDNF","MC","UNC","???","TMO"}; + +/* kernel glue structures */ + +static struct gendisk pd_gendisk = { + PD_MAJOR, /* Major number */ + PD_NAME, /* Major name */ + PD_BITS, /* Bits to shift to get real from partition */ + PD_PARTNS, /* Number of partitions per real */ + PD_UNITS, /* maximum number of real */ + pd_geninit, /* init function */ + pd_hd, /* hd struct */ + pd_sizes, /* block sizes */ + 0, /* number */ + NULL, /* internal */ + NULL /* next */ +}; + +static struct file_operations pd_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + pd_ioctl, /* ioctl */ + NULL, /* mmap */ + pd_open, /* open */ + pd_release, /* release */ + block_fsync, /* fsync */ + NULL, /* fasync */ + pd_check_media, /* media change ? */ + pd_revalidate /* revalidate new media */ +}; + +void pd_init_units( void ) + +{ int unit, j; + + pd_drive_count = 0; + for (unit=0;unit<PD_UNITS;unit++) { + PD.pi = & PD.pia; + PD.access = 0; + PD.changed = 1; + PD.capacity = 0; + PD.drive = DU[D_SLV]; + PD.present = 0; + j = 0; + while ((j < PD_NAMELEN-2) && (PD.name[j]=name[j])) j++; + PD.name[j++] = 'a' + unit; + PD.name[j] = 0; + PD.alt_geom = DU[D_GEO]; + PD.standby = DU[D_SBY]; + if (DU[D_PRT]) pd_drive_count++; + } +} + +int pd_init (void) + +{ int i; + + if (disable) return -1; + + if (register_blkdev(MAJOR_NR,name,&pd_fops)) { + printk("%s: unable to get major number %d\n", + name,major); + return -1; + } + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ + + pd_gendisk.major = major; + pd_gendisk.major_name = name; + pd_gendisk.next = gendisk_head; + gendisk_head = &pd_gendisk; + + for(i=0;i<PD_DEVS;i++) pd_blocksizes[i] = 1024; + blksize_size[MAJOR_NR] = pd_blocksizes; + + printk("%s: %s version %s, major %d, cluster %d, nice %d\n", + name,name,PD_VERSION,major,cluster,nice); + + return 0; +} + +static void pd_geninit (struct gendisk *ignored) + +{ pd_init_units(); + pd_gendisk.nr_real = pd_detect(); + +#ifdef MODULE + if (!pd_gendisk.nr_real) cleanup_module(); +#endif + +} + +static int pd_open (struct inode *inode, struct file *file) + +{ int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV; + + MOD_INC_USE_COUNT; + + while (!pd_valid) sleep_on(&pd_wait_open); + + PD.access++; + + if (PD.removable) { + pd_media_check(unit); + pd_doorlock(unit,IDE_DOORLOCK); + } + return 0; +} + +static int pd_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg) + +{ struct hd_geometry *geo = (struct hd_geometry *) arg; + int dev, err, unit; + + if ((!inode) || (!inode->i_rdev)) return -EINVAL; + dev = MINOR(inode->i_rdev); + unit = DEVICE_NR(inode->i_rdev); + if (dev >= PD_DEVS) return -EINVAL; + if (!PD.present) return -ENODEV; + + switch (cmd) { + case CDROMEJECT: + if (PD.access == 1) pd_eject(unit); + return 0; + case HDIO_GETGEO: + if (!geo) return -EINVAL; + err = verify_area(VERIFY_WRITE,geo,sizeof(*geo)); + if (err) return err; + + if (PD.alt_geom) { + put_user(PD.capacity/(PD_LOG_HEADS*PD_LOG_SECTS), + (short *) &geo->cylinders); + put_user(PD_LOG_HEADS, (char *) &geo->heads); + put_user(PD_LOG_SECTS, (char *) &geo->sectors); + } else { + put_user(PD.cylinders, (short *) &geo->cylinders); + put_user(PD.heads, (char *) &geo->heads); + put_user(PD.sectors, (char *) &geo->sectors); + } + put_user(pd_hd[dev].start_sect,(long *)&geo->start); + return 0; + case BLKRASET: + if(!suser()) return -EACCES; + if(!(inode->i_rdev)) return -EINVAL; + if(arg > 0xff) return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long)); + if (err) return (err); + put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg); + return (0); + case BLKGETSIZE: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long)); + if (err) return (err); + put_user(pd_hd[dev].nr_sects,(long *) arg); + return (0); + case BLKFLSBUF: + if(!suser()) return -EACCES; + if(!(inode->i_rdev)) return -EINVAL; + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + case BLKRRPART: + if (!suser()) return -EACCES; + return pd_revalidate(inode->i_rdev); + RO_IOCTLS(inode->i_rdev,arg); + default: + return -EINVAL; + } +} + +static void pd_release (struct inode *inode, struct file *file) + +{ kdev_t devp; + int unit; + + devp = inode->i_rdev; + unit = DEVICE_NR(devp); + + if ((unit >= PD_UNITS) || (PD.access <= 0)) + return; + + PD.access--; + + if (!PD.access) { + fsync_dev(devp); + + invalidate_inodes(devp); + + invalidate_buffers(devp); + if (PD.removable) pd_doorlock(unit,IDE_DOORUNLOCK); + } + + MOD_DEC_USE_COUNT; + +} + +static int pd_check_media( kdev_t dev) + +{ int r, unit; + + unit = DEVICE_NR(dev); + if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV; + if (!PD.removable) return 0; + pd_media_check(unit); + r = PD.changed; + PD.changed = 0; + return r; +} + +static int pd_revalidate(kdev_t dev) + +{ int p, unit, minor; + long flags; + kdev_t devp; + + unit = DEVICE_NR(dev); + if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV; + + save_flags(flags); + cli(); + if (PD.access > 1) { + restore_flags(flags); + return -EBUSY; + } + pd_valid = 0; + restore_flags(flags); + + for (p=(PD_PARTNS-1);p>=0;p--) { + minor = p + unit*PD_PARTNS; + devp = MKDEV(MAJOR_NR, minor); + fsync_dev(devp); + + invalidate_inodes(devp); + + invalidate_buffers(devp); + pd_hd[minor].start_sect = 0; + pd_hd[minor].nr_sects = 0; + } + + pd_identify(unit); + resetup_one_dev(&pd_gendisk,unit); + + pd_valid = 1; + wake_up(&pd_wait_open); + + return 0; +} + +#ifdef MODULE + +/* Glue for modules ... */ + +void cleanup_module(void); + +int init_module(void) + +{ int err, unit; + + err = pd_init(); + if (err) return err; + + pd_geninit(&pd_gendisk); + + if (!pd_gendisk.nr_real) return -1; + + pd_valid = 0; + for (unit=0;unit<PD_UNITS;unit++) + if (PD.present) resetup_one_dev(&pd_gendisk,unit); + pd_valid = 1; + + return 0; +} + +void cleanup_module(void) + +{ struct gendisk **gdp; + int unit; + + unregister_blkdev(MAJOR_NR,name); + + for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next)) + if (*gdp == &pd_gendisk) break; + if (*gdp) *gdp = (*gdp)->next; + + for (unit=0;unit<PD_UNITS;unit++) + if (PD.present) pi_release(PI); +} + +#endif + +#define WR(c,r,v) pi_write_regr(PI,c,r,v) +#define RR(c,r) (pi_read_regr(PI,c,r)) + +#define DRIVE (0xa0+0x10*PD.drive) + +/* ide command interface */ + +static void pd_print_error( int unit, char * msg, int status ) + +{ int i; + + printk("%s: %s: status = 0x%x =",PD.name,msg,status); + for(i=0;i<18;i++) if (status & (1<<i)) printk(" %s",pd_errs[i]); + printk("\n"); +} + +static void pd_reset( int unit ) /* called only for MASTER drive */ + +{ pi_connect(PI); + WR(1,6,4); + udelay(50); + WR(1,6,0); + pi_disconnect(PI); + udelay(250); +} + +#define DBMSG(msg) NULL + +static int pd_wait_for( int unit, int w, char * msg ) /* polled wait */ + +{ int k, r, e; + + k=0; + while(k < PD_SPIN) { + r = RR(1,6); + k++; + if (((r & w) == w) && !(r & STAT_BUSY)) break; + udelay(PD_SPIN_DEL); + } + e = (RR(0,1)<<8) + RR(0,7); + if (k >= PD_SPIN) e |= ERR_TMO; + if ((e & (STAT_ERR|ERR_TMO)) && (msg != NULL)) + pd_print_error(unit,msg,e); + return e; +} + +static void pd_send_command( int unit, int n, int s, int h, + int c0, int c1, int func ) + +{ + WR(0,6,DRIVE+h); + WR(0,1,0); /* the IDE task file */ + WR(0,2,n); + WR(0,3,s); + WR(0,4,c0); + WR(0,5,c1); + WR(0,7,func); + + udelay(1); +} + +static void pd_ide_command( int unit, int func, int block, int count ) + +/* Don't use this call if the capacity is zero. */ + +{ int c1, c0, h, s; + + s = ( block % PD.sectors) + 1; + h = ( block / PD.sectors) % PD.heads; + c0 = ( block / (PD.sectors*PD.heads)) % 256; + c1 = ( block / (PD.sectors*PD.heads*256)); + + pd_send_command(unit,count,s,h,c0,c1,func); +} + +/* According to the ATA standard, the default CHS geometry should be + available following a reset. Some Western Digital drives come up + in a mode where only LBA addresses are accepted until the device + parameters are initialised. +*/ + +static void pd_init_dev_parms( int unit ) + +{ pi_connect(PI); + pd_wait_for(unit,0,DBMSG("before init_dev_parms")); + pd_send_command(unit,PD.sectors,0,PD.heads-1,0,0,IDE_INIT_DEV_PARMS); + udelay(300); + pd_wait_for(unit,0,"Initialise device parameters"); + pi_disconnect(PI); +} + +static void pd_doorlock( int unit, int func ) + +{ pi_connect(PI); + if (pd_wait_for(unit,STAT_READY,"Lock") & STAT_ERR) { + pi_disconnect(PI); + return; + } + pd_send_command(unit,1,0,0,0,0,func); + pd_wait_for(unit,STAT_READY,"Lock done"); + pi_disconnect(PI); +} + +static void pd_eject( int unit ) + +{ pi_connect(PI); + pd_wait_for(unit,0,DBMSG("before unlock on eject")); + pd_send_command(unit,1,0,0,0,0,IDE_DOORUNLOCK); + pd_wait_for(unit,0,DBMSG("after unlock on eject")); + pd_wait_for(unit,0,DBMSG("before eject")); + pd_send_command(unit,0,0,0,0,0,IDE_EJECT); + pd_wait_for(unit,0,DBMSG("after eject")); + pi_disconnect(PI); +} + +static void pd_media_check( int unit ) + +{ int r; + + pi_connect(PI); + r = pd_wait_for(unit,STAT_READY,DBMSG("before media_check")); + if (!(r & STAT_ERR)) { + pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY); + r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after READ_VRFY")); + } else PD.changed = 1; /* say changed if other error */ + if (r & ERR_MC) { + PD.changed = 1; + pd_send_command(unit,1,0,0,0,0,IDE_ACKCHANGE); + pd_wait_for(unit,STAT_READY,DBMSG("RDY after ACKCHANGE")); + pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY); + r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after VRFY")); + } + pi_disconnect(PI); + +} + +static void pd_standby_off( int unit ) + +{ pi_connect(PI); + pd_wait_for(unit,0,DBMSG("before STANDBY")); + pd_send_command(unit,0,0,0,0,0,IDE_STANDBY); + pd_wait_for(unit,0,DBMSG("after STANDBY")); + pi_disconnect(PI); +} + +#define word_val(n) ((pd_scratch[2*n]&0xff)+256*(pd_scratch[2*n+1]&0xff)) + +static int pd_identify( int unit ) + +{ int j; + char id[PD_ID_LEN+1]; + +/* WARNING: here there may be dragons. reset() applies to both drives, + but we call it only on probing the MASTER. This should allow most + common configurations to work, but be warned that a reset can clear + settings on the SLAVE drive. +*/ + + if (PD.drive == 0) pd_reset(unit); + + pi_connect(PI); + WR(0,6,DRIVE); + pd_wait_for(unit,0,DBMSG("before IDENT")); + pd_send_command(unit,1,0,0,0,0,IDE_IDENTIFY); + + if (pd_wait_for(unit,STAT_DRQ,DBMSG("IDENT DRQ")) & STAT_ERR) { + pi_disconnect(PI); + return 0; + } + pi_read_block(PI,pd_scratch,512); + pi_disconnect(PI); + PD.sectors = word_val(6); + PD.heads = word_val(3); + PD.cylinders = word_val(1); + PD.capacity = PD.sectors*PD.heads*PD.cylinders; + + for(j=0;j<PD_ID_LEN;j++) id[j^1] = pd_scratch[j+PD_ID_OFF]; + j = PD_ID_LEN-1; + while ((j >= 0) && (id[j] <= 0x20)) j--; + j++; id[j] = 0; + + PD.removable = (word_val(0) & 0x80); + + printk("%s: %s, %s, %d blocks [%dM], (%d/%d/%d), %s media\n", + PD.name,id, + PD.drive?"slave":"master", + PD.capacity,PD.capacity/2048, + PD.cylinders,PD.heads,PD.sectors, + PD.removable?"removable":"fixed"); + + if (PD.capacity) pd_init_dev_parms(unit); + if (!PD.standby) pd_standby_off(unit); + + pd_hd[unit<<PD_BITS].nr_sects = PD.capacity; + pd_hd[unit<<PD_BITS].start_sect = 0; + + return 1; +} + +static int pd_probe_drive( int unit ) + +{ if (PD.drive == -1) { + for (PD.drive=0;PD.drive<=1;PD.drive++) + if (pd_identify(unit)) return 1; + return 0; + } + else return pd_identify(unit); +} + +static int pd_detect( void ) + +{ int k, unit; + + k = 0; + if (pd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */ + unit = 0; + if (pi_init(PI,1,-1,-1,-1,-1,-1,pd_scratch, + PI_PD,verbose,PD.name)) { + if (pd_probe_drive(unit)) { + PD.present = 1; + k = 1; + } else pi_release(PI); + } + + } else for (unit=0;unit<PD_UNITS;unit++) if (DU[D_PRT]) + if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI], + DU[D_PRO],DU[D_DLY],pd_scratch, + PI_PD,verbose,PD.name)) { + if (pd_probe_drive(unit)) { + PD.present = 1; + k = unit+1; + } else pi_release(PI); + } + +/* We lie about the number of drives found, as the generic partition + scanner assumes that the drives are numbered sequentially from 0. + This can result in some bogus error messages if non-sequential + drive numbers are used. +*/ + + if (k) return k; + + printk("%s: no valid drive found\n",name); + return 0; +} + +/* The i/o request engine */ + +static int pd_ready( void ) + +{ int unit = pd_unit; + + return (!(RR(1,6) & STAT_BUSY)) ; +} + +static void do_pd_request (void) + +{ struct buffer_head * bh; + struct request * req; + int unit; + + if (pd_busy) return; +repeat: + if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + + pd_dev = MINOR(CURRENT->rq_dev); + pd_unit = unit = DEVICE_NR(CURRENT->rq_dev); + pd_block = CURRENT->sector; + pd_count = CURRENT->nr_sectors; + + bh = CURRENT->bh; + req = CURRENT; + if (bh->b_reqnext) + printk("%s: OUCH: b_reqnext != NULL\n",PD.name); + + if ((pd_dev >= PD_DEVS) || + ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) { + end_request(0); + goto repeat; + } + + pd_cmd = CURRENT->cmd; + pd_run = pd_count; + while ((pd_run <= cluster) && + (req = req->next) && + (pd_block+pd_run == req->sector) && + (pd_cmd == req->cmd) && + (pd_dev == MINOR(req->rq_dev))) + pd_run += req->nr_sectors; + + pd_poffs = pd_hd[pd_dev].start_sect; + pd_block += pd_poffs; + pd_buf = CURRENT->buffer; + pd_retries = 0; + + pd_busy = 1; + if (pd_cmd == READ) pi_do_claimed(PI,do_pd_read); + else if (pd_cmd == WRITE) pi_do_claimed(PI,do_pd_write); + else { pd_busy = 0; + end_request(0); + goto repeat; + } +} + +static void pd_next_buf( int unit ) + +{ long saved_flags; + + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + if (!pd_run) { spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + +/* paranoia */ + + if ((!CURRENT) || + (CURRENT->cmd != pd_cmd) || + (MINOR(CURRENT->rq_dev) != pd_dev) || + (CURRENT->rq_status == RQ_INACTIVE) || + (CURRENT->sector+pd_poffs != pd_block)) + printk("%s: OUCH: request list changed unexpectedly\n", + PD.name); + + pd_count = CURRENT->nr_sectors; + pd_buf = CURRENT->buffer; + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +static void do_pd_read( void ) + +{ ps_set_intr(do_pd_read_start,0,0,nice); +} + +static void do_pd_read_start( void ) + +{ int unit = pd_unit; + long saved_flags; + + pd_busy = 1; + + pi_connect(PI); + if (pd_wait_for(unit,STAT_READY,"do_pd_read") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_read_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pd_ide_command(unit,IDE_READ,pd_block,pd_run); + ps_set_intr(do_pd_read_drq,pd_ready,PD_TMO,nice); +} + +static void do_pd_read_drq( void ) + +{ int unit = pd_unit; + long saved_flags; + + while (1) { + if (pd_wait_for(unit,STAT_DRQ,"do_pd_read_drq") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_read_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_read_block(PI,pd_buf,512); + pd_count--; pd_run--; + pd_buf += 512; + pd_block++; + if (!pd_run) break; + if (!pd_count) pd_next_buf(unit); + } + pi_disconnect(PI); + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +static void do_pd_write( void ) + +{ ps_set_intr(do_pd_write_start,0,0,nice); +} + +static void do_pd_write_start( void ) + +{ int unit = pd_unit; + long saved_flags; + + pd_busy = 1; + + pi_connect(PI); + if (pd_wait_for(unit,STAT_READY,"do_pd_write") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pd_ide_command(unit,IDE_WRITE,pd_block,pd_run); + while (1) { + if (pd_wait_for(unit,STAT_DRQ,"do_pd_write_drq") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_write_block(PI,pd_buf,512); + pd_count--; pd_run--; + pd_buf += 512; + pd_block++; + if (!pd_run) break; + if (!pd_count) pd_next_buf(unit); + } + ps_set_intr(do_pd_write_done,pd_ready,PD_TMO,nice); +} + +static void do_pd_write_done( void ) + +{ int unit = pd_unit; + long saved_flags; + + if (pd_wait_for(unit,STAT_READY,"do_pd_write_done") & STAT_ERR) { + pi_disconnect(PI); + if (pd_retries < PD_MAX_RETRIES) { + pd_retries++; + pi_do_claimed(PI,do_pd_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_disconnect(PI); + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pd_busy = 0; + do_pd_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +/* end of pd.c */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c --- v2.0.34/linux/drivers/block/paride/pf.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/pf.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,1080 @@ +/* + pf.c (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + + Special 2.0.34 version + + + This is the high-level driver for parallel port ATAPI disk + drives based on chips supported by the paride module. + + By default, the driver will autoprobe for a single parallel + port ATAPI disk drive, but if their individual parameters are + specified, the driver can handle up to 4 drives. + + The behaviour of the pf driver can be altered by setting + some parameters from the insmod command line. The following + parameters are adjustable: + + drive0 These four arguments can be arrays of + drive1 1-7 integers as follows: + drive2 + drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<lun>,<dly> + + Where, + + <prt> is the base of the parallel port address for + the corresponding drive. (required) + + <pro> is the protocol number for the adapter that + supports this drive. These numbers are + logged by 'paride' when the protocol modules + are initialised. (0 if not given) + + <uni> for those adapters that support chained + devices, this is the unit selector for the + chain of devices on the given port. It should + be zero for devices that don't support chaining. + (0 if not given) + + <mod> this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + <slv> ATAPI CDroms can be jumpered to master or slave. + Set this to 0 to choose the master drive, 1 to + choose the slave, -1 (the default) to choose the + first drive found. + + <lun> Some ATAPI devices support multiple LUNs. + One example is the ATAPI PD/CD drive from + Matshita/Panasonic. This device has a + CD drive on LUN 0 and a PD drive on LUN 1. + By default, the driver will search for the + first LUN with a supported device. Set + this parameter to force it to use a specific + LUN. (default -1) + + <dly> some parallel ports require the driver to + go more slowly. -1 sets a default value that + should work with the chosen protocol. Otherwise, + set this to a small integer, the larger it is + the slower the port i/o. In some cases, setting + this to zero will speed up the device. (default -1) + + major You may use this parameter to overide the + default major number (47) that this driver + will use. Be sure to change the device + name as well. + + name This parameter is a character string that + contains the name the kernel will use for this + device (in /proc output, for instance). + (default "pf"). + + cluster The driver will attempt to aggregate requests + for adjacent blocks into larger multi-block + clusters. The maximum cluster size (in 512 + byte sectors) is set with this parameter. + (default 64) + + verbose This parameter controls the amount of logging + that is done while the driver probes for + devices. Set it to 0 for a quiet load, or 1 to + see all the progress messages. (default 0) + + nice This parameter controls the driver's use of + idle CPU time, at the expense of some speed. + + If this driver is built into the kernel, you can use the + following command line parameters, with the same values + as the corresponding module parameters listed above: + + pf.drive0 + pf.drive1 + pf.drive2 + pf.drive3 + pf.cluster + pf.nice + + In addition, you can use the parameter pf.disable to disable + the driver entirely. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.03 Changes for SMP. Eliminate sti(). + Fix for drives that don't clear STAT_ERR + until after next CDB delivered. + Small change in pf_completion to round + up transfer size. + 1.02 GRG 1998.06.16 Eliminated an Ugh + +*/ + +#define PF_VERSION "1.02s" +#define PF_MAJOR 47 +#define PF_NAME "pf" +#define PF_UNITS 4 + +/* Here are things one can override from the insmod command. + Most are autoprobed by paride unless set here. Verbose is off + by default. + +*/ + +static int verbose = 0; +static int major = PF_MAJOR; +static char *name = PF_NAME; +static int cluster = 64; +static int nice = 0; +static int disable = 0; + +static int drive0[7] = {0,0,0,-1,-1,-1,-1}; +static int drive1[7] = {0,0,0,-1,-1,-1,-1}; +static int drive2[7] = {0,0,0,-1,-1,-1,-1}; +static int drive3[7] = {0,0,0,-1,-1,-1,-1}; + +static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3}; +static int pf_drive_count; + +#define D_PRT 0 +#define D_PRO 1 +#define D_UNI 2 +#define D_MOD 3 +#define D_SLV 4 +#define D_LUN 5 +#define D_DLY 6 + +#define DU (*drives[unit]) + +/* end of parameters */ + + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/genhd.h> +#include <linux/hdreg.h> +#include <linux/cdrom.h> +#include "spinlock.h" + +#include <asm/segment.h> + +#ifndef MODULE + +#include "setup.h" + +static STT pf_stt[7] = {{"drive0",7,drive0}, + {"drive1",7,drive1}, + {"drive2",7,drive2}, + {"drive3",7,drive3}, + {"disable",1,&disable}, + {"cluster",1,&cluster}, + {"nice",1,&nice}}; + +void pf_setup( char *str, int *ints) + +{ generic_setup(pf_stt,7,str); +} + +#endif + +#include "paride.h" + +/* set up defines for blk.h, why don't all drivers do it this way ? */ + +#define MAJOR_NR major +#define DEVICE_NAME "PF" +#define DEVICE_REQUEST do_pf_request +#define DEVICE_NR(device) MINOR(device) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include <linux/blk.h> + +#include "pseudo.h" + +/* constants for faking geometry numbers */ + +#define PF_FD_MAX 8192 /* use FD geometry under this size */ +#define PF_FD_HDS 2 +#define PF_FD_SPT 18 +#define PF_HD_HDS 64 +#define PF_HD_SPT 32 + +#define PF_MAX_RETRIES 5 +#define PF_TMO 800 /* interrupt timeout in jiffies */ +#define PF_SPIN_DEL 50 /* spin delay in micro-seconds */ + +#define PF_SPIN (10000/PF_SPIN_DEL)*PF_TMO + +#define STAT_ERR 0x00001 +#define STAT_INDEX 0x00002 +#define STAT_ECC 0x00004 +#define STAT_DRQ 0x00008 +#define STAT_SEEK 0x00010 +#define STAT_WRERR 0x00020 +#define STAT_READY 0x00040 +#define STAT_BUSY 0x00080 + +#define ATAPI_REQ_SENSE 0x03 +#define ATAPI_LOCK 0x1e +#define ATAPI_DOOR 0x1b +#define ATAPI_MODE_SENSE 0x5a +#define ATAPI_CAPACITY 0x25 +#define ATAPI_IDENTIFY 0x12 +#define ATAPI_READ_10 0x28 +#define ATAPI_WRITE_10 0x2a + +int pf_init(void); +#ifdef MODULE +void cleanup_module( void ); +#endif +static int pf_open(struct inode *inode, struct file *file); +static void do_pf_request(void); +static int pf_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg); + +static void pf_release (struct inode *inode, struct file *file); + +static int pf_detect(void); +static void do_pf_read(void); +static void do_pf_read_start(void); +static void do_pf_write(void); +static void do_pf_write_start(void); +static void do_pf_read_drq( void ); +static void do_pf_write_done( void ); + +static int pf_identify (int unit); +static void pf_lock(int unit, int func); +static void pf_eject(int unit); +static int pf_check_media(kdev_t dev); + +static int pf_blocksizes[PF_UNITS]; + +#define PF_NM 0 +#define PF_RO 1 +#define PF_RW 2 + +#define PF_NAMELEN 8 + +struct pf_unit { + struct pi_adapter pia; /* interface to paride layer */ + struct pi_adapter *pi; + int removable; /* removable media device ? */ + int media_status; /* media present ? WP ? */ + int drive; /* drive */ + int lun; + int access; /* count of active opens ... */ + int capacity; /* Size of this volume in sectors */ + int present; /* device present ? */ + char name[PF_NAMELEN]; /* pf0, pf1, ... */ + }; + +struct pf_unit pf[PF_UNITS]; + +/* 'unit' must be defined in all functions - either as a local or a param */ + +#define PF pf[unit] +#define PI PF.pi + +static char pf_scratch[512]; /* scratch block buffer */ + +/* the variables below are used mainly in the I/O request engine, which + processes only one request at a time. +*/ + +static int pf_retries = 0; /* i/o error retry count */ +static int pf_busy = 0; /* request being processed ? */ +static int pf_block; /* address of next requested block */ +static int pf_count; /* number of blocks still to do */ +static int pf_run; /* sectors in current cluster */ +static int pf_cmd; /* current command READ/WRITE */ +static int pf_unit; /* unit of current request */ +static int pf_mask; /* stopper for pseudo-int */ +static char * pf_buf; /* buffer for request in progress */ + +/* kernel glue structures */ + +static struct file_operations pf_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + pf_ioctl, /* ioctl */ + NULL, /* mmap */ + pf_open, /* open */ + pf_release, /* release */ + block_fsync, /* fsync */ + NULL, /* fasync */ + pf_check_media, /* media change ? */ + NULL /* revalidate new media */ +}; + +void pf_init_units( void ) + +{ int unit, j; + + pf_drive_count = 0; + for (unit=0;unit<PF_UNITS;unit++) { + PF.pi = & PF.pia; + PF.access = 0; + PF.media_status = PF_NM; + PF.capacity = 0; + PF.present = 0; + PF.drive = DU[D_SLV]; + PF.lun = DU[D_LUN]; + j = 0; + while ((j < PF_NAMELEN-2) && (PF.name[j]=name[j])) j++; + PF.name[j++] = '0' + unit; + PF.name[j] = 0; + if (DU[D_PRT]) pf_drive_count++; + } +} + +int pf_init (void) /* preliminary initialisation */ + +{ int i; + + if (disable) return -1; + + pf_init_units(); + + if (pf_detect()) return -1; + pf_busy = 0; + + if (register_blkdev(MAJOR_NR,name,&pf_fops)) { + printk("pf_init: unable to get major number %d\n", + major); + return -1; + } + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ + + for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024; + blksize_size[MAJOR_NR] = pf_blocksizes; + + return 0; +} + +static int pf_open (struct inode *inode, struct file *file) + +{ int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PF_UNITS) || (!PF.present)) return -ENODEV; + + MOD_INC_USE_COUNT; + + pf_identify(unit); + + if (PF.media_status == PF_NM) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + if ((PF.media_status == PF_RO) && (file ->f_mode & 2)) { + MOD_DEC_USE_COUNT; + return -EROFS; + } + + PF.access++; + if (PF.removable) pf_lock(unit,1); + + return 0; +} + +static int pf_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg) + +{ int err, unit; + struct hd_geometry *geo = (struct hd_geometry *) arg; + + if ((!inode) || (!inode->i_rdev)) return -EINVAL; + unit = DEVICE_NR(inode->i_rdev); + if (unit >= PF_UNITS) return -EINVAL; + if (!PF.present) return -ENODEV; + + switch (cmd) { + case CDROMEJECT: + if (PF.access == 1) { + pf_eject(unit); + return 0; + } + case HDIO_GETGEO: + if (!geo) return -EINVAL; + err = verify_area(VERIFY_WRITE,geo,sizeof(*geo)); + if (err) return err; + if (PF.capacity < PF_FD_MAX) { + put_user(PF.capacity/(PF_FD_HDS*PF_FD_SPT), + (short *) &geo->cylinders); + put_user(PF_FD_HDS, (char *) &geo->heads); + put_user(PF_FD_SPT, (char *) &geo->sectors); + } else { + put_user(PF.capacity/(PF_HD_HDS*PF_HD_SPT), + (short *) &geo->cylinders); + put_user(PF_HD_HDS, (char *) &geo->heads); + put_user(PF_HD_SPT, (char *) &geo->sectors); + } + put_user(0,(long *)&geo->start); + return 0; + case BLKRASET: + if(!suser()) return -EACCES; + if(!(inode->i_rdev)) return -EINVAL; + if(arg > 0xff) return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long)); + if (err) return (err); + put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg); + return (0); + case BLKGETSIZE: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long)); + if (err) return (err); + put_user(PF.capacity,(long *) arg); + return (0); + case BLKFLSBUF: + if(!suser()) return -EACCES; + if(!(inode->i_rdev)) return -EINVAL; + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + RO_IOCTLS(inode->i_rdev,arg); + default: + return -EINVAL; + } +} + + +static void pf_release (struct inode *inode, struct file *file) + +{ kdev_t devp; + int unit; + + devp = inode->i_rdev; + unit = DEVICE_NR(devp); + + if ((unit >= PF_UNITS) || (PF.access <= 0)) + return; + + PF.access--; + + if (!PF.access) { + fsync_dev(devp); + + invalidate_inodes(devp); + + invalidate_buffers(devp); + if (PF.removable) pf_lock(unit,0); + } + + MOD_DEC_USE_COUNT; + +} + +static int pf_check_media( kdev_t dev) + +{ return 1; +} + +#ifdef MODULE + +/* Glue for modules ... */ + +void cleanup_module(void); + +int init_module(void) + +{ int err; + + err = pf_init(); + + return err; +} + +void cleanup_module(void) + +{ int unit; + + unregister_blkdev(MAJOR_NR,name); + + for (unit=0;unit<PF_UNITS;unit++) + if (PF.present) pi_release(PI); +} + +#endif + +#define WR(c,r,v) pi_write_regr(PI,c,r,v) +#define RR(c,r) (pi_read_regr(PI,c,r)) + +#define LUN (0x20*PF.lun) +#define DRIVE (0xa0+0x10*PF.drive) + +static int pf_wait( int unit, int go, int stop, char * fun, char * msg ) + +{ int j, r, e, s, p; + + j = 0; + while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PF_SPIN)) + udelay(PF_SPIN_DEL); + + if ((r&(STAT_ERR&stop))||(j>=PF_SPIN)) { + s = RR(0,7); + e = RR(0,1); + p = RR(0,2); + if (j >= PF_SPIN) e |= 0x100; + if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x" + " loop=%d phase=%d\n", + PF.name,fun,msg,r,s,e,j,p); + return (e<<8)+s; + } + return 0; +} + +static int pf_command( int unit, char * cmd, int dlen, char * fun ) + +{ pi_connect(PI); + + WR(0,6,DRIVE); + + if (pf_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) { + pi_disconnect(PI); + return -1; + } + + WR(0,4,dlen % 256); + WR(0,5,dlen / 256); + WR(0,7,0xa0); /* ATAPI packet command */ + + if (pf_wait(unit,STAT_BUSY,STAT_DRQ,fun,"command DRQ")) { + pi_disconnect(PI); + return -1; + } + + if (RR(0,2) != 1) { + printk("%s: %s: command phase error\n",PF.name,fun); + pi_disconnect(PI); + return -1; + } + + pi_write_block(PI,cmd,12); + + return 0; +} + +static int pf_completion( int unit, char * buf, char * fun ) + +{ int r, s, n; + + r = pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR, + fun,"completion"); + + if ((RR(0,2)&2) && (RR(0,7)&STAT_DRQ)) { + n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc); + pi_read_block(PI,buf,n); + } + + s = pf_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done"); + + pi_disconnect(PI); + + return (r?r:s); +} + +static void pf_req_sense( int unit, int quiet ) + +{ char rs_cmd[12] = { ATAPI_REQ_SENSE,LUN,0,0,16,0,0,0,0,0,0,0 }; + char buf[16]; + int r; + + r = pf_command(unit,rs_cmd,16,"Request sense"); + udelay(1000); + if (!r) pf_completion(unit,buf,"Request sense"); + + if ((!r)&&(!quiet)) + printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n", + PF.name,buf[2]&0xf,buf[12],buf[13]); +} + +static int pf_atapi( int unit, char * cmd, int dlen, char * buf, char * fun ) + +{ int r; + + r = pf_command(unit,cmd,dlen,fun); + udelay(1000); + if (!r) r = pf_completion(unit,buf,fun); + if (r) pf_req_sense(unit,!fun); + + return r; +} + +#define DBMSG(msg) NULL + +static void pf_lock(int unit, int func) + +{ char lo_cmd[12] = { ATAPI_LOCK,LUN,0,0,func,0,0,0,0,0,0,0 }; + + pf_atapi(unit,lo_cmd,0,pf_scratch,func?"unlock":"lock"); +} + + +static void pf_eject( int unit ) + +{ char ej_cmd[12] = { ATAPI_DOOR,LUN,0,0,2,0,0,0,0,0,0,0 }; + + pf_lock(unit,0); + pf_atapi(unit,ej_cmd,0,pf_scratch,"eject"); +} + +#define PF_RESET_TMO 30 /* in tenths of a second */ + +static void pf_sleep( int cs ) + +{ current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + cs; + schedule(); +} + + +static int pf_reset( int unit ) + +/* the ATAPI standard actually specifies the contents of all 7 registers + after a reset, but the specification is ambiguous concerning the last + two bytes, and different drives interpret the standard differently. +*/ + +{ int i, k, flg; + int expect[5] = {1,1,1,0x14,0xeb}; + + pi_connect(PI); + WR(0,6,DRIVE); + WR(0,7,8); + + pf_sleep(2); + + k = 0; + while ((k++ < PF_RESET_TMO) && (RR(1,6)&STAT_BUSY)) + pf_sleep(10); + + flg = 1; + for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]); + + if (verbose) { + printk("%s: Reset (%d) signature = ",PF.name,k); + for (i=0;i<5;i++) printk("%3x",RR(0,i+1)); + if (!flg) printk(" (incorrect)"); + printk("\n"); + } + + pi_disconnect(PI); + return flg-1; +} + +static void pf_mode_sense( int unit ) + +{ char ms_cmd[12] = { ATAPI_MODE_SENSE,LUN,0,0,0,0,0,0,8,0,0,0}; + char buf[8]; + + pf_atapi(unit,ms_cmd,8,buf,DBMSG("mode sense")); + PF.media_status = PF_RW; + if (buf[3] & 0x80) PF.media_status = PF_RO; +} + +static void xs( char *buf, char *targ, int offs, int len ) + +{ int j,k,l; + + j=0; l=0; + for (k=0;k<len;k++) + if((buf[k+offs]!=0x20)||(buf[k+offs]!=l)) + l=targ[j++]=buf[k+offs]; + if (l==0x20) j--; targ[j]=0; +} + +static int xl( char *buf, int offs ) + +{ int v,k; + + v=0; + for(k=0;k<4;k++) v=v*256+(buf[k+offs]&0xff); + return v; +} + +static void pf_get_capacity( int unit ) + +{ char rc_cmd[12] = { ATAPI_CAPACITY,LUN,0,0,0,0,0,0,0,0,0,0}; + char buf[8]; + int bs; + + if (pf_atapi(unit,rc_cmd,8,buf,DBMSG("get capacity"))) { + PF.media_status = PF_NM; + return; + } + PF.capacity = xl(buf,0) + 1; + bs = xl(buf,4); + if (bs != 512) { + PF.capacity = 0; + if (verbose) printk("%s: Drive %d, LUN %d," + " unsupported block size %d\n", + PF.name,PF.drive,PF.lun,bs); + } +} + +static int pf_identify( int unit ) + +{ int dt, s; + char *ms[2] = {"master","slave"}; + char mf[10], id[18]; + char id_cmd[12] = { ATAPI_IDENTIFY,LUN,0,0,36,0,0,0,0,0,0,0}; + char buf[36]; + + s = pf_atapi(unit,id_cmd,36,buf,"identify"); + if (s) return -1; + + dt = buf[0] & 0x1f; + if ((dt != 0) && (dt != 7)) { + if (verbose) + printk("%s: Drive %d, LUN %d, unsupported type %d\n", + PF.name,PF.drive,PF.lun,dt); + return -1; + } + + xs(buf,mf,8,8); + xs(buf,id,16,16); + + PF.removable = (buf[1] & 0x80); + + pf_mode_sense(unit); + pf_mode_sense(unit); + pf_mode_sense(unit); + + pf_get_capacity(unit); + + printk("%s: %s %s, %s LUN %d, type %d", + PF.name,mf,id,ms[PF.drive],PF.lun,dt); + if (PF.removable) printk(", removable"); + if (PF.media_status == PF_NM) + printk(", no media\n"); + else { if (PF.media_status == PF_RO) printk(", RO"); + printk(", %d blocks\n",PF.capacity); + } + + return 0; +} + +static int pf_probe( int unit ) + +/* returns 0, with id set if drive is detected + -1, if drive detection failed +*/ + +{ if (PF.drive == -1) { + for (PF.drive=0;PF.drive<=1;PF.drive++) + if (!pf_reset(unit)) { + if (PF.lun != -1) return pf_identify(unit); + else for (PF.lun=0;PF.lun<8;PF.lun++) + if (!pf_identify(unit)) return 0; + } + } else { + if (pf_reset(unit)) return -1; + if (PF.lun != -1) return pf_identify(unit); + for (PF.lun=0;PF.lun<8;PF.lun++) + if (!pf_identify(unit)) return 0; + } + return -1; +} + +static int pf_detect( void ) + +{ int k, unit; + + printk("%s: %s version %s, major %d, cluster %d, nice %d\n", + name,name,PF_VERSION,major,cluster,nice); + + k = 0; + if (pf_drive_count == 0) { + unit = 0; + if (pi_init(PI,1,-1,-1,-1,-1,-1,pf_scratch, + PI_PF,verbose,PF.name)) { + if (!pf_probe(unit)) { + PF.present = 1; + k++; + } else pi_release(PI); + } + + } else for (unit=0;unit<PF_UNITS;unit++) if (DU[D_PRT]) + if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI], + DU[D_PRO],DU[D_DLY],pf_scratch,PI_PF,verbose, + PF.name)) { + if (!pf_probe(unit)) { + PF.present = 1; + k++; + } else pi_release(PI); + } + + if (k) return 0; + + printk("%s: No ATAPI disk detected\n",name); + return -1; +} + +/* The i/o request engine */ + +static int pf_start( int unit, int cmd, int b, int c ) + +{ int i; + char io_cmd[12] = {cmd,LUN,0,0,0,0,0,0,0,0,0,0}; + + for(i=0;i<4;i++) { + io_cmd[5-i] = b & 0xff; + b = b >> 8; + } + + io_cmd[8] = c & 0xff; + io_cmd[7] = (c >> 8) & 0xff; + + i = pf_command(unit,io_cmd,c*512,"start i/o"); + + udelay(1000); + + return i; +} + +static int pf_ready( void ) + +{ int unit = pf_unit; + + return (((RR(1,6)&(STAT_BUSY|pf_mask)) == pf_mask)); +} + +static void do_pf_request (void) + +{ struct buffer_head * bh; + struct request * req; + int unit; + + if (pf_busy) return; +repeat: + if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + + pf_unit = unit = DEVICE_NR(CURRENT->rq_dev); + pf_block = CURRENT->sector; + pf_count = CURRENT->nr_sectors; + + bh = CURRENT->bh; + req = CURRENT; + if (bh->b_reqnext) + printk("%s: OUCH: b_reqnext != NULL\n",PF.name); + + if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) { + end_request(0); + goto repeat; + } + + pf_cmd = CURRENT->cmd; + pf_run = pf_count; + while ((pf_run <= cluster) && + (req = req->next) && + (pf_block+pf_run == req->sector) && + (pf_cmd == req->cmd) && + (pf_unit == DEVICE_NR(req->rq_dev))) + pf_run += req->nr_sectors; + + pf_buf = CURRENT->buffer; + pf_retries = 0; + + pf_busy = 1; + if (pf_cmd == READ) pi_do_claimed(PI,do_pf_read); + else if (pf_cmd == WRITE) pi_do_claimed(PI,do_pf_write); + else { pf_busy = 0; + end_request(0); + goto repeat; + } +} + +static void pf_next_buf( int unit ) + +{ long saved_flags; + + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + if (!pf_run) { spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + +/* paranoia */ + + if ((!CURRENT) || + (CURRENT->cmd != pf_cmd) || + (DEVICE_NR(CURRENT->rq_dev) != pf_unit) || + (CURRENT->rq_status == RQ_INACTIVE) || + (CURRENT->sector != pf_block)) + printk("%s: OUCH: request list changed unexpectedly\n", + PF.name); + + pf_count = CURRENT->nr_sectors; + pf_buf = CURRENT->buffer; + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +static void do_pf_read( void ) + +/* detach from the calling context - in case the spinlock is held */ + +{ ps_set_intr(do_pf_read_start,0,0,nice); +} + +static void do_pf_read_start( void ) + +{ int unit = pf_unit; + long saved_flags; + + pf_busy = 1; + + if (pf_start(unit,ATAPI_READ_10,pf_block,pf_run)) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_retries++; + pi_do_claimed(PI,do_pf_read_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pf_mask = STAT_DRQ; + ps_set_intr(do_pf_read_drq,pf_ready,PF_TMO,nice); +} + +static void do_pf_read_drq( void ) + +{ int unit = pf_unit; + long saved_flags; + + while (1) { + if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR, + "read block","completion") & STAT_ERR) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_req_sense(unit,0); + pf_retries++; + pi_do_claimed(PI,do_pf_read_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_read_block(PI,pf_buf,512); + pf_count--; pf_run--; + pf_buf += 512; + pf_block++; + if (!pf_run) break; + if (!pf_count) pf_next_buf(unit); + } + pi_disconnect(PI); + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +static void do_pf_write( void ) + +{ ps_set_intr(do_pf_write_start,0,0,nice); +} + +static void do_pf_write_start( void ) + +{ int unit = pf_unit; + long saved_flags; + + pf_busy = 1; + + if (pf_start(unit,ATAPI_WRITE_10,pf_block,pf_run)) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_retries++; + pi_do_claimed(PI,do_pf_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + + while (1) { + if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR, + "write block","data wait") & STAT_ERR) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_retries++; + pi_do_claimed(PI,do_pf_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_write_block(PI,pf_buf,512); + pf_count--; pf_run--; + pf_buf += 512; + pf_block++; + if (!pf_run) break; + if (!pf_count) pf_next_buf(unit); + } + pf_mask = 0; + ps_set_intr(do_pf_write_done,pf_ready,PF_TMO,nice); +} + +static void do_pf_write_done( void ) + +{ int unit = pf_unit; + long saved_flags; + + if (pf_wait(unit,STAT_BUSY,0,"write block","done") & STAT_ERR) { + pi_disconnect(PI); + if (pf_retries < PF_MAX_RETRIES) { + pf_retries++; + pi_do_claimed(PI,do_pf_write_start); + return; + } + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(0); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + return; + } + pi_disconnect(PI); + spin_lock_irqsave(&io_request_lock,saved_flags); + end_request(1); + pf_busy = 0; + do_pf_request(); + spin_unlock_irqrestore(&io_request_lock,saved_flags); +} + +/* end of pf.c */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pg.c linux/drivers/block/paride/pg.c --- v2.0.34/linux/drivers/block/paride/pg.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/pg.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,682 @@ +/* + pg.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + + Special 2.0.35 version + + + The pg driver provides a simple character device interface for + sending ATAPI commands to a device. With the exception of the + ATAPI reset operation, all operations are performed by a pair + of read and write operations to the appropriate /dev/pgN device. + A write operation delivers a command and any outbound data in + a single buffer. Normally, the write will succeed unless the + device is offline or malfunctioning, or there is already another + command pending. If the write succeeds, it should be followed + immediately by a read operation, to obtain any returned data and + status information. A read will fail if there is no operation + in progress. + + As a special case, the device can be reset with a write operation, + and in this case, no following read is expected, or permitted. + + There are no ioctl() operations. Any single operation + may transfer at most PG_MAX_DATA bytes. Note that the driver must + copy the data through an internal buffer. In keeping with all + current ATAPI devices, command packets are assumed to be exactly + 12 bytes in length. + + To permit future changes to this interface, the headers in the + read and write buffers contain a single character "magic" flag. + Currently this flag must be the character "P". + + By default, the driver will autoprobe for a single parallel + port ATAPI device, but if their individual parameters are + specified, the driver can handle up to 4 devices. + + To use this device, you must have the following device + special files defined: + + /dev/pg0 b 97 0 + /dev/pg1 b 97 1 + /dev/pg2 b 97 2 + /dev/pg3 b 97 3 + + (You'll need to change the 97 to something else if you use + the 'major' parameter to install the driver on a different + major number.) + + The behaviour of the pg driver can be altered by setting + some parameters from the insmod command line. The following + parameters are adjustable: + + drive0 These four arguments can be arrays of + drive1 1-6 integers as follows: + drive2 + drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly> + + Where, + + <prt> is the base of the parallel port address for + the corresponding drive. (required) + + <pro> is the protocol number for the adapter that + supports this drive. These numbers are + logged by 'paride' when the protocol modules + are initialised. (0 if not given) + + <uni> for those adapters that support chained + devices, this is the unit selector for the + chain of devices on the given port. It should + be zero for devices that don't support chaining. + (0 if not given) + + <mod> this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + <slv> ATAPI devices can be jumpered to master or slave. + Set this to 0 to choose the master drive, 1 to + choose the slave, -1 (the default) to choose the + first drive found. + + <dly> some parallel ports require the driver to + go more slowly. -1 sets a default value that + should work with the chosen protocol. Otherwise, + set this to a small integer, the larger it is + the slower the port i/o. In some cases, setting + this to zero will speed up the device. (default -1) + + major You may use this parameter to overide the + default major number (97) that this driver + will use. Be sure to change the device + name as well. + + name This parameter is a character string that + contains the name the kernel will use for this + device (in /proc output, for instance). + (default "pg"). + + verbose This parameter controls the amount of logging + that is done by the driver. Set it to 0 for + quiet operation, to 1 to enable progress + messages while the driver probes for devices, + or to 2 for full debug logging. (default 0) + + If this driver is built into the kernel, you can use + the following command line parameters, with the same values + as the corresponding module parameters listed above: + + pg.drive0 + pg.drive1 + pg.drive2 + pg.drive3 + + In addition, you can use the parameter pg.disable to disable + the driver entirely. + +*/ + +/* Changes: + + 1.01 GRG 1998.06.16 Bug fixes +*/ + +#define PG_VERSION "1.01s" +#define PG_MAJOR 97 +#define PG_NAME "pg" +#define PG_UNITS 4 + +#ifndef PI_PG +#define PI_PG 4 +#endif + +/* Here are things one can override from the insmod command. + Most are autoprobed by paride unless set here. Verbose is 0 + by default. + +*/ + +static int verbose = 0; +static int major = PG_MAJOR; +static char *name = PG_NAME; +static int disable = 0; + +static int drive0[6] = {0,0,0,-1,-1,-1}; +static int drive1[6] = {0,0,0,-1,-1,-1}; +static int drive2[6] = {0,0,0,-1,-1,-1}; +static int drive3[6] = {0,0,0,-1,-1,-1}; + +static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3}; +static int pg_drive_count; + +#define D_PRT 0 +#define D_PRO 1 +#define D_UNI 2 +#define D_MOD 3 +#define D_SLV 4 +#define D_DLY 5 + +#define DU (*drives[unit]) + +/* end of parameters */ + + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/mtio.h> +#include <linux/pg.h> + +#include <asm/segment.h> + +#ifndef MODULE + +#include "setup.h" + +static STT pg_stt[5] = {{"drive0",6,drive0}, + {"drive1",6,drive1}, + {"drive2",6,drive2}, + {"drive3",6,drive3}, + {"disable",1,&disable}}; + +void pg_setup( char *str, int *ints) + +{ generic_setup(pg_stt,5,str); +} + +#endif + +#include "paride.h" + +#define PG_SPIN_DEL 50 /* spin delay in micro-seconds */ +#define PG_SPIN 200 +#define PG_TMO HZ +#define PG_CMD_TMO 3*HZ +#define PG_RESET_TMO 10*HZ + +#define STAT_ERR 0x01 +#define STAT_INDEX 0x02 +#define STAT_ECC 0x04 +#define STAT_DRQ 0x08 +#define STAT_SEEK 0x10 +#define STAT_WRERR 0x20 +#define STAT_READY 0x40 +#define STAT_BUSY 0x80 + +#define ATAPI_IDENTIFY 0x12 + +int pg_init(void); +#ifdef MODULE +void cleanup_module( void ); +#endif + +static int pg_open(struct inode *inode, struct file *file); +static void pg_release (struct inode *inode, struct file *file); +static int pg_read(struct inode *inode, struct file *filp, char *buf, int count); +static int pg_write(struct inode *inode, struct file *filp, + const char *buf, int count); +static int pg_detect(void); + +static int pg_identify (int unit, int log); + +#define PG_NAMELEN 8 + +struct pg_unit { + struct pi_adapter pia; /* interface to paride layer */ + struct pi_adapter *pi; + int busy; /* write done, read expected */ + int start; /* jiffies at command start */ + int dlen; /* transfer size requested */ + int timeout; /* timeout requested */ + int status; /* last sense key */ + int drive; /* drive */ + int access; /* count of active opens ... */ + int present; /* device present ? */ + char *bufptr; + char name[PG_NAMELEN]; /* pg0, pg1, ... */ + }; + +struct pg_unit pg[PG_UNITS]; + +/* 'unit' must be defined in all functions - either as a local or a param */ + +#define PG pg[unit] +#define PI PG.pi + +static char pg_scratch[512]; /* scratch block buffer */ + +/* kernel glue structures */ + +static struct file_operations pg_fops = { + NULL, /* lseek - default */ + pg_read, /* read */ + pg_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select */ + NULL, /* ioctl */ + NULL, /* mmap */ + pg_open, /* open */ + pg_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* media change ? */ + NULL /* revalidate new media */ +}; + +void pg_init_units( void ) + +{ int unit, j; + + pg_drive_count = 0; + for (unit=0;unit<PG_UNITS;unit++) { + PG.pi = & PG.pia; + PG.access = 0; + PG.busy = 0; + PG.present = 0; + PG.bufptr = NULL; + PG.drive = DU[D_SLV]; + j = 0; + while ((j < PG_NAMELEN-2) && (PG.name[j]=name[j])) j++; + PG.name[j++] = '0' + unit; + PG.name[j] = 0; + if (DU[D_PRT]) pg_drive_count++; + } +} + +int pg_init (void) /* preliminary initialisation */ + +{ int unit; + + if (disable) return -1; + + pg_init_units(); + + if (pg_detect()) return -1; + + if (register_chrdev(major,name,&pg_fops)) { + printk("pg_init: unable to get major number %d\n", + major); + for (unit=0;unit<PG_UNITS;unit++) + if (PG.present) pi_release(PI); + return -1; + } + + return 0; +} + +#ifdef MODULE + +/* Glue for modules ... */ + +void cleanup_module(void); + +int init_module(void) + +{ int err; + + err = pg_init(); + + return err; +} + +void cleanup_module(void) + +{ int unit; + + unregister_chrdev(major,name); + + for (unit=0;unit<PG_UNITS;unit++) + if (PG.present) pi_release(PI); +} + +#endif + +#define WR(c,r,v) pi_write_regr(PI,c,r,v) +#define RR(c,r) (pi_read_regr(PI,c,r)) + +#define DRIVE (0xa0+0x10*PG.drive) + +static void pg_sleep( int cs ) + +{ current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + cs; + schedule(); +} + +static int pg_wait( int unit, int go, int stop, int tmo, char * msg ) + +{ int j, r, e, s, p; + + PG.status = 0; + + j = 0; + while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(jiffies<tmo)) { + if (j++ < PG_SPIN) udelay(PG_SPIN_DEL); + else pg_sleep(1); + } + + if ((r&(STAT_ERR&stop))||(jiffies>=tmo)) { + s = RR(0,7); + e = RR(0,1); + p = RR(0,2); + if (verbose > 1) + printk("%s: %s: stat=0x%x err=0x%x phase=%d%s\n", + PG.name,msg,s,e,p,(jiffies>=tmo)?" timeout":""); + + + if (jiffies>=tmo) e |= 0x100; + PG.status = (e >> 4) & 0xff; + return -1; + } + return 0; +} + +static int pg_command( int unit, char * cmd, int dlen, int tmo ) + +{ int k; + + pi_connect(PI); + + WR(0,6,DRIVE); + + if (pg_wait(unit,STAT_BUSY|STAT_DRQ,0,tmo,"before command")) { + pi_disconnect(PI); + return -1; + } + + WR(0,4,dlen % 256); + WR(0,5,dlen / 256); + WR(0,7,0xa0); /* ATAPI packet command */ + + if (pg_wait(unit,STAT_BUSY,STAT_DRQ,tmo,"command DRQ")) { + pi_disconnect(PI); + return -1; + } + + if (RR(0,2) != 1) { + printk("%s: command phase error\n",PG.name); + pi_disconnect(PI); + return -1; + } + + pi_write_block(PI,cmd,12); + + if (verbose > 1) { + printk("%s: Command sent, dlen=%d packet= ", PG.name,dlen); + for (k=0;k<12;k++) printk("%02x ",cmd[k]&0xff); + printk("\n"); + } + return 0; +} + +static int pg_completion( int unit, char * buf, int tmo) + +{ int r, d, n, p; + + r = pg_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR, + tmo,"completion"); + + PG.dlen = 0; + + while (RR(0,7)&STAT_DRQ) { + d = (RR(0,4)+256*RR(0,5)); + n = ((d+3)&0xfffc); + p = RR(0,2)&3; + if (p == 0) pi_write_block(PI,buf,n); + if (p == 2) pi_read_block(PI,buf,n); + if (verbose > 1) printk("%s: %s %d bytes\n",PG.name, + p?"Read":"Write",n); + PG.dlen += (1-p)*d; + buf += d; + r = pg_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR, + tmo,"completion"); + } + + pi_disconnect(PI); + + return r; +} + +static int pg_reset( int unit ) + +{ int i, k, flg; + int expect[5] = {1,1,1,0x14,0xeb}; + + pi_connect(PI); + WR(0,6,DRIVE); + WR(0,7,8); + + pg_sleep(2); + + k = 0; + while ((k++ < PG_RESET_TMO) && (RR(1,6)&STAT_BUSY)) + pg_sleep(1); + + flg = 1; + for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]); + + if (verbose) { + printk("%s: Reset (%d) signature = ",PG.name,k); + for (i=0;i<5;i++) printk("%3x",RR(0,i+1)); + if (!flg) printk(" (incorrect)"); + printk("\n"); + } + + pi_disconnect(PI); + return flg-1; +} + +static void xs( char *buf, char *targ, int offs, int len ) + +{ int j,k,l; + + j=0; l=0; + for (k=0;k<len;k++) + if((buf[k+offs]!=0x20)||(buf[k+offs]!=l)) + l=targ[j++]=buf[k+offs]; + if (l==0x20) j--; targ[j]=0; +} + +static int pg_identify( int unit, int log ) + +{ int s; + char *ms[2] = {"master","slave"}; + char mf[10], id[18]; + char id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0}; + char buf[36]; + + s = pg_command(unit,id_cmd,36,jiffies+PG_TMO); + if (s) return -1; + s = pg_completion(unit,buf,jiffies+PG_TMO); + if (s) return -1; + + if (log) { + xs(buf,mf,8,8); + xs(buf,id,16,16); + printk("%s: %s %s, %s\n",PG.name,mf,id,ms[PG.drive]); + } + + return 0; +} + +static int pg_probe( int unit ) + +/* returns 0, with id set if drive is detected + -1, if drive detection failed +*/ + +{ if (PG.drive == -1) { + for (PG.drive=0;PG.drive<=1;PG.drive++) + if (!pg_reset(unit)) return pg_identify(unit,1); + } else { + if (!pg_reset(unit)) return pg_identify(unit,1); + } + return -1; +} + +static int pg_detect( void ) + +{ int k, unit; + + printk("%s: %s version %s, major %d\n", + name,name,PG_VERSION,major); + + k = 0; + if (pg_drive_count == 0) { + unit = 0; + if (pi_init(PI,1,-1,-1,-1,-1,-1,pg_scratch, + PI_PG,verbose,PG.name)) { + if (!pg_probe(unit)) { + PG.present = 1; + k++; + } else pi_release(PI); + } + + } else for (unit=0;unit<PG_UNITS;unit++) if (DU[D_PRT]) + if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI], + DU[D_PRO],DU[D_DLY],pg_scratch,PI_PG,verbose, + PG.name)) { + if (!pg_probe(unit)) { + PG.present = 1; + k++; + } else pi_release(PI); + } + + if (k) return 0; + + printk("%s: No ATAPI device detected\n",name); + return -1; +} + +#define DEVICE_NR(dev) (MINOR(dev) % 128) + +static int pg_open (struct inode *inode, struct file *file) + +{ int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PG_UNITS) || (!PG.present)) return -ENODEV; + + PG.access++; + + if (PG.access > 1) { + PG.access--; + return -EBUSY; + } + + MOD_INC_USE_COUNT; + + if (PG.busy) { + pg_reset(unit); + PG.busy = 0; + } + + pg_identify(unit,(verbose>1)); + + + PG.bufptr = kmalloc(PG_MAX_DATA,GFP_KERNEL); + if (PG.bufptr == NULL) { + PG.access--; + MOD_DEC_USE_COUNT; + printk("%s: buffer allocation failed\n",PG.name); + return -ENOMEM; + } + + return 0; +} + +static void pg_release (struct inode *inode, struct file *file) +{ + int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PG_UNITS) || (PG.access <= 0)) + return; + + PG.access--; + + kfree(PG.bufptr); + PG.bufptr = NULL; + + MOD_DEC_USE_COUNT; + +} + +static int pg_write(struct inode *inode, struct file *filp, + const char *buf, int count) + +{ int unit = DEVICE_NR(inode->i_rdev); + struct pg_write_hdr hdr; + int hs = sizeof(hdr); + + if (PG.busy) return -EBUSY; + if (count < hs) return -EINVAL; + + memcpy_fromfs((char *)&hdr,buf,hs); + + if (hdr.magic != PG_MAGIC) return -EINVAL; + if (hdr.dlen > PG_MAX_DATA) return -EINVAL; + if ((count - hs) > PG_MAX_DATA) return -EINVAL; + + if (hdr.func == PG_RESET) { + if (count != hs) return -EINVAL; + if (pg_reset(unit)) return -EIO; + return count; + } + + if (hdr.func != PG_COMMAND) return -EINVAL; + + PG.start = jiffies; + PG.timeout = hdr.timeout*HZ + HZ/2 + jiffies; + + if (pg_command(unit,hdr.packet,hdr.dlen,jiffies+PG_CMD_TMO)) { + if (PG.status & 0x10) return -ETIME; + return -EIO; + } + + PG.busy = 1; + + memcpy_fromfs(PG.bufptr,buf+hs,count-hs); + + return count; +} + +static int pg_read(struct inode *inode, struct file *filp, char *buf, int count) + +{ int unit = DEVICE_NR(inode->i_rdev); + struct pg_read_hdr hdr; + int hs = sizeof(hdr); + int copy; + + if (!PG.busy) return -EINVAL; + if (count < hs) return -EINVAL; + + PG.busy = 0; + + if (pg_completion(unit,PG.bufptr,PG.timeout)) + if (PG.status & 0x10) return -ETIME; + + hdr.magic = PG_MAGIC; + hdr.dlen = PG.dlen; + copy = 0; + + if (hdr.dlen < 0) { + hdr.dlen = -1 * hdr.dlen; + copy = hdr.dlen; + if (copy > (count - hs)) copy = count - hs; + } + + hdr.duration = (jiffies - PG.start + HZ/2) / HZ; + hdr.scsi = PG.status & 0x0f; + + memcpy_tofs(buf,(char *)&hdr,hs); + if (copy > 0) memcpy_tofs(buf+hs,PG.bufptr,copy); + + return copy+hs; +} + +/* end of pg.c */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pseudo.h linux/drivers/block/paride/pseudo.h --- v2.0.34/linux/drivers/block/paride/pseudo.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/pseudo.h Mon Jul 13 13:47:28 1998 @@ -0,0 +1,145 @@ +/* + pseudo.h (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + This is the "pseudo-interrupt" logic for parallel port drivers. + + This module is #included into each driver. It makes one + function available: + + ps_set_intr( void (*continuation)(void), + int (*ready)(void), + int timeout, + int nice ) + + Which will arrange for ready() to be evaluated frequently and + when either it returns true, or timeout jiffies have passed, + continuation() will be invoked. + + If nice is true, the test will done approximately once a + jiffy. If nice is 0, the test will also be done whenever + the scheduler runs (by adding it to a task queue). + +*/ + +/* Changes: + + 1.01 1998.05.03 Switched from cli()/sti() to spinlocks + +*/ + +#define PS_VERSION "1.01" + +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/tqueue.h> + +static void ps_timer_int( unsigned long data); +static void ps_tq_int( void *data); + +static int ps_use_tq = 1; +static void (* ps_continuation)(void); +static int (* ps_ready)(void); +static int ps_then; +static int ps_timeout; +static int ps_timer_active = 0; +static int ps_tq_active = 0; + +/* static spinlock_t ps_spinlock = SPIN_LOCK_UNLOCKED; */ + +static struct timer_list ps_timer = {0,0,0,0,ps_timer_int}; +static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL}; + +static void ps_set_intr( void (*continuation)(void), + int (*ready)(void), + int timeout, int nice ) + +{ long flags; + + spin_lock_irqsave(&ps_spinlock,flags); + + ps_continuation = continuation; + ps_ready = ready; + ps_then = jiffies; + ps_timeout = jiffies + timeout; + ps_use_tq = !nice; + + if (ps_use_tq && !ps_tq_active) { +#ifdef HAVE_DISABLE_HLT + disable_hlt(); +#endif + ps_tq_active = 1; + queue_task(&ps_tq,&tq_scheduler); + } + + if (!ps_timer_active) { + ps_timer_active = 1; + ps_timer.expires = jiffies; + add_timer(&ps_timer); + } + + spin_unlock_irqrestore(&ps_spinlock,flags); +} + +static void ps_tq_int( void *data ) + +{ void (*con)(void); + long flags; + + spin_lock_irqsave(&ps_spinlock,flags); + + con = ps_continuation; + +#ifdef HAVE_DISABLE_HLT + enable_hlt(); +#endif + + ps_tq_active = 0; + + if (!con) { + spin_unlock_irqrestore(&ps_spinlock,flags); + return; + } + if (!ps_ready || ps_ready() || (jiffies >= ps_timeout)) { + ps_continuation = NULL; + spin_unlock_irqrestore(&ps_spinlock,flags); + con(); + return; + } + +#ifdef HAVE_DISABLE_HLT + disable_hlt(); +#endif + + ps_tq_active = 1; + queue_task(&ps_tq,&tq_scheduler); + spin_unlock_irqrestore(&ps_spinlock,flags); +} + +static void ps_timer_int( unsigned long data) + +{ void (*con)(void); + long flags; + + spin_lock_irqsave(&ps_spinlock,flags); + + con = ps_continuation; + ps_timer_active = 0; + if (!con) { + spin_unlock_irqrestore(&ps_spinlock,flags); + return; + } + if (!ps_ready || ps_ready() || (jiffies >= ps_timeout)) { + ps_continuation = NULL; + spin_unlock_irqrestore(&ps_spinlock,flags); + con(); + return; + } + ps_timer_active = 1; + ps_timer.expires = jiffies; + add_timer(&ps_timer); + spin_unlock_irqrestore(&ps_spinlock,flags); +} + +/* end of pseudo.h */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pt.c linux/drivers/block/paride/pt.c --- v2.0.34/linux/drivers/block/paride/pt.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/pt.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,940 @@ +/* + pt.c (c) 1998 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + Special 2.0.35 version + + This is the high-level driver for parallel port ATAPI tape + drives based on chips supported by the paride module. + + The driver implements both rewinding and non-rewinding + devices, filemarks, and the rewind ioctl. It allocates + a small internal "bounce buffer" for each open device, but + otherwise expects buffering and blocking to be done at the + user level. As with most block-structured tapes, short + writes are padded to full tape blocks, so reading back a file + may return more data than was actually written. + + By default, the driver will autoprobe for a single parallel + port ATAPI tape drive, but if their individual parameters are + specified, the driver can handle up to 4 drives. + + The rewinding devices are named /dev/pt0, /dev/pt1, ... + while the non-rewinding devices are /dev/npt0, /dev/npt1, etc. + + The behaviour of the pt driver can be altered by setting + some parameters from the insmod command line. The following + parameters are adjustable: + + drive0 These four arguments can be arrays of + drive1 1-6 integers as follows: + drive2 + drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly> + + Where, + + <prt> is the base of the parallel port address for + the corresponding drive. (required) + + <pro> is the protocol number for the adapter that + supports this drive. These numbers are + logged by 'paride' when the protocol modules + are initialised. (0 if not given) + + <uni> for those adapters that support chained + devices, this is the unit selector for the + chain of devices on the given port. It should + be zero for devices that don't support chaining. + (0 if not given) + + <mod> this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + <slv> ATAPI devices can be jumpered to master or slave. + Set this to 0 to choose the master drive, 1 to + choose the slave, -1 (the default) to choose the + first drive found. + + <dly> some parallel ports require the driver to + go more slowly. -1 sets a default value that + should work with the chosen protocol. Otherwise, + set this to a small integer, the larger it is + the slower the port i/o. In some cases, setting + this to zero will speed up the device. (default -1) + + major You may use this parameter to overide the + default major number (96) that this driver + will use. Be sure to change the device + name as well. + + name This parameter is a character string that + contains the name the kernel will use for this + device (in /proc output, for instance). + (default "pt"). + + verbose This parameter controls the amount of logging + that is done while the driver probes for + devices. Set it to 0 for a quiet load, or 1 to + see all the progress messages. (default 0) + + If this driver is built into the kernel, you can use + the following command line parameters, with the same values + as the corresponding module parameters listed above: + + pt.drive0 + pt.drive1 + pt.drive2 + pt.drive3 + + In addition, you can use the parameter pt.disable to disable + the driver entirely. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.06 Round up transfer size, fix ready_wait, + loosed interpretation of ATAPI standard + for clearing error status. + Eliminate sti(); + 1.02 GRG 1998.06.16 Eliminate an Ugh. + +*/ + +#define PT_VERSION "1.02s" +#define PT_MAJOR 96 +#define PT_NAME "pt" +#define PT_UNITS 4 + +/* Here are things one can override from the insmod command. + Most are autoprobed by paride unless set here. Verbose is on + by default. + +*/ + +static int verbose = 0; +static int major = PT_MAJOR; +static char *name = PT_NAME; +static int disable = 0; + +static int drive0[6] = {0,0,0,-1,-1,-1}; +static int drive1[6] = {0,0,0,-1,-1,-1}; +static int drive2[6] = {0,0,0,-1,-1,-1}; +static int drive3[6] = {0,0,0,-1,-1,-1}; + +static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3}; +static int pt_drive_count; + +#define D_PRT 0 +#define D_PRO 1 +#define D_UNI 2 +#define D_MOD 3 +#define D_SLV 4 +#define D_DLY 5 + +#define DU (*drives[unit]) + +/* end of parameters */ + + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/mtio.h> + +#include <asm/segment.h> + +#ifndef MODULE + +#include "setup.h" + +static STT pt_stt[5] = {{"drive0",6,drive0}, + {"drive1",6,drive1}, + {"drive2",6,drive2}, + {"drive3",6,drive3}, + {"disable",1,&disable}}; + +void pt_setup( char *str, int *ints) + +{ generic_setup(pt_stt,5,str); +} + +#endif + +#include "paride.h" + +#define PT_MAX_RETRIES 5 +#define PT_TMO 800 /* interrupt timeout in jiffies */ +#define PT_SPIN_DEL 50 /* spin delay in micro-seconds */ +#define PT_RESET_TMO 30 /* 3 seconds */ +#define PT_READY_TMO 60 /* 60 seconds */ +#define PT_REWIND_TMO 1200 /* 20 minutes */ + +#define PT_SPIN (10000/PT_SPIN_DEL)*PT_TMO + +#define STAT_ERR 0x00001 +#define STAT_INDEX 0x00002 +#define STAT_ECC 0x00004 +#define STAT_DRQ 0x00008 +#define STAT_SEEK 0x00010 +#define STAT_WRERR 0x00020 +#define STAT_READY 0x00040 +#define STAT_BUSY 0x00080 +#define STAT_SENSE 0x1f000 + +#define ATAPI_TEST_READY 0x00 +#define ATAPI_REWIND 0x01 +#define ATAPI_REQ_SENSE 0x03 +#define ATAPI_READ_6 0x08 +#define ATAPI_WRITE_6 0x0a +#define ATAPI_WFM 0x10 +#define ATAPI_IDENTIFY 0x12 +#define ATAPI_MODE_SENSE 0x1a +#define ATAPI_LOG_SENSE 0x4d + +int pt_init(void); +#ifdef MODULE +void cleanup_module( void ); +#endif + +static int pt_open(struct inode *inode, struct file *file); +static int pt_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg); +static void pt_release (struct inode *inode, struct file *file); +static int pt_read(struct inode *inode, struct file *filp, char *buf, int count); +static int pt_write(struct inode *inode, struct file *filp, + const char *buf, int count); +static int pt_detect(void); + +static int pt_identify (int unit); + +/* bits in PT.flags */ + +#define PT_MEDIA 1 +#define PT_WRITE_OK 2 +#define PT_REWIND 4 +#define PT_WRITING 8 +#define PT_READING 16 +#define PT_EOF 32 + +#define PT_NAMELEN 8 +#define PT_BUFSIZE 16384 + +struct pt_unit { + struct pi_adapter pia; /* interface to paride layer */ + struct pi_adapter *pi; + int flags; /* various state flags */ + int last_sense; /* result of last request sense */ + int drive; /* drive */ + int access; /* count of active opens ... */ + int bs; /* block size */ + int capacity; /* Size of tape in KB */ + int present; /* device present ? */ + char *bufptr; + char name[PT_NAMELEN]; /* pf0, pf1, ... */ + }; + +struct pt_unit pt[PT_UNITS]; + +/* 'unit' must be defined in all functions - either as a local or a param */ + +#define PT pt[unit] +#define PI PT.pi + +static char pt_scratch[512]; /* scratch block buffer */ + +/* kernel glue structures */ + +static struct file_operations pt_fops = { + NULL, /* lseek - default */ + pt_read, /* read */ + pt_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select */ + pt_ioctl, /* ioctl */ + NULL, /* mmap */ + pt_open, /* open */ + pt_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* media change ? */ + NULL /* revalidate new media */ +}; + +void pt_init_units( void ) + +{ int unit, j; + + pt_drive_count = 0; + for (unit=0;unit<PT_UNITS;unit++) { + PT.pi = & PT.pia; + PT.access = 0; + PT.flags = 0; + PT.last_sense = 0; + PT.present = 0; + PT.bufptr = NULL; + PT.drive = DU[D_SLV]; + j = 0; + while ((j < PT_NAMELEN-2) && (PT.name[j]=name[j])) j++; + PT.name[j++] = '0' + unit; + PT.name[j] = 0; + if (DU[D_PRT]) pt_drive_count++; + } +} + +int pt_init (void) /* preliminary initialisation */ + +{ int unit; + + if (disable) return -1; + + pt_init_units(); + + if (pt_detect()) return -1; + + if (register_chrdev(major,name,&pt_fops)) { + printk("pt_init: unable to get major number %d\n", + major); + for (unit=0;unit<PT_UNITS;unit++) + if (PT.present) pi_release(PI); + return -1; + } + + return 0; +} + +#ifdef MODULE + +/* Glue for modules ... */ + +void cleanup_module(void); + +int init_module(void) + +{ int err; + + err = pt_init(); + + return err; +} + +void cleanup_module(void) + +{ int unit; + + unregister_chrdev(major,name); + + for (unit=0;unit<PT_UNITS;unit++) + if (PT.present) pi_release(PI); +} + +#endif + +#define WR(c,r,v) pi_write_regr(PI,c,r,v) +#define RR(c,r) (pi_read_regr(PI,c,r)) + +#define DRIVE (0xa0+0x10*PT.drive) + +static int pt_wait( int unit, int go, int stop, char * fun, char * msg ) + +{ int j, r, e, s, p; + + j = 0; + while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PT_SPIN)) + udelay(PT_SPIN_DEL); + + if ((r&(STAT_ERR&stop))||(j>=PT_SPIN)) { + s = RR(0,7); + e = RR(0,1); + p = RR(0,2); + if (j >= PT_SPIN) e |= 0x100; + if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x" + " loop=%d phase=%d\n", + PT.name,fun,msg,r,s,e,j,p); + return (e<<8)+s; + } + return 0; +} + +static int pt_command( int unit, char * cmd, int dlen, char * fun ) + +{ pi_connect(PI); + + WR(0,6,DRIVE); + + if (pt_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) { + pi_disconnect(PI); + return -1; + } + + WR(0,4,dlen % 256); + WR(0,5,dlen / 256); + WR(0,7,0xa0); /* ATAPI packet command */ + + if (pt_wait(unit,STAT_BUSY,STAT_DRQ,fun,"command DRQ")) { + pi_disconnect(PI); + return -1; + } + + if (RR(0,2) != 1) { + printk("%s: %s: command phase error\n",PT.name,fun); + pi_disconnect(PI); + return -1; + } + + pi_write_block(PI,cmd,12); + + return 0; +} + +static int pt_completion( int unit, char * buf, char * fun ) + +{ int r, s, n, p; + + r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR, + fun,"completion"); + + if (RR(0,7)&STAT_DRQ) { + n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc); + p = RR(0,2)&3; + if (p == 0) pi_write_block(PI,buf,n); + if (p == 2) pi_read_block(PI,buf,n); + } + + s = pt_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done"); + + pi_disconnect(PI); + + return (r?r:s); +} + +static void pt_req_sense( int unit, int quiet ) + +{ char rs_cmd[12] = { ATAPI_REQ_SENSE,0,0,0,16,0,0,0,0,0,0,0 }; + char buf[16]; + int r; + + r = pt_command(unit,rs_cmd,16,"Request sense"); + udelay(1000); + if (!r) pt_completion(unit,buf,"Request sense"); + + PT.last_sense = -1; + if (!r) { + if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n", + PT.name,buf[2]&0xf,buf[12],buf[13]); + PT.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8) + | ((buf[13]&0xff)<<16) ; + } +} + +static int pt_atapi( int unit, char * cmd, int dlen, char * buf, char * fun ) + +{ int r; + + r = pt_command(unit,cmd,dlen,fun); + udelay(1000); + if (!r) r = pt_completion(unit,buf,fun); + if (r) pt_req_sense(unit,!fun); + + return r; +} + +static void pt_sleep( int cs ) + +{ current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + cs; + schedule(); +} + +static int pt_poll_dsc( int unit, int pause, int tmo, char *msg ) + +{ int k, e, s; + + k = 0; + while (k < tmo) { + pt_sleep(pause); + k++; + pi_connect(PI); + WR(0,6,DRIVE); + s = RR(0,7); + e = RR(0,1); + pi_disconnect(PI); + if (s & (STAT_ERR|STAT_SEEK)) break; + } + if ((k >= tmo) || (s & STAT_ERR)) { + if (k >= tmo) printk("%s: %s DSC timeout\n",PT.name,msg); + else printk("%s: %s stat=0x%x err=0x%x\n",PT.name,msg,s,e); + pt_req_sense(unit,0); + return 0; + } + return 1; +} + +static void pt_media_access_cmd( int unit, int tmo, char *cmd, char *fun) + +{ if (pt_command(unit,cmd,0,fun)) { + pt_req_sense(unit,0); + return; + } + pi_disconnect(PI); + pt_poll_dsc(unit,100,tmo,fun); +} + +static void pt_rewind( int unit ) + +{ char rw_cmd[12] = {ATAPI_REWIND,0,0,0,0,0,0,0,0,0,0,0}; + + pt_media_access_cmd(unit,PT_REWIND_TMO,rw_cmd,"rewind"); +} + +static void pt_write_fm( int unit ) + +{ char wm_cmd[12] = {ATAPI_WFM,0,0,0,1,0,0,0,0,0,0,0}; + + pt_media_access_cmd(unit,PT_TMO,wm_cmd,"write filemark"); +} + +#define DBMSG(msg) NULL + +static int pt_reset( int unit ) + +{ int i, k, flg; + int expect[5] = {1,1,1,0x14,0xeb}; + + pi_connect(PI); + WR(0,6,DRIVE); + WR(0,7,8); + + pt_sleep(2); + + k = 0; + while ((k++ < PT_RESET_TMO) && (RR(1,6)&STAT_BUSY)) + pt_sleep(10); + + flg = 1; + for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]); + + if (verbose) { + printk("%s: Reset (%d) signature = ",PT.name,k); + for (i=0;i<5;i++) printk("%3x",RR(0,i+1)); + if (!flg) printk(" (incorrect)"); + printk("\n"); + } + + pi_disconnect(PI); + return flg-1; +} + +static int pt_ready_wait( int unit, int tmo ) + +{ char tr_cmd[12] = {ATAPI_TEST_READY,0,0,0,0,0,0,0,0,0,0,0}; + int k, p; + + k = 0; + while (k < tmo) { + PT.last_sense = 0; + pt_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready")); + p = PT.last_sense; + if (!p) return 0; + if (!(((p & 0xffff) == 0x0402)||((p & 0xff) == 6))) return p; + k++; + pt_sleep(100); + } + return 0x000020; /* timeout */ +} + +static void xs( char *buf, char *targ, int offs, int len ) + +{ int j,k,l; + + j=0; l=0; + for (k=0;k<len;k++) + if((buf[k+offs]!=0x20)||(buf[k+offs]!=l)) + l=targ[j++]=buf[k+offs]; + if (l==0x20) j--; targ[j]=0; +} + +static int xn( char *buf, int offs, int size ) + +{ int v,k; + + v=0; + for(k=0;k<size;k++) v=v*256+(buf[k+offs]&0xff); + return v; +} + +static int pt_identify( int unit ) + +{ int dt, s; + char *ms[2] = {"master","slave"}; + char mf[10], id[18]; + char id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0}; + char ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,128,0,0,0,0,0,0,0}; + char ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,128,0,0,0}; + char buf[36]; + + s = pt_atapi(unit,id_cmd,36,buf,"identify"); + if (s) return -1; + + dt = buf[0] & 0x1f; + if (dt != 1) { + if (verbose) + printk("%s: Drive %d, unsupported type %d\n", + PT.name,PT.drive,dt); + return -1; + } + + xs(buf,mf,8,8); + xs(buf,id,16,16); + + PT.flags = 0; + PT.capacity = 0; + PT.bs = 0; + + if (!pt_ready_wait(unit,PT_READY_TMO)) PT.flags |= PT_MEDIA; + + if (!pt_atapi(unit,ms_cmd,36,buf,"mode sense")) { + if (!(buf[2] & 0x80)) PT.flags |= PT_WRITE_OK; + PT.bs = xn(buf,10,2); + } + + if (!pt_atapi(unit,ls_cmd,36,buf,"log sense")) + PT.capacity = xn(buf,24,4); + + printk("%s: %s %s, %s", + PT.name,mf,id,ms[PT.drive]); + if (!(PT.flags & PT_MEDIA)) + printk(", no media\n"); + else { if (!(PT.flags & PT_WRITE_OK)) printk(", RO"); + printk(", blocksize %d, %d MB\n", + PT.bs,PT.capacity/1024); + } + + return 0; +} + +static int pt_probe( int unit ) + +/* returns 0, with id set if drive is detected + -1, if drive detection failed +*/ + +{ if (PT.drive == -1) { + for (PT.drive=0;PT.drive<=1;PT.drive++) + if (!pt_reset(unit)) return pt_identify(unit); + } else { + if (!pt_reset(unit)) return pt_identify(unit); + } + return -1; +} + +static int pt_detect( void ) + +{ int k, unit; + + printk("%s: %s version %s, major %d\n", + name,name,PT_VERSION,major); + + k = 0; + if (pt_drive_count == 0) { + unit = 0; + if (pi_init(PI,1,-1,-1,-1,-1,-1,pt_scratch, + PI_PT,verbose,PT.name)) { + if (!pt_probe(unit)) { + PT.present = 1; + k++; + } else pi_release(PI); + } + + } else for (unit=0;unit<PT_UNITS;unit++) if (DU[D_PRT]) + if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI], + DU[D_PRO],DU[D_DLY],pt_scratch,PI_PT,verbose, + PT.name)) { + if (!pt_probe(unit)) { + PT.present = 1; + k++; + } else pi_release(PI); + } + + if (k) return 0; + + printk("%s: No ATAPI tape drive detected\n",name); + return -1; +} + +#define DEVICE_NR(dev) (MINOR(dev) % 128) + +static int pt_open (struct inode *inode, struct file *file) + +{ int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PT_UNITS) || (!PT.present)) return -ENODEV; + + PT.access++; + + if (PT.access > 1) { + PT.access--; + return -EBUSY; + } + + MOD_INC_USE_COUNT; + + pt_identify(unit); + + if (!PT.flags & PT_MEDIA) { + PT.access--; + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + if ((!PT.flags & PT_WRITE_OK) && (file ->f_mode & 2)) { + PT.access--; + MOD_DEC_USE_COUNT; + return -EROFS; + } + + if (!(MINOR(inode->i_rdev) & 128)) + PT.flags |= PT_REWIND; + + PT.bufptr = kmalloc(PT_BUFSIZE,GFP_KERNEL); + if (PT.bufptr == NULL) { + PT.access--; + MOD_DEC_USE_COUNT; + printk("%s: buffer allocation failed\n",PT.name); + return -ENOMEM; + } + + return 0; +} + +static int pt_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg) +{ + int unit; + struct mtop mtop; + + if (!inode || !inode->i_rdev) + return -EINVAL; + unit = DEVICE_NR(inode->i_rdev); + if (unit >= PT_UNITS) + return -EINVAL; + if (!PT.present) + return -ENODEV; + + switch (cmd) { + case MTIOCTOP: + memcpy_fromfs((char *)&mtop, (char *)arg, + sizeof(struct mtop)); + + switch (mtop.mt_op) { + + case MTREW: + pt_rewind(unit); + return 0; + + default: + printk("%s: Unimplemented mt_op %d\n",PT.name, + mtop.mt_op); + return -EINVAL; + } + + default: + printk("%s: Unimplemented ioctl 0x%x\n",PT.name,cmd); + return -EINVAL; + + } +} + + +static void pt_release (struct inode *inode, struct file *file) +{ + int unit = DEVICE_NR(inode->i_rdev); + + if ((unit >= PT_UNITS) || (PT.access <= 0)) + return; + + if (PT.flags & PT_WRITING) pt_write_fm(unit); + + if (PT.flags & PT_REWIND) pt_rewind(unit); + + PT.access--; + + kfree(PT.bufptr); + PT.bufptr = NULL; + + MOD_DEC_USE_COUNT; + +} + +static int pt_read(struct inode *inode, struct file *filp, char *buf, int count) + +{ int unit = DEVICE_NR(inode->i_rdev); + char rd_cmd[12] = {ATAPI_READ_6,1,0,0,0,0,0,0,0,0,0,0}; + int k, n, r, p, s, t, b; + + if (!(PT.flags & (PT_READING|PT_WRITING))) { + PT.flags |= PT_READING; + if (pt_atapi(unit,rd_cmd,0,NULL,"start read-ahead")) + return -EIO; + } else if (PT.flags & PT_WRITING) return -EIO; + + if (PT.flags & PT_EOF) return 0; + + t = 0; + + while (count > 0) { + + if (!pt_poll_dsc(unit,1,PT_TMO,"read")) return -EIO; + + n = count; + if (n > 32768) n = 32768; /* max per command */ + b = (n-1+PT.bs)/PT.bs; + n = b*PT.bs; /* rounded up to even block */ + + rd_cmd[4] = b; + + r = pt_command(unit,rd_cmd,n,"read"); + + udelay(1000); + + if (r) { + pt_req_sense(unit,0); + return -EIO; + } + + while (1) { + + r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY, + DBMSG("read DRQ"),""); + + if (r & STAT_SENSE) { + pi_disconnect(PI); + pt_req_sense(unit,0); + return -EIO; + } + + if (r) PT.flags |= PT_EOF; + + s = RR(0,7); + + if (!(s & STAT_DRQ)) break; + + n = (RR(0,4)+256*RR(0,5)); + p = (RR(0,2)&3); + if (p != 2) { + pi_disconnect(PI); + printk("%s: Phase error on read: %d\n",PT.name,p); + return -EIO; + } + + while (n > 0) { + k = n; + if (k > PT_BUFSIZE) k = PT_BUFSIZE; + pi_read_block(PI,PT.bufptr,k); + n -= k; + b = k; + if (b > count) b = count; + memcpy_tofs(buf+t,PT.bufptr,b); + t += b; + count -= b; + } + + } + pi_disconnect(PI); + if (PT.flags & PT_EOF) break; + } + + return t; + +} + +static int pt_write(struct inode *inode, struct file *filp, + const char *buf, int count) + +{ int unit = DEVICE_NR(inode->i_rdev); + char wr_cmd[12] = {ATAPI_WRITE_6,1,0,0,0,0,0,0,0,0,0,0}; + int k, n, r, p, s, t, b; + + if (!(PT.flags & PT_WRITE_OK)) return -EROFS; + + if (!(PT.flags & (PT_READING|PT_WRITING))) { + PT.flags |= PT_WRITING; + if (pt_atapi(unit,wr_cmd,0,NULL,"start buffer-available mode")) + return -EIO; + } else if (PT.flags&PT_READING) return -EIO; + + if (PT.flags & PT_EOF) return -ENOSPC; + + t = 0; + + while (count > 0) { + + if (!pt_poll_dsc(unit,1,PT_TMO,"write")) return -EIO; + + n = count; + if (n > 32768) n = 32768; /* max per command */ + b = (n-1+PT.bs)/PT.bs; + n = b*PT.bs; /* rounded up to even block */ + + wr_cmd[4] = b; + + r = pt_command(unit,wr_cmd,n,"write"); + + udelay(1000); + + if (r) { /* error delivering command only */ + pt_req_sense(unit,0); + return -EIO; + } + + while (1) { + + r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY, + DBMSG("write DRQ"),NULL); + + if (r & STAT_SENSE) { + pi_disconnect(PI); + pt_req_sense(unit,0); + return -EIO; + } + + if (r) PT.flags |= PT_EOF; + + s = RR(0,7); + + if (!(s & STAT_DRQ)) break; + + n = (RR(0,4)+256*RR(0,5)); + p = (RR(0,2)&3); + if (p != 0) { + pi_disconnect(PI); + printk("%s: Phase error on write: %d \n",PT.name,p); + return -EIO; + } + + while (n > 0) { + k = n; + if (k > PT_BUFSIZE) k = PT_BUFSIZE; + b = k; + if (b > count) b = count; + memcpy_fromfs(PT.bufptr,buf+t,b); + pi_write_block(PI,PT.bufptr,k); + t += b; + count -= b; + n -= k; + } + + } + pi_disconnect(PI); + if (PT.flags & PT_EOF) break; + } + + return t; +} + +/* end of pt.c */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/setup.h linux/drivers/block/paride/setup.h --- v2.0.34/linux/drivers/block/paride/setup.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/setup.h Mon Jul 13 13:47:28 1998 @@ -0,0 +1,69 @@ +/* + setup.h (c) 1997-8 Grant R. Guenther <grant@torque.net> + Under the terms of the GNU public license. + + This is a table driven setup function for kernel modules + using the module.variable=val,... command line notation. + +*/ + +/* Changes: + + 1.01 GRG 1998.05.05 Allow negative and defaulted values + +*/ + +#include <linux/ctype.h> +#include <linux/string.h> + +struct setup_tab_t { + + char *tag; /* variable name */ + int size; /* number of elements in array */ + int *iv; /* pointer to variable */ +}; + +typedef struct setup_tab_t STT; + +/* t is a table that describes the variables that can be set + by gen_setup + n is the number of entries in the table + ss is a string of the form: + + <tag>=[<val>,...]<val> +*/ + +static void generic_setup( STT t[], int n, char *ss ) + +{ int j,k, sgn; + + k = 0; + for (j=0;j<n;j++) { + k = strlen(t[j].tag); + if (strncmp(ss,t[j].tag,k) == 0) break; + } + if (j == n) return; + + if (ss[k] == 0) { + t[j].iv[0] = 1; + return; + } + + if (ss[k] != '=') return; + ss += (k+1); + + k = 0; + while (ss && (k < t[j].size)) { + if (!*ss) break; + sgn = 1; + if (*ss == '-') { ss++; sgn = -1; } + if (!*ss) break; + if (isdigit(*ss)) + t[j].iv[k] = sgn * simple_strtoul(ss,NULL,0); + k++; + if ((ss = strchr(ss,',')) != NULL) ss++; + } +} + +/* end of setup.h */ + diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/spinlock.h linux/drivers/block/paride/spinlock.h --- v2.0.34/linux/drivers/block/paride/spinlock.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/spinlock.h Mon Jul 13 13:47:28 1998 @@ -0,0 +1,6 @@ +/* spinlock.h -- dummy version for PARIDE-2.0.34 */ + +#define spin_lock_irqsave(a,b) { save_flags(b); cli(); } +#define spin_unlock_irqrestore(a,b) restore_flags(b); + + diff -u --recursive --new-file v2.0.34/linux/drivers/block/raid0.c linux/drivers/block/raid0.c --- v2.0.34/linux/drivers/block/raid0.c Mon Jul 13 13:46:26 1998 +++ linux/drivers/block/raid0.c Mon Jul 13 13:47:28 1998 @@ -243,6 +243,7 @@ data->strip_zone[j].size); } #endif + sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(mddev))); return sz; } @@ -251,11 +252,14 @@ { "raid0", raid0_map, + NULL, /* no special make_request */ + NULL, /* no special end_request */ raid0_run, raid0_stop, raid0_status, NULL, /* no ioctls */ - 0 + 0, + NULL, /* no error_handler */ }; diff -u --recursive --new-file v2.0.34/linux/drivers/block/raid1.c linux/drivers/block/raid1.c --- v2.0.34/linux/drivers/block/raid1.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/raid1.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,766 @@ +/************************************************************************ + * raid1.c : Multiple Devices driver for Linux + * Copyright (C) 1996 Ingo Molnar, Miguel de Icaza, Gadi Oxman + * + * RAID-1 management functions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/locks.h> +#include <linux/malloc.h> +#include <linux/md.h> +#include <linux/raid1.h> +#include <asm/bitops.h> +#include <asm/atomic.h> + +#define MAJOR_NR MD_MAJOR +#define MD_DRIVER +#define MD_PERSONALITY + +/* + * The following can be used to debug the driver + */ +/*#define RAID1_DEBUG*/ +#ifdef RAID1_DEBUG +#define PRINTK(x) do { printk x; } while (0); +#else +#define PRINTK(x) do { ; } while (0); +#endif + + +static struct md_personality raid1_personality; +static struct md_thread *raid1_thread = NULL; +struct buffer_head *raid1_retry_list = NULL; + +static int __raid1_map (struct md_dev *mddev, kdev_t *rdev, + unsigned long *rsector, unsigned long size) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + int i, n = raid_conf->raid_disks; + + /* + * Later we do read balancing on the read side + * now we use the first available disk. + */ + + PRINTK(("raid1_map().\n")); + + for (i=0; i<n; i++) { + if (raid_conf->mirrors[i].operational) { + *rdev = raid_conf->mirrors[i].dev; + return (0); + } + } + + printk (KERN_ERR "raid1_map(): huh, no more operational devices?\n"); + return (-1); +} + +static int raid1_map (struct md_dev *mddev, kdev_t *rdev, + unsigned long *rsector, unsigned long size) +{ + return 0; +} + +void raid1_reschedule_retry (struct buffer_head *bh) +{ + struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->private_bh); + + PRINTK(("raid1_reschedule_retry().\n")); + + r1_bh->next_retry = raid1_retry_list; + raid1_retry_list = bh; + md_wakeup_thread(raid1_thread); +} + +/* + * raid1_end_buffer_io() is called when we have finished servicing a mirrored + * operation and are ready to return a success/failture code to the buffer + * cache layer. + */ +static inline void raid1_end_buffer_io (struct buffer_head *bh, int uptodate) +{ + /* + * kfree() can sleep and we try to keep this bh operation atomic. + */ + struct raid1_bh * tmp = (struct raid1_bh *) bh->private_bh; + + clear_bit (BH_MD, &bh->b_state); + bh->private_bh = NULL; + bh->personality = NULL; + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + kfree(tmp); +} + +void raid1_end_request (struct buffer_head *bh, int uptodate) +{ + struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->private_bh); + unsigned long flags; + + save_flags(flags); + cli(); + PRINTK(("raid1_end_request().\n")); + + /* + * this branch is our 'one mirror IO has finished' event handler: + */ + if (!uptodate) + md_error (bh->b_dev, bh->b_rdev); + else { + /* + * Set BH_Uptodate in our master buffer_head, so that + * we will return a good error code for to the higher + * levels even if IO on some other mirrored buffer fails. + * + * The 'master' represents the complex operation to + * user-side. So if something waits for IO, then it will + * wait for the 'master' buffer_head. + */ + set_bit (BH_Uptodate, &r1_bh->state); + } + + /* + * We split up the read and write side, imho they are + * conceptually different. + */ + + if ( (r1_bh->cmd == READ) || (r1_bh->cmd == READA) ) { + + PRINTK(("raid1_end_request(), read branch.\n")); + + /* + * we have only one buffer_head on the read side + */ + if (uptodate) { + PRINTK(("raid1_end_request(), read branch, uptodate.\n")); + raid1_end_buffer_io (bh, uptodate); + restore_flags(flags); + return; + } + /* + * oops, read error: + */ + printk(KERN_ERR "raid1: %s: rescheduling block %lu\n", + kdevname(bh->b_dev), bh->b_blocknr); + raid1_reschedule_retry (bh); + restore_flags(flags); + return; + } + + /* + * WRITE or WRITEA. + */ + PRINTK(("raid1_end_request(), write branch.\n")); + + /* + * lets see if all mirrored write operations have finished + * already [we have irqs off, so we can decrease]: + */ + + if (!--r1_bh->remaining) { + struct md_dev *mddev = r1_bh->mddev; + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + int i, n = raid_conf->raid_disks; + + PRINTK(("raid1_end_request(), remaining == 0.\n")); + + /* + * kfree() can sleep? really? if yes then we are + * doomed here ... + */ + for ( i=0; i<n; i++) { + if (r1_bh->mirror_bh[i]) kfree(r1_bh->mirror_bh[i]); + } + + /* + * the 'master' bh is the one that is used in page IO, + * perhaps someone is waiting on it. Lets erase all + * signs of mirroring, and lets finish the bh operation: + * + * In particular, the "uptodate" value which we return + * to the higher level represents the entire mirror set. + * + * yes, and this is why i want to use the 'master' bh as + * a 'representative'. Thats why i think it's not clean to + * use the master bh for real IO. We mix concepts, which + * isnt too good. + * + * a buffer_head is basically a user-side file buffer. + * Normally it has direct relationship with the physical + * device, but as in this case, we have an abstract mapping + * between the file buffer and the physical layout. So i've + * reverted all changes that do this mixing. + * + * we 'waste' about 76 bytes for the one more buffer_head, + * but note that we will do the mirror bh allocation at once + * in the future, so this isnt really a valid point, i think. + * + * Also i dont like the current way of mixing the user-side buffer + * concept with the 'real' physical layout like raid0.c does + * now: it increases the size of buffer_head even for nonstriped + * devices, etc. + * + * IMHO, in the future, we should have a lightweight buffer_head + * structure, which holds almost no physical device information. + + * Abstract relationship between buffers: + * ===================================== + * + * [user] + * | + * | + * ['master' buffer_head] + [private_buffer_head] + * | + * | + * | + * [additional 'sub'-buffer_heads] + * | | | + * [dev1] [dev2] [dev3] + * + + * In this scheme it's not clean to use the 'master' as one of + * the 'sub' buffer_heads. If you think about it, currently we can + * do this only because raid0 introduced it's own private_buffer_head + * structure in buffer_head: rdev,rsector. And raid0 has a 1:1 + * relationship to the physical device. But this is really just a + * special case. Once we have our megafast bh pools running, we could + * clean up raid0.c too :)) + * + * Not that it isnt clean, it is lethal if in the future we insert our + * sub buffer_heads into the global block cache. The master request + * should be an IO operation label for the complex operation, nothing + * more. + * + * So we have almost no performance arguments, and alot of cleanness + * arguments. + * + * Comments? Gonna change it back to your way again if you can convince + * me :)) --mingo + * + */ + raid1_end_buffer_io ( r1_bh->master_bh, + test_bit (BH_Uptodate, &r1_bh->state)); + } + else PRINTK(("raid1_end_request(), remaining == %u.\n", r1_bh->remaining)); + restore_flags(flags); +} + +/* This routine checks if the undelying device is an md device and in that + * case it maps the blocks before putting the request on the queue + */ +static inline void +map_and_make_request (int rw, struct buffer_head *bh) +{ + if (MAJOR (bh->b_rdev) == MD_MAJOR){ + md_map (MINOR (bh->b_rdev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9); + } + make_request (MAJOR (bh->b_rdev), rw, bh); +} + +static int +raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) +{ + + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + struct buffer_head *mirror_bh[MD_SB_DISKS]; + struct raid1_bh * r1_bh; + int n = raid_conf->raid_disks, i, sum_bhs = 0, switch_disks = 0, sectors; + struct mirror_info *mirror; + + PRINTK(("raid1_make_request().\n")); + +/* + * We put allocations at the beginning, to avoid sleeping while doing + * atomic operations of buffer heads. This might or might not make much + * difference, but lets rather be careful. + * + * but this has two side effects (probably non harmless): + * + * 1. The buffer will not be locked while we sleep. + * 2. The rest of the kernel will see BH_Req without + * BH_Lock. + */ + while (!( /* FIXME: now we are rather fault tolerant than nice */ + r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL) + ) ) + printk ("raid1_make_request(#1): out of memory\n"); + memset (r1_bh, 0, sizeof (struct raid1_bh)); +/* + * make_request() can abort the operation when READA or WRITEA are being + * used and no empty request is available. + * + * Currently, just replace the command with READ/WRITE. + */ + if (rw == READA) rw = READ; + if (rw == WRITEA) rw = WRITE; + + if (rw == WRITE || rw == WRITEA) + mark_buffer_clean(bh); /* Too early ? */ + +/* + * i think the read and write branch should be separated completely, since we want + * to do read balancing on the read side for example. Comments? :) --mingo + */ + + r1_bh->master_bh=bh; + r1_bh->mddev=mddev; + r1_bh->cmd = rw; + + set_bit (BH_MD, &bh->b_state); + bh->personality = &raid1_personality; + bh->private_bh = (void*)(r1_bh); + + if (rw==READ || rw==READA) { + int last_used = raid_conf->last_used; + PRINTK(("raid1_make_request(), read branch.\n")); + mirror = raid_conf->mirrors + last_used; + bh->b_rdev = mirror->dev; + sectors = bh->b_size >> 9; + if (bh->b_blocknr * sectors == raid_conf->next_sect) { + raid_conf->sect_count += sectors; + if (raid_conf->sect_count >= mirror->sect_limit) + switch_disks = 1; + } else + switch_disks = 1; + raid_conf->next_sect = (bh->b_blocknr + 1) * sectors; + if (switch_disks) { + PRINTK(("read-balancing: switching %d -> %d (%d sectors)\n", last_used, mirror->next, raid_conf->sect_count)); + raid_conf->sect_count = 0; + raid_conf->last_used = mirror->next; + } + PRINTK (("raid1 read queue: %d %d\n", MAJOR (bh->b_rdev), MINOR (bh->b_rdev))); + + clear_bit (BH_Lock, &bh->b_state); + map_and_make_request (rw, bh); + return 0; + } + + /* + * WRITE or WRITEA. + */ +/* + * btw, we have no more master disk. 'slave' is gone too :) [i hate that word :))] + * + * We are now using the master bh for a real IO. It seems important that: + * + * 1. lock_buffer() will be called when we start to handle the request, + * before we do anything (done by ll_rw_blk.c). + * + * 2. It seems that Linus took great care to set mark_buffer_clean() + * atomically with cli() in effect just when the buffer was placed + * into the queue. To be compatible with this behavior, it would be + * best to lock the buffer *first*, but mark it clean *last*, and to + * do this by passing through the exact logic in ll_rw_blk.c. + * + * Note: i've reverted this #3 thing, see the big comment in this file. + * + * 3. We are now called from within make_request(), so the real bh + * will be automatically handled last when we return, so we only need + * to add the rest of the buffers (but remember to include the + * master bh in the remaining count). + */ + PRINTK(("raid1_make_request(n=%d), write branch.\n",n)); + + for (i = 0; i < n; i++) { + + if (!raid_conf->mirrors [i].operational) { + /* + * the r1_bh->mirror_bh[i] pointer remains NULL + */ + mirror_bh[i] = NULL; + continue; + } + + /* + * We should use a private pool (size depending on NR_REQUEST), + * to avoid writes filling up the memory with bhs + * + * Such pools are much faster than kmalloc anyways (so we waste almost + * nothing by not using the master bh when writing and win alot of cleanness) + * + * but for now we are cool enough. --mingo + * + * It's safe to sleep here, buffer heads cannot be used in a shared + * manner in the write branch. Look how we lock the buffer at the beginning + * of this function to grok the difference ;) + */ + while (!( /* FIXME: now we are rather fault tolerant than nice */ + mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL) + ) ) + printk ("raid1_make_request(#2): out of memory\n"); + memset (mirror_bh[i], 0, sizeof (struct buffer_head)); + + /* + * prepare mirrored bh (fields ordered for max mem throughput): + */ + mirror_bh [i]->b_blocknr = bh->b_blocknr; + mirror_bh [i]->b_dev = bh->b_dev; + mirror_bh [i]->b_rdev = raid_conf->mirrors [i].dev; + mirror_bh [i]->b_rsector = bh->b_rsector; + mirror_bh [i]->b_state = (1<<BH_MD) | (1<<BH_Req) | + (1<<BH_Touched) | (1<<BH_Dirty); + mirror_bh [i]->b_count = 1; + mirror_bh [i]->b_size = bh->b_size; + mirror_bh [i]->b_data = bh->b_data; + mirror_bh [i]->b_list = BUF_LOCKED; + mirror_bh [i]->personality = &raid1_personality; + mirror_bh [i]->private_bh = (void*)(r1_bh); + + r1_bh->mirror_bh[i] = mirror_bh[i]; + sum_bhs++; + } + + r1_bh->remaining = sum_bhs; + + PRINTK(("raid1_make_request(), write branch, sum_bhs=%d.\n",sum_bhs)); + + /* + * We have to be a bit careful about the semaphore above, thats why we + * start the requests separately. Since kmalloc() could fail, sleep and + * make_request() can sleep too, this is the safer solution. Imagine, + * end_request decreasing the semaphore before we could have set it up ... + * We could play tricks with the semaphore (presetting it and correcting + * at the end if sum_bhs is not 'n' but we have to do end_request by hand + * if all requests finish until we had a chance to set up the semaphore + * correctly ... lots of races). + */ + for (i = 0; i < n; i++) + if (mirror_bh [i] != NULL) + map_and_make_request (rw, mirror_bh [i]); + + return (0); +} + +static int raid1_status (char *page, int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + int sz = 0, i; + + sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks); + for (i = 0; i < raid_conf->raid_disks; i++) + sz += sprintf (page+sz, "%s", raid_conf->mirrors [i].operational ? "U" : "_"); + sz += sprintf (page+sz, "]"); + return sz; +} + +static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index) +{ + int disks = raid_conf->raid_disks; + int j; + + for (j = 0; j < disks; j++) + if (raid_conf->mirrors [j].next == failed_index) + raid_conf->mirrors [j].next = raid_conf->mirrors [failed_index].next; +} + +static int raid1_error (struct md_dev *mddev, kdev_t dev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + struct mirror_info *mirror; + md_superblock_t *sb = mddev->sb; + int disks = raid_conf->raid_disks; + int i; + + PRINTK(("raid1_error called\n")); + + if (raid_conf->working_disks == 1) { + /* + * Uh oh, we can do nothing if this is our last disk, but + * first check if this is a queued request for a device + * which has just failed. + */ + for (i = 0, mirror = raid_conf->mirrors; i < disks; i++, mirror++) + if (mirror->dev == dev && !mirror->operational) + return 0; + printk (KERN_ALERT "RAID1: only one disk left and IO error.\n"); + return 0; + } + + /* Mark disk as unusable */ + for (i = 0, mirror = raid_conf->mirrors; i < disks; i++, mirror++) { + if (mirror->dev == dev && mirror->operational){ + mirror->operational = 0; + raid1_fix_links (raid_conf, i); + sb->disks[mirror->number].state |= (1 << MD_FAULTY_DEVICE); + sb->disks[mirror->number].state &= ~(1 << MD_SYNC_DEVICE); + sb->disks[mirror->number].state &= ~(1 << MD_ACTIVE_DEVICE); + sb->active_disks--; + sb->working_disks--; + sb->failed_disks++; + mddev->sb_dirty = 1; + md_wakeup_thread(raid1_thread); + raid_conf->working_disks--; + printk (KERN_ALERT + "RAID1: Disk failure on %s, disabling device." + "Operation continuing on %d devices\n", + kdevname (dev), raid_conf->working_disks); + } + } + + return 0; +} + +/* + * This is a kernel thread which: + * + * 1. Retries failed read operations on working mirrors. + * 2. Updates the raid superblock when problems are encountered. + */ +void raid1d (void *data) +{ + struct buffer_head *bh; + kdev_t dev; + unsigned long flags; + struct raid1_bh * r1_bh; + struct md_dev *mddev; + + PRINTK(("raid1d() active\n")); + save_flags(flags); + cli(); + while (raid1_retry_list) { + bh = raid1_retry_list; + r1_bh = (struct raid1_bh *)(bh->private_bh); + raid1_retry_list = r1_bh->next_retry; + restore_flags(flags); + + mddev = md_dev + MINOR(bh->b_dev); + if (mddev->sb_dirty) { + mddev->sb_dirty = 0; + md_update_sb(MINOR(bh->b_dev)); + } + dev = bh->b_rdev; + __raid1_map (md_dev + MINOR(bh->b_dev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9); + if (bh->b_rdev == dev) { + printk (KERN_ALERT + "raid1: %s: unrecoverable I/O read error for block %lu\n", + kdevname(bh->b_dev), bh->b_blocknr); + raid1_end_buffer_io (bh, 0); + } else { + printk (KERN_ERR "raid1: %s: redirecting sector %lu to another mirror\n", + kdevname(bh->b_dev), bh->b_blocknr); + clear_bit (BH_Lock, &bh->b_state); + map_and_make_request (r1_bh->cmd, bh); + } + cli(); + } + restore_flags(flags); + +} + +/* + * This will catch the scenario in which one of the mirrors was + * mounted as a normal device rather than as a part of a raid set. + */ +static int check_consistenty (struct md_dev *mddev) +{ + struct raid1_data *raid_conf = mddev->private; + kdev_t dev; + struct buffer_head *bh = NULL; + int i, rc = 0; + char *buffer = NULL; + + for (i = 0; i < raid_conf->raid_disks; i++) { + if (!raid_conf->mirrors[i].operational) + continue; + dev = raid_conf->mirrors[i].dev; + set_blocksize(dev, 4096); + if ((bh = bread(dev, 0, 4096)) == NULL) + break; + if (!buffer) { + buffer = (char *) __get_free_page(GFP_KERNEL); + if (!buffer) + break; + memcpy(buffer, bh->b_data, 4096); + } else if (memcmp(buffer, bh->b_data, 4096)) { + rc = 1; + break; + } + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + bh = NULL; + } + if (buffer) + free_page((unsigned long) buffer); + if (bh) { + dev = bh->b_dev; + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + } + return rc; +} + +static int raid1_run (int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf; + int i, j, raid_disk; + md_superblock_t *sb = mddev->sb; + md_descriptor_t *descriptor; + struct real_dev *realdev; + + MOD_INC_USE_COUNT; + + if (sb->level != 1) { + printk("raid1: %s: raid level not set to mirroring (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + MOD_DEC_USE_COUNT; + return -EIO; + } + /**** + * copy the now verified devices into our private RAID1 bookkeeping area: + * + * [whatever we allocate in raid1_run(), should be freed in raid1_stop()] + */ + + while (!( /* FIXME: now we are rather fault tolerant than nice */ + mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL) + ) ) + printk ("raid1_run(): out of memory\n"); + raid_conf = mddev->private; + memset(raid_conf, 0, sizeof(*raid_conf)); + + PRINTK(("raid1_run(%d) called.\n", minor)); + + for (i = 0; i < mddev->nb_dev; i++) { + realdev = &mddev->devices[i]; + if (!realdev->sb) { + printk(KERN_ERR "raid1: disabled mirror %s (couldn't access raid superblock)\n", kdevname(realdev->dev)); + continue; + } + + /* + * This is important -- we are using the descriptor on + * the disk only to get a pointer to the descriptor on + * the main superblock, which might be more recent. + */ + descriptor = &sb->disks[realdev->sb->descriptor.number]; + if (descriptor->state & (1 << MD_FAULTY_DEVICE)) { + printk(KERN_ERR "raid1: disabled mirror %s (errors detected)\n", kdevname(realdev->dev)); + continue; + } + if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) { + if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) { + printk(KERN_ERR "raid1: disabled mirror %s (not in sync)\n", kdevname(realdev->dev)); + continue; + } + raid_disk = descriptor->raid_disk; + if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) { + printk(KERN_ERR "raid1: disabled mirror %s (inconsistent descriptor)\n", kdevname(realdev->dev)); + continue; + } + if (raid_conf->mirrors[raid_disk].operational) { + printk(KERN_ERR "raid1: disabled mirror %s (mirror %d already operational)\n", kdevname(realdev->dev), raid_disk); + continue; + } + printk(KERN_INFO "raid1: device %s operational as mirror %d\n", kdevname(realdev->dev), raid_disk); + raid_conf->mirrors[raid_disk].number = descriptor->number; + raid_conf->mirrors[raid_disk].raid_disk = raid_disk; + raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev; + raid_conf->mirrors[raid_disk].operational = 1; + raid_conf->mirrors[raid_disk].sect_limit = 128; + raid_conf->working_disks++; + } + } + if (!raid_conf->working_disks) { + printk(KERN_ERR "raid1: no operational mirrors for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + kfree(raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return -EIO; + } + + raid_conf->raid_disks = sb->raid_disks; + raid_conf->mddev = mddev; + + for (j = 0; !raid_conf->mirrors[j].operational; j++); + raid_conf->last_used = j; + for (i = raid_conf->raid_disks - 1; i >= 0; i--) { + if (raid_conf->mirrors[i].operational) { + PRINTK(("raid_conf->mirrors[%d].next == %d\n", i, j)); + raid_conf->mirrors[i].next = j; + j = i; + } + } + + if (check_consistenty(mddev)) { + printk(KERN_ERR "raid1: detected mirror differences -- run ckraid\n"); + sb->state |= 1 << MD_SB_ERRORS; + kfree(raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return -EIO; + } + + /* + * Regenerate the "device is in sync with the raid set" bit for + * each device. + */ + for (i = 0; i < sb->nr_disks ; i++) { + sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE); + for (j = 0; j < sb->raid_disks; j++) { + if (!raid_conf->mirrors[j].operational) + continue; + if (sb->disks[i].number == raid_conf->mirrors[j].number) + sb->disks[i].state |= 1 << MD_SYNC_DEVICE; + } + } + sb->active_disks = raid_conf->working_disks; + + printk("raid1: raid set %s active with %d out of %d mirrors\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks); + /* Ok, everything is just fine now */ + return (0); +} + +static int raid1_stop (int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + + kfree (raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return 0; +} + +static struct md_personality raid1_personality= +{ + "raid1", + raid1_map, + raid1_make_request, + raid1_end_request, + raid1_run, + raid1_stop, + raid1_status, + NULL, /* no ioctls */ + 0, + raid1_error +}; + +int raid1_init (void) +{ + if ((raid1_thread = md_register_thread(raid1d, NULL)) == NULL) + return -EBUSY; + return register_md_personality (RAID1, &raid1_personality); +} + +#ifdef MODULE +int init_module (void) +{ + return raid1_init(); +} + +void cleanup_module (void) +{ + md_unregister_thread (raid1_thread); + unregister_md_personality (RAID1); +} +#endif diff -u --recursive --new-file v2.0.34/linux/drivers/block/raid5.c linux/drivers/block/raid5.c --- v2.0.34/linux/drivers/block/raid5.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/raid5.c Mon Jul 13 13:47:28 1998 @@ -0,0 +1,1504 @@ +/***************************************************************************** + * raid5.c : Multiple Devices driver for Linux + * Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman + * + * RAID-5 management functions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/locks.h> +#include <linux/malloc.h> +#include <linux/md.h> +#include <linux/raid5.h> +#include <asm/bitops.h> +#include <asm/atomic.h> + +struct buffer_head *efind_buffer(kdev_t dev, int block, int size); + +#define MAJOR_NR MD_MAJOR +#define MD_DRIVER +#define MD_PERSONALITY + +static struct md_personality raid5_personality; + +struct stripe_head { + struct stripe_head *hash_next, **hash_pprev; /* hash pointers */ + struct stripe_head *handle_next; /* completed during hash scan pointers */ + struct raid5_data *raid_conf; + struct buffer_head *bh_old[MD_SB_DISKS]; /* disk image */ + struct buffer_head *bh_new[MD_SB_DISKS]; /* buffers of the MD device (present in buffer cache) */ + struct buffer_head *bh_copy[MD_SB_DISKS]; /* copy on write of bh_new (bh_new can change from under us) */ + int cmd_new[MD_SB_DISKS]; /* READ/WRITE for new */ + int new[MD_SB_DISKS]; /* buffer added since the last handle_stripe() */ + unsigned long sector; /* sector of this row */ + int size; /* buffers size */ + int pd_idx; /* parity disk index */ + int nr_pending; /* nr of pending cmds */ + __u32 state; /* state flags */ + int cmd; /* stripe cmd */ + int count; /* nr of waiters */ + int write_method; /* reconstruct-write / read-modify-write */ + int phase; /* PHASE_BEGIN, ..., PHASE_COMPLETE */ + struct wait_queue *wait; /* processes waiting for this stripe */ +}; + +/* + * Phase + */ +#define PHASE_BEGIN 0 +#define PHASE_READ_OLD 1 +#define PHASE_WRITE 2 +#define PHASE_READ 3 +#define PHASE_COMPLETE 4 + +/* + * Write method + */ +#define METHOD_NONE 0 +#define RECONSTRUCT_WRITE 1 +#define READ_MODIFY_WRITE 2 + +/* + * Stripe state + */ +#define STRIPE_LOCKED 0 +#define STRIPE_ERROR 1 + +/* + * Stripe commands + */ +#define STRIPE_NONE 0 +#define STRIPE_WRITE 1 +#define STRIPE_READ 2 + +/* + * Stripe cache + */ +#define RAID5_STRIPE_POOL_SIZE 128 +#define HASH_PAGES 1 +#define HASH_PAGES_ORDER 0 +#define NR_HASH (HASH_PAGES * PAGE_SIZE / sizeof(struct stripe_head *)) +#define HASH_MASK (NR_HASH - 1) +#define stripe_hash(sect, size) (stripe_hashtbl[((sect) / (size >> 9)) & HASH_MASK]) + +int nr_stripes = 0, nr_locked_stripes = 0, nr_pending_stripes = 0; +struct stripe_head **stripe_hashtbl; +static struct wait_queue *raid5_wait_for_stripe = NULL; +struct stripe_head *stripe_handle_list = NULL, *stripe_handle_tail = NULL; + +/* + * Free buffers pool + */ +#define RAID5_POOL_SIZE 3000 +static int nr_free_buffers = 0, nr_used_buffers = 0, max_nr_used_buffers = 0; +static struct buffer_head *raid5_buffer_list = NULL; +static struct wait_queue *raid5_wait_for_bh = NULL; + +/* + * The following can be used to debug the driver + */ +#define RAID5_DEBUG 0 + +#if RAID5_DEBUG +#define PRINTK(x) do { printk x; } while (0); +static int nr_pending = 0, free_1024 = 0, free_4096 = 0, used_1024 = 0, used_4096 = 0; +#else +#define PRINTK(x) do { ; } while (0) +#endif + +static inline int stripe_locked(struct stripe_head *sh) +{ + return test_bit(STRIPE_LOCKED, &sh->state); +} + +static inline int stripe_error(struct stripe_head *sh) +{ + return test_bit(STRIPE_ERROR, &sh->state); +} + +/* + * Stripes are locked whenever new buffers can't be added to them. + */ +static inline void lock_stripe(struct stripe_head *sh) +{ + if (!set_bit(STRIPE_LOCKED, &sh->state)) { + PRINTK(("locking stripe %lu\n", sh->sector)); + nr_locked_stripes++; + } +} + +static inline void unlock_stripe(struct stripe_head *sh) +{ + if (clear_bit(STRIPE_LOCKED, &sh->state)) { + PRINTK(("unlocking stripe %lu\n", sh->sector)); + nr_locked_stripes--; + wake_up(&sh->wait); + } +} + +static inline void finish_stripe(struct stripe_head *sh) +{ + unlock_stripe(sh); + sh->cmd = STRIPE_NONE; + sh->phase = PHASE_COMPLETE; + nr_pending_stripes--; + wake_up(&raid5_wait_for_stripe); +} + +static void unplug_devices(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int i; + + for (i = 0; i < raid_conf->raid_disks; i++) + unplug_device(blk_dev + MAJOR(raid_conf->disks[i].dev)); +} + +static void raid5d (void *data); + +void __wait_on_stripe(struct stripe_head *sh) +{ + struct wait_queue wait = { current, NULL }; + + PRINTK(("wait_on_stripe %lu\n", sh->sector)); + sh->count++; + add_wait_queue(&sh->wait, &wait); +repeat: + current->state = TASK_UNINTERRUPTIBLE; + if (stripe_locked(sh)) { + schedule(); + goto repeat; + } + PRINTK(("wait_on_stripe %lu done\n", sh->sector)); + remove_wait_queue(&sh->wait, &wait); + sh->count--; + current->state = TASK_RUNNING; +} + +static inline void wait_on_stripe(struct stripe_head *sh) +{ + if (stripe_locked(sh)) + __wait_on_stripe(sh); +} + +static inline void remove_hash(struct stripe_head *sh) +{ + PRINTK(("remove_hash(), stripe %lu\n", sh->sector)); + + if (sh->hash_pprev) { + if (sh->hash_next) + sh->hash_next->hash_pprev = sh->hash_pprev; + *sh->hash_pprev = sh->hash_next; + sh->hash_pprev = NULL; + nr_stripes--; + } +} + +static inline void insert_hash(struct stripe_head *sh) +{ + struct stripe_head **shp = &stripe_hash(sh->sector, sh->size); + + PRINTK(("insert_hash(), stripe %lu, nr_stripes %d\n", sh->sector, nr_stripes)); + + if ((sh->hash_next = *shp) != NULL) + (*shp)->hash_pprev = &sh->hash_next; + *shp = sh; + sh->hash_pprev = shp; + nr_stripes++; +} + +static void add_bh (struct buffer_head *bh) +{ + unsigned long flags; + + save_flags(flags); + cli(); + bh->b_next = raid5_buffer_list; + raid5_buffer_list = bh; + nr_free_buffers++; +#if RAID5_DEBUG + if (bh->b_size == 1024) + free_1024++; + if (bh->b_size == 4096) + free_4096++; +#endif + restore_flags(flags); +} + +static void raid5_kfree_bh (struct buffer_head *bh) +{ + unsigned long flags; + + save_flags(flags); + cli(); + nr_used_buffers--; +#if RAID5_DEBUG + if (bh->b_size == 1024) + used_1024--; + if (bh->b_size == 4096) + used_4096--; +#endif + if (nr_free_buffers < RAID5_POOL_SIZE) { +#if 0 /* This can magically catch races :-) */ + char *b_data = ((volatile struct buffer_head *) bh)->b_data; + int b_size = ((volatile struct buffer_head *) bh)->b_size; + memset (bh, 0, sizeof (struct buffer_head)); + ((volatile struct buffer_head *) bh)->b_data = b_data; + ((volatile struct buffer_head *) bh)->b_size = b_size; +#endif + add_bh (bh); + wake_up (&raid5_wait_for_bh); + } else { + if (bh->b_size == PAGE_SIZE) + free_page ((unsigned long) bh->b_data); + else + kfree (bh->b_data); +#if 0 + memset (bh, 0, sizeof (struct buffer_head)); +#endif + kfree (bh); + } +#if RAID5_DEBUG + printk ("kfree_bh: nr_free == %d, nr_used == %d, max_nr_used == %d\n", nr_free_buffers, nr_used_buffers, max_nr_used_buffers); +#endif + restore_flags(flags); +} + +static void raid5_kfree_old_bh(struct stripe_head *sh, int i) +{ + if (!sh->bh_old[i]) { + printk("raid5_kfree_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i); + return; + } + raid5_kfree_bh(sh->bh_old[i]); + sh->bh_old[i] = NULL; +} + +static void raid5_update_old_bh(struct stripe_head *sh, int i) +{ + PRINTK(("stripe %lu, idx %d, updating cache copy\n", sh->sector, i)); + if (!sh->bh_copy[i]) { + printk("raid5_update_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i); + return; + } + if (sh->bh_old[i]) + raid5_kfree_old_bh(sh, i); + sh->bh_old[i] = sh->bh_copy[i]; + sh->bh_copy[i] = NULL; +} + +static void kfree_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int disks = raid_conf->raid_disks, j; + + PRINTK(("kfree_stripe called, stripe %lu\n", sh->sector)); + if (sh->phase != PHASE_COMPLETE || stripe_locked(sh) || sh->count) { + printk("raid5: kfree_stripe(), sector %lu, phase %d, locked %d, count %d\n", sh->sector, sh->phase, stripe_locked(sh), sh->count); + return; + } + for (j = 0; j < disks; j++) { + if (sh->bh_old[j]) + raid5_kfree_old_bh(sh, j); + if (sh->bh_new[j] || sh->bh_copy[j]) + printk("raid5: bug: sector %lu, new %p, copy %p\n", sh->sector, sh->bh_new[j], sh->bh_copy[j]); + } + remove_hash(sh); + kfree(sh); +} + +static int shrink_stripe_cache(int nr) +{ + struct stripe_head *sh; + int i, count = 0; + static int clock = 0; + + PRINTK(("shrink_stripe_cache called, %d/%d, clock %d\n", nr, nr_stripes, clock)); + for (i = 0; i < NR_HASH; i++) { +repeat: + sh = stripe_hashtbl[(i + clock) & HASH_MASK]; + for (; sh; sh = sh->hash_next) { + if (sh->phase != PHASE_COMPLETE) + continue; + if (stripe_locked(sh)) + continue; + if (sh->count) + continue; + kfree_stripe(sh); + if (++count == nr) { + PRINTK(("shrink completed, nr_stripes %d\n", nr_stripes)); + clock = (i + clock) & HASH_MASK; + return nr; + } + goto repeat; + } + } + PRINTK(("shrink completed, nr_stripes %d\n", nr_stripes)); + return count; +} + +static struct stripe_head *find_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +{ + struct stripe_head *sh; + + if (raid_conf->buffer_size != size) { + PRINTK(("switching size, %d --> %d\n", raid_conf->buffer_size, size)); + shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE); + raid_conf->buffer_size = size; + } + + PRINTK(("find_stripe, sector %lu\n", sector)); + for (sh = stripe_hash(sector, size); sh; sh = sh->hash_next) + if (sh->sector == sector && sh->raid_conf == raid_conf) { + if (sh->size == size) { + PRINTK(("found stripe %lu\n", sector)); + return sh; + } else { + PRINTK(("switching size for %lu, %d --> %d\n", sector, sh->size, size)); + kfree_stripe(sh); + break; + } + } + PRINTK(("stripe %lu not in cache\n", sector)); + return NULL; +} + +static struct stripe_head *kmalloc_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +{ + struct stripe_head *sh = NULL, *tmp; + + PRINTK(("kmalloc_stripe called\n")); + + while (nr_stripes > RAID5_STRIPE_POOL_SIZE) { + shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE / 8); + if (nr_stripes <= RAID5_STRIPE_POOL_SIZE) + break; + md_wakeup_thread(raid_conf->thread); + PRINTK(("waiting for some stripes to complete\n")); + sleep_on(&raid5_wait_for_stripe); + } + md_wakeup_thread(raid_conf->thread); + sh = kmalloc(sizeof(*sh), GFP_KERNEL); + + /* + * The above might have slept, so perhaps another process + * already created the stripe for us.. + */ + if ((tmp = find_stripe(raid_conf, sector, size)) != NULL) { + kfree(sh); + wait_on_stripe(tmp); + return tmp; + } + if (sh) { + memset(sh, 0, sizeof(*sh)); + sh->phase = PHASE_COMPLETE; + sh->cmd = STRIPE_NONE; + sh->raid_conf = raid_conf; + sh->sector = sector; + sh->size = size; + insert_hash(sh); + } + return sh; +} + +static struct stripe_head *get_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +{ + struct stripe_head *sh; + + PRINTK(("get_stripe, sector %lu\n", sector)); + sh = find_stripe(raid_conf, sector, size); + if (sh) + wait_on_stripe(sh); + else + sh = kmalloc_stripe(raid_conf, sector, size); + return sh; +} + +static struct buffer_head *remove_bh (int b_size) +{ + struct buffer_head *bh, *bhp = NULL; + unsigned long flags; + + save_flags(flags); + cli(); + if ((bh = raid5_buffer_list) == NULL) + return NULL; + do { + if (bh->b_size == b_size || b_size == -1) + break; + bhp = bh; + bh = bh->b_next; + } while (bh); + if (!bh) + return NULL; + if (bhp) + bhp->b_next = bh->b_next; + else + raid5_buffer_list = bh->b_next; +#if RAID5_DEBUG + if (bh->b_size == 1024) + free_1024--; + if (bh->b_size == 4096) + free_4096--; +#endif + nr_free_buffers--; + if (!nr_free_buffers && raid5_buffer_list) + printk ("raid5: bug: buffer_list != NULL, nr_free_buffers == 0\n"); + restore_flags(flags); + return bh; +} + + +static void shrink_buffers (int num) +{ + struct buffer_head *bh; + + while (num--) { + if ((bh = remove_bh(-1)) == NULL) + return; + if (bh->b_size == PAGE_SIZE) + free_page ((unsigned long) bh->b_data); + else + kfree (bh->b_data); + kfree (bh); + } +} + +static void grow_buffers (int num, int b_size, int priority) +{ + struct buffer_head *bh; + + while (num--) { + bh = kmalloc (sizeof (struct buffer_head), priority); + if (!bh) + break; + memset (bh, 0, sizeof (struct buffer_head)); + if (b_size == PAGE_SIZE) + bh->b_data = (char *) __get_free_page (priority); + else + bh->b_data = kmalloc (b_size, priority); + if (!bh->b_data) { + kfree (bh); + break; + } + bh->b_size = b_size; + add_bh (bh); + } +} + +static struct buffer_head *raid5_kmalloc_bh (struct stripe_head *sh, int b_size) +{ + struct buffer_head *bh; + struct raid5_data *raid_conf = sh->raid_conf; + unsigned long flags; + + bh = remove_bh(b_size); + if (!bh && nr_free_buffers > RAID5_POOL_SIZE / 10) + shrink_buffers (RAID5_POOL_SIZE / 10); + if (!bh && nr_used_buffers < RAID5_POOL_SIZE) { +#if 0 + grow_buffers (200, b_size, GFP_BUFFER); +#else + grow_buffers (200, b_size, GFP_KERNEL); +#endif + bh = remove_bh(b_size); + } + if (bh == NULL && nr_used_buffers > RAID5_POOL_SIZE / 2) { + shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE / 2); + bh = remove_bh(b_size); + } + + while (bh == NULL && nr_used_buffers > 3 * RAID5_POOL_SIZE / 4) { + md_wakeup_thread(raid_conf->thread); + run_task_queue (&tq_disk); + unplug_devices(sh); + PRINTK(("waiting for bh\n")); + sleep_on (&raid5_wait_for_bh); + bh = remove_bh(b_size); + } + if (bh == NULL) { + grow_buffers (200, b_size, GFP_KERNEL); + bh = remove_bh(b_size); + } + if (bh) { + save_flags(flags); + cli(); + nr_used_buffers++; + if (nr_used_buffers > max_nr_used_buffers) + max_nr_used_buffers = nr_used_buffers; +#if RAID5_DEBUG + if (bh->b_size == 1024) + used_1024++; + if (bh->b_size == 4096) + used_4096++; + printk ("kmalloc_bh: free, used, pending, max = %d, %d, %d, %d\n", nr_free_buffers, nr_used_buffers, nr_pending, max_nr_used_buffers); + printk ("kmalloc_bh: free1, used1, free4, used4 = %d, %d, %d, %d\n", free_1024, used_1024, free_4096, used_4096); +#endif + restore_flags(flags); + } + return bh; +} + +static inline void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptodate) +{ + struct buffer_head *bh = sh->bh_new[i]; + + sh->bh_new[i] = NULL; + clear_bit (BH_MD, &bh->b_state); + bh->private_bh = NULL; + bh->personality = NULL; + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + if (!uptodate) + printk(KERN_ALERT "raid5: %s: unrecoverable I/O error for " + "block %lu\n", kdevname(bh->b_dev), bh->b_blocknr); +} + +static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptodate) +{ + if (uptodate) + set_bit(BH_Uptodate, &bh->b_state); + else + clear_bit(BH_Uptodate, &bh->b_state); +} + +static void raid5_end_request (struct buffer_head * bh, int uptodate) +{ + struct stripe_head *sh = bh->private_bh; + struct raid5_data *raid_conf = sh->raid_conf; + int disks = raid_conf->raid_disks, i; + unsigned long flags; + + PRINTK(("end_request %lu, nr_pending %d\n", sh->sector, sh->nr_pending)); + save_flags(flags); + cli(); + raid5_mark_buffer_uptodate(bh, uptodate); + --sh->nr_pending; + if (!sh->nr_pending) { + md_wakeup_thread(raid_conf->thread); + atomic_inc(&raid_conf->nr_handle); + if (!stripe_handle_tail) + stripe_handle_list = sh; + else + stripe_handle_tail->handle_next = sh; + sh->handle_next = NULL; + stripe_handle_tail = sh; + } + if (!uptodate) + md_error(bh->b_dev, bh->b_rdev); + if (raid_conf->failed_disks) { + for (i = 0; i < disks; i++) { + if (raid_conf->disks[i].operational) + continue; + if (bh != sh->bh_old[i] && bh != sh->bh_new[i] && bh != sh->bh_copy[i]) + continue; + set_bit(STRIPE_ERROR, &sh->state); + } + } + restore_flags(flags); +} + +static int raid5_map (struct md_dev *mddev, kdev_t *rdev, + unsigned long *rsector, unsigned long size) +{ + /* No complex mapping used: the core of the work is done in the + * request routine + */ + return 0; +} + +static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, int i) +{ + struct raid5_data *raid_conf = sh->raid_conf; + struct md_dev *mddev = raid_conf->mddev; + int minor = (int) (mddev - md_dev); + char *b_data; + + b_data = ((volatile struct buffer_head *) bh)->b_data; + memset (bh, 0, sizeof (struct buffer_head)); + ((volatile struct buffer_head *) bh)->b_data = b_data; + + bh->personality = &raid5_personality; + bh->private_bh = (void *) sh; + + bh->b_rdev = raid_conf->disks[i].dev; + bh->b_dev = MKDEV(MD_MAJOR, minor); + bh->b_rsector = sh->sector; + bh->b_blocknr = sh->sector / (sh->size >> 9); + + bh->b_state = (1 << BH_MD) | (1 << BH_Req); + bh->b_count = 1; + bh->b_size = sh->size; + bh->b_list = BUF_LOCKED; +} + +static int raid5_error (struct md_dev *mddev, kdev_t dev) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + md_superblock_t *sb = mddev->sb; + struct disk_info *disk; + int i; + + PRINTK(("raid5_error called\n")); + for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++) + if (disk->dev == dev && disk->operational) { + disk->operational = 0; + sb->disks[disk->number].state |= (1 << MD_FAULTY_DEVICE); + sb->disks[disk->number].state &= ~(1 << MD_SYNC_DEVICE); + sb->disks[disk->number].state &= ~(1 << MD_ACTIVE_DEVICE); + sb->active_disks--; + sb->working_disks--; + sb->failed_disks++; + mddev->sb_dirty = 1; + raid_conf->working_disks--; + raid_conf->failed_disks++; + md_wakeup_thread(raid_conf->thread); + printk (KERN_ALERT + "RAID5: Disk failure on %s, disabling device." + "Operation continuing on %d devices\n", + kdevname (dev), raid_conf->working_disks); + } + return 0; +} + +/* + * Input: a 'big' sector number, + * Output: index of the data and parity disk, and the sector # in them. + */ +static inline unsigned long +raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_disks, + unsigned int * dd_idx, unsigned int * pd_idx, + struct raid5_data *raid_conf) +{ + unsigned int stripe; + int chunk_number, chunk_offset; + unsigned long new_sector; + int sectors_per_chunk = raid_conf->chunk_size >> 9; + + /* First compute the information on this sector */ + + /* + * Compute the chunk number and the sector offset inside the chunk + */ + chunk_number = r_sector / sectors_per_chunk; + chunk_offset = r_sector % sectors_per_chunk; + + /* + * Compute the stripe number + */ + stripe = chunk_number / data_disks; + + /* + * Compute the data disk and parity disk indexes inside the stripe + */ + *dd_idx = chunk_number % data_disks; + + /* + * Select the parity disk based on the user selected algorithm. + */ + if (raid_conf->level == 4) + *pd_idx = data_disks; + else switch (raid_conf->algorithm) { + case ALGORITHM_LEFT_ASYMMETRIC: + *pd_idx = data_disks - stripe % raid_disks; + if (*dd_idx >= *pd_idx) + (*dd_idx)++; + break; + case ALGORITHM_RIGHT_ASYMMETRIC: + *pd_idx = stripe % raid_disks; + if (*dd_idx >= *pd_idx) + (*dd_idx)++; + break; + case ALGORITHM_LEFT_SYMMETRIC: + *pd_idx = data_disks - stripe % raid_disks; + *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks; + break; + case ALGORITHM_RIGHT_SYMMETRIC: + *pd_idx = stripe % raid_disks; + *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks; + break; + default: + printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm); + } + + /* + * Finally, compute the new sector number + */ + new_sector = stripe * sectors_per_chunk + chunk_offset; + +#if 0 + if ( *dd_idx > data_disks || *pd_idx > data_disks || + chunk_offset + bh->b_size / 512 > sectors_per_chunk ) + + printk ("raid5: bug: dd_idx == %d, pd_idx == %d, chunk_offset == %d\n", + *dd_idx, *pd_idx, chunk_offset); +#endif + + return new_sector; +} + +static unsigned long compute_blocknr(struct stripe_head *sh, int i) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int raid_disks = raid_conf->raid_disks, data_disks = raid_disks - 1; + unsigned long new_sector = sh->sector, check; + int sectors_per_chunk = raid_conf->chunk_size >> 9; + unsigned long stripe = new_sector / sectors_per_chunk; + int chunk_offset = new_sector % sectors_per_chunk; + int chunk_number, dummy1, dummy2, dd_idx = i; + unsigned long r_sector, blocknr; + + switch (raid_conf->algorithm) { + case ALGORITHM_LEFT_ASYMMETRIC: + case ALGORITHM_RIGHT_ASYMMETRIC: + if (i > sh->pd_idx) + i--; + break; + case ALGORITHM_LEFT_SYMMETRIC: + case ALGORITHM_RIGHT_SYMMETRIC: + if (i < sh->pd_idx) + i += raid_disks; + i -= (sh->pd_idx + 1); + break; + default: + printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm); + } + + chunk_number = stripe * data_disks + i; + r_sector = chunk_number * sectors_per_chunk + chunk_offset; + blocknr = r_sector / (sh->size >> 9); + + check = raid5_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, raid_conf); + if (check != sh->sector || dummy1 != dd_idx || dummy2 != sh->pd_idx) { + printk("compute_blocknr: map not correct\n"); + return 0; + } + return blocknr; +} + +static void xor_block(struct buffer_head *dest, struct buffer_head *source) +{ + int lines = dest->b_size / (sizeof (int)) / 8, i; + int *destp = (int *) dest->b_data, *sourcep = (int *) source->b_data; + + for (i = lines; i > 0; i--) { + *(destp + 0) ^= *(sourcep + 0); + *(destp + 1) ^= *(sourcep + 1); + *(destp + 2) ^= *(sourcep + 2); + *(destp + 3) ^= *(sourcep + 3); + *(destp + 4) ^= *(sourcep + 4); + *(destp + 5) ^= *(sourcep + 5); + *(destp + 6) ^= *(sourcep + 6); + *(destp + 7) ^= *(sourcep + 7); + destp += 8; + sourcep += 8; + } +} + +static void compute_block(struct stripe_head *sh, int dd_idx) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int i, disks = raid_conf->raid_disks; + + PRINTK(("compute_block, stripe %lu, idx %d\n", sh->sector, dd_idx)); + + if (sh->bh_old[dd_idx] == NULL) + sh->bh_old[dd_idx] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_old[dd_idx], dd_idx); + + memset(sh->bh_old[dd_idx]->b_data, 0, sh->size); + for (i = 0; i < disks; i++) { + if (i == dd_idx) + continue; + if (sh->bh_old[i]) { + xor_block(sh->bh_old[dd_idx], sh->bh_old[i]); + continue; + } else + printk("compute_block() %d, stripe %lu, %d not present\n", dd_idx, sh->sector, i); + } + raid5_mark_buffer_uptodate(sh->bh_old[dd_idx], 1); +} + +static void compute_parity(struct stripe_head *sh, int method) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int i, pd_idx = sh->pd_idx, disks = raid_conf->raid_disks; + + PRINTK(("compute_parity, stripe %lu, method %d\n", sh->sector, method)); + for (i = 0; i < disks; i++) { + if (i == pd_idx || !sh->bh_new[i]) + continue; + if (!sh->bh_copy[i]) + sh->bh_copy[i] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_copy[i], i); + mark_buffer_clean(sh->bh_new[i]); + memcpy(sh->bh_copy[i]->b_data, sh->bh_new[i]->b_data, sh->size); + } + if (sh->bh_copy[pd_idx] == NULL) + sh->bh_copy[pd_idx] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_copy[pd_idx], sh->pd_idx); + + if (method == RECONSTRUCT_WRITE) { + memset(sh->bh_copy[pd_idx]->b_data, 0, sh->size); + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i]) { + xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]); + continue; + } + if (sh->bh_old[i]) { + xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]); + continue; + } + } + } else if (method == READ_MODIFY_WRITE) { + memcpy(sh->bh_copy[pd_idx]->b_data, sh->bh_old[pd_idx]->b_data, sh->size); + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i] && sh->bh_old[i]) { + xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]); + xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]); + continue; + } + } + } + raid5_mark_buffer_uptodate(sh->bh_copy[pd_idx], 1); +} + +static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int dd_idx, int rw) +{ + struct raid5_data *raid_conf = sh->raid_conf; + + if (sh->bh_new[dd_idx]) + printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector); + + set_bit(BH_MD, &bh->b_state); + set_bit(BH_Lock, &bh->b_state); + bh->personality = &raid5_personality; + bh->private_bh = (void *) sh; + bh->b_rdev = raid_conf->disks[dd_idx].dev; + bh->b_rsector = sh->sector; + + if (sh->phase == PHASE_COMPLETE && sh->cmd == STRIPE_NONE) { + sh->phase = PHASE_BEGIN; + sh->cmd = (rw == READ) ? STRIPE_READ : STRIPE_WRITE; + nr_pending_stripes++; + atomic_inc(&raid_conf->nr_handle); + } + sh->bh_new[dd_idx] = bh; + sh->cmd_new[dd_idx] = rw; + sh->new[dd_idx] = 1; +} + +static void complete_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int disks = raid_conf->raid_disks; + int i, new = 0; + + PRINTK(("complete_stripe %lu\n", sh->sector)); + for (i = 0; i < disks; i++) { + if (sh->cmd == STRIPE_WRITE && i == sh->pd_idx) + raid5_update_old_bh(sh, i); + if (sh->bh_new[i]) { + if (!sh->new[i]) { +#if 0 + if (sh->cmd == STRIPE_WRITE) { + if (memcmp(sh->bh_new[i]->b_data, sh->bh_copy[i]->b_data, sh->size)) { + printk("copy differs, %s, sector %lu ", + test_bit(BH_Dirty, &sh->bh_new[i]->b_state) ? "dirty" : "clean", + sh->sector); + } else if (test_bit(BH_Dirty, &sh->bh_new[i]->b_state)) + printk("sector %lu dirty\n", sh->sector); + } +#endif + if (sh->cmd == STRIPE_WRITE) + raid5_update_old_bh(sh, i); + raid5_end_buffer_io(sh, i, 1); + continue; + } else + new++; + } + if (new && sh->cmd == STRIPE_WRITE) + printk("raid5: bug, completed STRIPE_WRITE with new == %d\n", new); + } + if (!new) + finish_stripe(sh); + else { + PRINTK(("stripe %lu, new == %d\n", sh->sector, new)); + sh->phase = PHASE_BEGIN; + } +} + +/* + * handle_stripe() is our main logic routine. Note that: + * + * 1. lock_stripe() should be used whenever we can't accept additonal + * buffers, either during short sleeping in handle_stripe() or + * during io operations. + * + * 2. We should be careful to set sh->nr_pending whenever we sleep, + * to prevent re-entry of handle_stripe() for the same sh. + * + * 3. raid_conf->failed_disks and disk->operational can be changed + * from an interrupt. This complicates things a bit, but it allows + * us to stop issuing requests for a failed drive as soon as possible. + */ +static void handle_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + struct md_dev *mddev = raid_conf->mddev; + int minor = (int) (mddev - md_dev); + struct buffer_head *bh; + int disks = raid_conf->raid_disks; + int i, nr = 0, nr_read = 0, nr_write = 0; + int nr_cache = 0, nr_cache_other = 0, nr_cache_overwrite = 0, parity = 0; + int nr_failed_other = 0, nr_failed_overwrite = 0, parity_failed = 0; + int reading = 0, nr_writing = 0; + int method1 = INT_MAX, method2 = INT_MAX; + int block; + unsigned long flags; + int operational[MD_SB_DISKS], failed_disks = raid_conf->failed_disks; + + PRINTK(("handle_stripe(), stripe %lu\n", sh->sector)); + if (sh->nr_pending) { + printk("handle_stripe(), stripe %lu, io still pending\n", sh->sector); + return; + } + if (sh->phase == PHASE_COMPLETE) { + printk("handle_stripe(), stripe %lu, already complete\n", sh->sector); + return; + } + + atomic_dec(&raid_conf->nr_handle); + + if (clear_bit(STRIPE_ERROR, &sh->state)) { + printk("raid5: restarting stripe %lu\n", sh->sector); + sh->phase = PHASE_BEGIN; + } + + if ((sh->cmd == STRIPE_WRITE && sh->phase == PHASE_WRITE) || + (sh->cmd == STRIPE_READ && sh->phase == PHASE_READ)) { + /* + * Completed + */ + complete_stripe(sh); + if (sh->phase == PHASE_COMPLETE) + return; + } + + save_flags(flags); + cli(); + for (i = 0; i < disks; i++) + operational[i] = raid_conf->disks[i].operational; + failed_disks = raid_conf->failed_disks; + restore_flags(flags); + + if (failed_disks > 1) { + for (i = 0; i < disks; i++) { + if (sh->bh_new[i]) { + raid5_end_buffer_io(sh, i, 0); + continue; + } + } + finish_stripe(sh); + return; + } + + for (i = 0; i < disks; i++) { + if (sh->bh_old[i]) + nr_cache++; + if (i == sh->pd_idx) { + if (sh->bh_old[i]) + parity = 1; + else if(!operational[i]) + parity_failed = 1; + continue; + } + if (!sh->bh_new[i]) { + if (sh->bh_old[i]) + nr_cache_other++; + else if (!operational[i]) + nr_failed_other++; + continue; + } + sh->new[i] = 0; + nr++; + if (sh->cmd_new[i] == READ) + nr_read++; + if (sh->cmd_new[i] == WRITE) + nr_write++; + if (sh->bh_old[i]) + nr_cache_overwrite++; + else if (!operational[i]) + nr_failed_overwrite++; + } + + if (nr_write && nr_read) + printk("raid5: bug, nr_write == %d, nr_read == %d, sh->cmd == %d\n", nr_write, nr_read, sh->cmd); + + if (nr_write) { + /* + * Attempt to add entries :-) + */ + if (nr_write != disks - 1) { + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i]) + continue; + block = (int) compute_blocknr(sh, i); + bh = efind_buffer(MKDEV(MD_MAJOR, minor), block, sh->size); + if (bh && bh->b_count == 0 && buffer_dirty(bh) && !buffer_locked(bh)) { + PRINTK(("Whee.. sector %lu, index %d (%d) found in the buffer cache!\n", sh->sector, i, block)); + add_stripe_bh(sh, bh, i, WRITE); + sh->new[i] = 0; + nr++; nr_write++; + if (sh->bh_old[i]) { + nr_cache_overwrite++; + nr_cache_other--; + } else if (!operational[i]) { + nr_failed_overwrite++; + nr_failed_other--; + } + } + } + } + PRINTK(("handle_stripe() -- begin writing, stripe %lu\n", sh->sector)); + /* + * Writing, need to update parity buffer. + * + * Compute the number of I/O requests in the "reconstruct + * write" and "read modify write" methods. + */ + if (!nr_failed_other) + method1 = (disks - 1) - (nr_write + nr_cache_other); + if (!nr_failed_overwrite && !parity_failed) + method2 = nr_write - nr_cache_overwrite + (1 - parity); + + if (method1 == INT_MAX && method2 == INT_MAX) + printk("raid5: bug: method1 == method2 == INT_MAX\n"); + PRINTK(("handle_stripe(), sector %lu, nr_write %d, method1 %d, method2 %d\n", sh->sector, nr_write, method1, method2)); + + if (!method1 || !method2) { + lock_stripe(sh); + sh->nr_pending++; + sh->phase = PHASE_WRITE; + compute_parity(sh, method1 <= method2 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE); + for (i = 0; i < disks; i++) { + if (!operational[i]) + continue; + if (i == sh->pd_idx || sh->bh_new[i]) + nr_writing++; + } + + sh->nr_pending = nr_writing; + PRINTK(("handle_stripe() %lu, writing back %d\n", sh->sector, sh->nr_pending)); + + for (i = 0; i < disks; i++) { + if (!operational[i]) + continue; + bh = sh->bh_copy[i]; + if (i != sh->pd_idx && ((bh == NULL) ^ (sh->bh_new[i] == NULL))) + printk("raid5: bug: bh == %p, bh_new[%d] == %p\n", bh, i, sh->bh_new[i]); + if (i == sh->pd_idx && !bh) + printk("raid5: bug: bh == NULL, i == pd_idx == %d\n", i); + if (bh) { + bh->b_state |= (1<<BH_Dirty); + PRINTK(("making request for buffer %d\n", i)); + clear_bit(BH_Lock, &bh->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), WRITE, bh); + } + } + return; + } + + lock_stripe(sh); + sh->nr_pending++; + if (method1 < method2) { + sh->write_method = RECONSTRUCT_WRITE; + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i] || sh->bh_old[i]) + continue; + sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + reading++; + } + } else { + sh->write_method = READ_MODIFY_WRITE; + for (i = 0; i < disks; i++) { + if (sh->bh_old[i]) + continue; + if (!sh->bh_new[i] && i != sh->pd_idx) + continue; + sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + reading++; + } + } + sh->phase = PHASE_READ_OLD; + sh->nr_pending = reading; + PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (!sh->bh_old[i]) + continue; + if (buffer_uptodate(sh->bh_old[i])) + continue; + clear_bit(BH_Lock, &sh->bh_old[i]->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]); + } + } else { + /* + * Reading + */ + method1 = nr_read - nr_cache_overwrite; + lock_stripe(sh); + sh->nr_pending++; + + PRINTK(("handle_stripe(), sector %lu, nr_read %d, nr_cache %d, method1 %d\n", sh->sector, nr_read, nr_cache, method1)); + if (!method1 || (method1 == 1 && nr_cache == disks - 1)) { + PRINTK(("read %lu completed from cache\n", sh->sector)); + for (i = 0; i < disks; i++) { + if (!sh->bh_new[i]) + continue; + if (!sh->bh_old[i]) + compute_block(sh, i); + memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size); + } + sh->nr_pending--; + complete_stripe(sh); + return; + } + if (nr_failed_overwrite) { + sh->phase = PHASE_READ_OLD; + sh->nr_pending = (disks - 1) - nr_cache; + PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (sh->bh_old[i]) + continue; + if (!operational[i]) + continue; + sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + clear_bit(BH_Lock, &sh->bh_old[i]->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]); + } + } else { + sh->phase = PHASE_READ; + sh->nr_pending = nr_read - nr_cache_overwrite; + PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (!sh->bh_new[i]) + continue; + if (sh->bh_old[i]) { + memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size); + continue; + } + clear_bit(BH_Lock, &sh->bh_new[i]->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_new[i]); + } + } + } +} + +static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + const unsigned int raid_disks = raid_conf->raid_disks; + const unsigned int data_disks = raid_disks - 1; + unsigned int dd_idx, pd_idx; + unsigned long new_sector; + + struct stripe_head *sh; + + if (rw == READA) rw = READ; + if (rw == WRITEA) rw = WRITE; + + new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks, + &dd_idx, &pd_idx, raid_conf); + + PRINTK(("raid5_make_request, sector %lu\n", new_sector)); + sh = get_stripe(raid_conf, new_sector, bh->b_size); + if ((rw == READ && sh->cmd == STRIPE_WRITE) || (rw == WRITE && sh->cmd == STRIPE_READ)) { + printk("raid5: lock contention, rw == %d, sh->cmd == %d\n", rw, sh->cmd); + lock_stripe(sh); + if (!sh->nr_pending) + handle_stripe(sh); + wait_on_stripe(sh); + } + sh->pd_idx = pd_idx; + if (sh->phase != PHASE_COMPLETE && sh->phase != PHASE_BEGIN) + PRINTK(("stripe %lu catching the bus!\n", sh->sector)); + add_stripe_bh(sh, bh, dd_idx, rw); + + md_wakeup_thread(raid_conf->thread); + return 0; +} + +/* + * This is our raid5 kernel thread. + * + * We scan the hash table for stripes which can be handled now. + * During the scan, completed stripes are saved for us by the interrupt + * handler, so that they will not have to wait for our next wakeup. + */ +static void raid5d (void *data) +{ + struct stripe_head *sh; + struct raid5_data *raid_conf = data; + struct md_dev *mddev = raid_conf->mddev; + int i, handled = 0, unplug = 0; + unsigned long flags; + + PRINTK(("+++ raid5d active\n")); + + if (mddev->sb_dirty) { + mddev->sb_dirty = 0; + md_update_sb((int) (mddev - md_dev)); + } + save_flags(flags); + cli(); + stripe_handle_list = stripe_handle_tail = NULL; + restore_flags(flags); + + for (i = 0; i < NR_HASH; i++) { +repeat: + sh = stripe_hashtbl[i]; + for (; sh; sh = sh->hash_next) { + if (sh->raid_conf != raid_conf) + continue; + if (sh->phase == PHASE_COMPLETE) + continue; + if (sh->nr_pending) + continue; + if (sh->sector == raid_conf->next_sector) { + raid_conf->sector_count += (sh->size >> 9); + if (raid_conf->sector_count >= 128) + unplug = 1; + } else + unplug = 1; + if (unplug) { + PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, raid_conf->sector_count)); + unplug_devices(sh); + unplug = 0; + raid_conf->sector_count = 0; + } + raid_conf->next_sector = sh->sector + (sh->size >> 9); + handled++; + handle_stripe(sh); + goto repeat; + } + } + if (raid_conf) { + PRINTK(("%d stripes handled, nr_handle %d\n", handled, raid_conf->nr_handle)); + save_flags(flags); + cli(); + if (!raid_conf->nr_handle) + clear_bit(THREAD_WAKEUP, &raid_conf->thread->flags); + } + PRINTK(("--- raid5d inactive\n")); +} + +static int raid5_run (int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf; + int i, j, raid_disk; + md_superblock_t *sb = mddev->sb; + md_descriptor_t *descriptor; + struct real_dev *realdev; + + MOD_INC_USE_COUNT; + + if (sb->level != 5 && sb->level != 4) { + printk("raid5: %s: raid level not set to 4/5 (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + MOD_DEC_USE_COUNT; + return -EIO; + } + + mddev->private = kmalloc (sizeof (struct raid5_data), GFP_KERNEL); + raid_conf = mddev->private; + memset (raid_conf, 0, sizeof (*raid_conf)); + raid_conf->mddev = mddev; + + PRINTK(("raid5_run(%d) called.\n", minor)); + + for (i = 0; i < mddev->nb_dev; i++) { + realdev = &mddev->devices[i]; + if (!realdev->sb) { + printk(KERN_ERR "raid5: disabled device %s (couldn't access raid superblock)\n", kdevname(realdev->dev)); + continue; + } + + /* + * This is important -- we are using the descriptor on + * the disk only to get a pointer to the descriptor on + * the main superblock, which might be more recent. + */ + descriptor = &sb->disks[realdev->sb->descriptor.number]; + if (descriptor->state & (1 << MD_FAULTY_DEVICE)) { + printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", kdevname(realdev->dev)); + continue; + } + if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) { + if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) { + printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", kdevname(realdev->dev)); + continue; + } + raid_disk = descriptor->raid_disk; + if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) { + printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", kdevname(realdev->dev)); + continue; + } + if (raid_conf->disks[raid_disk].operational) { + printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", kdevname(realdev->dev), raid_disk); + continue; + } + printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", kdevname(realdev->dev), raid_disk); + + raid_conf->disks[raid_disk].number = descriptor->number; + raid_conf->disks[raid_disk].raid_disk = raid_disk; + raid_conf->disks[raid_disk].dev = mddev->devices[i].dev; + raid_conf->disks[raid_disk].operational = 1; + + raid_conf->working_disks++; + } + } + raid_conf->raid_disks = sb->raid_disks; + raid_conf->failed_disks = raid_conf->raid_disks - raid_conf->working_disks; + raid_conf->mddev = mddev; + raid_conf->chunk_size = sb->chunk_size; + raid_conf->level = sb->level; + raid_conf->algorithm = sb->parity_algorithm; + + if (!raid_conf->chunk_size || raid_conf->chunk_size % 4) { + printk(KERN_ERR "raid5: invalid chunk size %d for %s\n", raid_conf->chunk_size, kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + if (raid_conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) { + printk(KERN_ERR "raid5: unsupported parity algorithm %d for %s\n", raid_conf->algorithm, kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + if (raid_conf->failed_disks > 1) { + printk(KERN_ERR "raid5: not enough operational devices for %s (%d/%d failed)\n", kdevname(MKDEV(MD_MAJOR, minor)), raid_conf->failed_disks, raid_conf->raid_disks); + goto abort; + } + +#if 0 + if (check_consistenty(mddev)) { + printk(KERN_ERR "raid5: detected raid-5 xor inconsistenty -- run ckraid\n"); + sb->state |= 1 << MD_SB_ERRORS; + goto abort; + } +#endif + + if ((raid_conf->thread = md_register_thread(raid5d, raid_conf)) == NULL) { + printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + + /* + * Regenerate the "device is in sync with the raid set" bit for + * each device. + */ + for (i = 0; i < sb->nr_disks ; i++) { + sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE); + for (j = 0; j < sb->raid_disks; j++) { + if (!raid_conf->disks[j].operational) + continue; + if (sb->disks[i].number == raid_conf->disks[j].number) + sb->disks[i].state |= 1 << MD_SYNC_DEVICE; + } + } + sb->active_disks = raid_conf->working_disks; + + if (sb->active_disks == sb->raid_disks) + printk("raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm); + else + printk(KERN_ALERT "raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm); + + /* Ok, everything is just fine now */ + return (0); +abort: + if (raid_conf) + kfree(raid_conf); + mddev->private = NULL; + printk(KERN_ALERT "raid5: failed to run raid set %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + MOD_DEC_USE_COUNT; + return -EIO; +} + +static int raid5_stop (int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + + md_unregister_thread(raid_conf->thread); + kfree (raid_conf); + shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE); + shrink_buffers(RAID5_POOL_SIZE); + MOD_DEC_USE_COUNT; + return 0; +} + +static int raid5_status (char *page, int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + md_superblock_t *sb = mddev->sb; + int sz = 0, i; + + sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->parity_algorithm); + sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks); + for (i = 0; i < raid_conf->raid_disks; i++) + sz += sprintf (page+sz, "%s", raid_conf->disks[i].operational ? "U" : "_"); + sz += sprintf (page+sz, "]"); + return sz; +} + +static struct md_personality raid5_personality= +{ + "raid5", + raid5_map, + raid5_make_request, + raid5_end_request, + raid5_run, + raid5_stop, + raid5_status, + NULL, /* no ioctls */ + 0, + raid5_error +}; + +int raid5_init (void) +{ + if ((stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0)) == NULL) + return -ENOMEM; + memset(stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE); + return register_md_personality (RAID5, &raid5_personality); +} + +#ifdef MODULE +int init_module (void) +{ + return raid5_init(); +} + +void cleanup_module (void) +{ + free_pages((unsigned long) stripe_hashtbl, HASH_PAGES_ORDER); + shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE); + shrink_buffers(RAID5_POOL_SIZE); + unregister_md_personality (RAID5); +} +#endif diff -u --recursive --new-file v2.0.34/linux/drivers/block/rd.c linux/drivers/block/rd.c --- v2.0.34/linux/drivers/block/rd.c Mon Jul 13 13:46:26 1998 +++ linux/drivers/block/rd.c Mon Jul 13 13:47:28 1998 @@ -582,7 +582,7 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/block/triton.c linux/drivers/block/triton.c --- v2.0.34/linux/drivers/block/triton.c Mon Jul 13 13:46:26 1998 +++ linux/drivers/block/triton.c Mon Jul 13 13:47:28 1998 @@ -374,7 +374,7 @@ * safely use __get_free_page() here instead * of __get_dma_pages() -- no ISA limitations. */ - dmatable = __get_free_page(GFP_KERNEL); + dmatable = __get_free_pages(GFP_KERNEL, 1, 0); } if (dmatable) { hwif->dmatable = (unsigned long *) dmatable; @@ -503,3 +503,25 @@ quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc)); } +void ide_init_promise (byte bus, byte fn, ide_hwif_t *hwif0, ide_hwif_t *hwif1, unsigned short dma) +{ + int rc; + unsigned short pcicmd; + unsigned int bmiba = 0; + + printk("ide: Enabling DMA for Promise Technology IDE Ultra-DMA 33 on PCI bus %d function %d, port 0x%04x\n", bus, fn, dma); + if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)) || (pcicmd & 1) == 0 || (pcicmd & 4) == 0) + goto abort; + if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba))) + goto abort; + bmiba &= 0xfff0; /* extract port base address */ + if (bmiba != dma || !bmiba) + goto abort; + hwif0->chipset = ide_promise_udma; + hwif1->chipset = ide_promise_udma; + init_triton_dma(hwif0, bmiba); + init_triton_dma(hwif1, bmiba + 0x08); + return; +abort: + printk(KERN_WARNING "ide: Promise/33 not configured correctly (BIOS)\n"); +} diff -u --recursive --new-file v2.0.34/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c --- v2.0.34/linux/drivers/cdrom/cm206.c Sat Aug 17 11:19:26 1996 +++ linux/drivers/cdrom/cm206.c Mon Jul 13 13:47:28 1998 @@ -739,7 +739,7 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c --- v2.0.34/linux/drivers/cdrom/mcd.c Wed Nov 13 23:30:59 1996 +++ linux/drivers/cdrom/mcd.c Mon Jul 13 13:47:28 1998 @@ -311,7 +311,10 @@ { 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 -u --recursive --new-file v2.0.34/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c --- v2.0.34/linux/drivers/cdrom/sonycd535.c Mon Apr 1 21:43:06 1996 +++ linux/drivers/cdrom/sonycd535.c Mon Jul 13 13:47:28 1998 @@ -4,7 +4,7 @@ * This is a modified version of the CDU-31A device driver (see below). * Changes were made using documentation for the CDU-531 (which Sony * assures me is very similar to the 535) and partial disassembly of the - * DOS driver. I used Minyard's driver and replaced the the CDU-31A + * DOS driver. I used Minyard's driver and replaced the CDU-31A * commands with the CDU-531 commands. This was complicated by a different * interface protocol with the drive. The driver is still polled. * @@ -31,6 +31,10 @@ * More changes to support CDU-510/515 series * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>) * + * 1997-11-18 + * Blocksize awareness + * Dong Liu <qian!dliu@arrow.njit.edu> + * * Things to do: * - handle errors and status better, put everything into a single word * - use interrupts (code mostly there, but a big hole still missing) @@ -589,11 +593,13 @@ * 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 @@ 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 @@ 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 @@ /* 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 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog --- v2.0.34/linux/drivers/char/ChangeLog Thu Jun 6 12:23:08 1996 +++ linux/drivers/char/ChangeLog Mon Jul 13 13:47:28 1998 @@ -253,7 +253,7 @@ Fri Feb 17 09:34:09 1995 Theodore Y. Ts'o (tytso@rt-11) * serial.c (rs_interrupt_single, rs_interrupt, rs_interrupt_multi): - Change the the number of passes made from 64 to be 256, + Change the number of passes made from 64 to be 256, configurable with the #define RS_ISR_PASS_LIMIT. * serial.c (rs_init, set_serial_info, get_serial_info, rs_close): diff -u --recursive --new-file v2.0.34/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.0.34/linux/drivers/char/Makefile Mon Jul 13 13:46:26 1998 +++ linux/drivers/char/Makefile Mon Jul 13 13:47:28 1998 @@ -217,14 +217,6 @@ endif endif -ifeq ($(CONFIG_BAYCOM),y) -L_OBJS += baycom.o -else - ifeq ($(CONFIG_BAYCOM),m) - M_OBJS += baycom.o - endif -endif - ifdef CONFIG_TGA_CONSOLE L_OBJS += tga.o ifdef CONFIG_VGA_CONSOLE diff -u --recursive --new-file v2.0.34/linux/drivers/char/README.stallion linux/drivers/char/README.stallion --- v2.0.34/linux/drivers/char/README.stallion Wed Apr 24 02:48:04 1996 +++ linux/drivers/char/README.stallion Mon Jul 13 13:47:29 1998 @@ -1,52 +1,183 @@ -Stallion Multiport Serial Drivers ---------------------------------- +Stallion Multiport Serial Driver Readme +--------------------------------------- + +Copyright (C) 1994-1998, Stallion Technologies (support@stallion.com). + +Version: 5.4.4 +Date: 20MAR98 -Version: 1.1.3 -Date: 23APR96 -Author: Greg Ungerer (gerg@stallion.oz.au) 1. INTRODUCTION -There are two drivers that work with the different families of Stallion -multiport serial boards. One is for the Stallion smart boards - that is -EasyIO and EasyConnection 8/32, the other for the true Stallion intelligent -multiport boards - EasyConnection 8/64, ONboard, Brumby and Stallion. - -If you are using any of the Stallion intelligent multiport boards (Brumby, -ONboard, Stallion, EasyConnection 8/64) with Linux you will need to get the -driver utility package. This package is available at most of the Linux -archive sites (and on CD's that contain these archives). The file will be -called stallion-X.X.X.tar.gz where X.X.X will be the version number. In -particular this package contains the board embedded executable images that -are required for these boards. It also contains the downloader program. -These boards cannot be used without this. - -The following ftp sites (and their mirrors) definitely have the stallion -driver utility package: ftp.stallion.com, tsx-11.mit.edu, sunsite.unc.edu. - -ftp.stallion.com:/drivers/ata5/Linux/stallion-1.1.2.tar.gz -tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-1.1.2.tar.gz -sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-1.1.2.tar.gz - -If you are using the EasyIO or EasyConnection 8/32 boards then you don't -need this package. Although it does have a handy script to create the -/dev device nodes for these boards, and a serial stats display program. - -If you require DIP switch settings, EISA/MCA configuration files, or any -other information related to Stallion boards then have a look at Stallion's -web pages at http://www.stallion.com. +This is a Linux driver for some of the Stallion Technologies range of +multiport serial boards. There are really two drivers in this package. +One is for the Stallion smart boards, the other for the true Stallion +intelligent multiport boards. + +The drivers included in this package are intended as a replacement for +those shipped with Linux kernel versions in the 2.0.X series. For later +versions of the kernel (2.1.0 and above) use the driver source supplied +with the kernel. The drivers in this package specifically add support +for the most recent releases of Stallion hardware - which are not supported +in the Stallion drivers supplied in the 2.0.X kernels. The drivers in this +package do not support kernel versions earlier than 2.0.0. + +The other utilities supplied in this package can be used with Stallion +drivers on any version of the kernel. + +If you have any trouble getting Stallion boards to work in Linux systems, +please contact Stallion Technologies support department via email or phone. +Contact information for Stallion Technologies offices is included in the +file "Offices" contained in this distribution. + +Please note the disclaimers set out in the GNU general public license +included with this driver package. + +All host driver source is included in this package, and is copyrighted under +the GNU GPL. The board "firmware" code in this package is copyright Stallion +Technologies (the files cdk.sys and 2681.sys). + + +1.1 SMART MULTIPORT BOARD DRIVER + +This driver supports the EasyIO and EasyConnection 8/32 range of boards. +These boards are not classic intelligent multiport boards, but are host +based multiport boards that use Cirrus Logic CL-CD1400 UART's, or on +newer versions of the hardware use the Signetics 26C198 UART. Both of +these are high performance UART's with built in FIFO's, automatic flow +control and a host of other features. + +The EasyIO range of cards comes in 4 forms, the EasyIO-4, EasyIO-8, +EasyIO-8M and EasyIO-8-PCI. The first three are ISA based boards while +the last is a PCI bus board. All of these are non-expandable, low cost, +multiport boards with 4 or 8 RS-232C ports. Each ISA EasyIO board requires 8 +bytes of I/O address space and 1 interrupt. The PCI EasyIO board uses 64 +bytes of I/O address space and 1 interrupt. On EISA and PCI systems it is +possible to share 1 interrupt between multiple boards. The EasyIO-4 has 10 +pin RJ connectors, and the EasyIO-8 comes with a dongle cable with either 10 +pin RJ connectors or DB-25 connectors. The EasyIO-8M has 6 pin RJ connectors. + +The EasyConnection 8/32 family of boards is a relatively low cost modular +range of multiport serial boards. The EasyConnection 8/32 boards can be +configured to have from 8 to 32 serial ports by plugging in external serial +port modules that contain either 8 or 16 ports each. There is a wide range +of external modules available that offer: DB-25 connectors, RJ-45 connectors +(both with RS-232 D and E compatible drivers), and also RS-422 and RS-485 +ports. The EasyConnection 8/32 boards come in ISA, PCI and MCA bus versions. +The board takes the form of a host adapter card, with an external connector +cable that plugs into the external modules. The external modules just clip +together to add ports (BTW, they are NOT hot pluggable). Each ISA +EasyConnection 8/32 board requires two separate I/O address ranges, one two +bytes in size and a secondary region of 32 bytes. Each PCI EasyConnection +8/32 requires two regions of I/O address space, normally these will be +automatically allocated by the system BIOS at power on time. Each MCA +EasyConnection board requires one I/O address region 64 bytes in size. All +board types also require one interrupt. On EISA systems multiple boards can +share one interrupt. The secondary I/O range of the ISA board (the 32 byte +range) can be shared between multiple boards on any bus type. + +The EasyConnection 8/64-PCI family is similar to the EasyConnection 8/32-PCI +board, and uses the same external modules. It is supported by the smart +board driver - not the intelligent board driver. It uses 2 regions of I/O +address space, both 64 bytes in size, and 1 interrupt. + + + +1.2 INTELLIGENT MULTIPORT BOARD DRIVER + +This driver is for Stallion's range of true intelligent multiport boards. +It supports the EasyConnection 8/64, ONboard and Brumby families of multiport +boards. The EasyConnection 8/64 and ONboard boards come in ISA, EISA and +Microchannel bus versions. The Brumby boards are only available in ISA +versions. This driver can also work with the original Stallion board, but +these are no longer supported by Stallion Technologies. + +The EasyConnection 8/64 family of boards is a medium cost, high performance, +modular range of intelligent multiport serial boards. The EasyConnection 8/64 +boards can be configured to have from 8 to 64 serial ports by plugging in +external serial port modules that contain either 8 or 16 ports each (these +modules are the same used by the EasyConnection 8/32 board). There is a wide +range of external modules available that offer: DB-25 connectors, RJ-45 +connectors (both with RS-232 D and E compatible drivers), and also RS-422 and +RS-485 ports. The board takes the form of a host adapter card, with an external +connector cable that plugs into the external modules. The external modules +just clip together to add ports (BTW, they are NOT hot pluggable). Each +EasyConnection 8/64 board requires 4 bytes of I/O address space and a region +of memory space. The size of the memory region required depends on the exact +board type. The EISA version requires 64 Kbytes of address space (that can +reside anywhere in the 4 Gigabyte physical address space). The ISA and MCA +boards require 4 Kbytes of address space (which must reside in the lower +1 Mbyte of physical address space - typically in the c8000 to e0000 range). +No interrupts are required. The physical memory region of multiple +EasyConnection 8/64 boards can be shared, but each board must have a separate +I/O address. + +The ONboard family of boards are traditional intelligent multiport serial +boards. They are Stallion's older range of boards with a limited expansion +capability. They come in 4, 8, 12, 16 and 32 port versions. The board uses +the same base card (which has 4 ports on it) and is expanded to more ports via +a mezzanine board that attaches directly onto the base card. External panels +plug into the ONboard providing RS-232C ports with DB-25 plugs. An RS-422 +DB-25 dual interface panel is also available. The ISA and microchannel +ONboards require 16 bytes of I/O address space and 64K bytes of memory +space. The memory space can be anywhere in the 16 Mbyte ISA bus address +range. No interrupt is required. The EISA ONboard requires 64 Kbytes of +memory space that can be anywhere in the 4 Gigabyte physical address space. +All ONboard boards can share their memory region with other ONboards (or +EasyConnection 8/64 boards). + +The Brumby family of boards are traditional, low cost intelligent multiport +serial boards. They are non-expandable and come in 4, 8 and 16 port versions. +They are only available for the ISA bus. The serial ports are all on DB-25 +"dongle" cables that attach to the rear of the board. Each Brumby board +requires 16 bytes of I/O address space and 16 Kbytes of memory space. No +interrupts are required. + +The original Stallion boards are old. They went out of production some years +back and are no longer supported. They offer limited expandability and are +available in 8 or 16 port configurations. An external panel houses 16 RS-232C +ports with DB-9 connectors. They require 16 bytes of I/O address space, and +either 64K or 128K of memory space. No interrupt is required. + +That's the boards supported by the second driver. The ONboard, Brumby and +Stallion boards are Stallion's older range of intelligent multiports - so +there are lots of them around. They only support a maximum baud rate of +38400. The EasyConnection 8/64 is a true high performance intelligent +multiport board, having much greater throughput than any of Stallion's +older boards. It also supports speeds up to 460800 baud. + + +1.3 HOW TO GET BOARDS + +Stallion Technologies has offices all over the world, as well as many more +distributors and resellers. To find out about local availability please +contact the nearest Stallion office and they can give you all the information +you need. Look in the "Offices" file in the driver package for a current list +of Stallion Technologies offices. + +Another good source of information about the Stallion range of boards and +local availability is on the Stallion Web page. Check it out at +http://www.stallion.com. 2. INSTALLATION +This version of the driver is intended for kernel versions 2.0.0 and later. +It will not work on earlier kernel versions, due to kernel interface changes. +(Note that older versions of these drivers do work on older kernels.) +If you are using a more recent development kernel (versions 2.1.X and +greater) you should use the Stallion drivers supplied with that kernel, +they are more up to date. + The drivers can be used as loadable modules or compiled into the kernel. -You can choose which when doing a "make config" on the kernel. +Depending on which form of driver loading you decide to use, the installation +procedure will be a little different. All ISA, EISA and MCA boards that you want to use need to be entered into -the driver(s) configuration structures. All PCI boards will be automatically +the driver(s) configuration structures. PCI boards will be automatically detected when you load the driver - so they do not need to be entered into the driver(s) configuration structure. (Note that kernel PCI BIOS32 support is required to use PCI boards.) @@ -54,45 +185,135 @@ Entering ISA, EISA and MCA boards into the driver(s) configuration structure involves editing the driver(s) source file. It's pretty easy if you follow the instructions below. Both drivers can support up to 4 boards. The smart -card driver (the stallion.c driver) supports any combination of EasyIO and -EasyConnection 8/32 boards (up to a total of 4). The intelligent driver -supports any combination of ONboards, Brumbys, Stallions and EasyConnection -8/64 boards (up to a total of 4). - -To set up the driver(s) for the boards that you want to use you need to -edit the appropriate driver file and add configuration entries. - -If using EasyIO or EasyConnection 8/32 ISA or MCA boards, do: +card driver supports any combination of EasyIO, EasyConnection 8/32 and +EasyConnection 8/64-PCI boards (up to a total of 4). The intelligent driver +supports any combination of ONboards, Brumbys, Stallions and +EasyConnection 8/64 boards (up to a total of 4). + + +2.1 LOADABLE MODULE DRIVERS + +You will need the gcc compiler and make installed on your system to make the +driver modules. You will also need to have the kernel source on the system, +and have at least done a "make config" and "make dep" on it. (If you haven't +done this before then you may want to read the kernel source README file, +usually found in /usr/src/linux.) + +To build the driver modules: +1. Setup the driver configuration for the boards. If using EasyIO or + EasyConnection 8/32 ISA or MCA boards, do: vi stallion.c - find the definition of the stl_brdconf array (of structures) near the top of the file - modify this to match the boards you are going to install (the comments before this structure should help) - save and exit - -If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do: + If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do: + vi istallion.c + - find the definition of the stli_brdconf array (of structures) + near the top of the file + - modify this to match the boards you are going to install + (the comments before this structure should help) + - save and exit +2. cp stallion.h cd1400.h sc26198.h /usr/include/linux/include/linux + cp istallion.h cdk.h comstats.h /usr/include/linux/include/linux +3. make modules + This will compile the driver modules, as stallion and istallion. + +The stallion module is the EasyIO, EasyConnection 8/32 and +EasyConnection 8/64-PCI driver, the istallion module is the ONboard, +Brumby, Stallion and EasyConnection 8/64 driver. + +To load up the smart board driver use: + insmod ./stallion +This will load the EasyIO and EasyConnection 8/32 driver. It will output a +message to say that it loaded and print the driver version number. It +will also print out whether it found the configured boards or not. (These +messages may appear in your /var/adm/messages file depending on how the +klogd and syslogd daemons are setup on your system). + +To load the intelligent board driver use: + insmod ./istallion +It will output similar messages to the smart board driver. + + +2.2 STATIC DRIVERS (KERNEL LINKED) + +You will need to build a new kernel to link in the Stallion drivers. The first +thing you need is to have the full kernel source. Most people will have this. +The following assumes that the kernel source is in /usr/src/linux. + +To install the drivers: +1. cp stallion.c istallion.c /usr/src/linux/drivers/char + cp stallion.h cd1400.h sc26198.h /usr/include/linux/include/linux + cp istallion.h cdk.h comstats.h /usr/include/linux/include/linux +2. cd /usr/src/linux/drivers/char +3. Setup the driver configuration for the boards. If using EasyIO, + EasyConnection 8/32 or EasyConnection 8/64-PCI boards, do: + vi stallion.c + - find the definition of the stl_brdconf array (of structures) + near the top of the file + - modify this to match the boards you are going to install + (the comments before this structure should help) + - save and exit + If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do: vi istallion.c - find the definition of the stli_brdconf array (of structures) near the top of the file - modify this to match the boards you are going to install (the comments before this structure should help) - save and exit +4. cd /usr/src/linux +5. build a new kernel - if you haven't done this before you may want to + read the README file in /usr/src/linux. + +Once you have a new kernel built, reboot to start it up. On startup the +driver will output a message to say it is operational (with the driver +version number). It will also print out if it could find the boards listed +in its configuration structure or not. + + +2.3 INTELLIGENT DRIVER OPERATION + +The intelligent boards also need to have their "firmware" code downloaded +to them. This is done via a user level application supplied in the driver +package called "stlload". Compile this program where ever you dropped the +package files, by typing "make". In its simplest form you can then type + ./stlload -i cdk.sys +in this directory and that will download board 0 (assuming board 0 is an +EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do: + ./stlload -i 2681.sys + +Normally you would want all boards to be downloaded as part of the standard +system startup. To achieve this, add one of the lines above into the +/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add +the "-b <brd-number>" option to the line. You will need to download code for +every board. You should probably move the stlload program into a system +directory, such as /usr/sbin. Also, the default location of the cdk.sys image +file in the stlload down-loader is /usr/lib/stallion. Create that directory +and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put +them anyway). As an example your /etc/rc.d/rc.S file might have the +following lines added to it (if you had 3 boards): + /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys + /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys + /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys -Once you have set up the board configurations then you are ready to build -the kernel or modules. +The image files cdk.sys and 2681.sys are specific to the board types. The +cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly +the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards. +If you load the wrong image file into a board it will fail to start up, and +of course the ports will not be operational! -When the new kernel is booted, or the loadable module loaded then the -driver will emit some kernel trace messages about whether the configured -boards where detected or not. Depending on how your system logger is set -up these may come out on the console, or just be logged to -/var/adm/messages. You should check the messages to confirm that all is well. +If you are using the module version of the driver you might want to put the +insmod calls in the startup script as well (before the download lines +obviously). -2.1 SHARING INTERRUPTS +2.4 SHARING INTERRUPTS -It is possible to share interrupts between multiple EasyIO and -EasyConnection 8/32 boards in an EISA system. To do this you will need to -do a couple of things: +As mentioned in the introduction, it is possible to share interrupts between +multiple EasyIO and EasyConnection 8/32 boards in an EISA system. To do this +you will need to do a couple of things: 1. When entering the board resources into the stallion.c file you need to mark the boards as using level triggered interrupts. Do this by replacing @@ -109,17 +330,17 @@ that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32 EISA configuration files required are supplied by Stallion Technologies on the DOS Utilities floppy (usually supplied in the box with the board - when purchased. If not, you can pick it up from Stallion's FTP site, - ftp.stallion.com). You will need to edit the board resources to choose - level triggered interrupts, and make sure to set each board's interrupt - to the same IRQ number. + when purchased. If not, you can pick it up from Stallion's FTP site + ftp.stallion.com or web site http://www.stallion.com). You will need to + edit the board resources to choose level triggered interrupts, and make + sure to set each board's interrupt to the same IRQ number. You must complete both the above steps for this to work. When you reboot or load the driver your EasyIO and EasyConnection 8/32 boards will be sharing interrupts. -2.2 USING HIGH SHARED MEMORY +2.5 USING HIGH SHARED MEMORY The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of using shared memory addresses above the usual 640K - 1Mb range. The ONboard @@ -128,75 +349,52 @@ ONboard/E can be programmed for memory addresses up to 4Gb (the EISA bus addressing limit). -The higher than 1Mb memory addresses are fully supported by this driver. -Just enter the address as you normally would for a lower than 1Mb address -(in the drivers board configuration structure). +The istallion driver offers direct support for these higher memory regions. +To use them just enter the high memory address as if it were a low memory +address (in the driver board configuration structure). + +2.6 LINUX KERNEL VERSIONS 2.1.X +There may be some minor differences between the driver source code in this +package and that in the Linux kernel source. This will be due to changes +needed in the drivers so that they work correctly on newer kernels. The +driver source included in this package is intended for use with 2.0.X +series kernels. If you have a kernel version 2.1.0 or later then use the +source provided with the kernel - it will be more up to date. Stallion +Technologies regularly submits the latest driver source to be included in +the new kernel source releases. -2.3 TROUBLE SHOOTING + +2.7 TROUBLE SHOOTING If a board is not found by the driver but is actually in the system then the most likely problem is that the I/O address is wrong. Change it in the driver -stallion.c or istallion.c configuration structure and rebuild the kernel or -modules, or change it on the board. On EasyIO and EasyConnection 8/32 boards -the IRQ is software programmable, so if there is a conflict you may need to -change the IRQ used for a board in the stallion.c configuration structure. -There are no interrupts to worry about for ONboard, Brumby, Stallion or -EasyConnection 8/64 boards. The memory region on EasyConnection 8/64 and -ONboard boards is software programmable, but not on the Brumbys or Stallions. +stallion.c or istallion.c configuration structure and rebuild the kernel +or modules, or change it on the board. On EasyIO and EasyConnection 8/32 +boards the IRQ is software programmable, so if there is a conflict you may +need to change the IRQ used for a board in the stallion.c configuration +structure. There are no interrupts to worry about for ONboard, Brumby, +Stallion or EasyConnection 8/64 boards. The memory region on EasyConnection +8/64 and ONboard boards is software programmable, but not on the Brumbys or +Stallions. 3. USING THE DRIVERS -3.1 INTELLIGENT DRIVER OPERATION - -The intelligent boards also need to have their "firmware" code downloaded -to them. This is done via a user level application supplied in the driver -package called "stlload". Compile this program where ever you dropped the -package files, by typing "make". In its simplest form you can then type - ./stlload -i cdk.sys -in this directory and that will download board 0 (assuming board 0 is an -EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do: - ./stlload -i 2681.sys - -Normally you would want all boards to be downloaded as part of the standard -system startup. To achieve this, add one of the lines above into the -/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add -the "-b <brd-number>" option to the line. You will need to download code for -every board. You should probably move the stlload program into a system -directory, such as /usr/sbin. Also, the default location of the cdk.sys image -file in the stlload down-loader is /usr/lib/stallion. Create that directory -and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put -them anyway). As an example your /etc/rc.d/rc.S file might have the -following lines added to it (if you had 3 boards): - /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys - /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys - /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys - -The image files cdk.sys and 2681.sys are specific to the board types. The -cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly -the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards. -If you load the wrong image file into a board it will fail to start up, and -of course the ports will not be operational! - -If you are using the modularized version of the driver you might want to put -the insmod calls in the startup script as well (before the download lines -obviously). - - -3.2 USING THE SERIAL PORTS - Once the driver is installed you will need to setup some device nodes to -access the serial ports. The simplest method is to use the stallion utility -"mkdevnods" script. It will automatically create all possible device entries -required for all 4 boards. This will create the normal serial port devices as -/dev/ttyE# where # is the port number starting from 0. A bank of 64 minor -device numbers is allocated to each board, so the first port on the second -board is port 64, etc. A set of callout type devices is also created. They -are created as the devices /dev/cue# where # is the same as for the ttyE -devices. +access the serial ports. Use the supplied "mkdevnods" script to automatically +create all required device entries for one board. This will create the normal +serial port devices as /dev/ttyE# where # is the port number starting from 0. +A set of callout type devices is also created. They are created as the devices +/dev/cue# where # is the same as for the ttyE devices. + +A bank of 64 minor device numbers is allocated to each board. To create +device nodes for ports on multiple boards supply a number of boards argument +to the "mkdevnods" script. For example to create nodes for four boards use +"mkdevnods 4". This means that the first port on the second board is port 64, +the first port on the third board is 128, etc. For the most part the Stallion driver tries to emulate the standard PC system COM ports and the standard Linux serial driver. The idea is that you should @@ -214,9 +412,18 @@ also be used (excepting the ability to auto-configure the I/O and IRQ addresses of boards). Higher baud rates are supported in the usual fashion through setserial or using the CBAUDEX extensions. Note that the EasyIO and -EasyConnection (all types) support 57600 and 115200 baud. The older boards -including ONboard, Brumby and the original Stallion support a maximum baud -rate of 38400. +EasyConnection (all types) support 57600 and 115200 baud, and the newer XP +versions also support 230400 and 460800 baud. The older boards including +ONboard, Brumby and the original Stallion support a maximum baud rate of +38400. + +This driver should work with anything that works on standard Linux serial +ports. Having said that, it has been used on at least the following types of +"things" under Linux: + a) standard dumb terminals (using agetty, getty) + b) serial mice (under X) + c) modems (using cu, uucp, minicom, seyon, uugetty) + d) slip and ppp connections If you are unfamiliar with how to use serial ports, then get the Serial-HOWTO by Greg Hankins. It will explain everything you need to know! @@ -225,6 +432,11 @@ 4. NOTES +The major device numbers used by this driver are conformant with the Linux +Device Registry, so they shouldn't clash with any other devices. Also the +device naming scheme is the "standard" used by most Linux serial port +devices. + You can use both drivers at once if you have a mix of board types installed in a system. However to do this you will need to change the major numbers used by one of the drivers. Currently both drivers use major numbers 24, 25 @@ -234,20 +446,17 @@ major numbers 60, 61 and 62. You will also need to create device nodes with different names for the ports, for example ttyF# and cuf#. -The original Stallion board is no longer supported by Stallion Technologies. -Although it is known to work with the istallion driver. - Finding a free physical memory address range can be a problem. The older boards like the Stallion and ONboard need large areas (64K or even 128K), so they can be very difficult to get into a system. If you have 16 Mb of RAM then you have no choice but to put them somewhere in the 640K -> 1Mb range. ONboards require 64K, so typically 0xd0000 is good, or 0xe0000 on some -systems. If you have an original Stallion board, "V4.0" or Rev.O, then you -need a 64K memory address space, so again 0xd0000 and 0xe0000 are good. -Older Stallion boards are a much bigger problem. They need 128K of address -space and must be on a 128K boundary. If you don't have a VGA card then -0xc0000 might be usable - there is really no other place you can put them -below 1Mb. +systems. If you have an original Stallion board, "V4.0" or Rev.O, +then you need a 64K memory address space, so again 0xd0000 and 0xe0000 are +good. Older Stallion boards are a much bigger problem. They need 128K of +address space and must be on a 128K boundary. If you don't have a VGA card +then 0xc0000 might be usable - there is really no other place you can put +them below 1Mb. Both the ONboard and old Stallion boards can use higher memory addresses as well, but you must have less than 16Mb of RAM to be able to use them. Usual @@ -265,19 +474,48 @@ ranges is the best option. Typically the 2Gb range is convenient for them, and gets them well out of the way. +There is a new utility program included called "stlstty". Most people +will not need to use this. If you have an ONboard/16 which has partial +signals on the upper 12 ports then this program can be used to set the +upper ports to have modem control instead of hardware flow control. Use +the "mapcts maprts" flag options to this utility on the port(s) that you +wish to do this mapping on, eg + ./stlstty maprts mapcts < /dev/cue0 +This enables RTS to act like DTR and CTS to act like DCD on the specified +port. + The ports of the EasyIO-8M board do not have DCD or DTR signals. So these -ports cannot be used as real modem devices. Generally, when using these +ports cannot be used as real modem devices. Generally when using these ports you should only use the cueX devices. -The driver utility package contains a couple of very useful programs. One -is a serial port statistics collection and display program - very handy -for solving serial port problems. The other is an extended option setting -program that works with the intelligent boards. +There is another new utility in this package that reports statistics on +the serial ports. You will need to have the curses libray installed on +your system to build it. + +To build the statistics display program type: + make stlstats +Once compiled simply run it (you will need to be root) and it will display +a port sumary for the first board and panel installed. Use the digits to +select different board numbers, or 'n' to cycle through the panels on a +board. To look at detailed port information then hit 'p', that will display +detailed port 0 information. Use the digits and letters 'a' through 'f' to +select the different ports (on this board and panel). + + + +5. ACKNOWLEDGEMENTS + +This driver is loosely based on code written by Theodore T'so, Linus +Torvalds, and others, so a big thanks to them all. -5. DISCLAIMER +6. DISCLAIMER -I do not speak for Stallion Technologies in any capacity, officially or -unofficially. +The information contained in this document is believed to be accurate and +reliable. However, no responsibility is assumed by Stallion Technologies +Pty. Ltd. for its use, nor any infringements of patents or other rights +of third parties resulting from its use. Stallion Technologies reserves +the right to modify the design of its products and will endeavour to change +the information in manuals and accompanying documentation accordingly. diff -u --recursive --new-file v2.0.34/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c --- v2.0.34/linux/drivers/char/apm_bios.c Mon Jul 13 13:46:26 1998 +++ linux/drivers/char/apm_bios.c Mon Jul 13 13:47:29 1998 @@ -36,7 +36,7 @@ * Linux 1.3.85 * 1.1: support user-space standby and suspend, power off after system * halted, Linux 1.3.98 - * 1.2: When resetting RTC after resume, take care so that the the time + * 1.2: When resetting RTC after resume, take care so that the time * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth * <jtoth@princeton.edu>); improve interaction between * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4 diff -u --recursive --new-file v2.0.34/linux/drivers/char/baycom.c linux/drivers/char/baycom.c --- v2.0.34/linux/drivers/char/baycom.c Mon Jul 8 00:21:45 1996 +++ linux/drivers/char/baycom.c Mon Jul 13 13:47:29 1998 @@ -1,2327 +0,0 @@ -/*****************************************************************************/ - -/* - * baycom.c -- baycom ser12 and par96 radio modem driver. - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * - * Supported modems - * - * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - * of a modulator/demodulator chip, usually a TI TCM3105. The computer - * is responsible for regenerating the receiver bit clock, as well as - * for handling the HDLC protocol. The modem connects to a serial port, - * hence the name. Since the serial port is not used as an async serial - * port, the kernel driver for serial ports cannot be used, and this - * driver only supports standard serial hardware (8250, 16450, 16550) - * - * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. - * The modem does all the filtering and regenerates the receiver clock. - * Data is transferred from and to the PC via a shift register. - * The shift register is filled with 16 bits and an interrupt is - * signalled. The PC then empties the shift register in a burst. This - * modem connects to the parallel port, hence the name. The modem - * leaves the implementation of the HDLC protocol and the scrambler - * polynomial to the PC. - * - * par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The - * modem is protocol compatible to par96, but uses only three low - * power ICs and can therefore be fed from the parallel port and - * does not require an additional power supply. - * - * - * Command line options (insmod command line) - * - * major major number the driver should use; default 60 - * modem modem type of the first channel (minor 0); 1=ser12, - * 2=par96/par97, any other value invalid - * iobase base address of the port; common values are for ser12 0x3f8, - * 0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc - * irq interrupt line of the port; common values are for ser12 3,4 - * and for par96/par97 7 - * options 0=use hardware DCD, 1=use software DCD - * - * - * History: - * 0.1 03.05.96 Renamed from ser12 0.5 and added support for par96 - * Various resource allocation cleanups - * 0.2 12.05.96 Changed major to allocated 51. Integrated into kernel - * source tree - * 0.3 04.06.96 Major bug fixed (forgot to wake up after write) which - * interestingly manifested only with kernel ax25 - * (the slip line discipline) - * introduced bottom half and tq_baycom - * HDLC processing now done with interrupts on - */ - -/*****************************************************************************/ - -#include <linux/module.h> -#include <linux/version.h> - -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/major.h> -#include <asm/segment.h> -#include <linux/kernel.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/malloc.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/ioport.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/interrupt.h> -#include <linux/tqueue.h> -#include <linux/baycom.h> - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_TYPE_NORMAL 0 /* not used */ -#define TTY_DRIVER_TYPE_BAYCOM 6 - -/* - * ser12 options: - * BAYCOM_OPTIONS_SOFTDCD: if undefined, you must use the transmitters - * hardware carrier detect circuitry, the driver will report DCD as soon as - * there are transitions on the input line. Advantage: lower interrupt load - * on the system. Disadvantage: slower, since hardware carrier detect - * circuitry is usually slow. - */ - -#define BUFLEN_RX 8192 -#define BUFLEN_TX 8192 - -#define NR_PORTS 4 - -#define KISS_VERBOSE - -#define BAYCOM_MAGIC 0x3105bac0 - -/* --------------------------------------------------------------------- */ - -/* - * user settable parameters (from the command line) - */ -#ifndef MODULE -static -#endif /* MODULE */ -int major = BAYCOM_MAJOR; - -/* --------------------------------------------------------------------- */ - -static struct tty_struct *baycom_table[NR_PORTS]; -static struct termios *baycom_termios[NR_PORTS]; -static struct termios *baycom_termios_locked[NR_PORTS]; - -static int baycom_refcount; - -static struct tty_driver baycom_driver; - -static struct { - int modem, iobase, irq, options; -} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, }; - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define SER12_EXTENT 8 - -#define LPT_DATA(iobase) (iobase+0) -#define LPT_STATUS(iobase) (iobase+1) -#define LPT_CONTROL(iobase) (iobase+2) -#define LPT_IRQ_ENABLE 0x10 -#define PAR96_BURSTBITS 16 -#define PAR96_BURST 4 -#define PAR96_PTT 2 -#define PAR96_TXBIT 1 -#define PAR96_ACK 0x40 -#define PAR96_RXBIT 0x20 -#define PAR96_DCD 0x10 -#define PAR97_POWER 0xf8 - -#define PAR96_EXTENT 3 - -/* ---------------------------------------------------------------------- */ - -struct access_params { - int tx_delay; - int tx_tail; - int slottime; - int ppersist; - int fulldup; -}; - -struct hdlc_state_rx { - int rx_state; /* 0 = sync hunt, != 0 receiving */ - unsigned int bitstream; - unsigned int bitbuf; - int numbits; - unsigned int shreg1, shreg2; - - int len; - unsigned char *bp; - unsigned char buffer[BAYCOM_MAXFLEN+2]; /* make room for CRC */ -}; - -struct hdlc_state_tx { - /* - * 0 = send flags - * 1 = send txtail (flags) - * 2 = send packet - */ - int tx_state; - int numflags; - unsigned int bitstream; - unsigned int current_byte; - unsigned char ptt; - - unsigned int bitbuf; - int numbits; - unsigned int shreg1, shreg2; - - int len; - unsigned char *bp; - unsigned char buffer[BAYCOM_MAXFLEN+2]; /* make room for CRC */ -}; - -struct modem_state_ser12 { - unsigned char last_sample; - unsigned char interm_sample; - unsigned int bit_pll; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; - unsigned char last_rxbit; - unsigned char tx_bit; -}; - -struct modem_state_par96 { - int dcd_count; - unsigned int dcd_shreg; - unsigned long descram; - unsigned long scram; -}; - -struct modem_state { - unsigned char dcd; - short arb_divider; - unsigned char flags; - struct modem_state_ser12 ser12; - struct modem_state_par96 par96; -}; - -struct packet_buffer { - unsigned int rd; - unsigned int wr; - - unsigned int buflen; - unsigned char *buffer; -}; - -struct packet_hdr { - unsigned int next; - unsigned int len; - /* packet following */ -}; - -#ifdef BAYCOM_DEBUG -struct bit_buffer { - unsigned int rd; - unsigned int wr; - unsigned int shreg; - unsigned char buffer[64]; -}; - -struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; -}; -#endif /* BAYCOM_DEBUG */ - -struct kiss_decode { - unsigned char dec_state; /* 0 = hunt FEND */ - unsigned char escaped; - unsigned char pkt_buf[BAYCOM_MAXFLEN+1]; - unsigned int wr; -}; - -/* ---------------------------------------------------------------------- */ - -struct baycom_state { - int magic; - - unsigned char modem_type; - - unsigned int iobase; - unsigned int irq; - unsigned int options; - - int opened; - struct tty_struct *tty; - -#ifdef BAYCOM_USE_BH - struct tq_struct tq_receiver, tq_transmitter, tq_arbitrate; -#endif /* BAYCOM_USE_BH */ - - struct packet_buffer rx_buf; - struct packet_buffer tx_buf; - - struct access_params ch_params; - - struct hdlc_state_rx hdlc_rx; - struct hdlc_state_tx hdlc_tx; - - int calibrate; - - struct modem_state modem; - -#ifdef BAYCOM_DEBUG - struct bit_buffer bitbuf_channel; - struct bit_buffer bitbuf_hdlc; - - struct debug_vals debug_vals; -#endif /* BAYCOM_DEBUG */ - - struct kiss_decode kiss_decode; - - struct baycom_statistics stat; -}; - -/* --------------------------------------------------------------------- */ - -struct baycom_state baycom_state[NR_PORTS]; - -#ifdef BAYCOM_USE_BH -DECLARE_TASK_QUEUE(tq_baycom); -#endif /* BAYCOM_USE_BH */ - -/* --------------------------------------------------------------------- */ - -/* - * the CRC routines are stolen from WAMPES - * by Dieter Deyke - */ - -static const unsigned short crc_ccitt_table[] = { - 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, - 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, - 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, - 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, - 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, - 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, - 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, - 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, - 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, - 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, - 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, - 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, - 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, - 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, - 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, - 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, - 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, - 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, - 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, - 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, - 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, - 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 -}; - -/*---------------------------------------------------------------------------*/ - -static inline void append_crc_ccitt(unsigned char *buffer, int len) -{ - unsigned int crc = 0xffff; - - for (;len>0;len--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff]; - crc ^= 0xffff; - *buffer++ = crc; - *buffer++ = crc >> 8; -} - -/*---------------------------------------------------------------------------*/ - -static inline int check_crc_ccitt(const unsigned char *buf,int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - return (crc & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -#if 0 -static int calc_crc_ccitt(const unsigned char *buf,int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - crc ^= 0xffff; - return (crc & 0xffff); -} -#endif - -/* ---------------------------------------------------------------------- */ - -static int store_packet(struct packet_buffer *buf, unsigned char *data, - char from_user, unsigned int len) -{ - unsigned int free; - struct packet_hdr *hdr; - unsigned int needed = sizeof(struct packet_hdr)+len; - - free = buf->rd-buf->wr; - if(buf->rd <= buf->wr) { - free = buf->buflen - buf->wr; - if((free < needed) && (buf->rd >= needed)) { - hdr = (struct packet_hdr *)(buf->buffer+buf->wr); - hdr->next = 0; - hdr->len = 0; - buf->wr = 0; - free = buf->rd; - } - } - if(free < needed) return 0; /* buffer overrun */ - hdr = (struct packet_hdr *)(buf->buffer+buf->wr); - if (from_user) - memcpy_fromfs(hdr+1,data,len); - else - memcpy(hdr+1,data,len); - hdr->len = len; - hdr->next = buf->wr+needed; - if (hdr->next + sizeof(struct packet_hdr) >= buf->buflen) - hdr->next = 0; - buf->wr = hdr->next; - return 1; -} - -/* ---------------------------------------------------------------------- */ - -static void get_packet(struct packet_buffer *buf, unsigned char **data, - unsigned int *len) -{ - struct packet_hdr *hdr; - - *data = NULL; - *len = 0; - if (buf->rd == buf->wr) - return; - hdr = (struct packet_hdr *)(buf->buffer+buf->rd); - while (!(hdr->len)) { - buf->rd = hdr->next; - if (buf->rd == buf->wr) - return; - hdr = (struct packet_hdr *)(buf->buffer+buf->rd); - } - *data = (unsigned char *)(hdr+1); - *len = hdr->len; -} - -/* ---------------------------------------------------------------------- */ - -static void ack_packet(struct packet_buffer *buf) -{ - struct packet_hdr *hdr; - - if (buf->rd == buf->wr) - return; - hdr = (struct packet_hdr *)(buf->buffer+buf->rd); - buf->rd = hdr->next; -} - -/* ---------------------------------------------------------------------- */ - -static int store_kiss_packet(struct packet_buffer *buf, unsigned char *data, - unsigned int len) -{ - unsigned char *bp = data; - int ln = len; - /* - * variables of buf - */ - unsigned int rd; - unsigned int wr; - unsigned int buflen; - unsigned char *buffer; - - if (!len || !data || !buf) - return 0; - buflen = buf->buflen; - rd = buf->rd; - wr = buf->wr; - buffer = buf->buffer; - -#define ADD_CHAR(c) {\ - buffer[wr++] = c;\ - if (wr >= buflen) wr = 0;\ - if (wr == rd) return 0;\ - } -#define ADD_KISSCHAR(c) {\ - if (((c) & 0xff) == KISS_FEND) {\ - ADD_CHAR(KISS_FESC);\ - ADD_CHAR(KISS_TFEND);\ - } else if (((c) & 0xff) == KISS_FESC) {\ - ADD_CHAR(KISS_FESC);\ - ADD_CHAR(KISS_TFESC);\ - } else {\ - ADD_CHAR(c);\ - }\ - } - - ADD_CHAR(KISS_FEND); - ADD_KISSCHAR(KISS_CMD_DATA); - for(; ln > 0; ln--,bp++) { - ADD_KISSCHAR(*bp); - } - ADD_CHAR(KISS_FEND); - buf->wr = wr; -#undef ADD_CHAR -#undef ADD_KISSCHAR - return 1; -} - -/* ---------------------------------------------------------------------- */ - -#ifdef BAYCOM_DEBUG -static inline void add_bitbuffer(struct bit_buffer * buf, unsigned int bit) -{ - unsigned char new; - - if (!buf) return; - new = buf->shreg & 1; - buf->shreg >>= 1; - if (bit) - buf->shreg |= 0x80; - if (new) { - buf->buffer[buf->wr] = buf->shreg; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - buf->shreg = 0x80; - } -} - -static inline void add_bitbuffer_word(struct bit_buffer * buf, - unsigned int bits) -{ - buf->buffer[buf->wr] = bits & 0xff; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - buf->buffer[buf->wr] = (bits >> 8) & 0xff; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - -} -#endif /* BAYCOM_DEBUG */ - -/* ---------------------------------------------------------------------- */ - -static inline unsigned int tenms_to_2flags(struct baycom_state *bc, - unsigned int tenms) -{ - switch (bc->modem_type) { - case BAYCOM_MODEM_SER12: - return tenms * 3 / 4; - case BAYCOM_MODEM_PAR96: - return tenms * 6; - default: - return 0; - } -} - -/* ---------------------------------------------------------------------- */ -/* - * The HDLC routines - */ - -static inline int hdlc_rx_add_bytes(struct baycom_state *bc, - unsigned int bits, int num) -{ - int added = 0; - while (bc->hdlc_rx.rx_state && num >= 8) { - if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) { - bc->hdlc_rx.rx_state = 0; - return 0; - } - *bc->hdlc_rx.bp++ = bits >> (32-num); - bc->hdlc_rx.len++; - num -= 8; - added += 8; - } - return added; -} - -static inline void hdlc_rx_flag(struct baycom_state *bc) -{ - if (bc->hdlc_rx.len < 4) - return; - if (!check_crc_ccitt(bc->hdlc_rx.buffer, bc->hdlc_rx.len)) - return; - bc->stat.rx_packets++; - if (!store_kiss_packet(&bc->rx_buf, - bc->hdlc_rx.buffer, - bc->hdlc_rx.len-2)) - bc->stat.rx_bufferoverrun++; -} - -static void hdlc_rx_word(struct baycom_state *bc, unsigned int word) -{ - int i; - unsigned int mask1, mask2, mask3, mask4, mask5, mask6; - - if (!bc) return; - - word &= 0xffff; -#ifdef BAYCOM_DEBUG - add_bitbuffer_word(&bc->bitbuf_hdlc, word); -#endif /* BAYCOM_DEBUG */ - bc->hdlc_rx.bitstream >>= 16; - bc->hdlc_rx.bitstream |= word << 16; - bc->hdlc_rx.bitbuf >>= 16; - bc->hdlc_rx.bitbuf |= word << 16; - bc->hdlc_rx.numbits += 16; - for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00, - mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; - i >= 0; - i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, - mask5 <<= 1, mask6 = (mask6 << 1) | 1) { - if ((bc->hdlc_rx.bitstream & mask1) == mask1) - bc->hdlc_rx.rx_state = 0; /* abort received */ - else if ((bc->hdlc_rx.bitstream & mask2) == mask3) { - /* flag received */ - if (bc->hdlc_rx.rx_state) { - hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf << - (8 + i), bc->hdlc_rx.numbits - - 8 - i); - hdlc_rx_flag(bc); - } - bc->hdlc_rx.len = 0; - bc->hdlc_rx.bp = bc->hdlc_rx.buffer; - bc->hdlc_rx.rx_state = 1; - bc->hdlc_rx.numbits = i; - } else if ((bc->hdlc_rx.bitstream & mask4) == mask5) { - /* stuffed bit */ - bc->hdlc_rx.numbits--; - bc->hdlc_rx.bitbuf = (bc->hdlc_rx.bitbuf & (~mask6)) | - ((bc->hdlc_rx.bitbuf & mask6) << 1); - } - } - bc->hdlc_rx.numbits -= hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf, - bc->hdlc_rx.numbits); -} - -/* ---------------------------------------------------------------------- */ - -static unsigned int hdlc_tx_word(struct baycom_state *bc) -{ - unsigned int mask1, mask2, mask3; - int i; - - if (!bc || !bc->hdlc_tx.ptt) - return 0; - for (;;) { - if (bc->hdlc_tx.numbits >= 16) { - unsigned int ret = bc->hdlc_tx.bitbuf & 0xffff; - bc->hdlc_tx.bitbuf >>= 16; - bc->hdlc_tx.numbits -= 16; - return ret; - } - switch (bc->hdlc_tx.tx_state) { - default: - bc->hdlc_tx.ptt = 0; - bc->hdlc_tx.tx_state = 0; - return 0; - case 0: - case 1: - if (bc->hdlc_tx.numflags) { - bc->hdlc_tx.numflags--; - bc->hdlc_tx.bitbuf |= - 0x7e7e << bc->hdlc_tx.numbits; - bc->hdlc_tx.numbits += 16; - break; - } - if (bc->hdlc_tx.tx_state == 1) { - bc->hdlc_tx.ptt = 0; - return 0; - } - get_packet(&bc->tx_buf, &bc->hdlc_tx.bp, - &bc->hdlc_tx.len); - if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) { - bc->hdlc_tx.tx_state = 1; - bc->hdlc_tx.numflags = tenms_to_2flags - (bc, bc->ch_params.tx_tail); - break; - } - if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) { - bc->hdlc_tx.tx_state = 0; - bc->hdlc_tx.numflags = 1; - ack_packet(&bc->tx_buf); - break; - } - memcpy(bc->hdlc_tx.buffer, bc->hdlc_tx.bp, - bc->hdlc_tx.len); - ack_packet(&bc->tx_buf); - bc->hdlc_tx.bp = bc->hdlc_tx.buffer; - append_crc_ccitt(bc->hdlc_tx.buffer, bc->hdlc_tx.len); - /* the appended CRC */ - bc->hdlc_tx.len += 2; - bc->hdlc_tx.tx_state = 2; - bc->hdlc_tx.bitstream = 0; - bc->stat.tx_packets++; - break; - case 2: - if (!bc->hdlc_tx.len) { - bc->hdlc_tx.tx_state = 0; - bc->hdlc_tx.numflags = 1; - break; - } - bc->hdlc_tx.len--; - bc->hdlc_tx.bitbuf |= *bc->hdlc_tx.bp << - bc->hdlc_tx.numbits; - bc->hdlc_tx.bitstream >>= 8; - bc->hdlc_tx.bitstream |= (*bc->hdlc_tx.bp++) << 16; - mask1 = 0x1f000; - mask2 = 0x10000; - mask3 = 0xffffffff >> (31-bc->hdlc_tx.numbits); - bc->hdlc_tx.numbits += 8; - for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, - mask3 = (mask3 << 1) | 1) { - if ((bc->hdlc_tx.bitstream & mask1) != mask1) - continue; - bc->hdlc_tx.bitstream &= ~mask2; - bc->hdlc_tx.bitbuf = - (bc->hdlc_tx.bitbuf & mask3) | - ((bc->hdlc_tx.bitbuf & - (~mask3)) << 1); - bc->hdlc_tx.numbits++; - mask3 = (mask3 << 1) | 1; - } - break; - } - } -} - -/* ---------------------------------------------------------------------- */ - -static unsigned short random_seed; - -static inline unsigned short random_num(void) -{ - random_seed = 28629 * random_seed + 157; - return random_seed; -} - -/* ---------------------------------------------------------------------- */ - -static inline void tx_arbitrate(struct baycom_state *bc) -{ - unsigned char *bp; - unsigned int len; - - if (!bc || bc->hdlc_tx.ptt || bc->modem.dcd) - return; - get_packet(&bc->tx_buf, &bp, &len); - if (!bp || !len) - return; - - if (!bc->ch_params.fulldup) { - if ((random_num() % 256) > bc->ch_params.ppersist) - return; - } - bc->hdlc_tx.tx_state = 0; - bc->hdlc_tx.numflags = tenms_to_2flags(bc, bc->ch_params.tx_delay); - bc->hdlc_tx.numbits = bc->hdlc_tx.bitbuf = bc->hdlc_tx.bitstream = 0; - bc->hdlc_tx.ptt = 1; - bc->stat.ptt_keyed++; -} - -/* --------------------------------------------------------------------- */ - -#ifdef BAYCOM_DEBUG -static void inline baycom_int_freq(struct baycom_state *bc) -{ - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -} -#endif /* BAYCOM_DEBUG */ - -/* --------------------------------------------------------------------- */ - -static inline void rx_chars_to_flip(struct baycom_state *bc) -{ - int flip_free; - unsigned int cnt; - unsigned int new_rd; - unsigned long flags; - - if ((!bc) || (!bc->tty) || (bc->tty->flip.count >= TTY_FLIPBUF_SIZE) || - (bc->rx_buf.rd == bc->rx_buf.wr) || - (!bc->tty->flip.char_buf_ptr) || - (!bc->tty->flip.flag_buf_ptr)) - return; - for(;;) { - flip_free = TTY_FLIPBUF_SIZE - bc->tty->flip.count; - if (bc->rx_buf.rd <= bc->rx_buf.wr) - cnt = bc->rx_buf.wr - bc->rx_buf.rd; - else - cnt = bc->rx_buf.buflen - bc->rx_buf.rd; - if ((flip_free <= 0) || (!cnt)) { - tty_schedule_flip(bc->tty); - return; - } - if (cnt > flip_free) - cnt = flip_free; - save_flags(flags); cli(); - memcpy(bc->tty->flip.char_buf_ptr, bc->rx_buf.buffer+bc->rx_buf.rd, cnt); - memset(bc->tty->flip.flag_buf_ptr, TTY_NORMAL, cnt); - bc->tty->flip.count += cnt; - bc->tty->flip.char_buf_ptr += cnt; - bc->tty->flip.flag_buf_ptr += cnt; - restore_flags(flags); - new_rd = bc->rx_buf.rd+cnt; - if (new_rd >= bc->rx_buf.buflen) - new_rd -= bc->rx_buf.buflen; - bc->rx_buf.rd = new_rd; - } -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== SER12 specific routines ========================= - */ - -static void inline ser12_set_divisor(struct baycom_state *bc, - unsigned char divisor) -{ - outb(0x81, LCR(bc->iobase)); /* DLAB = 1 */ - outb(divisor, DLL(bc->iobase)); - outb(0, DLM(bc->iobase)); - outb(0x01, LCR(bc->iobase)); /* word length = 6 */ -} - -/* --------------------------------------------------------------------- */ - -/* - * must call the TX arbitrator every 10ms - */ -#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \ - 36 : 24) -#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \ - 240 : 12) - -static void baycom_ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct baycom_state *bc = (struct baycom_state *)dev_id; - unsigned char cur_s; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(bc->iobase)); - rx_chars_to_flip(bc); -#ifdef BAYCOM_DEBUG - baycom_int_freq(bc); -#endif /* BAYCOM_DEBUG */ - /* - * check if transmitter active - */ - if (bc->hdlc_tx.ptt || bc->calibrate > 0) { - ser12_set_divisor(bc, 12); /* one interrupt per channel bit */ - /* - * first output the last bit (!) then call HDLC transmitter, - * since this may take quite long - */ - outb(0x0e | (bc->modem.ser12.tx_bit ? 1 : 0), MCR(bc->iobase)); - if (bc->hdlc_tx.shreg1 <= 1) { - if (bc->calibrate > 0) { - bc->hdlc_tx.shreg1 = 0x10000; - bc->calibrate--; - } else { -#ifdef BAYCOM_USE_BH - bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2; - bc->hdlc_tx.shreg2 = 0; - queue_task_irq_off(&bc->tq_transmitter, - &tq_baycom); - mark_bh(BAYCOM_BH); -#ifdef HDLC_LOOPBACK - bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1; - queue_task_irq_off(&bc->tq_receiver, - &tq_baycom); -#endif /* HDLC_LOOPBACK */ -#else /* BAYCOM_USE_BH */ - bc->hdlc_tx.shreg1 = hdlc_tx_word(bc) - | 0x10000; -#ifdef HDLC_LOOPBACK - hdlc_rx_word(bc, bc->hdlc_tx.shreg1); -#endif /* HDLC_LOOPBACK */ -#endif /* BAYCOM_USE_BH */ - } - } - if (!(bc->hdlc_tx.shreg1 & 1)) - bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit; - bc->hdlc_tx.shreg1 >>= 1; - return; - } - /* - * do demodulator - */ - outb(0x0d, MCR(bc->iobase)); /* transmitter off */ - cur_s = inb(MSR(bc->iobase)) & 0x10; /* the CTS line */ -#ifdef BAYCOM_DEBUG - add_bitbuffer(&bc->bitbuf_channel, cur_s); -#endif /* BAYCOM_DEBUG */ - bc->modem.ser12.dcd_shreg <<= 1; - if(cur_s != bc->modem.ser12.last_sample) { - bc->modem.ser12.dcd_shreg |= 1; - - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - unsigned int dcdspos, dcdsneg; - - dcdspos = dcdsneg = 0; - dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); - if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) - dcdspos += 2; - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); - - bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; - } else - bc->modem.ser12.dcd_sum0--; - } - bc->modem.ser12.last_sample = cur_s; - if(!bc->modem.ser12.dcd_time) { - bc->modem.dcd = (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0; - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - /* offset to ensure DCD off on silent input */ - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); - } - bc->modem.ser12.dcd_time--; - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - /* - * PLL code for the improved software DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(bc, 4); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 7) { - case 1: /* transition too late */ - ser12_set_divisor(bc, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 4: /* transition too early */ - ser12_set_divisor(bc, 3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(bc, 4); - break; - } - bc->hdlc_rx.shreg1 >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->hdlc_rx.shreg1 |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - if (++bc->modem.ser12.interm_sample >= 3) - bc->modem.ser12.interm_sample = 0; - } else { - /* - * PLL algorithm for the hardware squelch DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(bc, 6); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 3) { - case 1: /* transition too late */ - ser12_set_divisor(bc, 7); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 2: /* transition too early */ - ser12_set_divisor(bc, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(bc, 6); - break; - } - bc->hdlc_rx.shreg1 >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->hdlc_rx.shreg1 |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample; - } - if (bc->hdlc_rx.shreg1 & 1) { -#ifdef BAYCOM_USE_BH - bc->hdlc_rx.shreg2 = (bc->hdlc_rx.shreg1 >> 1) | 0x10000; - queue_task_irq_off(&bc->tq_receiver, &tq_baycom); - mark_bh(BAYCOM_BH); -#else /* BAYCOM_USE_BH */ - hdlc_rx_word(bc, bc->hdlc_rx.shreg1 >> 1); -#endif /* BAYCOM_USE_BH */ - bc->hdlc_rx.shreg1 = 0x10000; - } - if (--bc->modem.arb_divider <= 0) { -#ifdef BAYCOM_USE_BH - queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom); - mark_bh(BAYCOM_BH); -#else /* BAYCOM_USE_BH */ - tx_arbitrate(bc); -#endif /* BAYCOM_USE_BH */ - bc->modem.arb_divider = bc->ch_params.slottime * - SER12_ARB_DIVIDER(bc); - } -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = - { "unknown", "8250", "16450", "16550", "16550A" }; - -static enum uart ser12_check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_allocate_resources(unsigned int iobase, unsigned int irq, - unsigned int options) -{ - enum uart u; - - if (!iobase || iobase > 0xfff || irq < 2 || irq > 15) - return -ENXIO; - if (check_region(iobase, SER12_EXTENT)) - return -EACCES; - if ((u = ser12_check_uart(iobase)) == c_uart_unknown) - return -EIO; - request_region(iobase, SER12_EXTENT, "baycom_ser12"); - outb(0, FCR(iobase)); /* disable FIFOs */ - outb(0x0d, MCR(iobase)); - printk(KERN_INFO "baycom: ser12 at iobase 0x%x irq %u options 0x%x " - "uart %s\n", iobase, irq, options, uart_str[u]); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void ser12_deallocate_resources(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_SER12) - return; - /* - * disable interrupts - */ - outb(0, IER(bc->iobase)); - outb(1, MCR(bc->iobase)); - /* - * this should prevent kernel: Trying to free IRQx - * messages - */ - if (bc->opened > 0) - free_irq(bc->irq, bc); - release_region(bc->iobase, SER12_EXTENT); - bc->modem_type = BAYCOM_MODEM_INVALID; - printk(KERN_INFO "baycom: release ser12 at iobase 0x%x irq %u\n", - bc->iobase, bc->irq); - bc->iobase = bc->irq = bc->options = 0; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_on_open(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_SER12) - return -ENXIO; - /* - * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that - * we get exactly (hopefully) 2 or 3 interrupts per radio symbol, - * depending on the usage of the software DCD routine - */ - ser12_set_divisor(bc, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6); - outb(0x0d, MCR(bc->iobase)); - outb(0, IER(bc->iobase)); - if (request_irq(bc->irq, baycom_ser12_interrupt, SA_INTERRUPT, - "baycom_ser12", bc)) - return -EBUSY; - /* - * enable transmitter empty interrupt - */ - outb(2, IER(bc->iobase)); - /* - * the value here serves to power the modem - */ - outb(0x00, THR(bc->iobase)); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void ser12_on_close(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_SER12) - return; - /* - * disable interrupts - */ - outb(0, IER(bc->iobase)); - outb(1, MCR(bc->iobase)); - free_irq(bc->irq, bc); -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== PAR96 specific routines ========================= - */ - -#define PAR96_DESCRAM_TAP1 0x20000 -#define PAR96_DESCRAM_TAP2 0x01000 -#define PAR96_DESCRAM_TAP3 0x00001 - -#define PAR96_DESCRAM_TAPSH1 17 -#define PAR96_DESCRAM_TAPSH2 12 -#define PAR96_DESCRAM_TAPSH3 0 - -#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */ -#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -/* --------------------------------------------------------------------- */ - -static void baycom_par96_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - register struct baycom_state *bc = (struct baycom_state *)dev_id; - int i; - unsigned int data, descx, mask, mask2; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - - rx_chars_to_flip(bc); -#ifdef BAYCOM_DEBUG - baycom_int_freq(bc); -#endif /* BAYCOM_DEBUG */ - /* - * check if transmitter active - */ - if (bc->hdlc_tx.ptt || bc->calibrate > 0) { - /* - * first output the last 16 bits (!) then call HDLC - * transmitter, since this may take quite long - * do the differential encoder and the scrambler on the fly - */ - data = bc->hdlc_tx.shreg1; - for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) { - unsigned char val = PAR97_POWER; - bc->modem.par96.scram = ((bc->modem.par96.scram << 1) | - (bc->modem.par96.scram & 1)); - if (!(data & 1)) - bc->modem.par96.scram ^= 1; - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1)) - bc->modem.par96.scram ^= - (PAR96_SCRAM_TAPN << 1); - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2)) - val |= PAR96_TXBIT; - outb(val, LPT_DATA(bc->iobase)); - outb(val | PAR96_BURST, LPT_DATA(bc->iobase)); - } - if (bc->calibrate > 0) { - bc->hdlc_tx.shreg1 = 0x10000; - bc->calibrate--; - } else { -#ifdef BAYCOM_USE_BH - bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2; - bc->hdlc_tx.shreg2 = 0; - queue_task_irq_off(&bc->tq_transmitter, &tq_baycom); - mark_bh(BAYCOM_BH); -#ifdef HDLC_LOOPBACK - bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1; - queue_task_irq_off(&bc->tq_receiver, &tq_baycom); -#endif /* HDLC_LOOPBACK */ -#else /* BAYCOM_USE_BH */ - bc->hdlc_tx.shreg1 = hdlc_tx_word(bc); -#ifdef HDLC_LOOPBACK - hdlc_rx_word(bc, bc->hdlc_tx.shreg1); -#endif /* HDLC_LOOPBACK */ -#endif /* BAYCOM_USE_BH */ - } - return; - } - /* - * do receiver; differential decode and descramble on the fly - */ - for(data = i = 0; i < PAR96_BURSTBITS; i++) { - bc->modem.par96.descram = (bc->modem.par96.descram << 1); - if (inb(LPT_STATUS(bc->iobase)) & PAR96_RXBIT) - bc->modem.par96.descram |= 1; - descx = bc->modem.par96.descram ^ - (bc->modem.par96.descram >> 1); - /* now the diff decoded data is inverted in descram */ - outb(PAR97_POWER | PAR96_PTT, LPT_DATA(bc->iobase)); - descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^ - (descx >> PAR96_DESCRAM_TAPSH2)); - data >>= 1; - if (!(descx & 1)) - data |= 0x8000; - outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, - LPT_DATA(bc->iobase)); - } -#ifdef BAYCOM_USE_BH - bc->hdlc_rx.shreg2 = bc->hdlc_rx.shreg1; - bc->hdlc_rx.shreg1 = data | 0x10000; - queue_task_irq_off(&bc->tq_receiver, &tq_baycom); - mark_bh(BAYCOM_BH); -#else /* BAYCOM_USE_BH */ - hdlc_rx_word(bc, data); -#endif /* BAYCOM_USE_BH */ - /* - * do DCD algorithm - */ - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16) - | (data << 16); - /* search for flags and set the dcd counter appropriately */ - for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if ((bc->modem.par96.dcd_shreg & mask) == mask2) - bc->modem.par96.dcd_count = BAYCOM_MAXFLEN+4; - /* check for abort/noise sequences */ - for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if (((bc->modem.par96.dcd_shreg & mask) == mask2) && - (bc->modem.par96.dcd_count >= 0)) - bc->modem.par96.dcd_count -= BAYCOM_MAXFLEN-10; - /* decrement and set the dcd variable */ - if (bc->modem.par96.dcd_count >= 0) - bc->modem.par96.dcd_count -= 2; - bc->modem.dcd = bc->modem.par96.dcd_count > 0; - } else { - bc->modem.dcd = !!(inb(LPT_STATUS(bc->iobase)) - & PAR96_DCD); - } - if (--bc->modem.arb_divider <= 0) { -#ifdef BAYCOM_USE_BH - queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom); - mark_bh(BAYCOM_BH); -#else /* BAYCOM_USE_BH */ - tx_arbitrate(bc); -#endif /* BAYCOM_USE_BH */ - bc->modem.arb_divider = bc->ch_params.slottime * 6; - } -} - -/* --------------------------------------------------------------------- */ - -static int par96_check_lpt(unsigned int iobase) -{ - unsigned char b1,b2; - int i; - - b1 = inb(LPT_DATA(iobase)); - b2 = inb(LPT_CONTROL(iobase)); - outb(0xaa, LPT_DATA(iobase)); - i = inb(LPT_DATA(iobase)) == 0xaa; - outb(0x55, LPT_DATA(iobase)); - i &= inb(LPT_DATA(iobase)) == 0x55; - outb(0x0a, LPT_CONTROL(iobase)); - i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a; - outb(0x05, LPT_CONTROL(iobase)); - i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05; - outb(b1, LPT_DATA(iobase)); - outb(b2, LPT_CONTROL(iobase)); - return !i; -} - -/* --------------------------------------------------------------------- */ - -static int par96_allocate_resources(unsigned int iobase, unsigned int irq, - unsigned int options) -{ - if (!iobase || iobase > 0xfff || irq < 2 || irq > 15) - return -ENXIO; - if (check_region(iobase, PAR96_EXTENT)) - return -EACCES; - if (par96_check_lpt(iobase)) - return -EIO; - request_region(iobase, PAR96_EXTENT, "baycom_par96"); - outb(0, LPT_CONTROL(iobase)); /* disable interrupt */ - outb(PAR96_PTT | PAR97_POWER, LPT_DATA(iobase)); /* switch off PTT */ - printk(KERN_INFO "baycom: par96 at iobase 0x%x irq %u options 0x%x\n", - iobase, irq, options); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void par96_deallocate_resources(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96) - return; - outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */ - outb(PAR96_PTT, LPT_DATA(bc->iobase)); /* switch off PTT */ - /* - * this should prevent kernel: Trying to free IRQx - * messages - */ - if (bc->opened > 0) - free_irq(bc->irq, bc); - release_region(bc->iobase, PAR96_EXTENT); - bc->modem_type = BAYCOM_MODEM_INVALID; - printk(KERN_INFO "baycom: release par96 at iobase 0x%x irq %u\n", - bc->iobase, bc->irq); - bc->iobase = bc->irq = bc->options = 0; -} - -/* --------------------------------------------------------------------- */ - -static int par96_on_open(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96) - return -ENXIO; - outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */ - /* switch off PTT */ - outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase)); - if (request_irq(bc->irq, baycom_par96_interrupt, SA_INTERRUPT, - "baycom_par96", bc)) - return -EBUSY; - outb(LPT_IRQ_ENABLE, LPT_CONTROL(bc->iobase)); /* enable interrupt */ - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void par96_on_close(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96) - return; - outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */ - /* switch off PTT */ - outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase)); - free_irq(bc->irq, bc); -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== Bottom half (soft interrupt) ==================== - */ - -#ifdef BAYCOM_USE_BH -static void bh_receiver(void *private) -{ - struct baycom_state *bc = (struct baycom_state *)private; - unsigned int temp; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - if (!bc->hdlc_rx.shreg2) - return; - temp = bc->hdlc_rx.shreg2; - bc->hdlc_rx.shreg2 = 0; - hdlc_rx_word(bc, temp); -} - -/* --------------------------------------------------------------------- */ - -static void bh_transmitter(void *private) -{ - struct baycom_state *bc = (struct baycom_state *)private; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - if (bc->hdlc_tx.shreg2) - return; - bc->hdlc_tx.shreg2 = hdlc_tx_word(bc) | 0x10000; -} - -/* --------------------------------------------------------------------- */ - -static void bh_arbitrate(void *private) -{ - struct baycom_state *bc = (struct baycom_state *)private; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - tx_arbitrate(bc); -} - -/* --------------------------------------------------------------------- */ - -static void baycom_bottom_half(void) -{ - run_task_queue(&tq_baycom); -} -#endif /* BAYCOM_USE_BH */ - -/* --------------------------------------------------------------------- */ -/* - * ===================== TTY interface routines ========================== - */ - -static inline int baycom_paranoia_check(struct baycom_state *bc, - const char *routine) -{ - if (!bc || bc->magic != BAYCOM_MAGIC) { - printk(KERN_ERR "baycom: bad magic number for baycom struct " - "in routine %s\n", routine); - return 1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * Here the tty driver code starts - */ - -static void baycom_put_fend(struct baycom_state *bc) -{ - if (bc->kiss_decode.wr <= 0 || - (bc->kiss_decode.pkt_buf[0] & 0xf0) != 0) - return; - - switch (bc->kiss_decode.pkt_buf[0] & 0xf) { - case KISS_CMD_DATA: - if (bc->kiss_decode.wr <= 8) - break; - if (!store_packet(&bc->tx_buf, bc->kiss_decode.pkt_buf+1, 0, - bc->kiss_decode.wr-1)) - bc->stat.tx_bufferoverrun++; - break; - - case KISS_CMD_TXDELAY: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.tx_delay = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: TX delay = %ums\n", - bc->ch_params.tx_delay * 10); -#endif /* KISS_VERBOSE */ - break; - - case KISS_CMD_PPERSIST: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.ppersist = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: p-persistence = %u\n", - bc->ch_params.ppersist); -#endif /* KISS_VERBOSE */ - break; - - case KISS_CMD_SLOTTIME: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.slottime = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: slottime = %ums\n", - bc->ch_params.slottime * 10); -#endif /* KISS_VERBOSE */ - break; - - case KISS_CMD_TXTAIL: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.tx_tail = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: TX tail = %ums\n", - bc->ch_params.tx_tail * 10); -#endif /* KISS_VERBOSE */ - break; - - case KISS_CMD_FULLDUP: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.fulldup = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: %s duplex\n", - bc->ch_params.fulldup ? "full" : "half"); -#endif /* KISS_VERBOSE */ - break; - - default: -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: unhandled KISS packet code %u\n", - bc->kiss_decode.pkt_buf[0] & 0xf); -#endif /* KISS_VERBOSE */ - break; - } -} - -/* --------------------------------------------------------------------- */ - -static void baycom_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct baycom_state *bc; - - if (!tty) - return; - if (baycom_paranoia_check(bc = tty->driver_data, "put_char")) - return; - - if (ch == KISS_FEND) { - baycom_put_fend(bc); - bc->kiss_decode.wr = 0; - bc->kiss_decode.escaped = 0; - bc->kiss_decode.dec_state = 1; - return; - } - if (!bc->kiss_decode.dec_state) - return; - if (ch == KISS_FESC) { - bc->kiss_decode.escaped = 1; - return; - } - if (bc->kiss_decode.wr >= sizeof(bc->kiss_decode.pkt_buf)) { - bc->kiss_decode.wr = 0; - bc->kiss_decode.dec_state = 0; - return; - } - if (bc->kiss_decode.escaped) { - if (ch == KISS_TFEND) - bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = - KISS_FEND; - else if (ch == KISS_TFESC) - bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = - KISS_FESC; - else { - bc->kiss_decode.wr = 0; - bc->kiss_decode.dec_state = 0; - } - bc->kiss_decode.escaped = 0; - return; - } - bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = ch; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_write(struct tty_struct * tty, int from_user, - const unsigned char *buf, int count) -{ - int c; - const unsigned char *bp; - struct baycom_state *bc; - - if (!tty || !buf || count <= 0) - return count; - - if (baycom_paranoia_check(bc = tty->driver_data, "write")) - return count; - - if (from_user) { - for(c = count, bp = buf; c > 0; c--,bp++) - baycom_put_char(tty, get_user(bp)); - } else { - for(c = count, bp = buf; c > 0; c--,bp++) - baycom_put_char(tty, *bp); - } - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - return count; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_write_room(struct tty_struct *tty) -{ - int free; - struct baycom_state *bc; - - if (!tty) - return 0; - if (baycom_paranoia_check(bc = tty->driver_data, "write_room")) - return 0; - - free = bc->tx_buf.rd - bc->tx_buf.wr; - if (free <= 0) { - free = bc->tx_buf.buflen - bc->tx_buf.wr; - if (free < bc->tx_buf.rd) - free = bc->tx_buf.rd; /* we may fold */ - } - - return free / 2; /* a rather pessimistic estimate */ -} - -/* --------------------------------------------------------------------- */ - -static int baycom_chars_in_buffer(struct tty_struct *tty) -{ - int cnt; - struct baycom_state *bc; - - if (!tty) - return 0; - if (baycom_paranoia_check(bc = tty->driver_data, "chars_in_buffer")) - return 0; - - cnt = bc->tx_buf.wr - bc->tx_buf.rd; - if (cnt < 0) - cnt += bc->tx_buf.buflen; - - return cnt; -} - -/* --------------------------------------------------------------------- */ - -static void baycom_flush_buffer(struct tty_struct *tty) -{ - struct baycom_state *bc; - - if (!tty) - return; - if (baycom_paranoia_check(bc = tty->driver_data, "flush_buffer")) - return; - - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} - -/* --------------------------------------------------------------------- */ - -static inline void baycom_dealloc_hw(struct baycom_state *bc) -{ - if (!bc || bc->magic != BAYCOM_MAGIC || - bc->modem_type == BAYCOM_MODEM_INVALID) - return; - switch(bc->modem_type) { - case BAYCOM_MODEM_SER12: - ser12_deallocate_resources(bc); - break; - case BAYCOM_MODEM_PAR96: - par96_deallocate_resources(bc); - break; - } -} - -/* --------------------------------------------------------------------- */ - -static int baycom_set_hardware(struct baycom_state *bc, - unsigned int modem_type, unsigned int iobase, - unsigned int irq, unsigned int options) -{ - int i; - - if (!bc) - return -EINVAL; - - if (modem_type == BAYCOM_MODEM_SER12) { - i = ser12_allocate_resources(iobase, irq, options); - if (i < 0) - return i; - } else if (modem_type == BAYCOM_MODEM_PAR96) { - i = par96_allocate_resources(iobase, irq, options); - if (i < 0) - return i; - } else if (modem_type == BAYCOM_MODEM_INVALID) { - iobase = irq = options = 0; - } else { - return -ENXIO; - } - baycom_dealloc_hw(bc); - bc->modem_type = modem_type; - bc->iobase = iobase; - bc->irq = irq; - bc->options = options; - i = 0; - if (bc->opened > 0) { - switch(bc->modem_type) { - case BAYCOM_MODEM_SER12: - i = ser12_on_open(bc); - break; - case BAYCOM_MODEM_PAR96: - i = par96_on_open(bc); - break; - } - } - return i; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - int i; - struct baycom_state *bc; - struct baycom_params par; - - if (!tty) - return -EINVAL; - if (baycom_paranoia_check(bc = tty->driver_data, "ioctl")) - return -EINVAL; - - switch (cmd) { - default: - return -ENOIOCTLCMD; - - case TIOCMGET: - i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); - if (i) - return i; - i = (bc->modem.dcd ? TIOCM_CAR : 0) | - (bc->hdlc_tx.ptt ? TIOCM_RTS : 0); - put_user(i, (int *) arg); - return 0; - - case BAYCOMCTL_GETDCD: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned char)); - if (!i) - put_user(bc->modem.dcd, (unsigned char *) arg); - return i; - - case BAYCOMCTL_GETPTT: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned char)); - if (!i) - put_user(bc->hdlc_tx.ptt, (unsigned char *) arg); - return i; - - case BAYCOMCTL_PARAM_TXDELAY: - if (arg > 255) - return -EINVAL; - bc->ch_params.tx_delay = arg; - return 0; - - case BAYCOMCTL_PARAM_PPERSIST: - if (arg > 255) - return -EINVAL; - bc->ch_params.ppersist = arg; - return 0; - - case BAYCOMCTL_PARAM_SLOTTIME: - if (arg > 255) - return -EINVAL; - bc->ch_params.slottime = arg; - return 0; - - case BAYCOMCTL_PARAM_TXTAIL: - if (arg > 255) - return -EINVAL; - bc->ch_params.tx_tail = arg; - return 0; - - case BAYCOMCTL_PARAM_FULLDUP: - bc->ch_params.fulldup = arg ? 1 : 0; - return 0; - - case BAYCOMCTL_CALIBRATE: - bc->calibrate = arg * ((bc->modem_type == BAYCOM_MODEM_PAR96) ? - 600 : 75); - return 0; - - case BAYCOMCTL_GETPARAMS: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(par)); - if (i) - return i; - par.modem_type = bc->modem_type; - par.iobase = bc->iobase; - par.irq = bc->irq; - par.options = bc->options; - par.tx_delay = bc->ch_params.tx_delay; - par.tx_tail = bc->ch_params.tx_tail; - par.slottime = bc->ch_params.slottime; - par.ppersist = bc->ch_params.ppersist; - par.fulldup = bc->ch_params.fulldup; - memcpy_tofs((void *)arg, &par, sizeof(par)); - return 0; - - case BAYCOMCTL_SETPARAMS: - if (!suser()) - return -EPERM; - i = verify_area(VERIFY_READ, (void *) arg, - sizeof(par)); - if (i) - return i; - memcpy_fromfs(&par, (void *)arg, sizeof(par)); - printk(KERN_INFO "baycom: changing hardware type: modem %u " - "iobase 0x%x irq %u options 0x%x\n", par.modem_type, - par.iobase, par.irq, par.options); - i = baycom_set_hardware(bc, par.modem_type, par.iobase, - par.irq, par.options); - if (i) - return i; - bc->ch_params.tx_delay = par.tx_delay; - bc->ch_params.tx_tail = par.tx_tail; - bc->ch_params.slottime = par.slottime; - bc->ch_params.ppersist = par.ppersist; - bc->ch_params.fulldup = par.fulldup; - return 0; - - case BAYCOMCTL_GETSTAT: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct baycom_statistics)); - if (i) - return i; - memcpy_tofs((void *)arg, &bc->stat, - sizeof(struct baycom_statistics)); - return 0; - - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETSAMPLES: - if (bc->bitbuf_channel.rd == bc->bitbuf_channel.wr) - return -EAGAIN; - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned char)); - if (!i) { - put_user(bc->bitbuf_channel.buffer - [bc->bitbuf_channel.rd], - (unsigned char *) arg); - bc->bitbuf_channel.rd = (bc->bitbuf_channel.rd+1) % - sizeof(bc->bitbuf_channel.buffer); - } - return i; - - case BAYCOMCTL_GETBITS: - if (bc->bitbuf_hdlc.rd == bc->bitbuf_hdlc.wr) - return -EAGAIN; - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned char)); - if (!i) { - put_user(bc->bitbuf_hdlc.buffer[bc->bitbuf_hdlc.rd], - (unsigned char *) arg); - bc->bitbuf_hdlc.rd = (bc->bitbuf_hdlc.rd+1) % - sizeof(bc->bitbuf_hdlc.buffer); - } - return i; - - case BAYCOMCTL_DEBUG1: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned long)); - if (!i) - put_user((bc->rx_buf.wr-bc->rx_buf.rd) % - bc->rx_buf.buflen, (unsigned long *)arg); - return i; - - case BAYCOMCTL_DEBUG2: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned long)); - if (!i) - put_user(bc->debug_vals.last_intcnt, - (unsigned long *)arg); - return i; - - case BAYCOMCTL_DEBUG3: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned long)); - if (!i) - put_user((long)bc->debug_vals.last_pllcorr, - (long *)arg); - return i; -#endif /* BAYCOM_DEBUG */ - } -} - -/* --------------------------------------------------------------------- */ - -int baycom_open(struct tty_struct *tty, struct file * filp) -{ - int line; - struct baycom_state *bc; - int i; - - if(!tty) - return -ENODEV; - - line = MINOR(tty->device) - tty->driver.minor_start; - if (line < 0 || line >= NR_PORTS) - return -ENODEV; - bc = baycom_state+line; - - if (bc->opened > 0) { - bc->opened++; - MOD_INC_USE_COUNT; - return 0; - } - /* - * initialise some variables - */ - bc->calibrate = 0; - - /* - * allocate the buffer space - */ - if (bc->rx_buf.buffer) - kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen); - if (bc->tx_buf.buffer) - kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen); - bc->rx_buf.buflen = BUFLEN_RX; - bc->tx_buf.buflen = BUFLEN_TX; - bc->rx_buf.rd = bc->rx_buf.wr = 0; - bc->tx_buf.rd = bc->tx_buf.wr = 0; - bc->rx_buf.buffer = kmalloc(bc->rx_buf.buflen, GFP_KERNEL); - bc->tx_buf.buffer = kmalloc(bc->tx_buf.buflen, GFP_KERNEL); - if (!bc->rx_buf.buffer || !bc->tx_buf.buffer) { - if (bc->rx_buf.buffer) - kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen); - if (bc->tx_buf.buffer) - kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen); - bc->rx_buf.buffer = bc->tx_buf.buffer = NULL; - bc->rx_buf.buflen = bc->tx_buf.buflen = 0; - return -ENOMEM; - } - /* - * check if the modem type has been set - */ - switch(bc->modem_type) { - case BAYCOM_MODEM_SER12: - i = ser12_on_open(bc); - break; - case BAYCOM_MODEM_PAR96: - i = par96_on_open(bc); - break; - case BAYCOM_MODEM_INVALID: - /* - * may open even if no hardware specified, in order to - * subsequently allow the BAYCOMCTL_SETPARAMS ioctl - */ - i = 0; - break; - default: - return -ENODEV; - } - if (i) - return i; - - bc->opened++; - MOD_INC_USE_COUNT; - - tty->driver_data = bc; - bc->tty = tty; - - return 0; -} - - -/* --------------------------------------------------------------------- */ - -static void baycom_close(struct tty_struct *tty, struct file * filp) -{ - struct baycom_state *bc; - - if(!tty) return; - if (baycom_paranoia_check(bc = tty->driver_data, "close")) - return; - - MOD_DEC_USE_COUNT; - bc->opened--; - if (bc->opened <= 0) { - switch(bc->modem_type) { - case BAYCOM_MODEM_SER12: - ser12_on_close(bc); - break; - case BAYCOM_MODEM_PAR96: - par96_on_close(bc); - break; - } - tty->driver_data = NULL; - bc->tty = NULL; - bc->opened = 0; - /* - * free the buffers - */ - bc->rx_buf.rd = bc->rx_buf.wr = 0; - bc->tx_buf.rd = bc->tx_buf.wr = 0; - if (bc->rx_buf.buffer) - kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen); - if (bc->tx_buf.buffer) - kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen); - bc->rx_buf.buffer = bc->tx_buf.buffer = NULL; - bc->rx_buf.buflen = bc->tx_buf.buflen = 0; - } -} - -/* --------------------------------------------------------------------- */ -/* - * And now the modules code and kernel interface. - */ - -static void init_channel(struct baycom_state *bc) -{ - struct access_params dflt_ch_params = { 20, 2, 10, 40, 0 }; - - if (!bc) - return; - - bc->hdlc_rx.rx_state = 0; - - bc->hdlc_tx.tx_state = bc->hdlc_tx.numflags = 0; - bc->hdlc_tx.bitstream = 0; - bc->hdlc_tx.current_byte = bc->hdlc_tx.ptt = 0; - - memset(&bc->modem, 0, sizeof(bc->modem)); - -#ifdef BAYCOM_DEBUG - bc->bitbuf_channel.rd = bc->bitbuf_channel.wr = 0; - bc->bitbuf_channel.shreg = 0x80; - - bc->bitbuf_hdlc.rd = bc->bitbuf_hdlc.wr = 0; - bc->bitbuf_hdlc.shreg = 0x80; -#endif /* BAYCOM_DEBUG */ - - bc->kiss_decode.dec_state = bc->kiss_decode.escaped = - bc->kiss_decode.wr = 0; - - bc->ch_params = dflt_ch_params; - -#ifdef BAYCOM_USE_BH - bc->tq_receiver.next = bc->tq_transmitter.next = - bc->tq_arbitrate.next = NULL; - bc->tq_receiver.sync = bc->tq_transmitter.sync = - bc->tq_arbitrate.sync = 0; - bc->tq_receiver.data = bc->tq_transmitter.data = - bc->tq_arbitrate.data = bc; - bc->tq_receiver.routine = bh_receiver; - bc->tq_transmitter.routine = bh_transmitter; - bc->tq_arbitrate.routine = bh_arbitrate; -#endif /* BAYCOM_USE_BH */ -} - -static void init_datastructs(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct baycom_state *bc = baycom_state+i; - - bc->magic = BAYCOM_MAGIC; - bc->modem_type = BAYCOM_MODEM_INVALID; - bc->iobase = bc->irq = bc->options = bc->opened = 0; - bc->tty = NULL; - - bc->rx_buf.rd = bc->rx_buf.wr = 0; - bc->rx_buf.buflen = 0; - bc->rx_buf.buffer = NULL; - - bc->tx_buf.rd = bc->tx_buf.wr = 0; - bc->tx_buf.buflen = 0; - bc->tx_buf.buffer = NULL; - - memset(&bc->stat, 0, sizeof(bc->stat)); - - init_channel(bc); - } -} - -int baycom_init(void) { - int i, j; - - /* - * initialize the data structures - */ - init_datastructs(); - /* - * initialize bottom half handler - */ -#ifdef BAYCOM_USE_BH - init_bh(BAYCOM_BH, baycom_bottom_half); -#endif /* BAYCOM_USE_BH */ - /* - * register the driver as tty driver - */ - memset(&baycom_driver, 0, sizeof(struct tty_driver)); - baycom_driver.magic = TTY_DRIVER_MAGIC; - baycom_driver.name = "baycom"; - baycom_driver.major = major; - baycom_driver.minor_start = 0; - baycom_driver.num = NR_PORTS; - baycom_driver.type = TTY_DRIVER_TYPE_BAYCOM; - baycom_driver.subtype = BAYCOM_TYPE_NORMAL; - baycom_driver.init_termios.c_iflag = 0; - baycom_driver.init_termios.c_oflag = 0; - baycom_driver.init_termios.c_cflag = CS8 | B1200 | CREAD | CLOCAL; - baycom_driver.init_termios.c_lflag = 0; - baycom_driver.flags = TTY_DRIVER_REAL_RAW; - baycom_driver.refcount = &baycom_refcount; - baycom_driver.table = baycom_table; - baycom_driver.termios = baycom_termios; - baycom_driver.termios_locked = baycom_termios_locked; - /* - * the functions - */ - baycom_driver.open = baycom_open; - baycom_driver.close = baycom_close; - baycom_driver.write = baycom_write; - baycom_driver.put_char = baycom_put_char; - baycom_driver.flush_chars = NULL; - baycom_driver.write_room = baycom_write_room; - baycom_driver.chars_in_buffer = baycom_chars_in_buffer; - baycom_driver.flush_buffer = baycom_flush_buffer; - baycom_driver.ioctl = baycom_ioctl; - /* - * cannot throttle the transmitter on this layer - */ - baycom_driver.throttle = NULL; - baycom_driver.unthrottle = NULL; - /* - * no special actions on termio changes - */ - baycom_driver.set_termios = NULL; - /* - * no XON/XOFF and no hangup on the radio port - */ - baycom_driver.stop = NULL; - baycom_driver.start = NULL; - baycom_driver.hangup = NULL; - baycom_driver.set_ldisc = NULL; - - if (tty_register_driver(&baycom_driver)) { - printk(KERN_WARNING "baycom: tty_register_driver failed\n"); - return -EIO; - } - - for (i = 0; i < NR_PORTS && - baycom_ports[i].modem != BAYCOM_MODEM_INVALID; i++) { - j = baycom_set_hardware(baycom_state+i, - baycom_ports[i].modem, - baycom_ports[i].iobase, - baycom_ports[i].irq, - baycom_ports[i].options); - if (j < 0) { - const char *s; - switch (-j) { - case ENXIO: - s = "invalid iobase and/or irq"; - break; - case EACCES: - s = "io region already used"; - break; - case EIO: - s = "no uart/lpt port at iobase"; - break; - case EBUSY: - s = "interface already in use"; - break; - case EINVAL: - s = "internal error"; - break; - default: - s = "unknown error"; - break; - } - printk(KERN_WARNING "baycom: modem %u iobase 0x%x " - "irq %u: (%i) %s\n", baycom_ports[i].modem, - baycom_ports[i].iobase, baycom_ports[i].irq, - j, s); - } - } - - return 0; -} - -/* --------------------------------------------------------------------- */ - -#ifdef MODULE - -int modem = BAYCOM_MODEM_INVALID; -int iobase = 0x3f8; -int irq = 4; -int options = BAYCOM_OPTIONS_SOFTDCD; - -int init_module(void) -{ - int i; - - printk(KERN_INFO "baycom: init_module called\n"); - - baycom_ports[0].modem = modem; - baycom_ports[0].iobase = iobase; - baycom_ports[0].irq = irq; - baycom_ports[0].options = options; - baycom_ports[1].modem = BAYCOM_MODEM_INVALID; - - i = baycom_init(); - if (i) - return i; - - printk(KERN_INFO "baycom: version 0.3; " - "(C) 1996 by Thomas Sailer HB9JNX, sailer@ife.ee.ethz.ch\n"); - - return 0; -} - -/* --------------------------------------------------------------------- */ - -void cleanup_module(void) -{ - int i; - - printk(KERN_INFO "baycom: cleanup_module called\n"); - - disable_bh(BAYCOM_BH); - if (tty_unregister_driver(&baycom_driver)) - printk(KERN_WARNING "baycom: failed to unregister tty " - "driver\n"); - for(i = 0; i < NR_PORTS; i++) { - struct baycom_state *bc = baycom_state+i; - - if (bc->magic != BAYCOM_MAGIC) - printk(KERN_ERR "baycom: invalid magic in " - "cleanup_module\n"); - else { - baycom_dealloc_hw(bc); - /* - * free the buffers - */ - bc->rx_buf.rd = bc->rx_buf.wr = 0; - bc->tx_buf.rd = bc->tx_buf.wr = 0; - if (bc->rx_buf.buffer) - kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen); - if (bc->tx_buf.buffer) - kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen); - bc->rx_buf.buffer = bc->tx_buf.buffer = NULL; - bc->rx_buf.buflen = bc->tx_buf.buflen = 0; - } - } -} - -#else /* MODULE */ -/* --------------------------------------------------------------------- */ -/* - * format: baycom=modem,io,irq,options[,modem,io,irq,options] - * modem=1: ser12, modem=2: par96 - * options=0: hardware DCD, options=1: software DCD - */ - -void baycom_setup(char *str, int *ints) -{ - int i; - - for (i = 0; i < NR_PORTS; i++) - if (ints[0] >= 4*i+4) { - baycom_ports[i].modem = ints[4*i+1]; - baycom_ports[i].iobase = ints[4*i+2]; - baycom_ports[i].irq = ints[4*i+3]; - baycom_ports[i].options = ints[4*i+4]; - } else - baycom_ports[i].modem = BAYCOM_MODEM_INVALID; - -} - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.0.34/linux/drivers/char/ftape/ftape-bsm.h linux/drivers/char/ftape/ftape-bsm.h --- v2.0.34/linux/drivers/char/ftape/ftape-bsm.h Wed Mar 6 05:07:19 1996 +++ linux/drivers/char/ftape/ftape-bsm.h Mon Jul 13 13:47:29 1998 @@ -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 -u --recursive --new-file v2.0.34/linux/drivers/char/ftape/ftape-ctl.h linux/drivers/char/ftape/ftape-ctl.h --- v2.0.34/linux/drivers/char/ftape/ftape-ctl.h Wed Oct 15 15:10:48 1997 +++ linux/drivers/char/ftape/ftape-ctl.h Mon Jul 13 13:47:29 1998 @@ -61,7 +61,7 @@ */ 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 -u --recursive --new-file v2.0.34/linux/drivers/char/ftape/kernel-interface.h linux/drivers/char/ftape/kernel-interface.h --- v2.0.34/linux/drivers/char/ftape/kernel-interface.h Wed Oct 15 15:10:48 1997 +++ linux/drivers/char/ftape/kernel-interface.h Mon Jul 13 13:47:29 1998 @@ -59,7 +59,7 @@ /* 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 -u --recursive --new-file v2.0.34/linux/drivers/char/istallion.c linux/drivers/char/istallion.c --- v2.0.34/linux/drivers/char/istallion.c Mon May 6 02:26:05 1996 +++ linux/drivers/char/istallion.c Mon Jul 13 13:47:29 1998 @@ -3,6 +3,7 @@ /* * istallion.c -- stallion intelligent multiport serial driver. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This code is loosely based on the Linux serial driver, written by @@ -73,6 +74,8 @@ #define BRD_ECPE 24 #define BRD_ECPMC 25 #define BRD_ECHPCI 26 +#define BRD_ECH64PCI 27 +#define BRD_EASYIOPCI 28 #define BRD_BRUMBY BRD_BRUMBY4 @@ -90,6 +93,12 @@ * boards can share the same shared memory address space. No interrupt * is required for this board type. * Another example: + * { BRD_ECPE, 0x5000, 0, 0x80000000, 0, 0 }, + * This line will configure an EasyConnection 8/64 EISA in slot 5 and + * shared memory address of 0x80000000 (2 GByte). Multiple + * EasyConnection 8/64 EISA boards can share the same shared memory + * address space. No interrupt is required for this board type. + * Another example: * { BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 }, * This line will configure an ONboard (ISA type) at io address 240, * and shared memory address of d0000. Multiple ONboards can share @@ -156,8 +165,8 @@ * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ -static char *stli_drvname = "Stallion Intelligent Multiport Serial Driver"; -static char *stli_drvversion = "1.1.3"; +static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; +static char *stli_drvversion = "5.4.4"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -281,6 +290,8 @@ "EC8/64-EI", "EC8/64-MC", "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", }; /* @@ -436,6 +447,7 @@ #define ECH_PNLSTATUS 2 #define ECH_PNL16PORT 0x20 #define ECH_PNLIDMASK 0x07 +#define ECH_PNLXPID 0x40 #define ECH_PNLINTRPEND 0x80 /* @@ -473,9 +485,9 @@ /* * Define the maximal baud rate, and the default baud base for ports. */ -#define STL_MAXBAUD 230400 +#define STL_MAXBAUD 460800 #define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY 50 +#define STL_CLOSEDELAY (5 * HZ / 10) /*****************************************************************************/ @@ -492,7 +504,7 @@ */ static unsigned int stli_baudrates[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400 + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 }; /*****************************************************************************/ @@ -530,19 +542,16 @@ static void stli_flushbuffer(struct tty_struct *tty); static void stli_hangup(struct tty_struct *tty); -static int stli_initbrds(void); static int stli_brdinit(stlibrd_t *brdp); -static int stli_initecp(stlibrd_t *brdp); -static int stli_initonb(stlibrd_t *brdp); -static int stli_eisamemprobe(stlibrd_t *brdp); -static int stli_findeisabrds(void); -static int stli_initports(stlibrd_t *brdp); static int stli_startbrd(stlibrd_t *brdp); +static int stli_memopen(struct inode *ip, struct file *fp); +static void stli_memclose(struct inode *ip, struct file *fp); static int stli_memread(struct inode *ip, struct file *fp, char *buf, int count); static int stli_memwrite(struct inode *ip, struct file *fp, const char *buf, int count); static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); +static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp); static void stli_poll(unsigned long arg); -static int stli_hostcmd(stlibrd_t *brdp, int channr); +static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp); static int stli_initopen(stlibrd_t *brdp, stliport_t *portp); static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); @@ -561,6 +570,7 @@ static int stli_setserial(stliport_t *portp, struct serial_struct *sp); static int stli_getbrdstats(combrd_t *bp); static int stli_getportstats(stliport_t *portp, comstats_t *cp); +static int stli_portcmdstats(stliport_t *portp); static int stli_clrportstats(stliport_t *portp, comstats_t *cp); static int stli_getportstruct(unsigned long arg); static int stli_getbrdstruct(unsigned long arg); @@ -601,6 +611,13 @@ static stliport_t *stli_getport(int brdnr, int panelnr, int portnr); +static inline int stli_initbrds(void); +static inline int stli_initecp(stlibrd_t *brdp); +static inline int stli_initonb(stlibrd_t *brdp); +static inline int stli_findeisabrds(void); +static inline int stli_eisamemprobe(stlibrd_t *brdp); +static inline int stli_initports(stlibrd_t *brdp); + /*****************************************************************************/ /* @@ -617,8 +634,8 @@ NULL, stli_memioctl, NULL, - NULL, - NULL, + stli_memopen, + stli_memclose, NULL }; @@ -678,7 +695,8 @@ printk("cleanup_module()\n"); #endif - printk(KERN_INFO "Unloading %s: version %s\n", stli_drvname, stli_drvversion); + printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle, + stli_drvversion); save_flags(flags); cli(); @@ -695,12 +713,14 @@ i = tty_unregister_driver(&stli_serial); j = tty_unregister_driver(&stli_callout); if (i || j) { - printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j); + printk("STALLION: failed to un-register tty driver, " + "errno=%d,%d\n", -i, -j); restore_flags(flags); return; } if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) - printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i); + printk("STALLION: failed to un-register serial memory device, " + "errno=%d\n", -i); if (stli_tmpwritebuf != (char *) NULL) kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE); @@ -722,10 +742,8 @@ if (brdp->memaddr >= 0x100000) vfree(brdp->membase); - if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC)) - release_region(brdp->iobase, ECP_IOSIZE); - else - release_region(brdp->iobase, ONB_IOSIZE); + if (brdp->iosize > 0) + release_region(brdp->iobase, brdp->iosize); kfree_s(brdp, sizeof(stlibrd_t)); stli_brds[i] = (stlibrd_t *) NULL; } @@ -756,7 +774,8 @@ int brdnr, portnr, rc; #if DEBUG - printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device); + printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, + (int) filp, tty->device); #endif minordev = MINOR(tty->device); @@ -778,6 +797,8 @@ if (portp->devnr < 1) return(-ENODEV); + MOD_INC_USE_COUNT; + /* * Check if this port is in the middle of closing. If so then wait * until it is closed then return error status based on flag settings. @@ -842,10 +863,10 @@ return(-EBUSY); if (portp->flags & ASYNC_CALLOUT_ACTIVE) { if ((portp->flags & ASYNC_SESSION_LOCKOUT) && - (portp->session != current->session)) + (portp->session != current->session)) return(-EBUSY); if ((portp->flags & ASYNC_PGRP_LOCKOUT) && - (portp->pgrp != current->pgrp)) + (portp->pgrp != current->pgrp)) return(-EBUSY); } portp->flags |= ASYNC_CALLOUT_ACTIVE; @@ -892,10 +913,14 @@ save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } + if ((tty->count == 1) && (portp->refcount != 1)) + portp->refcount = 1; if (portp->refcount-- > 1) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -916,10 +941,8 @@ if (tty == stli_txcooktty) stli_flushchars(tty); tty->closing = 1; - if (test_bit(ST_TXBUSY, &portp->state)) { - if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, portp->closing_wait); - } + if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, portp->closing_wait); portp->flags &= ~ASYNC_INITIALIZED; brdp = stli_brds[portp->brdnr]; @@ -929,7 +952,8 @@ if (test_bit(ST_CMDING, &portp->state)) set_bit(ST_DOSIGS, &portp->state); else - stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0); } clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); @@ -940,7 +964,6 @@ stli_flushbuffer(tty); tty->closing = 0; - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; if (portp->openwaitcnt) { @@ -949,8 +972,10 @@ wake_up_interruptible(&portp->open_wait); } - portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | + ASYNC_CLOSING); wake_up_interruptible(&portp->close_wait); + MOD_DEC_USE_COUNT; restore_flags(flags); } @@ -981,23 +1006,27 @@ memset(&nt, 0, sizeof(asynotify_t)); nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK); nt.signal = SG_DCD; - if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, + sizeof(asynotify_t), 0)) < 0) return(rc); tty = portp->tty; if (tty == (struct tty_struct *) NULL) return(-ENODEV); stli_mkasyport(portp, &aport, tty->termios); - if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, + sizeof(asyport_t), 0)) < 0) return(rc); set_bit(ST_GETSIGS, &portp->state); - if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, + sizeof(asysigs_t), 1)) < 0) return(rc); if (clear_bit(ST_GETSIGS, &portp->state)) portp->sigs = stli_mktiocm(portp->asig.sigvalue); stli_mkasysigs(&portp->asig, 1, 1); - if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0)) < 0) return(rc); return(0); @@ -1021,7 +1050,8 @@ int rc; #if DEBUG - printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); + printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", + (int) brdp, (int) portp, (int) arg, wait); #endif /* @@ -1054,8 +1084,8 @@ cp->openarg = arg; cp->open = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); @@ -1101,7 +1131,8 @@ int rc; #if DEBUG - printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); + printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", + (int) brdp, (int) portp, (int) arg, wait); #endif save_flags(flags); @@ -1129,8 +1160,8 @@ cp->closearg = arg; cp->close = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); @@ -1173,7 +1204,9 @@ unsigned long flags; #if DEBUG - printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); + printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," + "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, + (int) arg, size, copyback); #endif save_flags(flags); @@ -1243,7 +1276,7 @@ static void stli_delay(int len) { #if DEBUG - printk("stl_delay(len=%d)\n", len); + printk("stli_delay(len=%d)\n", len); #endif if (len > 0) { current->state = TASK_INTERRUPTIBLE; @@ -1265,7 +1298,8 @@ int rc, doclocal; #if DEBUG - printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", (int) brdp, (int) portp, (int) filp); + printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", + (int) brdp, (int) portp, (int) filp); #endif rc = 0; @@ -1282,16 +1316,18 @@ save_flags(flags); cli(); portp->openwaitcnt++; - if (portp->refcount > 0) + if (! tty_hung_up_p(filp)) portp->refcount--; for (;;) { if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) { stli_mkasysigs(&portp->asig, 1, 1); - if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0)) < 0) break; } - if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) { + if (tty_hung_up_p(filp) || + ((portp->flags & ASYNC_INITIALIZED) == 0)) { if (portp->flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else @@ -1299,8 +1335,8 @@ break; } if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) && - ((portp->flags & ASYNC_CLOSING) == 0) && - (doclocal || (portp->sigs & TIOCM_CD))) { + ((portp->flags & ASYNC_CLOSING) == 0) && + (doclocal || (portp->sigs & TIOCM_CD))) { break; } if (current->signal & ~current->blocked) { @@ -1338,10 +1374,12 @@ unsigned long flags; #if DEBUG - printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count); + printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", + (int) tty, from_user, (int) buf, count); #endif - if ((tty == (struct tty_struct *) NULL) || (stli_tmpwritebuf == (char *) NULL)) + if ((tty == (struct tty_struct *) NULL) || + (stli_tmpwritebuf == (char *) NULL)) return(0); if (tty == stli_txcooktty) stli_flushchars(tty); @@ -1375,7 +1413,8 @@ tail = (unsigned int) ap->txq.tail; if (tail != ((unsigned int) ap->txq.tail)) tail = (unsigned int) ap->txq.tail; - len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : (tail - head - 1); + len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : + (tail - head - 1); count = MIN(len, count); EBRDDISABLE(brdp); @@ -1430,8 +1469,8 @@ ap->changed.data &= ~DT_TXEMPTY; } hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_TXBUSY, &portp->state); @@ -1562,8 +1601,8 @@ ap->changed.data &= ~DT_TXEMPTY; } hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_TXBUSY, &portp->state); @@ -1729,12 +1768,14 @@ memcpy_fromfs(&sio, sp, sizeof(struct serial_struct)); if (!suser()) { if ((sio.baud_base != portp->baud_base) || - (sio.close_delay != portp->close_delay) || - ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK))) + (sio.close_delay != portp->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (portp->flags & ~ASYNC_USR_MASK))) return(-EPERM); } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); + portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; @@ -1755,7 +1796,8 @@ int rc; #if DEBUG - printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg); + printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", + (int) tty, (int) file, cmd, (int) arg); #endif if (tty == (struct tty_struct *) NULL) @@ -1769,6 +1811,12 @@ if (brdp == (stlibrd_t *) NULL) return(0); + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return(-EIO); + } + rc = 0; switch (cmd) { @@ -1777,7 +1825,8 @@ tty_wait_until_sent(tty, 0); if (! arg) { val = 250; - rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0); + rc = stli_cmdwait(brdp, portp, A_BREAK, &val, + sizeof(unsigned long), 0); } } break; @@ -1785,72 +1834,98 @@ if ((rc = tty_check_change(tty)) == 0) { tty_wait_until_sent(tty, 0); val = (arg ? (arg * 100) : 250); - rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0); + rc = stli_cmdwait(brdp, portp, A_BREAK, &val, + sizeof(unsigned long), 0); } break; case TIOCGSOFTCAR: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0) - put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(long))) == 0) + put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), + (unsigned long *) arg); break; case TIOCSSOFTCAR: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0); } break; case TIOCMGET: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) { - if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int))) == 0) { + if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, + &portp->asig, sizeof(asysigs_t), 1)) < 0) return(rc); val = stli_mktiocm(portp->asig.sigvalue); put_fs_long(val, (unsigned long *) arg); } break; case TIOCMBIS: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((arg & TIOCM_DTR) ? 1 : -1), + ((arg & TIOCM_RTS) ? 1 : -1)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCMBIC: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((arg & TIOCM_DTR) ? 0 : -1), + ((arg & TIOCM_RTS) ? 0 : -1)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCMSET: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((arg & TIOCM_DTR) ? 1 : 0), + ((arg & TIOCM_RTS) ? 1 : 0)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCGSERIAL: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct))) == 0) stli_getserial(portp, (struct serial_struct *) arg); break; case TIOCSSERIAL: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0) - rc = stli_setserial(portp, (struct serial_struct *) arg); + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct))) == 0) + rc = stli_setserial(portp, (struct serial_struct *)arg); break; case STL_GETPFLAG: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned long))) == 0) put_fs_long(portp->pflag, (unsigned long *) arg); break; case STL_SETPFLAG: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned long))) == 0) { portp->pflag = get_fs_long((unsigned long *) arg); stli_setport(portp); } break; case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stli_getportstats(portp, (comstats_t *) arg); break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stli_clrportstats(portp, (comstats_t *) arg); break; case TIOCSERCONFIG: @@ -1898,13 +1973,15 @@ return; tiosp = tty->termios; - if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag)) + if ((tiosp->c_cflag == old->c_cflag) && + (tiosp->c_iflag == old->c_iflag)) return; stli_mkasyport(portp, &aport, tiosp); stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0); stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1); - stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0); if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) tty->hw_stopped = 0; if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) @@ -1996,7 +2073,7 @@ memset(&actrl, 0, sizeof(asyctrl_t)); actrl.txctrl = CT_STOPFLOW; #if 0 - stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t)); + stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); #endif } @@ -2030,7 +2107,7 @@ memset(&actrl, 0, sizeof(asyctrl_t)); actrl.txctrl = CT_STARTFLOW; #if 0 - stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t)); + stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); #endif } @@ -2104,7 +2181,8 @@ set_bit(ST_DOFLUSHTX, &portp->state); set_bit(ST_DOFLUSHRX, &portp->state); } else { - stli_sendcmd(brdp, portp, A_SETSIGNALSF, &portp->asig, sizeof(asysigs_t), 0); + stli_sendcmd(brdp, portp, A_SETSIGNALSF, + &portp->asig, sizeof(asysigs_t), 0); } } restore_flags(flags); @@ -2112,7 +2190,6 @@ clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); portp->refcount = 0; @@ -2164,12 +2241,14 @@ ftype |= FLUSHRX; clear_bit(ST_DOFLUSHRX, &portp->state); } - stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 0); + stli_sendcmd(brdp, portp, A_FLUSH, &ftype, + sizeof(unsigned long), 0); } restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); } @@ -2193,7 +2272,9 @@ unsigned long flags; #if DEBUG - printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); + printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," + "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, + (int) arg, size, copyback); #endif save_flags(flags); @@ -2217,8 +2298,8 @@ cp->status = 0; cp->cmd = cmd; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hdrp->slavereq |= portp->reqbit; - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_CMDING, &portp->state); EBRDDISABLE(brdp); @@ -2307,7 +2388,8 @@ int cmd; if (test_bit(ST_DOSIGS, &portp->state)) { - if (test_bit(ST_DOFLUSHTX, &portp->state) && test_bit(ST_DOFLUSHRX, &portp->state)) + if (test_bit(ST_DOFLUSHTX, &portp->state) && + test_bit(ST_DOFLUSHRX, &portp->state)) cmd = A_SETSIGNALSF; else if (test_bit(ST_DOFLUSHTX, &portp->state)) cmd = A_SETSIGNALSFTX; @@ -2318,11 +2400,13 @@ clear_bit(ST_DOFLUSHTX, &portp->state); clear_bit(ST_DOFLUSHRX, &portp->state); clear_bit(ST_DOSIGS, &portp->state); - memcpy((void *) &(cp->args[0]), (void *) &portp->asig, sizeof(asysigs_t)); + memcpy((void *) &(cp->args[0]), (void *) &portp->asig, + sizeof(asysigs_t)); cp->status = 0; cp->cmd = cmd; set_bit(ST_CMDING, &portp->state); - } else if (test_bit(ST_DOFLUSHTX, &portp->state) || test_bit(ST_DOFLUSHRX, &portp->state)) { + } else if (test_bit(ST_DOFLUSHTX, &portp->state) || + test_bit(ST_DOFLUSHRX, &portp->state)) { cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0); cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0); clear_bit(ST_DOFLUSHTX, &portp->state); @@ -2342,15 +2426,17 @@ * enabled and interrupts off when called. Notice that by servicing the * read data last we don't need to change the shared memory pointer * during processing (which is a slow IO operation). + * Return value indicates if this port is still awaiting actions from + * the slave (like open, command, or even TX data being sent). If 0 + * then port is still busy, otherwise no longer busy. */ -static inline int stli_hostcmd(stlibrd_t *brdp, int channr) +static inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp) { volatile cdkasy_t *ap; volatile cdkctrl_t *cp; struct tty_struct *tty; asynotify_t nt; - stliport_t *portp; unsigned long oldsigs; int rc, donerx; @@ -2358,7 +2444,6 @@ printk("stli_hostcmd(brdp=%x,channr=%d)\n", (int) brdp, channr); #endif - portp = brdp->ports[(channr - 1)]; ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); cp = &ap->ctrl; @@ -2402,7 +2487,8 @@ if (rc > 0) rc--; if (portp->argp != (void *) NULL) { - memcpy(portp->argp, (void *) &(cp->args[0]), portp->argsize); + memcpy(portp->argp, (void *) &(cp->args[0]), + portp->argsize); portp->argp = (void *) NULL; } cp->status = 0; @@ -2429,12 +2515,14 @@ oldsigs = portp->sigs; portp->sigs = stli_mktiocm(nt.sigvalue); clear_bit(ST_GETSIGS, &portp->state); - if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) + if ((portp->sigs & TIOCM_CD) && + ((oldsigs & TIOCM_CD) == 0)) wake_up_interruptible(&portp->open_wait); - if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) { + if ((oldsigs & TIOCM_CD) && + ((portp->sigs & TIOCM_CD) == 0)) { if (portp->flags & ASYNC_CHECK_CD) { if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) && - (portp->flags & ASYNC_CALLOUT_NOHUP))) { + (portp->flags & ASYNC_CALLOUT_NOHUP))) { if (tty != (struct tty_struct *) NULL) queue_task_irq_off(&portp->tqhangup, &tq_scheduler); } @@ -2446,7 +2534,8 @@ clear_bit(ST_TXBUSY, &portp->state); if (nt.data & (DT_TXEMPTY | DT_TXLOW)) { if (tty != (struct tty_struct *) NULL) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) { (tty->ldisc.write_wakeup)(tty); EBRDENABLE(brdp); } @@ -2460,12 +2549,10 @@ tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_BREAK; *tty->flip.char_buf_ptr++ = 0; -#ifndef MODULE if (portp->flags & ASYNC_SAK) { do_SAK(tty); EBRDENABLE(brdp); } -#endif tty_schedule_flip(tty); } } @@ -2490,7 +2577,74 @@ stli_read(brdp, portp); } - return(0); + return((test_bit(ST_OPENING, &portp->state) || + test_bit(ST_CLOSING, &portp->state) || + test_bit(ST_CMDING, &portp->state) || + test_bit(ST_TXBUSY, &portp->state) || + test_bit(ST_RXING, &portp->state)) ? 0 : 1); +} + +/*****************************************************************************/ + +/* + * Service all ports on a particular board. Assumes that the boards + * shared memory is enabled, and that the page pointer is pointed + * at the cdk header structure. + */ + +static inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp) +{ + stliport_t *portp; + unsigned char hostbits[(STL_MAXCHANS / 8) + 1]; + unsigned char slavebits[(STL_MAXCHANS / 8) + 1]; + unsigned char *slavep; + int bitpos, bitat, bitsize; + int channr, nrdevs, slavebitchange; + + bitsize = brdp->bitsize; + nrdevs = brdp->nrdevs; + +/* + * Check if slave wants any service. Basically we try to do as + * little work as possible here. There are 2 levels of service + * bits. So if there is nothing to do we bail early. We check + * 8 service bits at a time in the inner loop, so we can bypass + * the lot if none of them want service. + */ + memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset), + bitsize); + + memset(&slavebits[0], 0, bitsize); + slavebitchange = 0; + + for (bitpos = 0; (bitpos < bitsize); bitpos++) { + if (hostbits[bitpos] == 0) + continue; + channr = bitpos * 8; + for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) { + if (hostbits[bitpos] & bitat) { + portp = brdp->ports[(channr - 1)]; + if (stli_hostcmd(brdp, portp)) { + slavebitchange++; + slavebits[bitpos] |= bitat; + } + } + } + } + +/* + * If any of the ports are no longer busy then update them in the + * slave request bits. We need to do this after, since a host port + * service may initiate more slave requests. + */ + if (slavebitchange) { + hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); + slavep = ((unsigned char *) hdrp) + brdp->slaveoffset; + for (bitpos = 0; (bitpos < bitsize); bitpos++) { + if (slavebits[bitpos]) + slavep[bitpos] &= ~slavebits[bitpos]; + } + } } /*****************************************************************************/ @@ -2507,12 +2661,8 @@ static void stli_poll(unsigned long arg) { volatile cdkhdr_t *hdrp; - unsigned char bits[(STL_MAXCHANS / 8) + 1]; - unsigned char hostreq, slavereq; - stliport_t *portp; stlibrd_t *brdp; - int bitpos, bitat, bitsize; - int brdnr, channr, nrdevs; + int brdnr; stli_timerlist.expires = STLI_TIMEOUT; add_timer(&stli_timerlist); @@ -2529,67 +2679,8 @@ EBRDENABLE(brdp); hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - hostreq = hdrp->hostreq; - slavereq = hdrp->slavereq; - bitsize = brdp->bitsize; - nrdevs = brdp->nrdevs; - -/* - * Check if slave wants any service. Basically we try to do as - * little work as possible here. There are 2 levels of service - * bits. So if there is nothing to do we bail early. We check - * 8 service bits at a time in the inner loop, so we can bypass - * the lot if none of them want service. - */ - if (hostreq) { - memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize); - - for (bitpos = 0; (bitpos < bitsize); bitpos++) { - if (bits[bitpos] == 0) - continue; - channr = bitpos * 8; - for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) { - if (bits[bitpos] & bitat) { - stli_hostcmd(brdp, channr); - } - } - } - } - -/* - * Check if any of the out-standing host commands have completed. - * It is a bit unfortunate that we need to check stuff that we - * initiated! This ain't pretty, but it needs to be fast. - */ - if (slavereq) { - slavereq = 0; - hostreq = 0; - memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->slaveoffset), bitsize); - - for (bitpos = 0; (bitpos < bitsize); bitpos++) { - if (bits[bitpos] == 0) - continue; - channr = bitpos * 8; - for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) { - if (bits[bitpos] & bitat) { - portp = brdp->ports[(channr - 1)]; - if (test_bit(ST_OPENING, &portp->state) || - test_bit(ST_CLOSING, &portp->state) || - test_bit(ST_CMDING, &portp->state) || - test_bit(ST_TXBUSY, &portp->state)) { - slavereq |= portp->reqbit; - } else { - bits[bitpos] &= ~bitat; - hostreq++; - } - } - } - } - hdrp->slavereq = slavereq; - if (hostreq) - memcpy((((unsigned char *) hdrp) + brdp->slaveoffset), &bits[0], bitsize); - } - + if (hdrp->hostreq) + stli_brdpoll(brdp, hdrp); EBRDDISABLE(brdp); } } @@ -2604,7 +2695,8 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp) { #if DEBUG - printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp); + printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", + (int) portp, (int) pp, (int) tiosp); #endif memset(pp, 0, sizeof(asyport_t)); @@ -2615,7 +2707,7 @@ pp->baudout = tiosp->c_cflag & CBAUD; if (pp->baudout & CBAUDEX) { pp->baudout &= ~CBAUDEX; - if ((pp->baudout < 1) || (pp->baudout > 2)) + if ((pp->baudout < 1) || (pp->baudout > 5)) tiosp->c_cflag &= ~CBAUDEX; else pp->baudout += 15; @@ -2706,7 +2798,10 @@ /* * Transfer any persistent flags into the asyport structure. */ - pp->pflag = portp->pflag; + pp->pflag = (portp->pflag & 0xffff); + pp->vmin = (portp->pflag & P_RXIMIN) ? 1 : 0; + pp->vtime = (portp->pflag & P_RXITIME) ? 1 : 0; + pp->cc[1] = (portp->pflag & P_RXTHOLD) ? 1 : 0; } /*****************************************************************************/ @@ -2765,7 +2860,7 @@ * we need to do here is set up the appropriate per port data structures. */ -static int stli_initports(stlibrd_t *brdp) +static inline int stli_initports(stlibrd_t *brdp) { stliport_t *portp; int i, panelnr, panelport; @@ -2855,11 +2950,14 @@ unsigned char val; #if DEBUG - printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -2942,11 +3040,14 @@ unsigned char val; #if DEBUG - printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); + printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", + (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -2996,7 +3097,9 @@ unsigned char val; if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3071,11 +3174,14 @@ void *ptr; #if DEBUG - printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; } else { ptr = brdp->membase + (offset % ONB_ATPAGESIZE); @@ -3158,11 +3264,14 @@ unsigned char val; #if DEBUG - printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); + printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", + (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3224,11 +3333,14 @@ unsigned char val; #if DEBUG - printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3282,11 +3394,14 @@ void *ptr; #if DEBUG - printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; } else { ptr = brdp->membase + (offset % STAL_PAGESIZE); @@ -3319,12 +3434,13 @@ * board types. */ -static int stli_initecp(stlibrd_t *brdp) +static inline int stli_initecp(stlibrd_t *brdp) { cdkecpsig_t sig; cdkecpsig_t *sigsp; unsigned int status, nxtid; - int panelnr; + char *name; + int panelnr, nrports; #if DEBUG printk("stli_initecp(brdp=%x)\n", (int) brdp); @@ -3336,6 +3452,11 @@ if ((brdp->iobase == 0) || (brdp->memaddr == 0)) return(-ENODEV); + brdp->iosize = ECP_IOSIZE; + if (check_region(brdp->iobase, brdp->iosize)) + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->iobase); + /* * Based on the specific board type setup the common vars to access * and enable shared memory. Set all board specific information now @@ -3353,6 +3474,7 @@ brdp->getmemptr = stli_ecpgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpreset; + name = "serial(EC8/64)"; break; case BRD_ECPE: @@ -3366,6 +3488,7 @@ brdp->getmemptr = stli_ecpeigetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpeireset; + name = "serial(EC8/64-EI)"; break; case BRD_ECPMC: @@ -3379,6 +3502,7 @@ brdp->getmemptr = stli_ecpmcgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpmcreset; + name = "serial(EC8/64-MCA)"; break; default: @@ -3410,10 +3534,11 @@ EBRDDISABLE(brdp); #if 0 - printk("%s(%d): sig-> magic=%x romver=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n", + printk("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n", __FILE__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0], - (int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3], - (int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6], + (int) sig.panelid[1], (int) sig.panelid[2], + (int) sig.panelid[3], (int) sig.panelid[4], + (int) sig.panelid[5], (int) sig.panelid[6], (int) sig.panelid[7]); #endif @@ -3428,20 +3553,18 @@ status = sig.panelid[nxtid]; if ((status & ECH_PNLIDMASK) != nxtid) break; - if (status & ECH_PNL16PORT) { - brdp->panels[panelnr] = 16; - brdp->nrports += 16; - nxtid += 2; - } else { - brdp->panels[panelnr] = 8; - brdp->nrports += 8; - nxtid++; - } + brdp->panelids[panelnr] = status; + nrports = (status & ECH_PNL16PORT) ? 16 : 8; + if ((nrports == 16) && ((status & ECH_PNLXPID) == 0)) + nxtid++; + brdp->panels[panelnr] = nrports; + brdp->nrports += nrports; + nxtid++; brdp->nrpanels++; } - request_region(brdp->iobase, ECP_IOSIZE, "serial(ECP)"); + request_region(brdp->iobase, brdp->iosize, name); brdp->state |= BST_FOUND; return(0); } @@ -3453,10 +3576,11 @@ * This handles only these board types. */ -static int stli_initonb(stlibrd_t *brdp) +static inline int stli_initonb(stlibrd_t *brdp) { cdkonbsig_t sig; cdkonbsig_t *sigsp; + char *name; int i; #if DEBUG @@ -3469,6 +3593,11 @@ if ((brdp->iobase == 0) || (brdp->memaddr == 0)) return(-ENODEV); + brdp->iosize = ONB_IOSIZE; + if (check_region(brdp->iobase, brdp->iosize)) + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->iobase); + /* * Based on the specific board type setup the common vars to access * and enable shared memory. Set all board specific information now @@ -3494,6 +3623,7 @@ brdp->enabval = ONB_MEMENABHI; else brdp->enabval = ONB_MEMENABLO; + name = "serial(ONBoard)"; break; case BRD_ONBOARDE: @@ -3507,6 +3637,7 @@ brdp->getmemptr = stli_onbegetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_onbereset; + name = "serial(ONBoard/E)"; break; case BRD_BRUMBY4: @@ -3522,6 +3653,7 @@ brdp->getmemptr = stli_bbygetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_bbyreset; + name = "serial(Brumby)"; break; case BRD_STALLION: @@ -3535,6 +3667,7 @@ brdp->getmemptr = stli_stalgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_stalreset; + name = "serial(Stallion)"; break; default: @@ -3572,7 +3705,7 @@ #endif if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) || - (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3)) + (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3)) return(-ENODEV); /* @@ -3591,7 +3724,7 @@ } brdp->panels[0] = brdp->nrports; - request_region(brdp->iobase, ONB_IOSIZE, "serial(ONB/BBY)"); + request_region(brdp->iobase, brdp->iosize, name); brdp->state |= BST_FOUND; return(0); } @@ -3626,14 +3759,16 @@ nrdevs = hdrp->nrdevs; #if 0 - printk("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x slavep=%x\n", + printk("%s(%d): CDK version %d.%d.%d --> " + "nrdevs=%d memp=%x hostp=%x slavep=%x\n", __FILE__, __LINE__, hdrp->ver_release, hdrp->ver_modification, hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp, (int) hdrp->slavep); #endif if (nrdevs < (brdp->nrports + 1)) { - printk("STALLION: slave failed to allocate memory for all devices, devices=%d\n", nrdevs); + printk("STALLION: slave failed to allocate memory for all " + "devices, devices=%d\n", nrdevs); brdp->nrports = nrdevs - 1; } brdp->nrdevs = nrdevs; @@ -3671,6 +3806,8 @@ portp->portbit = (unsigned char) (0x1 << (i % 8)); } + hdrp->slavereq = 0xff; + /* * For each port setup a local copy of the RX and TX buffer offsets * and sizes. We do this separate from the above, because we need to @@ -3743,20 +3880,27 @@ case BRD_ECH: case BRD_ECHMC: case BRD_ECHPCI: - printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]); + printk("STALLION: %s board type not supported in this driver\n", + stli_brdnames[brdp->brdtype]); return(ENODEV); default: - printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); + printk("STALLION: unit=%d is unknown board type=%d\n", + brdp->brdnr, brdp->brdtype); return(ENODEV); } if ((brdp->state & BST_FOUND) == 0) { - printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr); + printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", + stli_brdnames[brdp->brdtype], brdp->brdnr, + brdp->iobase, (int) brdp->memaddr); return(ENODEV); } stli_initports(brdp); - printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr, brdp->nrpanels, brdp->nrports); + printk("STALLION: %s found, unit=%d io=%x mem=%x " + "nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], + brdp->brdnr, brdp->iobase, (int) brdp->memaddr, + brdp->nrpanels, brdp->nrports); return(0); } @@ -3767,7 +3911,7 @@ * might be. This is a bit if hack, but it is the best we can do. */ -static int stli_eisamemprobe(stlibrd_t *brdp) +static inline int stli_eisamemprobe(stlibrd_t *brdp) { cdkecpsig_t ecpsig, *ecpsigp; cdkonbsig_t onbsig, *onbsigp; @@ -3820,15 +3964,19 @@ continue; } if (brdp->brdtype == BRD_ECPE) { - ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, CDK_SIGADDR, __LINE__); + ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, + CDK_SIGADDR, __LINE__); memcpy(&ecpsig, ecpsigp, sizeof(cdkecpsig_t)); if (ecpsig.magic == ECP_MAGIC) foundit = 1; } else { - onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, CDK_SIGADDR, __LINE__); + onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, + CDK_SIGADDR, __LINE__); memcpy(&onbsig, onbsigp, sizeof(cdkonbsig_t)); - if ((onbsig.magic0 == ONB_MAGIC0) && (onbsig.magic1 == ONB_MAGIC1) && - (onbsig.magic2 == ONB_MAGIC2) && (onbsig.magic3 == ONB_MAGIC3)) + if ((onbsig.magic0 == ONB_MAGIC0) && + (onbsig.magic1 == ONB_MAGIC1) && + (onbsig.magic2 == ONB_MAGIC2) && + (onbsig.magic3 == ONB_MAGIC3)) foundit = 1; } if (brdp->memaddr >= 0x100000) @@ -3849,7 +3997,9 @@ if (! foundit) { brdp->memaddr = 0; brdp->membase = 0; - printk("STALLION: failed to probe shared memory region for %s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], (brdp->iobase >> 12)); + printk("STALLION: failed to probe shared memory region for " + "%s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], + (brdp->iobase >> 12)); return(-ENODEV); } return(0); @@ -3867,7 +4017,7 @@ * do is go probing around in the usual places hoping we can find it. */ -static int stli_findeisabrds() +static inline int stli_findeisabrds() { stlibrd_t *brdp; unsigned int iobase, eid; @@ -3915,7 +4065,8 @@ * info table. */ if (stli_nrbrds >= STL_MAXBRDS) { - printk("STALLION: no room for more probed boards, maximum supported %d\n", STL_MAXBRDS); + printk("STALLION: no room for more probed boards, " + "maximum supported %d\n", STL_MAXBRDS); break; } @@ -3925,7 +4076,8 @@ */ brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); if (brdp == (stlibrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlibrd_t)); return(-ENOMEM); } memset(brdp, 0, sizeof(stlibrd_t)); @@ -3956,7 +4108,7 @@ * can find. */ -static int stli_initbrds() +static inline int stli_initbrds() { stlibrd_t *brdp, *nxtbrdp; stlconf_t *confp; @@ -3967,7 +4119,8 @@ #endif if (stli_nrbrds > STL_MAXBRDS) { - printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS); + printk("STALLION: too many boards in configuration table, " + "truncating to %d\n", STL_MAXBRDS); stli_nrbrds = STL_MAXBRDS; } @@ -3979,7 +4132,8 @@ confp = &stli_brdconf[i]; brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); if (brdp == (stlibrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlibrd_t)); return(-ENOMEM); } memset(brdp, 0, sizeof(stlibrd_t)); @@ -4013,7 +4167,9 @@ nxtbrdp = stli_brds[j]; if (nxtbrdp == (stlibrd_t *) NULL) continue; - if ((brdp->membase >= nxtbrdp->membase) && (brdp->membase <= (nxtbrdp->membase + nxtbrdp->memsize - 1))) { + if ((brdp->membase >= nxtbrdp->membase) && + (brdp->membase <= (nxtbrdp->membase + + nxtbrdp->memsize - 1))) { stli_shared++; break; } @@ -4053,7 +4209,8 @@ int brdnr, size, n; #if DEBUG - printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%d)\n", (int) ip, (int) fp, (int) buf, count); + printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%d)\n", (int) ip, + (int) fp, (int) buf, count); #endif brdnr = MINOR(ip->i_rdev); @@ -4103,7 +4260,8 @@ int brdnr, size, n; #if DEBUG - printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%x)\n", (int) ip, (int) fp, (int) buf, count); + printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%x)\n", (int) ip, + (int) fp, (int) buf, count); #endif brdnr = MINOR(ip->i_rdev); @@ -4205,31 +4363,28 @@ * what port to get stats for (used through board control device). */ -static int stli_getportstats(stliport_t *portp, comstats_t *cp) +static int stli_portcmdstats(stliport_t *portp) { unsigned long flags; stlibrd_t *brdp; int rc; - if (portp == (stliport_t *) NULL) { - memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t)); - portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); - if (portp == (stliport_t *) NULL) - return(-ENODEV); - } + memset(&stli_comstats, 0, sizeof(comstats_t)); + if (portp == (stliport_t *) NULL) + return(-ENODEV); brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); if (brdp->state & BST_STARTED) { - if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, &stli_cdkstats, sizeof(asystats_t), 1)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, + &stli_cdkstats, sizeof(asystats_t), 1)) < 0) return(rc); } else { memset(&stli_cdkstats, 0, sizeof(asystats_t)); } - memset(&stli_comstats, 0, sizeof(comstats_t)); stli_comstats.brd = portp->brdnr; stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; @@ -4272,6 +4427,37 @@ stli_comstats.hwid = stli_cdkstats.hwid; stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals); + return(0); +} + +/*****************************************************************************/ + +/* + * Return the port stats structure to user app. A NULL port struct + * pointer passed in means that we need to find out from the app + * what port to get stats for (used through board control device). + */ + +static int stli_getportstats(stliport_t *portp, comstats_t *cp) +{ + stlibrd_t *brdp; + int rc; + + if (portp == (stliport_t *) NULL) { + memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t)); + portp = stli_getport(stli_comstats.brd, stli_comstats.panel, + stli_comstats.port); + if (portp == (stliport_t *) NULL) + return(-ENODEV); + } + + brdp = stli_brds[portp->brdnr]; + if (brdp == (stlibrd_t *) NULL) + return(-ENODEV); + + if ((rc = stli_portcmdstats(portp)) < 0) + return(rc); + memcpy_tofs(cp, &stli_comstats, sizeof(comstats_t)); return(0); } @@ -4289,7 +4475,8 @@ if (portp == (stliport_t *) NULL) { memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t)); - portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); + portp = stli_getport(stli_comstats.brd, stli_comstats.panel, + stli_comstats.port); if (portp == (stliport_t *) NULL) return(-ENODEV); } @@ -4354,6 +4541,26 @@ /*****************************************************************************/ /* + * Memory device open code. Need to keep track of opens and close + * for module handling. + */ + +static int stli_memopen(struct inode *ip, struct file *fp) +{ + MOD_INC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +static void stli_memclose(struct inode *ip, struct file *fp) +{ + MOD_DEC_USE_COUNT; +} + +/*****************************************************************************/ + +/* * The "staliomem" device is also required to do some special operations on * the board. We need to be able to send an interrupt to the board, * reset it, and start/stop it. @@ -4365,7 +4572,8 @@ int brdnr, rc, done; #if DEBUG - printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg); + printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, + (int) fp, cmd, (int) arg); #endif /* @@ -4376,27 +4584,34 @@ switch (cmd) { case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stli_getportstats((stliport_t *) NULL, (comstats_t *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stli_getportstats((stliport_t *) NULL, + (comstats_t *) arg); done++; break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stli_clrportstats((stliport_t *) NULL, (comstats_t *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stli_clrportstats((stliport_t *) NULL, + (comstats_t *) arg); done++; break; case COM_GETBRDSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(combrd_t))) == 0) rc = stli_getbrdstats((combrd_t *) arg); done++; break; case COM_READPORT: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stliport_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stliport_t))) == 0) rc = stli_getportstruct(arg); done++; break; case COM_READBOARD: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlibrd_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlibrd_t))) == 0) rc = stli_getbrdstruct(arg); done++; break; @@ -4450,7 +4665,7 @@ int stli_init() { - printk(KERN_INFO "%s: version %s\n", stli_drvname, stli_drvversion); + printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion); stli_initbrds(); @@ -4459,10 +4674,12 @@ */ stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE); if (stli_tmpwritebuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE); + printk("STALLION: failed to allocate memory (size=%d)\n", + STLI_TXBUFSIZE); stli_txcookbuf = (char *) stli_memalloc(STLI_TXBUFSIZE); if (stli_txcookbuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE); + printk("STALLION: failed to allocate memory (size=%d)\n", + STLI_TXBUFSIZE); /* * Set up a character driver for the shared memory region. We need this diff -u --recursive --new-file v2.0.34/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v2.0.34/linux/drivers/char/lp.c Thu Apr 3 19:15:34 1997 +++ linux/drivers/char/lp.c Mon Jul 13 13:47:29 1998 @@ -89,7 +89,14 @@ while(wait != LP_WAIT(minor)) wait++; /* control port takes strobe high */ outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor ))); - while(wait) wait--; + /* Wait until NBUSY line goes high */ + count = 0; + do { + status = LP_S(minor); + count++; + if (need_resched) + schedule(); + } while (LP_READY(minor, status) && (count<LP_CHAR(minor))); /* take strobe low */ outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor ))); /* update waittime statistics */ diff -u --recursive --new-file v2.0.34/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c --- v2.0.34/linux/drivers/char/pcxx.c Sun Dec 22 05:07:00 1996 +++ linux/drivers/char/pcxx.c Mon Jul 13 13:47:29 1998 @@ -591,7 +591,7 @@ ** worth noting that while I'm not sure what this hunk of code is supposed ** to do, it is not present in the serial.c driver. Hmmm. If you know, ** please send me a note. brian@ilinx.com -** Dont know either what this is supposed to do clameter@waterf.org. +** Don't know either what this is supposed to do clameter@waterf.org. */ if(tty->ldisc.num != ldiscs[N_TTY].num) { if(tty->ldisc.close) diff -u --recursive --new-file v2.0.34/linux/drivers/char/stallion.c linux/drivers/char/stallion.c --- v2.0.34/linux/drivers/char/stallion.c Mon May 6 02:26:06 1996 +++ linux/drivers/char/stallion.c Mon Jul 13 13:47:30 1998 @@ -3,6 +3,7 @@ /* * stallion.c -- stallion multiport serial driver. * + * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This code is loosely based on the Linux serial driver, written by @@ -37,12 +38,13 @@ #include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/cd1400.h> +#include <linux/sc26198.h> #include <linux/comstats.h> #include <linux/stallion.h> #include <linux/string.h> #include <linux/malloc.h> #include <linux/ioport.h> -#include <linux/config.h> /* for CONFIG_PCI */ +#include <linux/config.h> #include <asm/system.h> #include <asm/io.h> #include <asm/segment.h> @@ -55,16 +57,16 @@ /*****************************************************************************/ /* - * Define different board types. At the moment I have only declared - * those boards that this driver supports. But I will use the standard - * "assigned" board numbers. In the future this driver will support - * some of the other Stallion boards. Currently supported boards are - * abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32. + * Define different board types. Use the standard Stallion "assigned" + * board numbers. Boards supported in this driver are abbreviated as + * EIO = EasyIO and ECH = EasyConnection 8/32. */ #define BRD_EASYIO 20 #define BRD_ECH 21 #define BRD_ECHMC 22 #define BRD_ECHPCI 26 +#define BRD_ECH64PCI 27 +#define BRD_EASYIOPCI 28 /* * Define a configuration structure to hold the board configuration. @@ -126,12 +128,11 @@ #define STL_DRVTYPCALLOUT 2 /* - * I haven't really decided (or measured) what TX buffer size gives - * a good balance between performance and memory usage. These seem - * to work pretty well... + * Set the TX buffer size. Bigger is better, but we don't want + * to chew too much memory with buffers! */ -#define STL_TXBUFLOW 256 -#define STL_TXBUFSIZE 2048 +#define STL_TXBUFLOW 512 +#define STL_TXBUFSIZE 4096 /*****************************************************************************/ @@ -139,8 +140,8 @@ * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ -static char *stl_drvname = "Stallion Multiport Serial Driver"; -static char *stl_drvversion = "1.1.3"; +static char *stl_drvtitle = "Stallion Multiport Serial Driver"; +static char *stl_drvversion = "5.4.4"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -185,6 +186,11 @@ static stlport_t stl_dummyport; /* + * Define global place to put buffer overflow characters. + */ +static char stl_unwanted[SC26198_RXFIFOSIZE]; + +/* * Keep track of what interrupts we have requested for us. * We don't need to request an interrupt twice if it is being * shared with another Stallion board. @@ -198,7 +204,7 @@ /* * Per board state flags. Used with the state field of the board struct. - * Not really much here yet! + * Not really much here! */ #define BRD_FOUND 0x1 @@ -210,6 +216,7 @@ #define ASYI_TXBUSY 1 #define ASYI_TXLOW 2 #define ASYI_DCDCHANGE 3 +#define ASYI_TXFLOWED 4 /* * Define an array of board names as printable strings. Handy for @@ -243,23 +250,33 @@ (char *) NULL, (char *) NULL, "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", }; /*****************************************************************************/ /* * Hardware ID bits for the EasyIO and ECH boards. These defines apply - * to the directly accessible io ports of these boards (not the cd1400 - * uarts - they are in cd1400.h). + * to the directly accessible io ports of these boards (not the uarts - + * they are in cd1400.h and sc26198.h). */ #define EIO_8PORTRS 0x04 #define EIO_4PORTRS 0x05 #define EIO_8PORTDI 0x00 #define EIO_8PORTM 0x06 +#define EIO_MK3 0x03 #define EIO_IDBITMASK 0x07 + +#define EIO_BRDMASK 0xf0 +#define ID_BRD4 0x10 +#define ID_BRD8 0x20 +#define ID_BRD16 0x30 + #define EIO_INTRPEND 0x08 #define EIO_INTEDGE 0x00 #define EIO_INTLEVEL 0x08 +#define EIO_0WS 0x10 #define ECH_ID 0xa0 #define ECH_IDBITMASK 0xe0 @@ -278,24 +295,10 @@ #define ECH_PNLSTATUS 2 #define ECH_PNL16PORT 0x20 #define ECH_PNLIDMASK 0x07 +#define ECH_PNLXPID 0x40 #define ECH_PNLINTRPEND 0x80 -#define ECH_ADDR2MASK 0x1e0 - -#define EIO_CLK 25000000 -#define EIO_CLK8M 20000000 -#define ECH_CLK EIO_CLK - -/* - * Define the offsets within the register bank for all io registers. - * These io address offsets are common to both the EIO and ECH. - */ -#define EREG_ADDR 0 -#define EREG_DATA 4 -#define EREG_RXACK 5 -#define EREG_TXACK 6 -#define EREG_MDACK 7 -#define EREG_BANKSIZE 8 +#define ECH_ADDR2MASK 0x1e0 /* * Define the vector mapping bits for the programmable interrupt board @@ -326,18 +329,51 @@ outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE), \ stl_brds[(brdnr)]->ioctrl); +#define STL_CD1400MAXBAUD 230400 +#define STL_SC26198MAXBAUD 460800 + +#define STL_BAUDBASE 115200 +#define STL_CLOSEDELAY (5 * HZ / 10) + +/*****************************************************************************/ + +#ifdef CONFIG_PCI + /* - * Define the cd1400 baud rate clocks. These are used when calculating - * what clock and divisor to use for the required baud rate. Also - * define the maximum baud rate allowed, and the default base baud. + * Define the Stallion PCI vendor and device IDs. */ -static int stl_cd1400clkdivs[] = { - CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4 +#ifndef PCI_VENDOR_ID_STALLION +#define PCI_VENDOR_ID_STALLION 0x124d +#endif +#ifndef PCI_DEVICE_ID_ECHPCI832 +#define PCI_DEVICE_ID_ECHPCI832 0x0000 +#endif +#ifndef PCI_DEVICE_ID_ECHPCI864 +#define PCI_DEVICE_ID_ECHPCI864 0x0002 +#endif +#ifndef PCI_DEVICE_ID_EIOPCI +#define PCI_DEVICE_ID_EIOPCI 0x0003 +#endif + +/* + * Define structure to hold all Stallion PCI boards. + */ +typedef struct stlpcibrd { + unsigned short vendid; + unsigned short devid; + int brdtype; +} stlpcibrd_t; + +static stlpcibrd_t stl_pcibrds[] = { + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864, BRD_ECH64PCI }, + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI, BRD_EASYIOPCI }, + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, BRD_ECHPCI }, + { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, BRD_ECHPCI }, }; -#define STL_MAXBAUD 230400 -#define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY 50 +static int stl_nrpcibrds = sizeof(stl_pcibrds) / sizeof(stlpcibrd_t); + +#endif /*****************************************************************************/ @@ -349,16 +385,14 @@ /* * Define a baud rate table that converts termios baud rate selector - * into the actual baud rate value. All baud rate calculates are based - * on the actual baud rate required. + * into the actual baud rate value. All baud rate calculations are + * based on the actual baud rate required. */ static unsigned int stl_baudrates[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400 + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 }; -/*****************************************************************************/ - /* * Define some handy local macros... */ @@ -392,15 +426,15 @@ static void stl_stop(struct tty_struct *tty); static void stl_start(struct tty_struct *tty); static void stl_flushbuffer(struct tty_struct *tty); +static void stl_waituntilsent(struct tty_struct *tty, int timeout); static void stl_hangup(struct tty_struct *tty); +static int stl_memopen(struct inode *ip, struct file *fp); +static void stl_memclose(struct inode *ip, struct file *fp); static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); -static int stl_initbrds(void); static int stl_brdinit(stlbrd_t *brdp); -static int stl_initeio(stlbrd_t *brdp); -static int stl_initech(stlbrd_t *brdp); static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp); -static int stl_mapirq(int irq); +static int stl_mapirq(int irq, char *name); static void stl_getserial(stlport_t *portp, struct serial_struct *sp); static int stl_setserial(stlport_t *portp, struct serial_struct *sp); static int stl_getbrdstats(combrd_t *bp); @@ -408,28 +442,212 @@ static int stl_clrportstats(stlport_t *portp, comstats_t *cp); static int stl_getportstruct(unsigned long arg); static int stl_getbrdstruct(unsigned long arg); -static void stl_setreg(stlport_t *portp, int regnr, int value); -static int stl_getreg(stlport_t *portp, int regnr); -static int stl_updatereg(stlport_t *portp, int regnr, int value); -static void stl_setport(stlport_t *portp, struct termios *tiosp); -static int stl_getsignals(stlport_t *portp); -static void stl_setsignals(stlport_t *portp, int dtr, int rts); -static void stl_ccrwait(stlport_t *portp); -static void stl_enablerxtx(stlport_t *portp, int rx, int tx); -static void stl_startrxtx(stlport_t *portp, int rx, int tx); -static void stl_disableintrs(stlport_t *portp); -static void stl_sendbreak(stlport_t *portp, long len); static int stl_waitcarrier(stlport_t *portp, struct file *filp); static void stl_delay(int len); static void stl_intr(int irq, void *dev_id, struct pt_regs *regs); +static void stl_eiointr(stlbrd_t *brdp); +static void stl_echatintr(stlbrd_t *brdp); +static void stl_echmcaintr(stlbrd_t *brdp); +static void stl_echpciintr(stlbrd_t *brdp); +static void stl_echpci64intr(stlbrd_t *brdp); static void stl_offintr(void *private); static void *stl_memalloc(int len); static stlport_t *stl_getport(int brdnr, int panelnr, int portnr); +static inline int stl_initbrds(void); +static inline int stl_initeio(stlbrd_t *brdp); +static inline int stl_initech(stlbrd_t *brdp); + #ifdef CONFIG_PCI -static int stl_findpcibrds(void); +static inline int stl_findpcibrds(void); +static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr); #endif +/* + * CD1400 uart specific handling functions. + */ +static void stl_cd1400setreg(stlport_t *portp, int regnr, int value); +static int stl_cd1400getreg(stlport_t *portp, int regnr); +static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value); +static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp); +static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp); +static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp); +static int stl_cd1400getsignals(stlport_t *portp); +static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts); +static void stl_cd1400ccrwait(stlport_t *portp); +static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx); +static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx); +static void stl_cd1400disableintrs(stlport_t *portp); +static void stl_cd1400sendbreak(stlport_t *portp, long len); +static void stl_cd1400flowctrl(stlport_t *portp, int state); +static void stl_cd1400sendflow(stlport_t *portp, int state); +static void stl_cd1400flush(stlport_t *portp); +static int stl_cd1400datastate(stlport_t *portp); +static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase); +static void stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase); +static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr); +static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr); +static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr); + +/* + * SC26198 uart specific handling functions. + */ +static void stl_sc26198setreg(stlport_t *portp, int regnr, int value); +static int stl_sc26198getreg(stlport_t *portp, int regnr); +static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value); +static int stl_sc26198getglobreg(stlport_t *portp, int regnr); +static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp); +static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp); +static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp); +static int stl_sc26198getsignals(stlport_t *portp); +static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts); +static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx); +static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx); +static void stl_sc26198disableintrs(stlport_t *portp); +static void stl_sc26198sendbreak(stlport_t *portp, long len); +static void stl_sc26198flowctrl(stlport_t *portp, int state); +static void stl_sc26198sendflow(stlport_t *portp, int state); +static void stl_sc26198flush(stlport_t *portp); +static int stl_sc26198datastate(stlport_t *portp); +static void stl_sc26198wait(stlport_t *portp); +static void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty); +static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase); +static void stl_sc26198txisr(stlport_t *port); +static void stl_sc26198rxisr(stlport_t *port, unsigned int iack); +static void stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch); +static void stl_sc26198rxbadchars(stlport_t *portp); +static void stl_sc26198otherisr(stlport_t *port, unsigned int iack); + +/*****************************************************************************/ + +/* + * Generic UART support structure. + */ +typedef struct uart { + int (*panelinit)(stlbrd_t *brdp, stlpanel_t *panelp); + void (*portinit)(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp); + void (*setport)(stlport_t *portp, struct termios *tiosp); + int (*getsignals)(stlport_t *portp); + void (*setsignals)(stlport_t *portp, int dtr, int rts); + void (*enablerxtx)(stlport_t *portp, int rx, int tx); + void (*startrxtx)(stlport_t *portp, int rx, int tx); + void (*disableintrs)(stlport_t *portp); + void (*sendbreak)(stlport_t *portp, long len); + void (*flowctrl)(stlport_t *portp, int state); + void (*sendflow)(stlport_t *portp, int state); + void (*flush)(stlport_t *portp); + int (*datastate)(stlport_t *portp); + void (*intr)(stlpanel_t *panelp, unsigned int iobase); +} uart_t; + +/* + * Define some macros to make calling these functions nice and clean. + */ +#define stl_panelinit (* ((uart_t *) panelp->uartp)->panelinit) +#define stl_portinit (* ((uart_t *) portp->uartp)->portinit) +#define stl_setport (* ((uart_t *) portp->uartp)->setport) +#define stl_getsignals (* ((uart_t *) portp->uartp)->getsignals) +#define stl_setsignals (* ((uart_t *) portp->uartp)->setsignals) +#define stl_enablerxtx (* ((uart_t *) portp->uartp)->enablerxtx) +#define stl_startrxtx (* ((uart_t *) portp->uartp)->startrxtx) +#define stl_disableintrs (* ((uart_t *) portp->uartp)->disableintrs) +#define stl_sendbreak (* ((uart_t *) portp->uartp)->sendbreak) +#define stl_flowctrl (* ((uart_t *) portp->uartp)->flowctrl) +#define stl_sendflow (* ((uart_t *) portp->uartp)->sendflow) +#define stl_flush (* ((uart_t *) portp->uartp)->flush) +#define stl_datastate (* ((uart_t *) portp->uartp)->datastate) + +/*****************************************************************************/ + +/* + * CD1400 UART specific data initialization. + */ +static uart_t stl_cd1400uart = { + stl_cd1400panelinit, + stl_cd1400portinit, + stl_cd1400setport, + stl_cd1400getsignals, + stl_cd1400setsignals, + stl_cd1400enablerxtx, + stl_cd1400startrxtx, + stl_cd1400disableintrs, + stl_cd1400sendbreak, + stl_cd1400flowctrl, + stl_cd1400sendflow, + stl_cd1400flush, + stl_cd1400datastate, + stl_cd1400eiointr +}; + +/* + * Define the offsets within the register bank of a cd1400 based panel. + * These io address offsets are common to the EasyIO board as well. + */ +#define EREG_ADDR 0 +#define EREG_DATA 4 +#define EREG_RXACK 5 +#define EREG_TXACK 6 +#define EREG_MDACK 7 + +#define EREG_BANKSIZE 8 + +#define CD1400_CLK 25000000 +#define CD1400_CLK8M 20000000 + +/* + * Define the cd1400 baud rate clocks. These are used when calculating + * what clock and divisor to use for the required baud rate. Also + * define the maximum baud rate allowed, and the default base baud. + */ +static int stl_cd1400clkdivs[] = { + CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4 +}; + +/*****************************************************************************/ + +/* + * SC26198 UART specific data initization. + */ +static uart_t stl_sc26198uart = { + stl_sc26198panelinit, + stl_sc26198portinit, + stl_sc26198setport, + stl_sc26198getsignals, + stl_sc26198setsignals, + stl_sc26198enablerxtx, + stl_sc26198startrxtx, + stl_sc26198disableintrs, + stl_sc26198sendbreak, + stl_sc26198flowctrl, + stl_sc26198sendflow, + stl_sc26198flush, + stl_sc26198datastate, + stl_sc26198intr +}; + +/* + * Define the offsets within the register bank of a sc26198 based panel. + */ +#define XP_DATA 0 +#define XP_ADDR 1 +#define XP_MODID 2 +#define XP_STATUS 2 +#define XP_IACK 3 + +#define XP_BANKSIZE 4 + +/* + * Define the sc26198 baud rate table. Offsets within the table + * represent the actual baud rate selector of sc26198 registers. + */ +static unsigned int sc26198_baudtable[] = { + 50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600, + 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200, + 230400, 460800, 921600 +}; + +#define SC26198_NRBAUDS (sizeof(sc26198_baudtable) / sizeof(unsigned int)) + /*****************************************************************************/ /* @@ -444,8 +662,8 @@ NULL, stl_memioctl, NULL, - NULL, - NULL, + stl_memopen, + stl_memclose, NULL }; @@ -487,7 +705,8 @@ printk("cleanup_module()\n"); #endif - printk(KERN_INFO "Unloading %s: version %s\n", stl_drvname, stl_drvversion); + printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle, + stl_drvversion); save_flags(flags); cli(); @@ -501,12 +720,14 @@ i = tty_unregister_driver(&stl_serial); j = tty_unregister_driver(&stl_callout); if (i || j) { - printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j); + printk("STALLION: failed to un-register tty driver, " + "errno=%d,%d\n", -i, -j); restore_flags(flags); return; } if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) - printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i); + printk("STALLION: failed to un-register serial memory device, " + "errno=%d\n", -i); if (stl_tmpwritebuf != (char *) NULL) kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE); @@ -515,33 +736,24 @@ brdp = stl_brds[i]; for (j = 0; (j < STL_MAXPANELS); j++) { panelp = brdp->panels[j]; - if (panelp != (stlpanel_t *) NULL) { - for (k = 0; (k < STL_PORTSPERPANEL); k++) { - portp = panelp->ports[k]; - if (portp != (stlport_t *) NULL) { - if (portp->tty != (struct tty_struct *) NULL) - stl_hangup(portp->tty); - if (portp->tx.buf != (char *) NULL) - kfree_s(portp->tx.buf, STL_TXBUFSIZE); - kfree_s(portp, sizeof(stlport_t)); - } - } - kfree_s(panelp, sizeof(stlpanel_t)); + if (panelp == (stlpanel_t *) NULL) + continue; + for (k = 0; (k < STL_PORTSPERPANEL); k++) { + portp = panelp->ports[k]; + if (portp == (stlport_t *) NULL) + continue; + if (portp->tty != (struct tty_struct *) NULL) + stl_hangup(portp->tty); + if (portp->tx.buf != (char *) NULL) + kfree_s(portp->tx.buf, STL_TXBUFSIZE); + kfree_s(portp, sizeof(stlport_t)); } - + kfree_s(panelp, sizeof(stlpanel_t)); } - if (brdp->brdtype == BRD_ECH) { - release_region(brdp->ioaddr1, 2); - release_region(brdp->ioaddr2, 32); - } else if (brdp->brdtype == BRD_ECHPCI) { - release_region(brdp->ioaddr1, 4); - release_region(brdp->ioaddr2, 8); - } else if (brdp->brdtype == BRD_ECHMC) { - release_region(brdp->ioaddr1, 64); - } else if (brdp->brdtype == BRD_EASYIO) { - release_region(brdp->ioaddr1, 8); - } + release_region(brdp->ioaddr1, brdp->iosize1); + if (brdp->iosize2 > 0) + release_region(brdp->ioaddr2, brdp->iosize2); kfree_s(brdp, sizeof(stlbrd_t)); stl_brds[i] = (stlbrd_t *) NULL; @@ -576,7 +788,8 @@ int brdnr, panelnr, portnr, rc; #if DEBUG - printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device); + printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, + (int) filp, tty->device); #endif minordev = MINOR(tty->device); @@ -603,6 +816,8 @@ if (portp == (stlport_t *) NULL) return(-ENODEV); + MOD_INC_USE_COUNT; + /* * On the first open of the device setup the port hardware, and * initialize the per port data structure. @@ -651,10 +866,10 @@ return(-EBUSY); if (portp->flags & ASYNC_CALLOUT_ACTIVE) { if ((portp->flags & ASYNC_SESSION_LOCKOUT) && - (portp->session != current->session)) + (portp->session != current->session)) return(-EBUSY); if ((portp->flags & ASYNC_PGRP_LOCKOUT) && - (portp->pgrp != current->pgrp)) + (portp->pgrp != current->pgrp)) return(-EBUSY); } portp->flags |= ASYNC_CALLOUT_ACTIVE; @@ -712,13 +927,14 @@ save_flags(flags); cli(); portp->openwaitcnt++; - if (portp->refcount > 0) + if (! tty_hung_up_p(filp)) portp->refcount--; for (;;) { if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) stl_setsignals(portp, 1, 1); - if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) { + if (tty_hung_up_p(filp) || + ((portp->flags & ASYNC_INITIALIZED) == 0)) { if (portp->flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else @@ -726,8 +942,8 @@ break; } if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) && - ((portp->flags & ASYNC_CLOSING) == 0) && - (doclocal || (portp->sigs & TIOCM_CD))) { + ((portp->flags & ASYNC_CLOSING) == 0) && + (doclocal || (portp->sigs & TIOCM_CD))) { break; } if (current->signal & ~current->blocked) { @@ -763,10 +979,14 @@ save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } + if ((tty->count == 1) && (portp->refcount != 1)) + portp->refcount = 1; if (portp->refcount-- > 1) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -781,14 +1001,14 @@ /* * May want to wait for any data to drain before closing. The BUSY - * flag keeps track of whether we are still sending or not - it allows - * for the FIFO in the cd1400. + * flag keeps track of whether we are still sending or not - it is + * very accurate for the cd1400, not quite so for the sc26198. + * (The sc26198 has no "end-of-data" interrupt only empty FIFO) */ tty->closing = 1; - if (test_bit(ASYI_TXBUSY, &portp->istate)) { - if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, portp->closing_wait); - } + if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, portp->closing_wait); + stl_waituntilsent(tty, (HZ / 2)); portp->flags &= ~ASYNC_INITIALIZED; stl_disableintrs(portp); @@ -808,7 +1028,6 @@ (tty->ldisc.flush_buffer)(tty); tty->closing = 0; - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; if (portp->openwaitcnt) { @@ -817,8 +1036,10 @@ wake_up_interruptible(&portp->open_wait); } - portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | + ASYNC_CLOSING); wake_up_interruptible(&portp->close_wait); + MOD_DEC_USE_COUNT; restore_flags(flags); } @@ -858,10 +1079,12 @@ char *head, *tail; #if DEBUG - printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count); + printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", + (int) tty, from_user, (int) buf, count); #endif - if ((tty == (struct tty_struct *) NULL) || (stl_tmpwritebuf == (char *) NULL)) + if ((tty == (struct tty_struct *) NULL) || + (stl_tmpwritebuf == (char *) NULL)) return(0); portp = tty->driver_data; if (portp == (stlport_t *) NULL) @@ -983,7 +1206,8 @@ return; #if 0 - if (tty->stopped || tty->hw_stopped || (portp->tx.head == portp->tx.tail)) + if (tty->stopped || tty->hw_stopped || + (portp->tx.head == portp->tx.tail)) return; #endif stl_startrxtx(portp, -1, 1); @@ -1066,7 +1290,6 @@ #endif memset(&sio, 0, sizeof(struct serial_struct)); - sio.type = PORT_CIRRUS; sio.line = portp->portnr; sio.port = portp->ioaddr; sio.flags = portp->flags; @@ -1074,8 +1297,14 @@ sio.close_delay = portp->close_delay; sio.closing_wait = portp->closing_wait; sio.custom_divisor = portp->custom_divisor; - sio.xmit_fifo_size = CD1400_TXFIFOSIZE; sio.hub6 = 0; + if (portp->uartp == &stl_cd1400uart) { + sio.type = PORT_CIRRUS; + sio.xmit_fifo_size = CD1400_TXFIFOSIZE; + } else { + sio.type = PORT_UNKNOWN; + sio.xmit_fifo_size = SC26198_TXFIFOSIZE; + } brdp = stl_brds[portp->brdnr]; if (brdp != (stlbrd_t *) NULL) @@ -1103,12 +1332,14 @@ memcpy_fromfs(&sio, sp, sizeof(struct serial_struct)); if (!suser()) { if ((sio.baud_base != portp->baud_base) || - (sio.close_delay != portp->close_delay) || - ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK))) + (sio.close_delay != portp->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (portp->flags & ~ASYNC_USR_MASK))) return(-EPERM); } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); + portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; @@ -1126,7 +1357,8 @@ int rc; #if DEBUG - printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg); + printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", + (int) tty, (int) file, cmd, (int) arg); #endif if (tty == (struct tty_struct *) NULL) @@ -1135,6 +1367,12 @@ if (portp == (stlport_t *) NULL) return(-ENODEV); + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return(-EIO); + } + rc = 0; switch (cmd) { @@ -1152,53 +1390,69 @@ } break; case TIOCGSOFTCAR: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0) - put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(long))) == 0) + put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), + (unsigned long *) arg); break; case TIOCSSOFTCAR: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0); } break; case TIOCMGET: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int))) == 0) { val = (unsigned long) stl_getsignals(portp); put_fs_long(val, (unsigned long *) arg); } break; case TIOCMBIS: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1)); + stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : -1), + ((arg & TIOCM_RTS) ? 1 : -1)); } break; case TIOCMBIC: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stl_setsignals(portp, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1)); + stl_setsignals(portp, ((arg & TIOCM_DTR) ? 0 : -1), + ((arg & TIOCM_RTS) ? 0 : -1)); } break; case TIOCMSET: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(long))) == 0) { arg = get_fs_long((unsigned long *) arg); - stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0)); + stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : 0), + ((arg & TIOCM_RTS) ? 1 : 0)); } break; case TIOCGSERIAL: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct))) == 0) stl_getserial(portp, (struct serial_struct *) arg); break; case TIOCSSERIAL: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct))) == 0) rc = stl_setserial(portp, (struct serial_struct *) arg); break; case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stl_getportstats(portp, (comstats_t *) arg); break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stl_clrportstats(portp, (comstats_t *) arg); break; case TIOCSERCONFIG: @@ -1234,11 +1488,13 @@ return; tiosp = tty->termios; - if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag)) + if ((tiosp->c_cflag == old->c_cflag) && + (tiosp->c_iflag == old->c_iflag)) return; stl_setport(portp, tiosp); - stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), -1); + stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), + -1); if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) { tty->hw_stopped = 0; stl_start(tty); @@ -1257,7 +1513,6 @@ static void stl_throttle(struct tty_struct *tty) { stlport_t *portp; - unsigned long flags; #if DEBUG printk("stl_throttle(tty=%x)\n", (int) tty); @@ -1268,24 +1523,7 @@ portp = tty->driver_data; if (portp == (stlport_t *) NULL) return; - - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - if (tty->termios->c_iflag & IXOFF) { - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_SENDSCHR2); - portp->stats.rxxoff++; - stl_ccrwait(portp); - } - if (tty->termios->c_cflag & CRTSCTS) { - stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0)); - stl_setreg(portp, MSVR2, 0); - portp->stats.rxrtsoff++; - } - BRDDISABLE(portp->brdnr); - restore_flags(flags); + stl_flowctrl(portp, 0); } /*****************************************************************************/ @@ -1297,7 +1535,6 @@ static void stl_unthrottle(struct tty_struct *tty) { stlport_t *portp; - unsigned long flags; #if DEBUG printk("stl_unthrottle(tty=%x)\n", (int) tty); @@ -1308,30 +1545,7 @@ portp = tty->driver_data; if (portp == (stlport_t *) NULL) return; - - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - if (tty->termios->c_iflag & IXOFF) { - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_SENDSCHR1); - portp->stats.rxxon++; - stl_ccrwait(portp); - } -/* - * Question: should we return RTS to what it was before? It may have - * been set by an ioctl... Suppose not, since if you have hardware - * flow control set then it is pretty silly to go and set the RTS line - * by hand. - */ - if (tty->termios->c_cflag & CRTSCTS) { - stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD)); - stl_setreg(portp, MSVR2, MSVR2_RTS); - portp->stats.rxrtson++; - } - BRDDISABLE(portp->brdnr); - restore_flags(flags); + stl_flowctrl(portp, 1); } /*****************************************************************************/ @@ -1354,7 +1568,6 @@ portp = tty->driver_data; if (portp == (stlport_t *) NULL) return; - stl_startrxtx(portp, -1, 0); } @@ -1377,7 +1590,6 @@ portp = tty->driver_data; if (portp == (stlport_t *) NULL) return; - stl_startrxtx(portp, -1, 1); } @@ -1417,7 +1629,6 @@ portp->tx.head = (char *) NULL; portp->tx.tail = (char *) NULL; } - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); portp->refcount = 0; @@ -1429,7 +1640,6 @@ static void stl_flushbuffer(struct tty_struct *tty) { stlport_t *portp; - unsigned long flags; #if DEBUG printk("stl_flushbuffer(tty=%x)\n", (int) tty); @@ -1441,439 +1651,183 @@ if (portp == (stlport_t *) NULL) return; - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_TXFLUSHFIFO); - stl_ccrwait(portp); - portp->tx.tail = portp->tx.head; - BRDDISABLE(portp->brdnr); - restore_flags(flags); - + stl_flush(portp); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); } /*****************************************************************************/ -/* - * These functions get/set/update the registers of the cd1400 UARTs. - * Access to the cd1400 registers is via an address/data io port pair. - * (Maybe should make this inline...) - */ - -static int stl_getreg(stlport_t *portp, int regnr) +static void stl_waituntilsent(struct tty_struct *tty, int timeout) { - outb((regnr + portp->uartaddr), portp->ioaddr); - return(inb(portp->ioaddr + EREG_DATA)); -} + stlport_t *portp; + unsigned long tend; -static void stl_setreg(stlport_t *portp, int regnr, int value) -{ - outb((regnr + portp->uartaddr), portp->ioaddr); - outb(value, portp->ioaddr + EREG_DATA); -} +#if DEBUG + printk("stl_waituntilsent(tty=%x,timeout=%d)\n", (int) tty, timeout); +#endif -static int stl_updatereg(stlport_t *portp, int regnr, int value) -{ - outb((regnr + portp->uartaddr), portp->ioaddr); - if (inb(portp->ioaddr + EREG_DATA) != value) { - outb(value, portp->ioaddr + EREG_DATA); - return(1); + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stlport_t *) NULL) + return; + + if (timeout == 0) + timeout = HZ; + tend = jiffies + timeout; + + while (stl_datastate(portp)) { + if (current->signal & ~current->blocked) + break; + stl_delay(2); + if (jiffies >= tend) + break; } - return(0); } /*****************************************************************************/ /* - * Transmit interrupt handler. This has gotta be fast! Handling TX - * chars is pretty simple, stuff as many as possible from the TX buffer - * into the cd1400 FIFO. Must also handle TX breaks here, since they - * are embedded as commands in the data stream. Oh no, had to use a goto! - * This could be optimized more, will do when I get time... - * In practice it is possible that interrupts are enabled but that the - * port has been hung up. Need to handle not having any TX buffer here, - * this is done by using the side effect that head and tail will also - * be NULL if the buffer has been freed. + * All board interrupts are vectored through here first. This code then + * calls off to the approrpriate board interrupt handlers. */ -static inline void stl_txisr(stlpanel_t *panelp, int ioaddr) +static void stl_intr(int irq, void *dev_id, struct pt_regs *regs) { - stlport_t *portp; - int len, stlen; - char *head, *tail; - unsigned char ioack, srer; + stlbrd_t *brdp; + int i; #if DEBUG - printk("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); + printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs); #endif - ioack = inb(ioaddr + EREG_TXACK); - if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { - printk("STALLION: bad TX interrupt ack value=%x\n", ioack); - return; + for (i = 0; (i < stl_nrbrds); i++) { + if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) + continue; + if (brdp->state == 0) + continue; + (* brdp->isr)(brdp); } - portp = panelp->ports[(ioack >> 3)]; +} + +/*****************************************************************************/ /* - * Unfortunately we need to handle breaks in the data stream, since - * this is the only way to generate them on the cd1400. Do it now if - * a break is to be sent. + * Interrupt service routine for EasyIO board types. */ - if (portp->brklen != 0) { - if (portp->brklen > 0) { - outb((TDR + portp->uartaddr), ioaddr); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_DELAY, (ioaddr + EREG_DATA)); - outb(portp->brklen, (ioaddr + EREG_DATA)); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); - portp->brklen = -1; - goto stl_txalldone; - } else { - outb((COR2 + portp->uartaddr), ioaddr); - outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA)); - portp->brklen = 0; - } - } - - head = portp->tx.head; - tail = portp->tx.tail; - len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); - if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { - set_bit(ASYI_TXLOW, &portp->istate); - queue_task_irq_off(&portp->tqueue, &tq_scheduler); - } - if (len == 0) { - outb((SRER + portp->uartaddr), ioaddr); - srer = inb(ioaddr + EREG_DATA); - if (srer & SRER_TXDATA) { - srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY; - } else { - srer &= ~(SRER_TXDATA | SRER_TXEMPTY); - clear_bit(ASYI_TXBUSY, &portp->istate); - } - outb(srer, (ioaddr + EREG_DATA)); - } else { - len = MIN(len, CD1400_TXFIFOSIZE); - portp->stats.txtotal += len; - stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail)); - outb((TDR + portp->uartaddr), ioaddr); - outsb((ioaddr + EREG_DATA), tail, stlen); - len -= stlen; - tail += stlen; - if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) - tail = portp->tx.buf; - if (len > 0) { - outsb((ioaddr + EREG_DATA), tail, len); - tail += len; - } - portp->tx.tail = tail; - } +static void stl_eiointr(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int iobase; -stl_txalldone: - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); + panelp = brdp->panels[0]; + iobase = panelp->iobase; + while (inb(brdp->iostatus) & EIO_INTRPEND) + (* panelp->isr)(panelp, iobase); } /*****************************************************************************/ /* - * Receive character interrupt handler. Determine if we have good chars - * or bad chars and then process appropriately. Good chars are easy - * just shove the lot into the RX buffer and set all status byte to 0. - * If a bad RX char then process as required. This routine needs to be - * fast! In practice it is possible that we get an interrupt on a port - * that is closed. This can happen on hangups - since they completely - * shutdown a port not in user context. Need to handle this case. + * Interrupt service routine for ECH-AT board types. */ -static inline void stl_rxisr(stlpanel_t *panelp, int ioaddr) +static void stl_echatintr(stlbrd_t *brdp) { - stlport_t *portp; - struct tty_struct *tty; - unsigned int ioack, len, buflen; - unsigned char status; - char ch; - static char unwanted[CD1400_RXFIFOSIZE]; + stlpanel_t *panelp; + unsigned int ioaddr; + int bnknr; -#if DEBUG - printk("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); -#endif + outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - ioack = inb(ioaddr + EREG_RXACK); - if ((ioack & panelp->ackmask) != 0) { - printk("STALLION: bad RX interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; - tty = portp->tty; - - if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { - outb((RDCR + portp->uartaddr), ioaddr); - len = inb(ioaddr + EREG_DATA); - if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) || - ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { - outb((RDSR + portp->uartaddr), ioaddr); - insb((ioaddr + EREG_DATA), &unwanted[0], len); - portp->stats.rxlost += len; - portp->stats.rxtotal += len; - } else { - len = MIN(len, buflen); - if (len > 0) { - outb((RDSR + portp->uartaddr), ioaddr); - insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len); - memset(tty->flip.flag_buf_ptr, 0, len); - tty->flip.flag_buf_ptr += len; - tty->flip.char_buf_ptr += len; - tty->flip.count += len; - tty_schedule_flip(tty); - portp->stats.rxtotal += len; - } - } - } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) { - outb((RDSR + portp->uartaddr), ioaddr); - status = inb(ioaddr + EREG_DATA); - ch = inb(ioaddr + EREG_DATA); - if (status & ST_PARITY) - portp->stats.rxparity++; - if (status & ST_FRAMING) - portp->stats.rxframing++; - if (status & ST_OVERRUN) - portp->stats.rxoverrun++; - if (status & ST_BREAK) - portp->stats.rxbreaks++; - if (status & ST_SCHARMASK) { - if ((status & ST_SCHARMASK) == ST_SCHAR1) - portp->stats.txxon++; - if ((status & ST_SCHARMASK) == ST_SCHAR2) - portp->stats.txxoff++; - goto stl_rxalldone; - } - if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) { - if (portp->rxmarkmsk & status) { - if (status & ST_BREAK) { - status = TTY_BREAK; -#ifndef MODULE - if (portp->flags & ASYNC_SAK) { - do_SAK(tty); - BRDENABLE(portp->brdnr, portp->pagenr); - } -#endif - } else if (status & ST_PARITY) { - status = TTY_PARITY; - } else if (status & ST_FRAMING) { - status = TTY_FRAME; - } else if(status & ST_OVERRUN) { - status = TTY_OVERRUN; - } else { - status = 0; - } - } else { - status = 0; - } - if (tty->flip.char_buf_ptr != (char *) NULL) { - if (tty->flip.count < TTY_FLIPBUF_SIZE) { - *tty->flip.flag_buf_ptr++ = status; - *tty->flip.char_buf_ptr++ = ch; - tty->flip.count++; - } - tty_schedule_flip(tty); + while (inb(brdp->iostatus) & ECH_INTRPEND) { + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); } } - } else { - printk("STALLION: bad RX interrupt ack value=%x\n", ioack); - return; } -stl_rxalldone: - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); + outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); } /*****************************************************************************/ /* - * Modem interrupt handler. The is called when the modem signal line - * (DCD) has changed state. Leave most of the work to the off-level - * processing routine. + * Interrupt service routine for ECH-MCA board types. */ -static inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr) +static void stl_echmcaintr(stlbrd_t *brdp) { - stlport_t *portp; - unsigned int ioack; - unsigned char misr; - -#if DEBUG - printk("stl_mdmisr(panelp=%x)\n", (int) panelp); -#endif - - ioack = inb(ioaddr + EREG_MDACK); - if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { - printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack); - return; - } - portp = panelp->ports[(ioack >> 3)]; + stlpanel_t *panelp; + unsigned int ioaddr; + int bnknr; - outb((MISR + portp->uartaddr), ioaddr); - misr = inb(ioaddr + EREG_DATA); - if (misr & MISR_DCD) { - set_bit(ASYI_DCDCHANGE, &portp->istate); - queue_task_irq_off(&portp->tqueue, &tq_scheduler); - portp->stats.modem++; + while (inb(brdp->iostatus) & ECH_INTRPEND) { + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + } + } } - - outb((EOSRR + portp->uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); } /*****************************************************************************/ /* - * Interrupt handler for EIO and ECH boards. This code ain't all that - * pretty, but the idea is to make it as fast as possible. This code is - * well suited to be assemblerized :-) We don't use the general purpose - * register access functions here, for speed we will go strait to the - * io region. + * Interrupt service routine for ECH-PCI board types. */ -static void stl_intr(int irq, void *dev_id, struct pt_regs *regs) +static void stl_echpciintr(stlbrd_t *brdp) { - stlbrd_t *brdp; stlpanel_t *panelp; - unsigned char svrtype; - int i, panelnr, iobase; - -#if DEBUG - printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs); -#endif + unsigned int ioaddr; + int bnknr, recheck; - panelp = (stlpanel_t *) NULL; - for (i = 0; (i < stl_nrbrds); ) { - if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) { - i++; - continue; - } - if (brdp->state == 0) { - i++; - continue; - } -/* - * The following section of code handles the subtle differences - * between board types. It is sort of similar, but different - * enough to handle each separately. - */ - if (brdp->brdtype == BRD_EASYIO) { - if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) { - i++; - continue; - } - panelp = brdp->panels[0]; - iobase = panelp->iobase; - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - if (brdp->nrports > 4) { - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } - } else if (brdp->brdtype == BRD_ECH) { - if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) { - i++; - continue; - } - outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - iobase = panelp->iobase; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - iobase += 0x8; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; + while (1) { + recheck = 0; + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl); + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + recheck++; } - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } else if (brdp->brdtype == BRD_ECHPCI) { - iobase = brdp->ioaddr2; - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - outb(panelp->pagenr, brdp->ioctrl); - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - outb((panelp->pagenr + 1), brdp->ioctrl); - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; - } - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } else if (brdp->brdtype == BRD_ECHMC) { - if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) { - i++; - continue; - } - for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { - panelp = brdp->panels[panelnr]; - iobase = panelp->iobase; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - if (panelp->nrports > 8) { - iobase += 0x8; - if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) - break; - } - } - if (panelnr >= brdp->nrpanels) { - i++; - continue; - } - outb(SVRR, iobase); - svrtype = inb(iobase + EREG_DATA); - outb((SVRR + 0x80), iobase); - svrtype |= inb(iobase + EREG_DATA); - } else { - printk("STALLION: unknown board type=%x\n", brdp->brdtype); - i++; - continue; } + if (! recheck) + break; + } +} + +/*****************************************************************************/ /* - * We have determined what type of service is required for a - * port. From here on in the service of a port is the same no - * matter what the board type... - */ - if (svrtype & SVRR_RX) - stl_rxisr(panelp, iobase); - if (svrtype & SVRR_TX) - stl_txisr(panelp, iobase); - if (svrtype & SVRR_MDM) - stl_mdmisr(panelp, iobase); + * Interrupt service routine for ECH-8/64-PCI board types. + */ - if (brdp->brdtype == BRD_ECH) - outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); +static void stl_echpci64intr(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int ioaddr; + int bnknr; + + while (inb(brdp->ioctrl) & 0x1) { + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + } + } } } @@ -1890,6 +1844,7 @@ unsigned int oldsigs; portp = private; + #if DEBUG printk("stl_offintr(portp=%x)\n", (int) portp); #endif @@ -1901,7 +1856,8 @@ return; if (test_bit(ASYI_TXLOW, &portp->istate)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); } @@ -1914,7 +1870,7 @@ if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) { if (portp->flags & ASYNC_CHECK_CD) { if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) && - (portp->flags & ASYNC_CALLOUT_NOHUP))) { + (portp->flags & ASYNC_CALLOUT_NOHUP))) { tty_hangup(tty); } } @@ -1925,90 +1881,1233 @@ /*****************************************************************************/ /* - * Wait for the command register to be ready. We will poll this, - * since it won't usually take too long to be ready. + * Map in interrupt vector to this driver. Check that we don't + * already have this vector mapped, we might be sharing this + * interrupt across multiple boards. */ -static void stl_ccrwait(stlport_t *portp) +static int stl_mapirq(int irq, char *name) { - int i; + int rc, i; - for (i = 0; (i < CCR_MAXWAIT); i++) { - if (stl_getreg(portp, CCR) == 0) { - return; +#if DEBUG + printk("stl_mapirq(irq=%d,name=%s)\n", irq, name); +#endif + + rc = 0; + for (i = 0; (i < stl_numintrs); i++) { + if (stl_gotintrs[i] == irq) + break; + } + if (i >= stl_numintrs) { + if (request_irq(irq, stl_intr, SA_INTERRUPT, name, NULL) != 0) { + printk("STALLION: failed to register interrupt " + "routine for %s irq=%d\n", name, irq); + rc = -ENODEV; + } else { + stl_gotintrs[stl_numintrs++] = irq; } } - - printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr); + return(rc); } /*****************************************************************************/ /* - * Set up the cd1400 registers for a port based on the termios port - * settings. + * Initialize all the ports on a panel. */ -static void stl_setport(stlport_t *portp, struct termios *tiosp) +static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) { - stlbrd_t *brdp; - unsigned long flags; - unsigned int clkdiv, baudrate; - unsigned char cor1, cor2, cor3; - unsigned char cor4, cor5, ccr; - unsigned char srer, sreron, sreroff; - unsigned char mcor1, mcor2, rtpr; - unsigned char clk, div; + stlport_t *portp; + int chipmask, i; - cor1 = 0; - cor2 = 0; - cor3 = 0; - cor4 = 0; - cor5 = 0; - ccr = 0; - rtpr = 0; - clk = 0; - div = 0; - mcor1 = 0; - mcor2 = 0; - sreron = 0; - sreroff = 0; +#if DEBUG + printk("stl_initports(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp); +#endif - brdp = stl_brds[portp->brdnr]; - if (brdp == (stlbrd_t *) NULL) - return; + chipmask = stl_panelinit(brdp, panelp); /* - * Set up the RX char ignore mask with those RX error types we - * can ignore. We can get the cd1400 to help us out a little here, - * it will ignore parity errors and breaks for us. + * All UART's are initialized (if found!). Now go through and setup + * each ports data structures. */ - portp->rxignoremsk = 0; - if (tiosp->c_iflag & IGNPAR) { - portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN); - cor1 |= COR1_PARIGNORE; - } - if (tiosp->c_iflag & IGNBRK) { - portp->rxignoremsk |= ST_BREAK; - cor4 |= COR4_IGNBRK; + for (i = 0; (i < panelp->nrports); i++) { + portp = (stlport_t *) stl_memalloc(sizeof(stlport_t)); + if (portp == (stlport_t *) NULL) { + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlport_t)); + break; + } + memset(portp, 0, sizeof(stlport_t)); + + portp->magic = STL_PORTMAGIC; + portp->portnr = i; + portp->brdnr = panelp->brdnr; + portp->panelnr = panelp->panelnr; + portp->uartp = panelp->uartp; + portp->clk = brdp->clk; + portp->baud_base = STL_BAUDBASE; + portp->close_delay = STL_CLOSEDELAY; + portp->closing_wait = 30 * HZ; + portp->normaltermios = stl_deftermios; + portp->callouttermios = stl_deftermios; + portp->tqueue.routine = stl_offintr; + portp->tqueue.data = portp; + portp->stats.brd = portp->brdnr; + portp->stats.panel = portp->panelnr; + portp->stats.port = portp->portnr; + panelp->ports[i] = portp; + stl_portinit(brdp, panelp, portp); } - portp->rxmarkmsk = ST_OVERRUN; - if (tiosp->c_iflag & (INPCK | PARMRK)) - portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING); - if (tiosp->c_iflag & BRKINT) - portp->rxmarkmsk |= ST_BREAK; + return(0); +} + +/*****************************************************************************/ /* - * Go through the char size, parity and stop bits and set all the - * option register appropriately. + * Try to find and initialize an EasyIO board. */ - switch (tiosp->c_cflag & CSIZE) { - case CS5: - cor1 |= COR1_CHL5; - break; - case CS6: - cor1 |= COR1_CHL6; + +static inline int stl_initeio(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int status; + char *name; + int rc; + +#if DEBUG + printk("stl_initeio(brdp=%x)\n", (int) brdp); +#endif + + brdp->ioctrl = brdp->ioaddr1 + 1; + brdp->iostatus = brdp->ioaddr1 + 2; + + status = inb(brdp->iostatus); + if ((status & EIO_IDBITMASK) == EIO_MK3) + brdp->ioctrl++; + +/* + * Handle board specific stuff now. The real difference is PCI + * or not PCI. + */ + if (brdp->brdtype == BRD_EASYIOPCI) { + brdp->iosize1 = 0x80; + brdp->iosize2 = 0x80; + name = "serial(EIO-PCI)"; + outb(0x41, (brdp->ioaddr2 + 0x4c)); + } else { + brdp->iosize1 = 8; + name = "serial(EIO)"; + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + return(-EINVAL); + } + outb((stl_vecmap[brdp->irq] | EIO_0WS | + ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), + brdp->ioctrl); + } + + if (check_region(brdp->ioaddr1, brdp->iosize1)) { + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->ioaddr1); + } + if (brdp->iosize2 > 0) { + if (check_region(brdp->ioaddr2, brdp->iosize2)) { + printk("STALLION: Warning, unit %d I/O address %x " + "conflicts with another device\n", + brdp->brdnr, brdp->ioaddr2); + } + } + +/* + * Everything looks OK, so lets go ahead and probe for the hardware. + */ + brdp->clk = CD1400_CLK; + brdp->isr = stl_eiointr; + + switch (status & EIO_IDBITMASK) { + case EIO_8PORTM: + brdp->clk = CD1400_CLK8M; + /* fall thru */ + case EIO_8PORTRS: + case EIO_8PORTDI: + brdp->nrports = 8; + break; + case EIO_4PORTRS: + brdp->nrports = 4; + break; + case EIO_MK3: + switch (status & EIO_BRDMASK) { + case ID_BRD4: + brdp->nrports = 4; + break; + case ID_BRD8: + brdp->nrports = 8; + break; + case ID_BRD16: + brdp->nrports = 16; + break; + default: + return(-ENODEV); + } + break; + default: + return(-ENODEV); + } + +/* + * We have verfied that the board is actually present, so now we + * can complete the setup. + */ + request_region(brdp->ioaddr1, brdp->iosize1, name); + if (brdp->iosize2 > 0) + request_region(brdp->ioaddr2, brdp->iosize2, name); + + panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); + if (panelp == (stlpanel_t *) NULL) { + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlpanel_t)); + return(-ENOMEM); + } + memset(panelp, 0, sizeof(stlpanel_t)); + + panelp->magic = STL_PANELMAGIC; + panelp->brdnr = brdp->brdnr; + panelp->panelnr = 0; + panelp->nrports = brdp->nrports; + panelp->iobase = brdp->ioaddr1; + panelp->hwid = status; + if ((status & EIO_IDBITMASK) == EIO_MK3) { + panelp->uartp = (void *) &stl_sc26198uart; + panelp->isr = stl_sc26198intr; + } else { + panelp->uartp = (void *) &stl_cd1400uart; + panelp->isr = stl_cd1400eiointr; + } + + brdp->panels[0] = panelp; + brdp->nrpanels = 1; + brdp->state |= BRD_FOUND; + brdp->hwid = status; + rc = stl_mapirq(brdp->irq, name); + return(rc); +} + +/*****************************************************************************/ + +/* + * Try to find an ECH board and initialize it. This code is capable of + * dealing with all types of ECH board. + */ + +static inline int stl_initech(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int status, nxtid, ioaddr, conflict; + int panelnr, banknr, i; + char *name; + +#if DEBUG + printk("stl_initech(brdp=%x)\n", (int) brdp); +#endif + + status = 0; + conflict = 0; + +/* + * Set up the initial board register contents for boards. This varies a + * bit between the different board types. So we need to handle each + * separately. Also do a check that the supplied IRQ is good. + */ + switch (brdp->brdtype) { + + case BRD_ECH: + brdp->isr = stl_echatintr; + brdp->ioctrl = brdp->ioaddr1 + 1; + brdp->iostatus = brdp->ioaddr1 + 1; + status = inb(brdp->iostatus); + if ((status & ECH_IDBITMASK) != ECH_ID) + return(-ENODEV); + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + return(-EINVAL); + } + status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); + status |= (stl_vecmap[brdp->irq] << 1); + outb((status | ECH_BRDRESET), brdp->ioaddr1); + brdp->ioctrlval = ECH_INTENABLE | + ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); + for (i = 0; (i < 10); i++) + outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); + brdp->iosize1 = 2; + brdp->iosize2 = 32; + name = "serial(EC8/32)"; + outb(status, brdp->ioaddr1); + break; + + case BRD_ECHMC: + brdp->isr = stl_echmcaintr; + brdp->ioctrl = brdp->ioaddr1 + 0x20; + brdp->iostatus = brdp->ioctrl; + status = inb(brdp->iostatus); + if ((status & ECH_IDBITMASK) != ECH_ID) + return(-ENODEV); + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + return(-EINVAL); + } + outb(ECHMC_BRDRESET, brdp->ioctrl); + outb(ECHMC_INTENABLE, brdp->ioctrl); + brdp->iosize1 = 64; + name = "serial(EC8/32-MC)"; + break; + + case BRD_ECHPCI: + brdp->isr = stl_echpciintr; + brdp->ioctrl = brdp->ioaddr1 + 2; + brdp->iosize1 = 4; + brdp->iosize2 = 8; + name = "serial(EC8/32-PCI)"; + break; + + case BRD_ECH64PCI: + brdp->isr = stl_echpci64intr; + brdp->ioctrl = brdp->ioaddr2 + 0x40; + outb(0x43, (brdp->ioaddr1 + 0x4c)); + brdp->iosize1 = 0x80; + brdp->iosize2 = 0x80; + name = "serial(EC8/64-PCI)"; + break; + + default: + printk("STALLION: unknown board type=%d\n", brdp->brdtype); + return(-EINVAL); + break; + } + +/* + * Check boards for possible IO address conflicts. We won't actually + * do anything about it here, just issue a warning... + */ + conflict = check_region(brdp->ioaddr1, brdp->iosize1) ? + brdp->ioaddr1 : 0; + if ((conflict == 0) && (brdp->iosize2 > 0)) + conflict = check_region(brdp->ioaddr2, brdp->iosize2) ? + brdp->ioaddr2 : 0; + if (conflict) { + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, conflict); + } + + request_region(brdp->ioaddr1, brdp->iosize1, name); + if (brdp->iosize2 > 0) + request_region(brdp->ioaddr2, brdp->iosize2, name); + +/* + * Scan through the secondary io address space looking for panels. + * As we find'em allocate and initialize panel structures for each. + */ + brdp->clk = CD1400_CLK; + brdp->hwid = status; + + ioaddr = brdp->ioaddr2; + banknr = 0; + panelnr = 0; + nxtid = 0; + + for (i = 0; (i < STL_MAXPANELS); i++) { + if (brdp->brdtype == BRD_ECHPCI) { + outb(nxtid, brdp->ioctrl); + ioaddr = brdp->ioaddr2; + } + status = inb(ioaddr + ECH_PNLSTATUS); + if ((status & ECH_PNLIDMASK) != nxtid) + break; + panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); + if (panelp == (stlpanel_t *) NULL) { + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlpanel_t)); + break; + } + memset(panelp, 0, sizeof(stlpanel_t)); + panelp->magic = STL_PANELMAGIC; + panelp->brdnr = brdp->brdnr; + panelp->panelnr = panelnr; + panelp->iobase = ioaddr; + panelp->pagenr = nxtid; + panelp->hwid = status; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS; + + if (status & ECH_PNLXPID) { + panelp->uartp = (void *) &stl_sc26198uart; + panelp->isr = stl_sc26198intr; + if (status & ECH_PNL16PORT) { + panelp->nrports = 16; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + 4 + + ECH_PNLSTATUS; + } else { + panelp->nrports = 8; + } + } else { + panelp->uartp = (void *) &stl_cd1400uart; + panelp->isr = stl_cd1400echintr; + if (status & ECH_PNL16PORT) { + panelp->nrports = 16; + panelp->ackmask = 0x80; + if (brdp->brdtype != BRD_ECHPCI) + ioaddr += EREG_BANKSIZE; + brdp->bnk2panel[banknr] = panelp; + brdp->bnkpageaddr[banknr] = ++nxtid; + brdp->bnkstataddr[banknr++] = ioaddr + + ECH_PNLSTATUS; + } else { + panelp->nrports = 8; + panelp->ackmask = 0xc0; + } + } + + nxtid++; + ioaddr += EREG_BANKSIZE; + brdp->nrports += panelp->nrports; + brdp->panels[panelnr++] = panelp; + if ((brdp->brdtype != BRD_ECHPCI) && + (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) + break; + } + + brdp->nrpanels = panelnr; + brdp->nrbnks = banknr; + if (brdp->brdtype == BRD_ECH) + outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); + + brdp->state |= BRD_FOUND; + i = stl_mapirq(brdp->irq, name); + return(i); +} + +/*****************************************************************************/ + +/* + * Initialize and configure the specified board. + * Scan through all the boards in the configuration and see what we + * can find. Handle EIO and the ECH boards a little differently here + * since the initial search and setup is very different. + */ + +static int stl_brdinit(stlbrd_t *brdp) +{ + int i; + +#if DEBUG + printk("stl_brdinit(brdp=%x)\n", (int) brdp); +#endif + + switch (brdp->brdtype) { + case BRD_EASYIO: + case BRD_EASYIOPCI: + stl_initeio(brdp); + break; + case BRD_ECH: + case BRD_ECHMC: + case BRD_ECHPCI: + case BRD_ECH64PCI: + stl_initech(brdp); + break; + default: + printk("STALLION: unit=%d is unknown board type=%d\n", + brdp->brdnr, brdp->brdtype); + return(ENODEV); + } + + stl_brds[brdp->brdnr] = brdp; + if ((brdp->state & BRD_FOUND) == 0) { + printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", + stl_brdnames[brdp->brdtype], brdp->brdnr, + brdp->ioaddr1, brdp->irq); + return(ENODEV); + } + + for (i = 0; (i < STL_MAXPANELS); i++) + if (brdp->panels[i] != (stlpanel_t *) NULL) + stl_initports(brdp, brdp->panels[i]); + + printk("STALLION: %s found, unit=%d io=%x irq=%d " + "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], + brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, + brdp->nrports); + return(0); +} + +/*****************************************************************************/ + +#ifdef CONFIG_PCI + +/* + * We have a Stallion board. Allocate a board structure and + * initialize it. Read its IO and IRQ resources from PCI + * configuration space. + */ + +static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr) +{ + unsigned int bar[4]; + stlbrd_t *brdp; + int i, rc; + unsigned char irq; + +#if DEBUG + printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", + brdtype, busnr, devnr); +#endif + + brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); + if (brdp == (stlbrd_t *) NULL) { + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlbrd_t)); + return(-ENOMEM); + } + + memset(brdp, 0, sizeof(stlbrd_t)); + brdp->magic = STL_BOARDMAGIC; + brdp->brdnr = stl_nrbrds++; + brdp->brdtype = brdtype; + +/* + * Read in all the BAR registers from this board. Different Stallion + * boards use these in different ways, so we just read in the whole + * lot and then figure out what is what later. + */ + for (i = 0; (i < 4); i++) { + rc = pcibios_read_config_dword(busnr, devnr, + (PCI_BASE_ADDRESS_0 + (i * 0x4)), &bar[i]); + if (rc) { + printk("STALLION: failed to read BAR register %d " + "from PCI board, errno=%x\n", i, rc); + return(0); + } + } + + rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq); + if (rc) { + printk("STALLION: failed to read INTERRUPT register " + "from PCI board, errno=%x\n", rc); + return(0); + } + +#if DEBUG + printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__, + bar[0], bar[1], bar[2], bar[3], irq); +#endif + +/* + * We have all resources from the board, so lets setup the actual + * board structure now. + */ + switch (brdtype) { + case BRD_ECHPCI: + brdp->ioaddr2 = (bar[0] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + case BRD_ECH64PCI: + brdp->ioaddr2 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + case BRD_EASYIOPCI: + brdp->ioaddr1 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr2 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + default: + printk("STALLION: unknown PCI board type=%d\n", brdtype); + break; + } + + brdp->irq = irq; + stl_brdinit(brdp); + + return(0); +} + + +/*****************************************************************************/ + +/* + * Find all Stallion PCI boards that might be installed. Initialize each + * one as it is found. + */ + + +static inline int stl_findpcibrds() +{ + unsigned char busnr, devnr; + unsigned short class; + int i, rc, brdtypnr; + +#if DEBUG + printk("stl_findpcibrds()\n"); +#endif + + if (! pcibios_present()) + return(0); + + for (i = 0; (i < stl_nrpcibrds); i++) { + for (brdtypnr = 0; ; brdtypnr++) { + + rc = pcibios_find_device(stl_pcibrds[i].vendid, + stl_pcibrds[i].devid, brdtypnr, &busnr, &devnr); + if (rc) + break; + +/* + * Check that we can handle more boards... + */ + if (stl_nrbrds >= STL_MAXBRDS) { + printk("STALLION: too many boards found, " + "maximum supported %d\n", STL_MAXBRDS); + i = stl_nrpcibrds; + break; + } + +/* + * Found a device on the PCI bus that has our vendor and + * device ID. Need to check now that it is really us. + */ + rc = pcibios_read_config_word(busnr, devnr, + PCI_CLASS_DEVICE, &class); + if (rc) { + printk("STALLION: failed to read class type " + "from PCI board, errno=%x\n", rc); + continue; + } + if (class == PCI_CLASS_STORAGE_IDE) + continue; + + rc = stl_initpcibrd(stl_pcibrds[i].brdtype, busnr, + devnr); + if (rc) + return(rc); + } + } + + return(0); +} + +#endif + +/*****************************************************************************/ + +/* + * Scan through all the boards in the configuration and see what we + * can find. Handle EIO and the ECH boards a little differently here + * since the initial search and setup is too different. + */ + +static inline int stl_initbrds() +{ + stlbrd_t *brdp; + stlconf_t *confp; + int i; + +#if DEBUG + printk("stl_initbrds()\n"); +#endif + + if (stl_nrbrds > STL_MAXBRDS) { + printk("STALLION: too many boards in configuration table, " + "truncating to %d\n", STL_MAXBRDS); + stl_nrbrds = STL_MAXBRDS; + } + +/* + * Firstly scan the list of static boards configured. Allocate + * resources and initialize the boards as found. + */ + for (i = 0; (i < stl_nrbrds); i++) { + confp = &stl_brdconf[i]; + brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); + if (brdp == (stlbrd_t *) NULL) { + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlbrd_t)); + return(-ENOMEM); + } + memset(brdp, 0, sizeof(stlbrd_t)); + + brdp->magic = STL_BOARDMAGIC; + brdp->brdnr = i; + brdp->brdtype = confp->brdtype; + brdp->ioaddr1 = confp->ioaddr1; + brdp->ioaddr2 = confp->ioaddr2; + brdp->irq = confp->irq; + brdp->irqtype = confp->irqtype; + stl_brdinit(brdp); + } + +#ifdef CONFIG_PCI +/* + * If the PCI BIOS support is compiled in then let's go looking for + * ECH-PCI boards. + */ + stl_findpcibrds(); +#endif + + return(0); +} + +/*****************************************************************************/ + +/* + * Return the board stats structure to user app. + */ + +static int stl_getbrdstats(combrd_t *bp) +{ + stlbrd_t *brdp; + stlpanel_t *panelp; + int i; + + memcpy_fromfs(&stl_brdstats, bp, sizeof(combrd_t)); + if (stl_brdstats.brd >= STL_MAXBRDS) + return(-ENODEV); + brdp = stl_brds[stl_brdstats.brd]; + if (brdp == (stlbrd_t *) NULL) + return(-ENODEV); + + memset(&stl_brdstats, 0, sizeof(combrd_t)); + stl_brdstats.brd = brdp->brdnr; + stl_brdstats.type = brdp->brdtype; + stl_brdstats.hwid = brdp->hwid; + stl_brdstats.state = brdp->state; + stl_brdstats.ioaddr = brdp->ioaddr1; + stl_brdstats.ioaddr2 = brdp->ioaddr2; + stl_brdstats.irq = brdp->irq; + stl_brdstats.nrpanels = brdp->nrpanels; + stl_brdstats.nrports = brdp->nrports; + for (i = 0; (i < brdp->nrpanels); i++) { + panelp = brdp->panels[i]; + stl_brdstats.panels[i].panel = i; + stl_brdstats.panels[i].hwid = panelp->hwid; + stl_brdstats.panels[i].nrports = panelp->nrports; + } + + memcpy_tofs(bp, &stl_brdstats, sizeof(combrd_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Resolve the referenced port number into a port struct pointer. + */ + +static stlport_t *stl_getport(int brdnr, int panelnr, int portnr) +{ + stlbrd_t *brdp; + stlpanel_t *panelp; + + if ((brdnr < 0) || (brdnr >= STL_MAXBRDS)) + return((stlport_t *) NULL); + brdp = stl_brds[brdnr]; + if (brdp == (stlbrd_t *) NULL) + return((stlport_t *) NULL); + if ((panelnr < 0) || (panelnr >= brdp->nrpanels)) + return((stlport_t *) NULL); + panelp = brdp->panels[panelnr]; + if (panelp == (stlpanel_t *) NULL) + return((stlport_t *) NULL); + if ((portnr < 0) || (portnr >= panelp->nrports)) + return((stlport_t *) NULL); + return(panelp->ports[portnr]); +} + +/*****************************************************************************/ + +/* + * Return the port stats structure to user app. A NULL port struct + * pointer passed in means that we need to find out from the app + * what port to get stats for (used through board control device). + */ + +static int stl_getportstats(stlport_t *portp, comstats_t *cp) +{ + unsigned char *head, *tail; + unsigned long flags; + + if (portp == (stlport_t *) NULL) { + memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t)); + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); + if (portp == (stlport_t *) NULL) + return(-ENODEV); + } + + portp->stats.state = portp->istate; + portp->stats.flags = portp->flags; + portp->stats.hwid = portp->hwid; + + portp->stats.ttystate = 0; + portp->stats.cflags = 0; + portp->stats.iflags = 0; + portp->stats.oflags = 0; + portp->stats.lflags = 0; + portp->stats.rxbuffered = 0; + + save_flags(flags); + cli(); + if (portp->tty != (struct tty_struct *) NULL) { + if (portp->tty->driver_data == portp) { + portp->stats.ttystate = portp->tty->flags; + portp->stats.rxbuffered = portp->tty->flip.count; + if (portp->tty->termios != (struct termios *) NULL) { + portp->stats.cflags = portp->tty->termios->c_cflag; + portp->stats.iflags = portp->tty->termios->c_iflag; + portp->stats.oflags = portp->tty->termios->c_oflag; + portp->stats.lflags = portp->tty->termios->c_lflag; + } + } + } + restore_flags(flags); + + head = portp->tx.head; + tail = portp->tx.tail; + portp->stats.txbuffered = ((head >= tail) ? (head - tail) : + (STL_TXBUFSIZE - (tail - head))); + + portp->stats.signals = (unsigned long) stl_getsignals(portp); + + memcpy_tofs(cp, &portp->stats, sizeof(comstats_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Clear the port stats structure. We also return it zeroed out... + */ + +static int stl_clrportstats(stlport_t *portp, comstats_t *cp) +{ + if (portp == (stlport_t *) NULL) { + memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t)); + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); + if (portp == (stlport_t *) NULL) + return(-ENODEV); + } + + memset(&portp->stats, 0, sizeof(comstats_t)); + portp->stats.brd = portp->brdnr; + portp->stats.panel = portp->panelnr; + portp->stats.port = portp->portnr; + memcpy_tofs(cp, &portp->stats, sizeof(comstats_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Return the entire driver ports structure to a user app. + */ + +static int stl_getportstruct(unsigned long arg) +{ + stlport_t *portp; + + memcpy_fromfs(&stl_dummyport, (void *) arg, sizeof(stlport_t)); + portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr, + stl_dummyport.portnr); + if (portp == (stlport_t *) NULL) + return(-ENODEV); + memcpy_tofs((void *) arg, portp, sizeof(stlport_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Return the entire driver board structure to a user app. + */ + +static int stl_getbrdstruct(unsigned long arg) +{ + stlbrd_t *brdp; + + memcpy_fromfs(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t)); + if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS)) + return(-ENODEV); + brdp = stl_brds[stl_dummybrd.brdnr]; + if (brdp == (stlbrd_t *) NULL) + return(-ENODEV); + memcpy_tofs((void *) arg, brdp, sizeof(stlbrd_t)); + return(0); +} + +/*****************************************************************************/ + +/* + * Memory device open code. Need to keep track of opens and close + * for module handling. + */ + +static int stl_memopen(struct inode *ip, struct file *fp) +{ + MOD_INC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +static void stl_memclose(struct inode *ip, struct file *fp) +{ + MOD_DEC_USE_COUNT; +} + +/*****************************************************************************/ + +/* + * The "staliomem" device is also required to do some special operations + * on the board and/or ports. In this driver it is mostly used for stats + * collection. + */ + +static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) +{ + int brdnr, rc; + +#if DEBUG + printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, + (int) fp, cmd, (int) arg); +#endif + + brdnr = MINOR(ip->i_rdev); + if (brdnr >= STL_MAXBRDS) + return(-ENODEV); + rc = 0; + + switch (cmd) { + case COM_GETPORTSTATS: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stl_getportstats((stlport_t *) NULL, + (comstats_t *) arg); + break; + case COM_CLRPORTSTATS: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stl_clrportstats((stlport_t *) NULL, + (comstats_t *) arg); + break; + case COM_GETBRDSTATS: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(combrd_t))) == 0) + rc = stl_getbrdstats((combrd_t *) arg); + break; + case COM_READPORT: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlport_t))) == 0) + rc = stl_getportstruct(arg); + break; + case COM_READBOARD: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlbrd_t))) == 0) + rc = stl_getbrdstruct(arg); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + + return(rc); +} + +/*****************************************************************************/ + +int stl_init(void) +{ + printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion); + + stl_initbrds(); + +/* + * Allocate a temporary write buffer. + */ + stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE); + if (stl_tmpwritebuf == (char *) NULL) + printk("STALLION: failed to allocate memory (size=%d)\n", + STL_TXBUFSIZE); + +/* + * Set up a character driver for per board stuff. This is mainly used + * to do stats ioctls on the ports. + */ + if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) + printk("STALLION: failed to register serial board device\n"); + +/* + * Set up the tty driver structure and register us as a driver. + * Also setup the callout tty device. + */ + memset(&stl_serial, 0, sizeof(struct tty_driver)); + stl_serial.magic = TTY_DRIVER_MAGIC; + stl_serial.name = stl_serialname; + stl_serial.major = STL_SERIALMAJOR; + stl_serial.minor_start = 0; + stl_serial.num = STL_MAXBRDS * STL_MAXPORTS; + stl_serial.type = TTY_DRIVER_TYPE_SERIAL; + stl_serial.subtype = STL_DRVTYPSERIAL; + stl_serial.init_termios = stl_deftermios; + stl_serial.flags = TTY_DRIVER_REAL_RAW; + stl_serial.refcount = &stl_refcount; + stl_serial.table = stl_ttys; + stl_serial.termios = stl_termios; + stl_serial.termios_locked = stl_termioslocked; + + stl_serial.open = stl_open; + stl_serial.close = stl_close; + stl_serial.write = stl_write; + stl_serial.put_char = stl_putchar; + stl_serial.flush_chars = stl_flushchars; + stl_serial.write_room = stl_writeroom; + stl_serial.chars_in_buffer = stl_charsinbuffer; + stl_serial.ioctl = stl_ioctl; + stl_serial.set_termios = stl_settermios; + stl_serial.throttle = stl_throttle; + stl_serial.unthrottle = stl_unthrottle; + stl_serial.stop = stl_stop; + stl_serial.start = stl_start; + stl_serial.hangup = stl_hangup; + stl_serial.flush_buffer = stl_flushbuffer; + + stl_callout = stl_serial; + stl_callout.name = stl_calloutname; + stl_callout.major = STL_CALLOUTMAJOR; + stl_callout.subtype = STL_DRVTYPCALLOUT; + + if (tty_register_driver(&stl_serial)) + printk("STALLION: failed to register serial driver\n"); + if (tty_register_driver(&stl_callout)) + printk("STALLION: failed to register callout driver\n"); + + return(0); +} + +/*****************************************************************************/ +/* CD1400 HARDWARE FUNCTIONS */ +/*****************************************************************************/ + +/* + * These functions get/set/update the registers of the cd1400 UARTs. + * Access to the cd1400 registers is via an address/data io port pair. + * (Maybe should make this inline...) + */ + +static int stl_cd1400getreg(stlport_t *portp, int regnr) +{ + outb((regnr + portp->uartaddr), portp->ioaddr); + return(inb(portp->ioaddr + EREG_DATA)); +} + +static void stl_cd1400setreg(stlport_t *portp, int regnr, int value) +{ + outb((regnr + portp->uartaddr), portp->ioaddr); + outb(value, portp->ioaddr + EREG_DATA); +} + +static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value) +{ + outb((regnr + portp->uartaddr), portp->ioaddr); + if (inb(portp->ioaddr + EREG_DATA) != value) { + outb(value, portp->ioaddr + EREG_DATA); + return(1); + } + return(0); +} + +/*****************************************************************************/ + +/* + * Inbitialize the UARTs in a panel. We don't care what sort of board + * these ports are on - since the port io registers are almost + * identical when dealing with ports. + */ + +static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp) +{ + unsigned int gfrcr; + int chipmask, i, j; + int nrchips, uartaddr, ioaddr; + +#if DEBUG + printk("stl_panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp); +#endif + + BRDENABLE(panelp->brdnr, panelp->pagenr); + +/* + * Check that each chip is present and started up OK. + */ + chipmask = 0; + nrchips = panelp->nrports / CD1400_PORTS; + for (i = 0; (i < nrchips); i++) { + if (brdp->brdtype == BRD_ECHPCI) { + outb((panelp->pagenr + (i >> 1)), brdp->ioctrl); + ioaddr = panelp->iobase; + } else { + ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1)); + } + uartaddr = (i & 0x01) ? 0x080 : 0; + outb((GFRCR + uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); + outb((CCR + uartaddr), ioaddr); + outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); + outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); + outb((GFRCR + uartaddr), ioaddr); + for (j = 0; (j < CCR_MAXWAIT); j++) { + if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0) + break; + } + if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) { + printk("STALLION: cd1400 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); + continue; + } + chipmask |= (0x1 << i); + outb((PPR + uartaddr), ioaddr); + outb(PPR_SCALAR, (ioaddr + EREG_DATA)); + } + + BRDDISABLE(panelp->brdnr); + return(chipmask); +} + +/*****************************************************************************/ + +/* + * Initialize hardware specific port registers. + */ + +static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp) +{ +#if DEBUG + printk("stl_cd1400portinit(brdp=%x,panelp=%x,portp=%x)\n", + (int) brdp, (int) panelp, (int) portp); +#endif + + if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || + (portp == (stlport_t *) NULL)) + return; + + portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) || + (portp->portnr < 8)) ? 0 : EREG_BANKSIZE); + portp->uartaddr = (portp->portnr & 0x04) << 5; + portp->pagenr = panelp->pagenr + (portp->portnr >> 3); + + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, LIVR, (portp->portnr << 3)); + portp->hwid = stl_cd1400getreg(portp, GFRCR); + BRDDISABLE(portp->brdnr); +} + +/*****************************************************************************/ + +/* + * Wait for the command register to be ready. We will poll this, + * since it won't usually take too long to be ready. + */ + +static void stl_cd1400ccrwait(stlport_t *portp) +{ + int i; + + for (i = 0; (i < CCR_MAXWAIT); i++) { + if (stl_cd1400getreg(portp, CCR) == 0) { + return; + } + } + + printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); +} + +/*****************************************************************************/ + +/* + * Set up the cd1400 registers for a port based on the termios port + * settings. + */ + +static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp) +{ + stlbrd_t *brdp; + unsigned long flags; + unsigned int clkdiv, baudrate; + unsigned char cor1, cor2, cor3; + unsigned char cor4, cor5, ccr; + unsigned char srer, sreron, sreroff; + unsigned char mcor1, mcor2, rtpr; + unsigned char clk, div; + + cor1 = 0; + cor2 = 0; + cor3 = 0; + cor4 = 0; + cor5 = 0; + ccr = 0; + rtpr = 0; + clk = 0; + div = 0; + mcor1 = 0; + mcor2 = 0; + sreron = 0; + sreroff = 0; + + brdp = stl_brds[portp->brdnr]; + if (brdp == (stlbrd_t *) NULL) + return; + +/* + * Set up the RX char ignore mask with those RX error types we + * can ignore. We can get the cd1400 to help us out a little here, + * it will ignore parity errors and breaks for us. + */ + portp->rxignoremsk = 0; + if (tiosp->c_iflag & IGNPAR) { + portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN); + cor1 |= COR1_PARIGNORE; + } + if (tiosp->c_iflag & IGNBRK) { + portp->rxignoremsk |= ST_BREAK; + cor4 |= COR4_IGNBRK; + } + + portp->rxmarkmsk = ST_OVERRUN; + if (tiosp->c_iflag & (INPCK | PARMRK)) + portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING); + if (tiosp->c_iflag & BRKINT) + portp->rxmarkmsk |= ST_BREAK; + +/* + * Go through the char size, parity and stop bits and set all the + * option register appropriately. + */ + switch (tiosp->c_cflag & CSIZE) { + case CS5: + cor1 |= COR1_CHL5; + break; + case CS6: + cor1 |= COR1_CHL6; break; case CS7: cor1 |= COR1_CHL7; @@ -2064,8 +3163,8 @@ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (portp->baud_base / portp->custom_divisor); } - if (baudrate > STL_MAXBAUD) - baudrate = STL_MAXBAUD; + if (baudrate > STL_CD1400MAXBAUD) + baudrate = STL_CD1400MAXBAUD; if (baudrate > 0) { for (clk = 0; (clk < CD1400_NUMCLKS); clk++) { @@ -2101,165 +3200,396 @@ cor2 |= COR2_IXM; } - if (tiosp->c_cflag & CRTSCTS) { - cor2 |= COR2_CTSAE; - mcor1 |= FIFO_RTSTHRESHOLD; - } + if (tiosp->c_cflag & CRTSCTS) { + cor2 |= COR2_CTSAE; + mcor1 |= FIFO_RTSTHRESHOLD; + } + +/* + * All cd1400 register values calculated so go through and set + * them all up. + */ + +#if DEBUG + printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); + printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", + cor1, cor2, cor3, cor4, cor5); + printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", + mcor1, mcor2, rtpr, sreron, sreroff); + printk(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); + printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); +#endif + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3)); + srer = stl_cd1400getreg(portp, SRER); + stl_cd1400setreg(portp, SRER, 0); + if (stl_cd1400updatereg(portp, COR1, cor1)) + ccr = 1; + if (stl_cd1400updatereg(portp, COR2, cor2)) + ccr = 1; + if (stl_cd1400updatereg(portp, COR3, cor3)) + ccr = 1; + if (ccr) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_CORCHANGE); + } + stl_cd1400setreg(portp, COR4, cor4); + stl_cd1400setreg(portp, COR5, cor5); + stl_cd1400setreg(portp, MCOR1, mcor1); + stl_cd1400setreg(portp, MCOR2, mcor2); + if (baudrate > 0) { + stl_cd1400setreg(portp, TCOR, clk); + stl_cd1400setreg(portp, TBPR, div); + stl_cd1400setreg(portp, RCOR, clk); + stl_cd1400setreg(portp, RBPR, div); + } + stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]); + stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]); + stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]); + stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]); + stl_cd1400setreg(portp, RTPR, rtpr); + mcor1 = stl_cd1400getreg(portp, MSVR1); + if (mcor1 & MSVR1_DCD) + portp->sigs |= TIOCM_CD; + else + portp->sigs &= ~TIOCM_CD; + stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron)); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + +/* + * Set the state of the DTR and RTS signals. + */ + +static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts) +{ + unsigned char msvr1, msvr2; + unsigned long flags; + +#if DEBUG + printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", + (int) portp, dtr, rts); +#endif + + msvr1 = 0; + msvr2 = 0; + if (dtr > 0) + msvr1 = MSVR1_DTR; + if (rts > 0) + msvr2 = MSVR2_RTS; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + if (rts >= 0) + stl_cd1400setreg(portp, MSVR2, msvr2); + if (dtr >= 0) + stl_cd1400setreg(portp, MSVR1, msvr1); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + +/* + * Return the state of the signals. + */ + +static int stl_cd1400getsignals(stlport_t *portp) +{ + unsigned char msvr1, msvr2; + unsigned long flags; + int sigs; + +#if DEBUG + printk("stl_cd1400getsignals(portp=%x)\n", (int) portp); +#endif + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + msvr1 = stl_cd1400getreg(portp, MSVR1); + msvr2 = stl_cd1400getreg(portp, MSVR2); + BRDDISABLE(portp->brdnr); + restore_flags(flags); + + sigs = 0; + sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; + sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; + sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; + sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; +#if 0 + sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; + sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; +#else + sigs |= TIOCM_DSR; +#endif + return(sigs); +} + +/*****************************************************************************/ + +/* + * Enable/Disable the Transmitter and/or Receiver. + */ + +static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx) +{ + unsigned char ccr; + unsigned long flags; + +#if DEBUG + printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); +#endif + ccr = 0; + + if (tx == 0) + ccr |= CCR_TXDISABLE; + else if (tx > 0) + ccr |= CCR_TXENABLE; + if (rx == 0) + ccr |= CCR_RXDISABLE; + else if (rx > 0) + ccr |= CCR_RXENABLE; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, ccr); + stl_cd1400ccrwait(portp); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ /* - * All register cd1400 register values calculated so go through and set - * them all up. + * Start/stop the Transmitter and/or Receiver. */ +static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx) +{ + unsigned char sreron, sreroff; + unsigned long flags; + #if DEBUG - printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr); - printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5); - printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff); - printk(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); - printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); + printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif + sreron = 0; + sreroff = 0; + if (tx == 0) + sreroff |= (SRER_TXDATA | SRER_TXEMPTY); + else if (tx == 1) + sreron |= SRER_TXDATA; + else if (tx >= 2) + sreron |= SRER_TXEMPTY; + if (rx == 0) + sreroff |= SRER_RXDATA; + else if (rx > 0) + sreron |= SRER_RXDATA; + save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x3)); - srer = stl_getreg(portp, SRER); - stl_setreg(portp, SRER, 0); - if (stl_updatereg(portp, COR1, cor1)) - ccr = 1; - if (stl_updatereg(portp, COR2, cor2)) - ccr = 1; - if (stl_updatereg(portp, COR3, cor3)) - ccr = 1; - if (ccr) { - stl_ccrwait(portp); - stl_setreg(portp, CCR, CCR_CORCHANGE); - } - stl_setreg(portp, COR4, cor4); - stl_setreg(portp, COR5, cor5); - stl_setreg(portp, MCOR1, mcor1); - stl_setreg(portp, MCOR2, mcor2); - if (baudrate > 0) { - stl_setreg(portp, TCOR, clk); - stl_setreg(portp, TBPR, div); - stl_setreg(portp, RCOR, clk); - stl_setreg(portp, RBPR, div); - } - stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]); - stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]); - stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]); - stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]); - stl_setreg(portp, RTPR, rtpr); - mcor1 = stl_getreg(portp, MSVR1); - if (mcor1 & MSVR1_DCD) - portp->sigs |= TIOCM_CD; - else - portp->sigs &= ~TIOCM_CD; - stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron)); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron)); BRDDISABLE(portp->brdnr); + if (tx > 0) + set_bit(ASYI_TXBUSY, &portp->istate); restore_flags(flags); } /*****************************************************************************/ /* - * Set the state of the DTR and RTS signals. + * Disable all interrupts from this port. */ -static void stl_setsignals(stlport_t *portp, int dtr, int rts) +static void stl_cd1400disableintrs(stlport_t *portp) { - unsigned char msvr1, msvr2; unsigned long flags; #if DEBUG - printk("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts); + printk("stl_cd1400disableintrs(portp=%x)\n", (int) portp); #endif + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, SRER, 0); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} - msvr1 = 0; - msvr2 = 0; - if (dtr > 0) - msvr1 = MSVR1_DTR; - if (rts > 0) - msvr2 = MSVR2_RTS; +/*****************************************************************************/ + +static void stl_cd1400sendbreak(stlport_t *portp, long len) +{ + unsigned long flags; + +#if DEBUG + printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", + (int) portp, (int) len); +#endif save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - if (rts >= 0) - stl_setreg(portp, MSVR2, msvr2); - if (dtr >= 0) - stl_setreg(portp, MSVR1, msvr1); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400setreg(portp, COR2, + (stl_cd1400getreg(portp, COR2) | COR2_ETC)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY)); BRDDISABLE(portp->brdnr); + len = len / 5; + portp->brklen = (len > 255) ? 255 : len; + portp->stats.txbreaks++; restore_flags(flags); } /*****************************************************************************/ /* - * Return the state of the signals. + * Take flow control actions... */ -static int stl_getsignals(stlport_t *portp) +static void stl_cd1400flowctrl(stlport_t *portp, int state) { - unsigned char msvr1, msvr2; - unsigned long flags; - int sigs; + struct tty_struct *tty; + unsigned long flags; #if DEBUG - printk("stl_getsignals(portp=%x)\n", (int) portp); + printk("stl_cd1400flowctrl(portp=%x,state=%x)\n", (int) portp, state); #endif + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; + save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - msvr1 = stl_getreg(portp, MSVR1); - msvr2 = stl_getreg(portp, MSVR2); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + + if (state) { + if (tty->termios->c_iflag & IXOFF) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); + portp->stats.rxxon++; + stl_cd1400ccrwait(portp); + } +/* + * Question: should we return RTS to what it was before? It may + * have been set by an ioctl... Suppose not, since if you have + * hardware flow control set then it is pretty silly to go and + * set the RTS line by hand. + */ + if (tty->termios->c_cflag & CRTSCTS) { + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) | + FIFO_RTSTHRESHOLD)); + stl_cd1400setreg(portp, MSVR2, MSVR2_RTS); + portp->stats.rxrtson++; + } + } else { + if (tty->termios->c_iflag & IXOFF) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); + portp->stats.rxxoff++; + stl_cd1400ccrwait(portp); + } + if (tty->termios->c_cflag & CRTSCTS) { + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) & 0xf0)); + stl_cd1400setreg(portp, MSVR2, 0); + portp->stats.rxrtsoff++; + } + } + BRDDISABLE(portp->brdnr); - sigs = 0; - sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; - sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; - sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; - sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; - sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; - sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; restore_flags(flags); - return(sigs); } /*****************************************************************************/ /* - * Enable/Disable the Transmitter and/or Receiver. + * Send a flow control character... */ -static void stl_enablerxtx(stlport_t *portp, int rx, int tx) +static void stl_cd1400sendflow(stlport_t *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + +#if DEBUG + printk("stl_cd1400sendflow(portp=%x,state=%x)\n", (int) portp, state); +#endif + + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + if (state) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); + portp->stats.rxxon++; + stl_cd1400ccrwait(portp); + } else { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); + portp->stats.rxxoff++; + stl_cd1400ccrwait(portp); + } + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + +static void stl_cd1400flush(stlport_t *portp) { - unsigned char ccr; unsigned long flags; #if DEBUG - printk("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); + printk("stl_cd1400flush(portp=%x)\n", (int) portp); #endif - ccr = 0; - if (tx == 0) - ccr |= CCR_TXDISABLE; - else if (tx > 0) - ccr |= CCR_TXENABLE; - if (rx == 0) - ccr |= CCR_RXDISABLE; - else if (rx > 0) - ccr |= CCR_RXENABLE; + if (portp == (stlport_t *) NULL) + return; save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_ccrwait(portp); - stl_setreg(portp, CCR, ccr); - stl_ccrwait(portp); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO); + stl_cd1400ccrwait(portp); + portp->tx.tail = portp->tx.head; BRDDISABLE(portp->brdnr); restore_flags(flags); } @@ -2267,137 +3597,400 @@ /*****************************************************************************/ /* - * Start/stop the Transmitter and/or Receiver. + * Return the current state of data flow on this port. This is only + * really interresting when determining if data has fully completed + * transmission or not... This is easy for the cd1400, it accurately + * maintains the busy port flag. + */ + +static int stl_cd1400datastate(stlport_t *portp) +{ +#if DEBUG + printk("stl_cd1400datastate(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return(0); + + return(test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0); +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for cd1400 EasyIO boards. + */ + +static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase) +{ + unsigned char svrtype; + +#if DEBUG + printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n", + (int) panelp, iobase); +#endif + + outb(SVRR, iobase); + svrtype = inb(iobase + EREG_DATA); + if (panelp->nrports > 4) { + outb((SVRR + 0x80), iobase); + svrtype |= inb(iobase + EREG_DATA); + } + + if (svrtype & SVRR_RX) + stl_cd1400rxisr(panelp, iobase); + else if (svrtype & SVRR_TX) + stl_cd1400txisr(panelp, iobase); + else if (svrtype & SVRR_MDM) + stl_cd1400mdmisr(panelp, iobase); +} + +/*****************************************************************************/ + +/* + * Interrupt service routine for cd1400 panels. + */ + +static void stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase) +{ + unsigned char svrtype; + +#if DEBUG + printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp, + iobase); +#endif + + outb(SVRR, iobase); + svrtype = inb(iobase + EREG_DATA); + outb((SVRR + 0x80), iobase); + svrtype |= inb(iobase + EREG_DATA); + if (svrtype & SVRR_RX) + stl_cd1400rxisr(panelp, iobase); + else if (svrtype & SVRR_TX) + stl_cd1400txisr(panelp, iobase); + else if (svrtype & SVRR_MDM) + stl_cd1400mdmisr(panelp, iobase); +} + +/*****************************************************************************/ + +/* + * Transmit interrupt handler. This has gotta be fast! Handling TX + * chars is pretty simple, stuff as many as possible from the TX buffer + * into the cd1400 FIFO. Must also handle TX breaks here, since they + * are embedded as commands in the data stream. Oh no, had to use a goto! + * This could be optimized more, will do when I get time... + * In practice it is possible that interrupts are enabled but that the + * port has been hung up. Need to handle not having any TX buffer here, + * this is done by using the side effect that head and tail will also + * be NULL if the buffer has been freed. + */ + +static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr) +{ + stlport_t *portp; + int len, stlen; + char *head, *tail; + unsigned char ioack, srer; + +#if DEBUG + printk("stl_cd1400txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); +#endif + + ioack = inb(ioaddr + EREG_TXACK); + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { + printk("STALLION: bad TX interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + +/* + * Unfortunately we need to handle breaks in the data stream, since + * this is the only way to generate them on the cd1400. Do it now if + * a break is to be sent. + */ + if (portp->brklen != 0) { + if (portp->brklen > 0) { + outb((TDR + portp->uartaddr), ioaddr); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_DELAY, (ioaddr + EREG_DATA)); + outb(portp->brklen, (ioaddr + EREG_DATA)); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); + portp->brklen = -1; + goto stl_txalldone; + } else { + outb((COR2 + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), + (ioaddr + EREG_DATA)); + portp->brklen = 0; + } + } + + head = portp->tx.head; + tail = portp->tx.tail; + len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { + set_bit(ASYI_TXLOW, &portp->istate); + queue_task_irq_off(&portp->tqueue, &tq_scheduler); + } + + if (len == 0) { + outb((SRER + portp->uartaddr), ioaddr); + srer = inb(ioaddr + EREG_DATA); + if (srer & SRER_TXDATA) { + srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY; + } else { + srer &= ~(SRER_TXDATA | SRER_TXEMPTY); + clear_bit(ASYI_TXBUSY, &portp->istate); + } + outb(srer, (ioaddr + EREG_DATA)); + } else { + len = MIN(len, CD1400_TXFIFOSIZE); + portp->stats.txtotal += len; + stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail)); + outb((TDR + portp->uartaddr), ioaddr); + outsb((ioaddr + EREG_DATA), tail, stlen); + len -= stlen; + tail += stlen; + if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) + tail = portp->tx.buf; + if (len > 0) { + outsb((ioaddr + EREG_DATA), tail, len); + tail += len; + } + portp->tx.tail = tail; + } + +stl_txalldone: + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); +} + +/*****************************************************************************/ + +/* + * Receive character interrupt handler. Determine if we have good chars + * or bad chars and then process appropriately. Good chars are easy + * just shove the lot into the RX buffer and set all status byte to 0. + * If a bad RX char then process as required. This routine needs to be + * fast! In practice it is possible that we get an interrupt on a port + * that is closed. This can happen on hangups - since they completely + * shutdown a port not in user context. Need to handle this case. */ -static void stl_startrxtx(stlport_t *portp, int rx, int tx) +static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr) { - unsigned char sreron, sreroff; - unsigned long flags; + stlport_t *portp; + struct tty_struct *tty; + unsigned int ioack, len, buflen; + unsigned char status; + char ch; #if DEBUG - printk("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); + printk("stl_cd1400rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); #endif - sreron = 0; - sreroff = 0; - if (tx == 0) - sreroff |= (SRER_TXDATA | SRER_TXEMPTY); - else if (tx == 1) - sreron |= SRER_TXDATA; - else if (tx >= 2) - sreron |= SRER_TXEMPTY; - if (rx == 0) - sreroff |= SRER_RXDATA; - else if (rx > 0) - sreron |= SRER_RXDATA; + ioack = inb(ioaddr + EREG_RXACK); + if ((ioack & panelp->ackmask) != 0) { + printk("STALLION: bad RX interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + tty = portp->tty; - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~sreroff) | sreron)); - BRDDISABLE(portp->brdnr); - if (tx > 0) - set_bit(ASYI_TXBUSY, &portp->istate); - restore_flags(flags); + if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { + outb((RDCR + portp->uartaddr), ioaddr); + len = inb(ioaddr + EREG_DATA); + if ((tty == (struct tty_struct *) NULL) || + (tty->flip.char_buf_ptr == (char *) NULL) || + ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + outb((RDSR + portp->uartaddr), ioaddr); + insb((ioaddr + EREG_DATA), &stl_unwanted[0], len); + portp->stats.rxlost += len; + portp->stats.rxtotal += len; + } else { + len = MIN(len, buflen); + if (len > 0) { + outb((RDSR + portp->uartaddr), ioaddr); + insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len); + memset(tty->flip.flag_buf_ptr, 0, len); + tty->flip.flag_buf_ptr += len; + tty->flip.char_buf_ptr += len; + tty->flip.count += len; + tty_schedule_flip(tty); + portp->stats.rxtotal += len; + } + } + } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) { + outb((RDSR + portp->uartaddr), ioaddr); + status = inb(ioaddr + EREG_DATA); + ch = inb(ioaddr + EREG_DATA); + if (status & ST_PARITY) + portp->stats.rxparity++; + if (status & ST_FRAMING) + portp->stats.rxframing++; + if (status & ST_OVERRUN) + portp->stats.rxoverrun++; + if (status & ST_BREAK) + portp->stats.rxbreaks++; + if (status & ST_SCHARMASK) { + if ((status & ST_SCHARMASK) == ST_SCHAR1) + portp->stats.txxon++; + if ((status & ST_SCHARMASK) == ST_SCHAR2) + portp->stats.txxoff++; + goto stl_rxalldone; + } + if ((tty != (struct tty_struct *) NULL) && + ((portp->rxignoremsk & status) == 0)) { + if (portp->rxmarkmsk & status) { + if (status & ST_BREAK) { + status = TTY_BREAK; + if (portp->flags & ASYNC_SAK) { + do_SAK(tty); + BRDENABLE(portp->brdnr, portp->pagenr); + } + } else if (status & ST_PARITY) { + status = TTY_PARITY; + } else if (status & ST_FRAMING) { + status = TTY_FRAME; + } else if(status & ST_OVERRUN) { + status = TTY_OVERRUN; + } else { + status = 0; + } + } else { + status = 0; + } + if (tty->flip.char_buf_ptr != (char *) NULL) { + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.flag_buf_ptr++ = status; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + } + tty_schedule_flip(tty); + } + } + } else { + printk("STALLION: bad RX interrupt ack value=%x\n", ioack); + return; + } + +stl_rxalldone: + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); } /*****************************************************************************/ /* - * Disable all interrupts from this port. + * Modem interrupt handler. The is called when the modem signal line + * (DCD) has changed state. Leave most of the work to the off-level + * processing routine. */ -static void stl_disableintrs(stlport_t *portp) +static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr) { - unsigned long flags; + stlport_t *portp; + unsigned int ioack; + unsigned char misr; #if DEBUG - printk("stl_disableintrs(portp=%x)\n", (int) portp); + printk("stl_cd1400mdmisr(panelp=%x)\n", (int) panelp); #endif - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_setreg(portp, SRER, 0); - BRDDISABLE(portp->brdnr); - restore_flags(flags); + + ioack = inb(ioaddr + EREG_MDACK); + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { + printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack); + return; + } + portp = panelp->ports[(ioack >> 3)]; + + outb((MISR + portp->uartaddr), ioaddr); + misr = inb(ioaddr + EREG_DATA); + if (misr & MISR_DCD) { + set_bit(ASYI_DCDCHANGE, &portp->istate); + queue_task_irq_off(&portp->tqueue, &tq_scheduler); + portp->stats.modem++; + } + + outb((EOSRR + portp->uartaddr), ioaddr); + outb(0, (ioaddr + EREG_DATA)); } /*****************************************************************************/ +/* SC26198 HARDWARE FUNCTIONS */ +/*****************************************************************************/ -static void stl_sendbreak(stlport_t *portp, long len) +/* + * These functions get/set/update the registers of the sc26198 UARTs. + * Access to the sc26198 registers is via an address/data io port pair. + * (Maybe should make this inline...) + */ + +static int stl_sc26198getreg(stlport_t *portp, int regnr) { - unsigned long flags; + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + return(inb(portp->ioaddr + XP_DATA)); +} -#if DEBUG - printk("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len); -#endif +static void stl_sc26198setreg(stlport_t *portp, int regnr, int value) +{ + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + outb(value, (portp->ioaddr + XP_DATA)); +} - save_flags(flags); - cli(); - BRDENABLE(portp->brdnr, portp->pagenr); - stl_setreg(portp, CAR, (portp->portnr & 0x03)); - stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC)); - stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY)); - BRDDISABLE(portp->brdnr); - len = len / 5; - portp->brklen = (len > 255) ? 255 : len; - portp->stats.txbreaks++; - restore_flags(flags); +static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value) +{ + outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR)); + if (inb(portp->ioaddr + XP_DATA) != value) { + outb(value, (portp->ioaddr + XP_DATA)); + return(1); + } + return(0); } /*****************************************************************************/ /* - * Map in interrupt vector to this driver. Check that we don't - * already have this vector mapped, we might be sharing this - * interrupt across multiple boards. + * Functions to get and set the sc26198 global registers. */ -static int stl_mapirq(int irq) +static int stl_sc26198getglobreg(stlport_t *portp, int regnr) { - int rc, i; - -#if DEBUG - printk("stl_mapirq(irq=%d)\n", irq); -#endif + outb(regnr, (portp->ioaddr + XP_ADDR)); + return(inb(portp->ioaddr + XP_DATA)); +} - rc = 0; - for (i = 0; (i < stl_numintrs); i++) { - if (stl_gotintrs[i] == irq) - break; - } - if (i >= stl_numintrs) { - if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) { - printk("STALLION: failed to register interrupt routine for irq=%d\n", irq); - rc = -ENODEV; - } else { - stl_gotintrs[stl_numintrs++] = irq; - } - } - return(rc); +#if 0 +static void stl_sc26198setglobreg(stlport_t *portp, int regnr, int value) +{ + outb(regnr, (portp->ioaddr + XP_ADDR)); + outb(value, (portp->ioaddr + XP_DATA)); } +#endif /*****************************************************************************/ /* - * Try to find and initialize all the ports on a panel. We don't care - * what sort of board these ports are on - since the port io registers - * are almost identical when dealing with ports. + * Inbitialize the UARTs in a panel. We don't care what sort of board + * these ports are on - since the port io registers are almost + * identical when dealing with ports. */ -static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) +static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp) { - stlport_t *portp; - unsigned int chipmask; - unsigned int gfrcr; - int nrchips, uartaddr, ioaddr; - int i, j; + int chipmask, i; + int nrchips, ioaddr; #if DEBUG - printk("stl_initports(panelp=%x)\n", (int) panelp); + printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", + (int) brdp, (int) panelp); #endif BRDENABLE(panelp->brdnr, panelp->pagenr); @@ -2406,773 +3999,959 @@ * Check that each chip is present and started up OK. */ chipmask = 0; - nrchips = panelp->nrports / CD1400_PORTS; - for (i = 0; (i < nrchips); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb((panelp->pagenr + (i >> 1)), brdp->ioctrl); - ioaddr = panelp->iobase; - } else { - ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1)); - } - uartaddr = (i & 0x01) ? 0x080 : 0; - outb((GFRCR + uartaddr), ioaddr); - outb(0, (ioaddr + EREG_DATA)); - outb((CCR + uartaddr), ioaddr); - outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); - outb(CCR_RESETFULL, (ioaddr + EREG_DATA)); - outb((GFRCR + uartaddr), ioaddr); - for (j = 0; (j < CCR_MAXWAIT); j++) { - if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0) - break; - } - if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) { - printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i); - continue; - } - chipmask |= (0x1 << i); - outb((PPR + uartaddr), ioaddr); - outb(PPR_SCALAR, (ioaddr + EREG_DATA)); - } + nrchips = (panelp->nrports + 4) / SC26198_PORTS; + if (brdp->brdtype == BRD_ECHPCI) + outb(panelp->pagenr, brdp->ioctrl); -/* - * All cd1400's are initialized (if found!). Now go through and setup - * each ports data structures. Also init the LIVR register of cd1400 - * for each port. - */ - ioaddr = panelp->iobase; - for (i = 0; (i < panelp->nrports); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb((panelp->pagenr + (i >> 3)), brdp->ioctrl); - ioaddr = panelp->iobase; - } else { - ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3)); - } - if ((chipmask & (0x1 << (i / 4))) == 0) - continue; - portp = (stlport_t *) stl_memalloc(sizeof(stlport_t)); - if (portp == (stlport_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t)); - break; - } - memset(portp, 0, sizeof(stlport_t)); - portp->magic = STL_PORTMAGIC; - portp->portnr = i; - portp->brdnr = panelp->brdnr; - portp->panelnr = panelp->panelnr; - portp->ioaddr = ioaddr; - portp->uartaddr = (i & 0x04) << 5; - portp->pagenr = panelp->pagenr + (i >> 3); - portp->clk = brdp->clk; - portp->baud_base = STL_BAUDBASE; - portp->close_delay = STL_CLOSEDELAY; - portp->closing_wait = 30 * HZ; - portp->normaltermios = stl_deftermios; - portp->callouttermios = stl_deftermios; - portp->tqueue.routine = stl_offintr; - portp->tqueue.data = portp; - portp->stats.brd = portp->brdnr; - portp->stats.panel = portp->panelnr; - portp->stats.port = portp->portnr; - stl_setreg(portp, CAR, (i & 0x03)); - stl_setreg(portp, LIVR, (i << 3)); - portp->hwid = stl_getreg(portp, GFRCR); - panelp->ports[i] = portp; + for (i = 0; (i < nrchips); i++) { + ioaddr = panelp->iobase + (i * 4); + outb(SCCR, (ioaddr + XP_ADDR)); + outb(CR_RESETALL, (ioaddr + XP_DATA)); + outb(TSTR, (ioaddr + XP_ADDR)); + if (inb(ioaddr + XP_DATA) != 0) { + printk("STALLION: sc26198 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); + continue; + } + chipmask |= (0x1 << i); + outb(GCCR, (ioaddr + XP_ADDR)); + outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA)); + outb(WDTRCR, (ioaddr + XP_ADDR)); + outb(0xff, (ioaddr + XP_DATA)); } BRDDISABLE(panelp->brdnr); - return(0); + return(chipmask); } /*****************************************************************************/ /* - * Try to find and initialize an EasyIO board. + * Initialize hardware specific port registers. */ -static int stl_initeio(stlbrd_t *brdp) +static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp) { - stlpanel_t *panelp; - unsigned int status; - int rc; - #if DEBUG - printk("stl_initeio(brdp=%x)\n", (int) brdp); + printk("stl_sc26198portinit(brdp=%x,panelp=%x,portp=%x)\n", + (int) brdp, (int) panelp, (int) portp); #endif - brdp->ioctrl = brdp->ioaddr1 + 1; - brdp->iostatus = brdp->ioaddr1 + 2; - brdp->clk = EIO_CLK; + if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || + (portp == (stlport_t *) NULL)) + return; - status = inb(brdp->iostatus); - switch (status & EIO_IDBITMASK) { - case EIO_8PORTM: - brdp->clk = EIO_CLK8M; - /* fall thru */ - case EIO_8PORTRS: - case EIO_8PORTDI: - brdp->nrports = 8; + portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4); + portp->uartaddr = (portp->portnr & 0x07) << 4; + portp->pagenr = panelp->pagenr; + portp->hwid = 0x1; + + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS); + BRDDISABLE(portp->brdnr); +} + +/*****************************************************************************/ + +/* + * Set up the sc26198 registers for a port based on the termios port + * settings. + */ + +static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp) +{ + stlbrd_t *brdp; + unsigned long flags; + unsigned int baudrate; + unsigned char mr0, mr1, mr2, clk; + unsigned char imron, imroff, iopr, ipr; + + mr0 = 0; + mr1 = 0; + mr2 = 0; + clk = 0; + iopr = 0; + imron = 0; + imroff = 0; + + brdp = stl_brds[portp->brdnr]; + if (brdp == (stlbrd_t *) NULL) + return; + +/* + * Set up the RX char ignore mask with those RX error types we + * can ignore. + */ + portp->rxignoremsk = 0; + if (tiosp->c_iflag & IGNPAR) + portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | + SR_RXOVERRUN); + if (tiosp->c_iflag & IGNBRK) + portp->rxignoremsk |= SR_RXBREAK; + + portp->rxmarkmsk = SR_RXOVERRUN; + if (tiosp->c_iflag & (INPCK | PARMRK)) + portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING); + if (tiosp->c_iflag & BRKINT) + portp->rxmarkmsk |= SR_RXBREAK; + +/* + * Go through the char size, parity and stop bits and set all the + * option register appropriately. + */ + switch (tiosp->c_cflag & CSIZE) { + case CS5: + mr1 |= MR1_CS5; break; - case EIO_4PORTRS: - brdp->nrports = 4; + case CS6: + mr1 |= MR1_CS6; + break; + case CS7: + mr1 |= MR1_CS7; break; default: - return(-ENODEV); + mr1 |= MR1_CS8; + break; + } + + if (tiosp->c_cflag & CSTOPB) + mr2 |= MR2_STOP2; + else + mr2 |= MR2_STOP1; + + if (tiosp->c_cflag & PARENB) { + if (tiosp->c_cflag & PARODD) + mr1 |= (MR1_PARENB | MR1_PARODD); + else + mr1 |= (MR1_PARENB | MR1_PAREVEN); + } else { + mr1 |= MR1_PARNONE; } - request_region(brdp->ioaddr1, 8, "serial(EIO)"); + mr1 |= MR1_ERRBLOCK; + +/* + * Set the RX FIFO threshold at 8 chars. This gives a bit of breathing + * space for hardware flow control and the like. This should be set to + * VMIN. + */ + mr2 |= MR2_RXFIFOHALF; /* - * Check that the supplied IRQ is good and then use it to setup the - * programmable interrupt bits on EIO board. Also set the edge/level - * triggered interrupt bit. + * Calculate the baud rate timers. For now we will just assume that + * the input and output baud are the same. The sc26198 has a fixed + * baud rate table, so only discrete baud rates possible. */ - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); - return(-EINVAL); + baudrate = tiosp->c_cflag & CBAUD; + if (baudrate & CBAUDEX) { + baudrate &= ~CBAUDEX; + if ((baudrate < 1) || (baudrate > 5)) + tiosp->c_cflag &= ~CBAUDEX; + else + baudrate += 15; + } + baudrate = stl_baudrates[baudrate]; + if ((tiosp->c_cflag & CBAUD) == B38400) { + if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baudrate = 57600; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baudrate = 115200; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + baudrate = (portp->baud_base / portp->custom_divisor); } - outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl); + if (baudrate > STL_SC26198MAXBAUD) + baudrate = STL_SC26198MAXBAUD; - panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); - if (panelp == (stlpanel_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t)); - return(-ENOMEM); + if (baudrate > 0) { + for (clk = 0; (clk < SC26198_NRBAUDS); clk++) { + if (baudrate <= sc26198_baudtable[clk]) + break; + } } - memset(panelp, 0, sizeof(stlpanel_t)); - panelp->magic = STL_PANELMAGIC; - panelp->brdnr = brdp->brdnr; - panelp->panelnr = 0; - panelp->nrports = brdp->nrports; - panelp->iobase = brdp->ioaddr1; - panelp->hwid = status; - brdp->panels[0] = panelp; - brdp->nrpanels = 1; - brdp->state |= BRD_FOUND; - brdp->hwid = status; - rc = stl_mapirq(brdp->irq); - return(rc); +/* + * Check what form of modem signaling is required and set it up. + */ + if (tiosp->c_cflag & CLOCAL) { + portp->flags &= ~ASYNC_CHECK_CD; + } else { + iopr |= IOPR_DCDCOS; + imron |= IR_IOPORT; + portp->flags |= ASYNC_CHECK_CD; + } + +/* + * Setup sc26198 enhanced modes if we can. In particular we want to + * handle as much of the flow control as possible automatically. As + * well as saving a few CPU cycles it will also greatly improve flow + * control reliability. + */ + if (tiosp->c_iflag & IXON) { + mr0 |= MR0_SWFTX | MR0_SWFT; + imron |= IR_XONXOFF; + } else { + imroff |= IR_XONXOFF; + } + if (tiosp->c_iflag & IXOFF) + mr0 |= MR0_SWFRX; + + if (tiosp->c_cflag & CRTSCTS) { + mr2 |= MR2_AUTOCTS; + mr1 |= MR1_AUTORTS; + } + +/* + * All sc26198 register values calculated so go through and set + * them all up. + */ + +#if DEBUG + printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); + printk(" mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk); + printk(" iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff); + printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); +#endif + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IMR, 0); + stl_sc26198updatereg(portp, MR0, mr0); + stl_sc26198updatereg(portp, MR1, mr1); + stl_sc26198setreg(portp, SCCR, CR_RXERRBLOCK); + stl_sc26198updatereg(portp, MR2, mr2); + stl_sc26198updatereg(portp, IOPIOR, + ((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr)); + + if (baudrate > 0) { + stl_sc26198setreg(portp, TXCSR, clk); + stl_sc26198setreg(portp, RXCSR, clk); + } + + stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]); + stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]); + + ipr = stl_sc26198getreg(portp, IPR); + if (ipr & IPR_DCD) + portp->sigs &= ~TIOCM_CD; + else + portp->sigs |= TIOCM_CD; + + portp->imr = (portp->imr & ~imroff) | imron; + stl_sc26198setreg(portp, IMR, portp->imr); + BRDDISABLE(portp->brdnr); + restore_flags(flags); } /*****************************************************************************/ /* - * Try to find an ECH board and initialize it. This code is capable of - * dealing with all types of ECH board. + * Set the state of the DTR and RTS signals. */ -static int stl_initech(stlbrd_t *brdp) +static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts) { - stlpanel_t *panelp; - unsigned int status, nxtid; - int panelnr, ioaddr, i; + unsigned char iopioron, iopioroff; + unsigned long flags; #if DEBUG - printk("stl_initech(brdp=%x)\n", (int) brdp); + printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", + (int) portp, dtr, rts); #endif - status = 0; + iopioron = 0; + iopioroff = 0; + if (dtr == 0) + iopioroff |= IPR_DTR; + else if (dtr > 0) + iopioron |= IPR_DTR; + if (rts == 0) + iopioroff |= IPR_RTS; + else if (rts > 0) + iopioron |= IPR_RTS; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IOPIOR, + ((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron)); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ /* - * Set up the initial board register contents for boards. This varies a - * bit between the different board types. So we need to handle each - * separately. Also do a check that the supplied IRQ is good. + * Return the state of the signals. */ - if (brdp->brdtype == BRD_ECH) { - brdp->ioctrl = brdp->ioaddr1 + 1; - brdp->iostatus = brdp->ioaddr1 + 1; - status = inb(brdp->iostatus); - if ((status & ECH_IDBITMASK) != ECH_ID) - return(-ENODEV); - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); - return(-EINVAL); - } - status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); - status |= (stl_vecmap[brdp->irq] << 1); - outb((status | ECH_BRDRESET), brdp->ioaddr1); - brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); - outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - outb(status, brdp->ioaddr1); +static int stl_sc26198getsignals(stlport_t *portp) +{ + unsigned char ipr; + unsigned long flags; + int sigs; - request_region(brdp->ioaddr1, 2, "serial(EC8/32)"); - request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)"); - } else if (brdp->brdtype == BRD_ECHMC) { - brdp->ioctrl = brdp->ioaddr1 + 0x20; - brdp->iostatus = brdp->ioctrl; - status = inb(brdp->iostatus); - if ((status & ECH_IDBITMASK) != ECH_ID) - return(-ENODEV); +#if DEBUG + printk("stl_sc26198getsignals(portp=%x)\n", (int) portp); +#endif - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); - return(-EINVAL); - } - outb(ECHMC_BRDRESET, brdp->ioctrl); - outb(ECHMC_INTENABLE, brdp->ioctrl); + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + ipr = stl_sc26198getreg(portp, IPR); + BRDDISABLE(portp->brdnr); + restore_flags(flags); - request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)"); - } else if (brdp->brdtype == BRD_ECHPCI) { - brdp->ioctrl = brdp->ioaddr1 + 2; - request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)"); - request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)"); - } + sigs = 0; + sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD; + sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS; + sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR; + sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS; + sigs |= TIOCM_DSR; + return(sigs); +} - brdp->clk = ECH_CLK; - brdp->hwid = status; +/*****************************************************************************/ /* - * Scan through the secondary io address space looking for panels. - * As we find'em allocate and initialize panel structures for each. + * Enable/Disable the Transmitter and/or Receiver. */ - ioaddr = brdp->ioaddr2; - panelnr = 0; - nxtid = 0; - for (i = 0; (i < STL_MAXPANELS); i++) { - if (brdp->brdtype == BRD_ECHPCI) { - outb(nxtid, brdp->ioctrl); - ioaddr = brdp->ioaddr2; - } - status = inb(ioaddr + ECH_PNLSTATUS); - if ((status & ECH_PNLIDMASK) != nxtid) - break; - panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); - if (panelp == (stlpanel_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t)); - break; - } - memset(panelp, 0, sizeof(stlpanel_t)); - panelp->magic = STL_PANELMAGIC; - panelp->brdnr = brdp->brdnr; - panelp->panelnr = panelnr; - panelp->iobase = ioaddr; - panelp->pagenr = nxtid; - panelp->hwid = status; - if (status & ECH_PNL16PORT) { - if ((brdp->nrports + 16) > 32) - break; - panelp->nrports = 16; - panelp->ackmask = 0x80; - brdp->nrports += 16; - ioaddr += (EREG_BANKSIZE * 2); - nxtid += 2; - } else { - panelp->nrports = 8; - panelp->ackmask = 0xc0; - brdp->nrports += 8; - ioaddr += EREG_BANKSIZE; - nxtid++; - } - brdp->panels[panelnr++] = panelp; - brdp->nrpanels++; - if (ioaddr >= (brdp->ioaddr2 + 0x20)) - break; - } +static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx) +{ + unsigned char ccr; + unsigned long flags; - if (brdp->brdtype == BRD_ECH) - outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); +#if DEBUG + printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); +#endif - brdp->state |= BRD_FOUND; - i = stl_mapirq(brdp->irq); - return(i); + ccr = portp->crenable; + if (tx == 0) + ccr &= ~CR_TXENABLE; + else if (tx > 0) + ccr |= CR_TXENABLE; + if (rx == 0) + ccr &= ~CR_RXENABLE; + else if (rx > 0) + ccr |= CR_RXENABLE; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, ccr); + BRDDISABLE(portp->brdnr); + portp->crenable = ccr; + restore_flags(flags); } /*****************************************************************************/ /* - * Initialize and configure the specified board. - * Scan through all the boards in the configuration and see what we - * can find. Handle EIO and the ECH boards a little differently here - * since the initial search and setup is too different. + * Start/stop the Transmitter and/or Receiver. */ -static int stl_brdinit(stlbrd_t *brdp) +static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx) { - int i; + unsigned char imr; + unsigned long flags; #if DEBUG - printk("stl_brdinit(brdp=%x)\n", (int) brdp); + printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif - switch (brdp->brdtype) { - case BRD_EASYIO: - stl_initeio(brdp); - break; - case BRD_ECH: - case BRD_ECHMC: - case BRD_ECHPCI: - stl_initech(brdp); - break; - default: - printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); - return(ENODEV); - } - - stl_brds[brdp->brdnr] = brdp; - if ((brdp->state & BRD_FOUND) == 0) { - printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq); - return(ENODEV); - } - - for (i = 0; (i < STL_MAXPANELS); i++) - if (brdp->panels[i] != (stlpanel_t *) NULL) - stl_initports(brdp, brdp->panels[i]); + imr = portp->imr; + if (tx == 0) + imr &= ~IR_TXRDY; + else if (tx == 1) + imr |= IR_TXRDY; + if (rx == 0) + imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG); + else if (rx > 0) + imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG; - printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports); - return(0); + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, IMR, imr); + BRDDISABLE(portp->brdnr); + portp->imr = imr; + if (tx > 0) + set_bit(ASYI_TXBUSY, &portp->istate); + restore_flags(flags); } /*****************************************************************************/ /* - * Find any ECH-PCI boards that might be installed. Initialize each - * one as it is found. + * Disable all interrupts from this port. */ -#ifdef CONFIG_PCI - -static int stl_findpcibrds() +static void stl_sc26198disableintrs(stlport_t *portp) { - stlbrd_t *brdp; - unsigned char busnr, devnr, irq; - unsigned short class; - unsigned int ioaddr; - int i, rc; + unsigned long flags; #if DEBUG - printk("stl_findpcibrds()\n"); + printk("stl_sc26198disableintrs(portp=%x)\n", (int) portp); #endif - if (pcibios_present()) { - for (i = 0; (i < STL_MAXBRDS); i++) { - if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr)) - break; - -/* - * Found a device on the PCI bus that has our vendor and - * device ID. Need to check now that it is really us. - */ - if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) { - printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc); - continue; - } - if (class == PCI_CLASS_STORAGE_IDE) - continue; - - if (stl_nrbrds >= STL_MAXBRDS) { - printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS); - break; - } + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + portp->imr = 0; + stl_sc26198setreg(portp, IMR, 0); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} -/* - * We have a Stallion board. Allocate a board structure - * and initialize it. Read its IO and IRQ resources - * from conf space. - */ - brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); - if (brdp == (stlbrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); - return(-ENOMEM); - } - memset(brdp, 0, sizeof(stlbrd_t)); - brdp->magic = STL_BOARDMAGIC; - brdp->brdnr = stl_nrbrds++; - brdp->brdtype = BRD_ECHPCI; +/*****************************************************************************/ - if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); - continue; - } - brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK); +static void stl_sc26198sendbreak(stlport_t *portp, long len) +{ + unsigned long flags; - if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); - continue; - } - brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK); #if DEBUG - printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1); + printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, + (int) len); #endif - if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); - continue; - } - brdp->irq = irq; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (len / (1000 / HZ)); - stl_brdinit(brdp); - } - } + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK); + BRDDISABLE(portp->brdnr); + portp->stats.txbreaks++; - return(0); -} + schedule(); -#endif + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK); + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} /*****************************************************************************/ /* - * Scan through all the boards in the configuration and see what we - * can find. Handle EIO and the ECH boards a little differently here - * since the initial search and setup is too different. + * Take flow control actions... */ -static int stl_initbrds() +static void stl_sc26198flowctrl(stlport_t *portp, int state) { - stlbrd_t *brdp; - stlconf_t *confp; - int i; + struct tty_struct *tty; + unsigned long flags; + unsigned char mr0; #if DEBUG - printk("stl_initbrds()\n"); + printk("stl_sc26198flowctrl(portp=%x,state=%x)\n", (int) portp, state); #endif - if (stl_nrbrds > STL_MAXBRDS) { - printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS); - stl_nrbrds = STL_MAXBRDS; - } + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; -/* - * Firstly scan the list of static boards configured. Allocate - * resources and initialize the boards as found. - */ - for (i = 0; (i < stl_nrbrds); i++) { - confp = &stl_brdconf[i]; - brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); - if (brdp == (stlbrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); - return(-ENOMEM); - } - memset(brdp, 0, sizeof(stlbrd_t)); + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); - brdp->magic = STL_BOARDMAGIC; - brdp->brdnr = i; - brdp->brdtype = confp->brdtype; - brdp->ioaddr1 = confp->ioaddr1; - brdp->ioaddr2 = confp->ioaddr2; - brdp->irq = confp->irq; - brdp->irqtype = confp->irqtype; - stl_brdinit(brdp); + if (state) { + if (tty->termios->c_iflag & IXOFF) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); + mr0 |= MR0_SWFRX; + portp->stats.rxxon++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } +/* + * Question: should we return RTS to what it was before? It may + * have been set by an ioctl... Suppose not, since if you have + * hardware flow control set then it is pretty silly to go and + * set the RTS line by hand. + */ + if (tty->termios->c_cflag & CRTSCTS) { + stl_sc26198setreg(portp, MR1, + (stl_sc26198getreg(portp, MR1) | MR1_AUTORTS)); + stl_sc26198setreg(portp, IOPIOR, + (stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS)); + portp->stats.rxrtson++; + } + } else { + if (tty->termios->c_iflag & IXOFF) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); + mr0 &= ~MR0_SWFRX; + portp->stats.rxxoff++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } + if (tty->termios->c_cflag & CRTSCTS) { + stl_sc26198setreg(portp, MR1, + (stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS)); + stl_sc26198setreg(portp, IOPIOR, + (stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS)); + portp->stats.rxrtsoff++; + } } -#ifdef CONFIG_PCI -/* - * If the PCI BIOS support is compiled in then let's go looking for - * ECH-PCI boards. - */ - stl_findpcibrds(); -#endif - - return(0); + BRDDISABLE(portp->brdnr); + restore_flags(flags); } /*****************************************************************************/ /* - * Return the board stats structure to user app. + * Send a flow control character. */ -static int stl_getbrdstats(combrd_t *bp) +static void stl_sc26198sendflow(stlport_t *portp, int state) { - stlbrd_t *brdp; - stlpanel_t *panelp; - int i; + struct tty_struct *tty; + unsigned long flags; + unsigned char mr0; - memcpy_fromfs(&stl_brdstats, bp, sizeof(combrd_t)); - if (stl_brdstats.brd >= STL_MAXBRDS) - return(-ENODEV); - brdp = stl_brds[stl_brdstats.brd]; - if (brdp == (stlbrd_t *) NULL) - return(-ENODEV); +#if DEBUG + printk("stl_sc26198sendflow(portp=%x,state=%x)\n", (int) portp, state); +#endif - memset(&stl_brdstats, 0, sizeof(combrd_t)); - stl_brdstats.brd = brdp->brdnr; - stl_brdstats.type = brdp->brdtype; - stl_brdstats.hwid = brdp->hwid; - stl_brdstats.state = brdp->state; - stl_brdstats.ioaddr = brdp->ioaddr1; - stl_brdstats.ioaddr2 = brdp->ioaddr2; - stl_brdstats.irq = brdp->irq; - stl_brdstats.nrpanels = brdp->nrpanels; - stl_brdstats.nrports = brdp->nrports; - for (i = 0; (i < brdp->nrpanels); i++) { - panelp = brdp->panels[i]; - stl_brdstats.panels[i].panel = i; - stl_brdstats.panels[i].hwid = panelp->hwid; - stl_brdstats.panels[i].nrports = panelp->nrports; - } + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; - memcpy_tofs(bp, &stl_brdstats, sizeof(combrd_t)); - return(0); + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + if (state) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); + mr0 |= MR0_SWFRX; + portp->stats.rxxon++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } else { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); + mr0 &= ~MR0_SWFRX; + portp->stats.rxxoff++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } + BRDDISABLE(portp->brdnr); + restore_flags(flags); } /*****************************************************************************/ -/* - * Resolve the referenced port number into a port struct pointer. - */ - -static stlport_t *stl_getport(int brdnr, int panelnr, int portnr) +static void stl_sc26198flush(stlport_t *portp) { - stlbrd_t *brdp; - stlpanel_t *panelp; + unsigned long flags; - if ((brdnr < 0) || (brdnr >= STL_MAXBRDS)) - return((stlport_t *) NULL); - brdp = stl_brds[brdnr]; - if (brdp == (stlbrd_t *) NULL) - return((stlport_t *) NULL); - if ((panelnr < 0) || (panelnr >= brdp->nrpanels)) - return((stlport_t *) NULL); - panelp = brdp->panels[panelnr]; - if (panelp == (stlpanel_t *) NULL) - return((stlport_t *) NULL); - if ((portnr < 0) || (portnr >= panelp->nrports)) - return((stlport_t *) NULL); - return(panelp->ports[portnr]); +#if DEBUG + printk("stl_sc26198flush(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_sc26198setreg(portp, SCCR, CR_TXRESET); + stl_sc26198setreg(portp, SCCR, portp->crenable); + BRDDISABLE(portp->brdnr); + portp->tx.tail = portp->tx.head; + restore_flags(flags); } /*****************************************************************************/ /* - * Return the port stats structure to user app. A NULL port struct - * pointer passed in means that we need to find out from the app - * what port to get stats for (used through board control device). + * Return the current state of data flow on this port. This is only + * really interresting when determining if data has fully completed + * transmission or not... The sc26198 interrupt scheme cannot + * determine when all data has actually drained, so we need to + * check the port statusy register to be sure. */ -static int stl_getportstats(stlport_t *portp, comstats_t *cp) +static int stl_sc26198datastate(stlport_t *portp) { - unsigned char *head, *tail; unsigned long flags; + unsigned char sr; - if (portp == (stlport_t *) NULL) { - memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t)); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); - if (portp == (stlport_t *) NULL) - return(-ENODEV); - } - - portp->stats.state = portp->istate; - portp->stats.flags = portp->flags; - portp->stats.hwid = portp->hwid; - - portp->stats.ttystate = 0; - portp->stats.cflags = 0; - portp->stats.iflags = 0; - portp->stats.oflags = 0; - portp->stats.lflags = 0; - portp->stats.rxbuffered = 0; +#if DEBUG + printk("stl_sc26198datastate(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return(0); + if (test_bit(ASYI_TXBUSY, &portp->istate)) + return(1); save_flags(flags); cli(); - if (portp->tty != (struct tty_struct *) NULL) { - if (portp->tty->driver_data == portp) { - portp->stats.ttystate = portp->tty->flags; - portp->stats.rxbuffered = portp->tty->flip.count; - if (portp->tty->termios != (struct termios *) NULL) { - portp->stats.cflags = portp->tty->termios->c_cflag; - portp->stats.iflags = portp->tty->termios->c_iflag; - portp->stats.oflags = portp->tty->termios->c_oflag; - portp->stats.lflags = portp->tty->termios->c_lflag; - } - } - } + BRDENABLE(portp->brdnr, portp->pagenr); + sr = stl_sc26198getreg(portp, SR); + BRDDISABLE(portp->brdnr); restore_flags(flags); - head = portp->tx.head; - tail = portp->tx.tail; - portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head))); + return((sr & SR_TXEMPTY) ? 0 : 1); +} - portp->stats.signals = (unsigned long) stl_getsignals(portp); +/*****************************************************************************/ - memcpy_tofs(cp, &portp->stats, sizeof(comstats_t)); - return(0); +/* + * Delay for a small amount of time, to give the sc26198 a chance + * to process a command... + */ + +static void stl_sc26198wait(stlport_t *portp) +{ + int i; + +#if DEBUG + printk("stl_sc26198wait(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return; + + for (i = 0; (i < 20); i++) + stl_sc26198getglobreg(portp, TSTR); } /*****************************************************************************/ /* - * Clear the port stats structure. We also return it zeroed out... + * If we are TX flow controlled and in IXANY mode then we may + * need to unflow control here. We gotta do this because of the + * automatic flow control modes of the sc26198. */ -static int stl_clrportstats(stlport_t *portp, comstats_t *cp) +static inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty) { - if (portp == (stlport_t *) NULL) { - memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t)); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); - if (portp == (stlport_t *) NULL) - return(-ENODEV); - } + unsigned char mr0; - memset(&portp->stats, 0, sizeof(comstats_t)); - portp->stats.brd = portp->brdnr; - portp->stats.panel = portp->panelnr; - portp->stats.port = portp->portnr; - memcpy_tofs(cp, &portp->stats, sizeof(comstats_t)); - return(0); + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_HOSTXON); + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + clear_bit(ASYI_TXFLOWED, &portp->istate); } /*****************************************************************************/ /* - * Return the entire driver ports structure to a user app. + * Interrupt service routine for sc26198 panels. */ -static int stl_getportstruct(unsigned long arg) +static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase) { stlport_t *portp; + unsigned int iack; - memcpy_fromfs(&stl_dummyport, (void *) arg, sizeof(stlport_t)); - portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr, - stl_dummyport.portnr); - if (portp == (stlport_t *) NULL) - return(-ENODEV); - memcpy_tofs((void *) arg, portp, sizeof(stlport_t)); - return(0); +/* + * Work around bug in sc26198 chip... Cannot have A6 address + * line of UART high, else iack will be returned as 0. + */ + outb(0, (iobase + 1)); + + iack = inb(iobase + XP_IACK); + portp = panelp->ports[(iack & IVR_CHANMASK) + ((iobase & 0x4) << 1)]; + + if (iack & IVR_RXDATA) + stl_sc26198rxisr(portp, iack); + else if (iack & IVR_TXDATA) + stl_sc26198txisr(portp); + else + stl_sc26198otherisr(portp, iack); } /*****************************************************************************/ /* - * Return the entire driver board structure to a user app. + * Transmit interrupt handler. This has gotta be fast! Handling TX + * chars is pretty simple, stuff as many as possible from the TX buffer + * into the sc26198 FIFO. + * In practice it is possible that interrupts are enabled but that the + * port has been hung up. Need to handle not having any TX buffer here, + * this is done by using the side effect that head and tail will also + * be NULL if the buffer has been freed. */ -static int stl_getbrdstruct(unsigned long arg) +static void stl_sc26198txisr(stlport_t *portp) { - stlbrd_t *brdp; + unsigned int ioaddr; + unsigned char mr0; + int len, stlen; + char *head, *tail; - memcpy_fromfs(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t)); - if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS)) - return(-ENODEV); - brdp = stl_brds[stl_dummybrd.brdnr]; - if (brdp == (stlbrd_t *) NULL) - return(-ENODEV); - memcpy_tofs((void *) arg, brdp, sizeof(stlbrd_t)); - return(0); +#if DEBUG + printk("stl_sc26198txisr(portp=%x)\n", (int) portp); +#endif + + ioaddr = portp->ioaddr; + head = portp->tx.head; + tail = portp->tx.tail; + len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { + set_bit(ASYI_TXLOW, &portp->istate); + queue_task_irq_off(&portp->tqueue, &tq_scheduler); + } + + if (len == 0) { + outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR)); + mr0 = inb(ioaddr + XP_DATA); + if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) { + portp->imr &= ~IR_TXRDY; + outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR)); + outb(portp->imr, (ioaddr + XP_DATA)); + clear_bit(ASYI_TXBUSY, &portp->istate); + } else { + mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY); + outb(mr0, (ioaddr + XP_DATA)); + } + } else { + len = MIN(len, SC26198_TXFIFOSIZE); + portp->stats.txtotal += len; + stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail)); + outb(GTXFIFO, (ioaddr + XP_ADDR)); + outsb((ioaddr + XP_DATA), tail, stlen); + len -= stlen; + tail += stlen; + if (tail >= (portp->tx.buf + STL_TXBUFSIZE)) + tail = portp->tx.buf; + if (len > 0) { + outsb((ioaddr + XP_DATA), tail, len); + tail += len; + } + portp->tx.tail = tail; + } } /*****************************************************************************/ /* - * The "staliomem" device is also required to do some special operations - * on the board and/or ports. In this driver it is mostly used for stats - * collection. + * Receive character interrupt handler. Determine if we have good chars + * or bad chars and then process appropriately. Good chars are easy + * just shove the lot into the RX buffer and set all status byte to 0. + * If a bad RX char then process as required. This routine needs to be + * fast! In practice it is possible that we get an interrupt on a port + * that is closed. This can happen on hangups - since they completely + * shutdown a port not in user context. Need to handle this case. */ -static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) +static void stl_sc26198rxisr(stlport_t *portp, unsigned int iack) { - int brdnr, rc; + struct tty_struct *tty; + unsigned int len, buflen, ioaddr; #if DEBUG - printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg); + printk("stl_sc26198rxisr(portp=%x,iack=%x)\n", (int) portp, iack); #endif - brdnr = MINOR(ip->i_rdev); - if (brdnr >= STL_MAXBRDS) - return(-ENODEV); - rc = 0; - - switch (cmd) { - case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg); - break; - case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg); - break; - case COM_GETBRDSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0) - rc = stl_getbrdstats((combrd_t *) arg); - break; - case COM_READPORT: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0) - rc = stl_getportstruct(arg); - break; - case COM_READBOARD: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0) - rc = stl_getbrdstruct(arg); - break; - default: - rc = -ENOIOCTLCMD; - break; + tty = portp->tty; + ioaddr = portp->ioaddr; + outb(GIBCR, (ioaddr + XP_ADDR)); + len = inb(ioaddr + XP_DATA) + 1; + + if ((iack & IVR_TYPEMASK) == IVR_RXDATA) { + if ((tty == (struct tty_struct *) NULL) || + (tty->flip.char_buf_ptr == (char *) NULL) || + ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + outb(GRXFIFO, (ioaddr + XP_ADDR)); + insb((ioaddr + XP_DATA), &stl_unwanted[0], len); + portp->stats.rxlost += len; + portp->stats.rxtotal += len; + } else { + len = MIN(len, buflen); + if (len > 0) { + outb(GRXFIFO, (ioaddr + XP_ADDR)); + insb((ioaddr + XP_DATA), tty->flip.char_buf_ptr, len); + memset(tty->flip.flag_buf_ptr, 0, len); + tty->flip.flag_buf_ptr += len; + tty->flip.char_buf_ptr += len; + tty->flip.count += len; + tty_schedule_flip(tty); + portp->stats.rxtotal += len; + } + } + } else { + stl_sc26198rxbadchars(portp); } - return(rc); +/* + * If we are TX flow controlled and in IXANY mode then we may need + * to unflow control here. We gotta do this because of the automatic + * flow control modes of the sc26198. + */ + if (test_bit(ASYI_TXFLOWED, &portp->istate)) { + if ((tty != (struct tty_struct *) NULL) && + (tty->termios != (struct termios *) NULL) && + (tty->termios->c_iflag & IXANY)) { + stl_sc26198txunflow(portp, tty); + } + } } /*****************************************************************************/ -int stl_init(void) +/* + * Process an RX bad character. + */ + +static void inline stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch) { - printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion); + struct tty_struct *tty; + unsigned int ioaddr; - stl_initbrds(); + tty = portp->tty; + ioaddr = portp->ioaddr; + + if (status & SR_RXPARITY) + portp->stats.rxparity++; + if (status & SR_RXFRAMING) + portp->stats.rxframing++; + if (status & SR_RXOVERRUN) + portp->stats.rxoverrun++; + if (status & SR_RXBREAK) + portp->stats.rxbreaks++; + + if ((tty != (struct tty_struct *) NULL) && + ((portp->rxignoremsk & status) == 0)) { + if (portp->rxmarkmsk & status) { + if (status & SR_RXBREAK) { + status = TTY_BREAK; + if (portp->flags & ASYNC_SAK) { + do_SAK(tty); + BRDENABLE(portp->brdnr, portp->pagenr); + } + } else if (status & SR_RXPARITY) { + status = TTY_PARITY; + } else if (status & SR_RXFRAMING) { + status = TTY_FRAME; + } else if(status & SR_RXOVERRUN) { + status = TTY_OVERRUN; + } else { + status = 0; + } + } else { + status = 0; + } + + if (tty->flip.char_buf_ptr != (char *) NULL) { + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.flag_buf_ptr++ = status; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + } + tty_schedule_flip(tty); + } + + if (status == 0) + portp->stats.rxtotal++; + } +} + +/*****************************************************************************/ /* - * Allocate a temporary write buffer. + * Process all characters in the RX FIFO of the UART. Check all char + * status bytes as well, and process as required. We need to check + * all bytes in the FIFO, in case some more enter the FIFO while we + * are here. To get the exact character error type we need to switch + * into CHAR error mode (that is why we need to make sure we empty + * the FIFO). */ - stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE); - if (stl_tmpwritebuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE); + +static void stl_sc26198rxbadchars(stlport_t *portp) +{ + unsigned char status, mr1; + char ch; /* - * Set up a character driver for per board stuff. This is mainly used - * to do stats ioctls on the ports. + * To get the precise error type for each character we must switch + * back into CHAR error mode. */ - if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) - printk("STALLION: failed to register serial board device\n"); + mr1 = stl_sc26198getreg(portp, MR1); + stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK)); + + while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) { + stl_sc26198setreg(portp, SCCR, CR_CLEARRXERR); + ch = stl_sc26198getreg(portp, RXFIFO); + stl_sc26198rxbadch(portp, status, ch); + } /* - * Set up the tty driver structure and register us as a driver. - * Also setup the callout tty device. + * To get correct interrupt class we must switch back into BLOCK + * error mode. */ - memset(&stl_serial, 0, sizeof(struct tty_driver)); - stl_serial.magic = TTY_DRIVER_MAGIC; - stl_serial.name = stl_serialname; - stl_serial.major = STL_SERIALMAJOR; - stl_serial.minor_start = 0; - stl_serial.num = STL_MAXBRDS * STL_MAXPORTS; - stl_serial.type = TTY_DRIVER_TYPE_SERIAL; - stl_serial.subtype = STL_DRVTYPSERIAL; - stl_serial.init_termios = stl_deftermios; - stl_serial.flags = TTY_DRIVER_REAL_RAW; - stl_serial.refcount = &stl_refcount; - stl_serial.table = stl_ttys; - stl_serial.termios = stl_termios; - stl_serial.termios_locked = stl_termioslocked; - - stl_serial.open = stl_open; - stl_serial.close = stl_close; - stl_serial.write = stl_write; - stl_serial.put_char = stl_putchar; - stl_serial.flush_chars = stl_flushchars; - stl_serial.write_room = stl_writeroom; - stl_serial.chars_in_buffer = stl_charsinbuffer; - stl_serial.ioctl = stl_ioctl; - stl_serial.set_termios = stl_settermios; - stl_serial.throttle = stl_throttle; - stl_serial.unthrottle = stl_unthrottle; - stl_serial.stop = stl_stop; - stl_serial.start = stl_start; - stl_serial.hangup = stl_hangup; - stl_serial.flush_buffer = stl_flushbuffer; + stl_sc26198setreg(portp, MR1, mr1); +} - stl_callout = stl_serial; - stl_callout.name = stl_calloutname; - stl_callout.major = STL_CALLOUTMAJOR; - stl_callout.subtype = STL_DRVTYPCALLOUT; +/*****************************************************************************/ - if (tty_register_driver(&stl_serial)) - printk("STALLION: failed to register serial driver\n"); - if (tty_register_driver(&stl_callout)) - printk("STALLION: failed to register callout driver\n"); +/* + * Other interrupt handler. This includes modem signals, flow + * control actions, etc. Most stuff is left to off-level interrupt + * processing time. + */ - return(0); +static void stl_sc26198otherisr(stlport_t *portp, unsigned int iack) +{ + unsigned char cir, ipr, xisr; + +#if DEBUG + printk("stl_sc26198otherisr(portp=%x,iack=%x)\n", (int) portp, iack); +#endif + + cir = stl_sc26198getglobreg(portp, CIR); + + switch (cir & CIR_SUBTYPEMASK) { + case CIR_SUBCOS: + ipr = stl_sc26198getreg(portp, IPR); + if (ipr & IPR_DCDCHANGE) { + set_bit(ASYI_DCDCHANGE, &portp->istate); + queue_task_irq_off(&portp->tqueue, &tq_scheduler); + portp->stats.modem++; + } + break; + case CIR_SUBXONXOFF: + xisr = stl_sc26198getreg(portp, XISR); + if (xisr & XISR_RXXONGOT) { + set_bit(ASYI_TXFLOWED, &portp->istate); + portp->stats.txxoff++; + } + if (xisr & XISR_RXXOFFGOT) { + clear_bit(ASYI_TXFLOWED, &portp->istate); + portp->stats.txxon++; + } + break; + case CIR_SUBBREAK: + stl_sc26198setreg(portp, SCCR, CR_BREAKRESET); + stl_sc26198rxbadchars(portp); + break; + default: + break; + } } /*****************************************************************************/ diff -u --recursive --new-file v2.0.34/linux/drivers/char/vt.c linux/drivers/char/vt.c --- v2.0.34/linux/drivers/char/vt.c Sun May 12 21:36:19 1996 +++ linux/drivers/char/vt.c Mon Jul 13 13:47:30 1998 @@ -23,6 +23,7 @@ #include <asm/io.h> #include <asm/segment.h> +#include <asm/bitops.h> #include "kbd_kern.h" #include "vt_kern.h" @@ -147,11 +148,16 @@ * We also return immediately, which is what was implied within the X * comments - KDMKTONE doesn't put the process to sleep. */ + +static unsigned int mksound_lock = 0; + static void kd_nosound(unsigned long ignored) { - /* disable counter 2 */ - outb(inb_p(0x61)&0xFC, 0x61); + /* if sound is being set up, don't turn it off */ + if (!mksound_lock) + /* disable counter 2 */ + outb(inb_p(0x61)&0xFC, 0x61); return; } @@ -165,25 +171,29 @@ if (hz > 20 && hz < 32767) count = 1193180 / hz; - - cli(); - del_timer(&sound_timer); - if (count) { - /* enable counter 2 */ - outb_p(inb_p(0x61)|3, 0x61); - /* set command for counter 2, 2 byte write */ - outb_p(0xB6, 0x43); - /* select desired HZ */ - outb_p(count & 0xff, 0x42); - outb((count >> 8) & 0xff, 0x42); - - if (ticks) { - sound_timer.expires = jiffies+ticks; - add_timer(&sound_timer); - } - } else - kd_nosound(0); - sti(); + + /* ignore multiple simultaneous requests for sound */ + if (!set_bit(0, &mksound_lock)) { + /* set_bit in 2.0.x is same as test-and-set in 2.1.x */ + del_timer(&sound_timer); + if (count) { + /* enable counter 2 */ + outb_p(inb_p(0x61)|3, 0x61); + /* set command for counter 2, 2 byte write */ + outb_p(0xB6, 0x43); + /* select desired HZ */ + outb_p(count & 0xff, 0x42); + outb((count >> 8) & 0xff, 0x42); + + if (ticks) { + sound_timer.expires = jiffies+ticks; + add_timer(&sound_timer); + } + } else + kd_nosound(0); + + mksound_lock = 0; + } return; } diff -u --recursive --new-file v2.0.34/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c --- v2.0.34/linux/drivers/isdn/isdn_net.c Mon Jul 13 13:46:28 1998 +++ linux/drivers/isdn/isdn_net.c Mon Jul 13 13:47:30 1998 @@ -229,7 +229,8 @@ { printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n", dev->name, reason); - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0 + 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 #endif diff -u --recursive --new-file v2.0.34/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c --- v2.0.34/linux/drivers/isdn/sc/interrupt.c Mon Aug 4 17:34:01 1997 +++ linux/drivers/isdn/sc/interrupt.c Mon Jul 13 13:47:30 1998 @@ -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 -u --recursive --new-file v2.0.34/linux/drivers/net/3c503.c linux/drivers/net/3c503.c --- v2.0.34/linux/drivers/net/3c503.c Thu Apr 11 23:49:36 1996 +++ linux/drivers/net/3c503.c Mon Jul 13 13:47:30 1998 @@ -75,7 +75,7 @@ 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 @@ */ 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 -u --recursive --new-file v2.0.34/linux/drivers/net/3c515.c linux/drivers/net/3c515.c --- v2.0.34/linux/drivers/net/3c515.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/3c515.c Mon Jul 13 13:47:31 1998 @@ -18,9 +18,9 @@ /* "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; @@ -149,7 +149,7 @@ PCI cards, with the bus master interface extensively modified to work with the ISA bus. -The card is capable of full-bus-master transfers with seperate +The card is capable of full-bus-master transfers with separate lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, DEC Tulip and Intel Speedo3. diff -u --recursive --new-file v2.0.34/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c --- v2.0.34/linux/drivers/net/3c59x.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/3c59x.c Mon Jul 13 13:47:31 1998 @@ -20,9 +20,9 @@ /* "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; @@ -194,7 +194,7 @@ versions of the FastEtherLink cards. The supported product IDs are 3c590, 3c592, 3c595, 3c597, 3c900, 3c905 -The ISA 3c515 is supported with a seperate driver, 3c515.c, included with +The ISA 3c515 is supported with a separate driver, 3c515.c, included with the kernel source or available from cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html @@ -212,7 +212,7 @@ series. The primary interface is two programmed-I/O FIFOs, with an alternate single-contiguous-region bus-master transfer (see next). -The 3c900 "Boomerang" series uses a full-bus-master interface with seperate +The 3c900 "Boomerang" series uses a full-bus-master interface with separate lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, DEC Tulip and Intel Speedo3. The first chip version retains a compatible programmed-I/O interface that will be removed in the 'B' and subsequent @@ -1342,7 +1342,7 @@ } /* - * Handle uncommon interrupt sources. This is a seperate routine to minimize + * Handle uncommon interrupt sources. This is a separate routine to minimize * the cache impact. */ static void diff -u --recursive --new-file v2.0.34/linux/drivers/net/Changelog.tlan linux/drivers/net/Changelog.tlan --- v2.0.34/linux/drivers/net/Changelog.tlan Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/Changelog.tlan Mon Jul 13 13:47:31 1998 @@ -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 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.0.34/linux/drivers/net/Config.in Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/Config.in Mon Jul 13 13:47:31 1998 @@ -25,16 +25,28 @@ bool 'Radio network interfaces' CONFIG_NET_RADIO if [ "$CONFIG_NET_RADIO" != "n" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'BAYCOM ser12 and par96 kiss emulation driver for AX.25' CONFIG_BAYCOM - fi - if [ "$CONFIG_AX25" = "y" ]; then - bool 'Gracilis PackeTwin support' CONFIG_PT - bool 'Ottawa PI and PI/2 support' CONFIG_PI + tristate 'BAYCOM ser12 and par96 driver for AX.25' CONFIG_BAYCOM + tristate 'Soundcard modem driver for AX.25' CONFIG_SOUNDMODEM + if [ "$CONFIG_SOUNDMODEM" != "n" ]; then + bool 'Soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC + bool 'Soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS + bool 'Soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200 + bool 'Soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_7 + bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8 + bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 + bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800 + bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 + bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 + fi fi + tristate 'Serial port KISS driver for AX.25' CONFIG_MKISS + tristate 'BPQ Ethernet driver for AX.25' CONFIG_BPQETHER + tristate 'Gracilis PackeTwin support for AX.25' CONFIG_PT + tristate 'Ottawa PI and PI/2 support for AX.25' CONFIG_PI + tristate 'Z8530 SCC KISS emulation driver for AX.25' CONFIG_SCC tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN tristate 'WIC Radio IP bridge (EXPERIMENTAL)' CONFIG_WIC - tristate 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC fi # # Ethernet diff -u --recursive --new-file v2.0.34/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.0.34/linux/drivers/net/Makefile Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/Makefile Mon Jul 13 13:47:31 1998 @@ -7,6 +7,10 @@ # are difficult for users to deal with. include CONFIG +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + L_TARGET := net.a L_OBJS := auto_irq.o M_OBJS := @@ -18,6 +22,8 @@ CONFIG_8390_MODULE := CONFIG_SLHC_BUILTIN := CONFIG_SLHC_MODULE := +CONFIG_HDLCDRV_BUILTIN := +CONFIG_HDLCDRV_MODULE := ifeq ($(CONFIG_ISDN),y) ifeq ($(CONFIG_ISDN_PPP),y) @@ -501,12 +507,36 @@ endif endif +ifeq ($(CONFIG_MKISS),y) +L_OBJS += mkiss.o +else + ifeq ($(CONFIG_MKISS),m) + M_OBJS += mkiss.o + endif +endif + ifeq ($(CONFIG_PI),y) L_OBJS += pi2.o +else + ifeq ($(CONFIG_PI),m) + M_OBJS += pi2.o + endif endif ifeq ($(CONFIG_PT),y) L_OBJS += pt.o +else + ifeq ($(CONFIG_PT),m) + M_OBJS += pt.o + endif +endif + +ifeq ($(CONFIG_BPQETHER),y) +L_OBJS += bpqether.o +else + ifeq ($(CONFIG_BPQETHER),m) + M_OBJS += bpqether.o + endif endif # If anything built-in uses slhc, then build it into the kernel also. @@ -516,6 +546,40 @@ else ifdef CONFIG_SLHC_MODULE MX_OBJS += slhc.o + endif +endif + + +ifeq ($(CONFIG_BAYCOM),y) +L_OBJS += baycom.o +CONFIG_HDLCDRV_BUILTIN = y +else + ifeq ($(CONFIG_BAYCOM),m) + CONFIG_HDLCDRV_MODULE = y + M_OBJS += baycom.o + endif +endif + +ifeq ($(CONFIG_SOUNDMODEM),y) +ALL_SUB_DIRS += soundmodem +SUB_DIRS += soundmodem +L_OBJS += soundmodem/soundmodem.o +CONFIG_HDLCDRV_BUILTIN = y +else + ifeq ($(CONFIG_SOUNDMODEM),m) + CONFIG_HDLCDRV_MODULE = y + ALL_SUB_DIRS += soundmodem + MOD_SUB_DIRS += soundmodem + endif +endif + +# If anything built-in uses the hdlcdrv, then build it into the kernel also. +# If not, but a module uses it, build as a module. +ifdef CONFIG_HDLCDRV_BUILTIN +LX_OBJS += hdlcdrv.o +else + ifdef CONFIG_HDLCDRV_MODULE + MX_OBJS += hdlcdrv.o endif endif diff -u --recursive --new-file v2.0.34/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.0.34/linux/drivers/net/Space.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/Space.c Mon Jul 13 13:47:31 1998 @@ -153,13 +153,13 @@ #if defined(CONFIG_SMC9194) && smc_init(dev) #endif -#if defined(CONFIG_WD80x3) || defined(WD80x3) +#if defined(CONFIG_WD80x3) && wd_probe(dev) #endif -#if defined(CONFIG_EL2) || defined(EL2) /* 3c503 */ +#if defined(CONFIG_EL2) /* 3c503 */ && el2_probe(dev) #endif -#if defined(CONFIG_HPLAN) || defined(HPLAN) +#if defined(CONFIG_HPLAN) && hp_probe(dev) #endif #if defined(CONFIG_HPLAN_PLUS) @@ -255,9 +255,6 @@ #ifdef CONFIG_TLAN && tlan_probe(dev) #endif -#ifdef CONFIG_PCNET32 - && pcnet32_probe(dev) -#endif #ifdef CONFIG_LANCE && lance_probe(dev) #endif @@ -275,18 +272,6 @@ # define NEXT_DEV (&sdla0_dev) #endif -#ifdef CONFIG_NETROM - extern int nr_init(struct device *); - - static struct device nr3_dev = { "nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, nr_init, }; - static struct device nr2_dev = { "nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr3_dev, nr_init, }; - static struct device nr1_dev = { "nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr2_dev, nr_init, }; - static struct device nr0_dev = { "nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr1_dev, nr_init, }; - -# undef NEXT_DEV -# define NEXT_DEV (&nr0_dev) -#endif - /* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */ #ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */ static struct device atp_dev = { @@ -356,6 +341,16 @@ #undef NEXT_DEV #define NEXT_DEV (&slip_bootstrap) #endif /* SLIP */ + +#if defined(CONFIG_MKISS) + /* To be exact, this node just hooks the initialization + routines to the device structures. */ +extern int mkiss_init_ctrl_dev(struct device *); +static struct device mkiss_bootstrap = { + "mkiss_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, mkiss_init_ctrl_dev, }; +#undef NEXT_DEV +#define NEXT_DEV (&mkiss_bootstrap) +#endif /* MKISS */ #if defined(CONFIG_STRIP) extern int strip_init_ctrl_dev(struct device *); diff -u --recursive --new-file v2.0.34/linux/drivers/net/baycom.c linux/drivers/net/baycom.c --- v2.0.34/linux/drivers/net/baycom.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/baycom.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,1068 @@ +/*****************************************************************************/ + +/* + * baycom.c -- baycom ser12 and par96 radio modem driver. + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + * + * Supported modems + * + * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only + * of a modulator/demodulator chip, usually a TI TCM3105. The computer + * is responsible for regenerating the receiver bit clock, as well as + * for handling the HDLC protocol. The modem connects to a serial port, + * hence the name. Since the serial port is not used as an async serial + * port, the kernel driver for serial ports cannot be used, and this + * driver only supports standard serial hardware (8250, 16450, 16550) + * + * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. + * The modem does all the filtering and regenerates the receiver clock. + * Data is transferred from and to the PC via a shift register. + * The shift register is filled with 16 bits and an interrupt is + * signalled. The PC then empties the shift register in a burst. This + * modem connects to the parallel port, hence the name. The modem + * leaves the implementation of the HDLC protocol and the scrambler + * polynomial to the PC. This modem is no longer available (at least + * from Baycom) and has been replaced by the PICPAR modem (see below). + * You may however still build one from the schematics published in + * cq-DL :-). + * + * picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The + * modem is protocol compatible to par96, but uses only three low + * power ICs and can therefore be fed from the parallel port and + * does not require an additional power supply. It features + * built in DCD circuitry. The driver should therefore be configured + * for hardware DCD. + * + * + * Command line options (insmod command line) + * + * mode driver mode string. Valid choices are ser12 and par96. An + * optional * enables software DCD. + * 2=par96/par97, any other value invalid + * iobase base address of the port; common values are for ser12 0x3f8, + * 0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc + * irq interrupt line of the port; common values are for ser12 3,4 + * and for par96/par97 7 + * + * + * History: + * 0.1 26.06.96 Adapted from baycom.c and made network driver interface + * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) + * 0.3 26.04.96 init code/data tagged + */ + +/*****************************************************************************/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/hdlcdrv.h> +#include <linux/baycom.h> + +/* --------------------------------------------------------------------- */ + +/* + * currently this module is supposed to support both module styles, i.e. + * the old one present up to about 2.1.9, and the new one functioning + * starting with 2.1.21. The reason is I have a kit allowing to compile + * this module also under 2.0.x which was requested by several people. + * This will go in 2.2 + */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= 0x20100 +#include <asm/uaccess.h> +#else +#include <asm/segment.h> +#include <linux/mm.h> + +#undef put_user +#undef get_user + +#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) +#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) + +extern inline int copy_from_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_READ, from, n); + if (i) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +extern inline int copy_to_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_WRITE, to, n); + if (i) + return i; + memcpy_tofs(to, from, n); + return 0; +} +#endif + +#if LINUX_VERSION_CODE >= 0x20123 +#include <linux/init.h> +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +/* --------------------------------------------------------------------- */ + +#define BAYCOM_DEBUG + +/* + * modem options; bit mask + */ +#define BAYCOM_OPTIONS_SOFTDCD 1 + +/* --------------------------------------------------------------------- */ + +static const char bc_drvname[] = "baycom"; +static const char bc_drvinfo[] = KERN_INFO "baycom: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "baycom: version 0.3 compiled " __TIME__ " " __DATE__ "\n"; + +/* --------------------------------------------------------------------- */ + +#define NR_PORTS 4 + +static struct device baycom_device[NR_PORTS]; + +static struct { + char *mode; + int iobase, irq; +} baycom_ports[NR_PORTS] = { { NULL, 0, 0 }, }; + +/* --------------------------------------------------------------------- */ + +#define RBR(iobase) (iobase+0) +#define THR(iobase) (iobase+0) +#define IER(iobase) (iobase+1) +#define IIR(iobase) (iobase+2) +#define FCR(iobase) (iobase+2) +#define LCR(iobase) (iobase+3) +#define MCR(iobase) (iobase+4) +#define LSR(iobase) (iobase+5) +#define MSR(iobase) (iobase+6) +#define SCR(iobase) (iobase+7) +#define DLL(iobase) (iobase+0) +#define DLM(iobase) (iobase+1) + +#define SER12_EXTENT 8 + +#define LPT_DATA(iobase) (iobase+0) +#define LPT_STATUS(iobase) (iobase+1) +#define LPT_CONTROL(iobase) (iobase+2) +#define LPT_IRQ_ENABLE 0x10 +#define PAR96_BURSTBITS 16 +#define PAR96_BURST 4 +#define PAR96_PTT 2 +#define PAR96_TXBIT 1 +#define PAR96_ACK 0x40 +#define PAR96_RXBIT 0x20 +#define PAR96_DCD 0x10 +#define PAR97_POWER 0xf8 + +#define PAR96_EXTENT 3 + +/* ---------------------------------------------------------------------- */ +/* + * Information that need to be kept for each board. + */ + +struct baycom_state { + struct hdlcdrv_state hdrv; + + unsigned int options; + + struct modem_state { + short arb_divider; + unsigned char flags; + unsigned int shreg; + struct modem_state_ser12 { + unsigned char last_sample; + unsigned char interm_sample; + unsigned int bit_pll; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + unsigned char last_rxbit; + unsigned char tx_bit; + } ser12; + struct modem_state_par96 { + int dcd_count; + unsigned int dcd_shreg; + unsigned long descram; + unsigned long scram; + } par96; + } modem; + +#ifdef BAYCOM_DEBUG + struct debug_vals { + unsigned long last_jiffies; + unsigned cur_intcnt; + unsigned last_intcnt; + int cur_pllcorr; + int last_pllcorr; + } debug_vals; +#endif /* BAYCOM_DEBUG */ +}; + +/* --------------------------------------------------------------------- */ + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/* --------------------------------------------------------------------- */ + +static void inline baycom_int_freq(struct baycom_state *bc) +{ +#ifdef BAYCOM_DEBUG + unsigned long cur_jiffies = jiffies; + /* + * measure the interrupt frequency + */ + bc->debug_vals.cur_intcnt++; + if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) { + bc->debug_vals.last_jiffies = cur_jiffies; + bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; + bc->debug_vals.cur_intcnt = 0; + bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; + bc->debug_vals.cur_pllcorr = 0; + } +#endif /* BAYCOM_DEBUG */ +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== SER12 specific routines ========================= + */ + +static void inline ser12_set_divisor(struct device *dev, + unsigned char divisor) +{ + outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ + outb(divisor, DLL(dev->base_addr)); + outb(0, DLM(dev->base_addr)); + outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ + /* + * make sure the next interrupt is generated; + * 0 must be used to power the modem; the modem draws its + * power from the TxD line + */ + outb(0x00, THR(dev->base_addr)); + /* + * it is important not to set the divider while transmitting; + * this reportedly makes some UARTs generating interrupts + * in the hundredthousands per second region + * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno) + */ +} + +/* --------------------------------------------------------------------- */ + +/* + * must call the TX arbitrator every 10ms + */ +#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \ + 36 : 24) +#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \ + 240 : 12) + +static inline void ser12_tx(struct device *dev, struct baycom_state *bc) +{ + /* one interrupt per channel bit */ + ser12_set_divisor(dev, 12); + /* + * first output the last bit (!) then call HDLC transmitter, + * since this may take quite long + */ + outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); + if (bc->modem.shreg <= 1) + bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); + bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ + (bc->modem.shreg & 1)); + bc->modem.shreg >>= 1; +} + +/* --------------------------------------------------------------------- */ + +static inline void ser12_rx(struct device *dev, struct baycom_state *bc) +{ + unsigned char cur_s; + /* + * do demodulator + */ + cur_s = inb(MSR(dev->base_addr)) & 0x10; /* the CTS line */ + hdlcdrv_channelbit(&bc->hdrv, cur_s); + bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) | + (cur_s != bc->modem.ser12.last_sample); + bc->modem.ser12.last_sample = cur_s; + if(bc->modem.ser12.dcd_shreg & 1) { + if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { + unsigned int dcdspos, dcdsneg; + + dcdspos = dcdsneg = 0; + dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); + if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) + dcdspos += 2; + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); + + bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; + } else + bc->modem.ser12.dcd_sum0--; + } + if(!bc->modem.ser12.dcd_time) { + hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + + bc->modem.ser12.dcd_sum1 + + bc->modem.ser12.dcd_sum2) < 0); + bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; + bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; + /* offset to ensure DCD off on silent input */ + bc->modem.ser12.dcd_sum0 = 2; + bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); + } + bc->modem.ser12.dcd_time--; + if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { + /* + * PLL code for the improved software DCD algorithm + */ + if (bc->modem.ser12.interm_sample) { + /* + * intermediate sample; set timing correction to normal + */ + ser12_set_divisor(dev, 4); + } else { + /* + * do PLL correction and call HDLC receiver + */ + switch (bc->modem.ser12.dcd_shreg & 7) { + case 1: /* transition too late */ + ser12_set_divisor(dev, 5); +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr++; +#endif /* BAYCOM_DEBUG */ + break; + case 4: /* transition too early */ + ser12_set_divisor(dev, 3); +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr--; +#endif /* BAYCOM_DEBUG */ + break; + default: + ser12_set_divisor(dev, 4); + break; + } + bc->modem.shreg >>= 1; + if (bc->modem.ser12.last_sample == + bc->modem.ser12.last_rxbit) + bc->modem.shreg |= 0x10000; + bc->modem.ser12.last_rxbit = + bc->modem.ser12.last_sample; + } + if (++bc->modem.ser12.interm_sample >= 3) + bc->modem.ser12.interm_sample = 0; + /* + * DCD stuff + */ + if (bc->modem.ser12.dcd_shreg & 1) { + unsigned int dcdspos, dcdsneg; + + dcdspos = dcdsneg = 0; + dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); + dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) + << 1; + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); + + bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; + } + } else { + /* + * PLL algorithm for the hardware squelch DCD algorithm + */ + if (bc->modem.ser12.interm_sample) { + /* + * intermediate sample; set timing correction to normal + */ + ser12_set_divisor(dev, 6); + } else { + /* + * do PLL correction and call HDLC receiver + */ + switch (bc->modem.ser12.dcd_shreg & 3) { + case 1: /* transition too late */ + ser12_set_divisor(dev, 7); +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr++; +#endif /* BAYCOM_DEBUG */ + break; + case 2: /* transition too early */ + ser12_set_divisor(dev, 5); +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr--; +#endif /* BAYCOM_DEBUG */ + break; + default: + ser12_set_divisor(dev, 6); + break; + } + bc->modem.shreg >>= 1; + if (bc->modem.ser12.last_sample == + bc->modem.ser12.last_rxbit) + bc->modem.shreg |= 0x10000; + bc->modem.ser12.last_rxbit = + bc->modem.ser12.last_sample; + } + bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample; + /* + * DCD stuff + */ + bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1); + } + outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ + if (bc->modem.shreg & 1) { + hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1); + bc->modem.shreg = 0x10000; + } + if(!bc->modem.ser12.dcd_time) { + hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + + bc->modem.ser12.dcd_sum1 + + bc->modem.ser12.dcd_sum2) < 0); + bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; + bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; + /* offset to ensure DCD off on silent input */ + bc->modem.ser12.dcd_sum0 = 2; + bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); + } + bc->modem.ser12.dcd_time--; +} + +/* --------------------------------------------------------------------- */ + +static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) + return; + + baycom_int_freq(bc); + /* + * check if transmitter active + */ + if (hdlcdrv_ptt(&bc->hdrv)) + ser12_tx(dev, bc); + else { + ser12_rx(dev, bc); + if (--bc->modem.arb_divider <= 0) { + bc->modem.arb_divider = SER12_ARB_DIVIDER(bc); + sti(); + hdlcdrv_arbitrate(dev, &bc->hdrv); + } + } + sti(); + hdlcdrv_transmitter(dev, &bc->hdrv); + hdlcdrv_receiver(dev, &bc->hdrv); +} + +/* --------------------------------------------------------------------- */ + +enum uart { c_uart_unknown, c_uart_8250, + c_uart_16450, c_uart_16550, c_uart_16550A}; +static const char *uart_str[] = + { "unknown", "8250", "16450", "16550", "16550A" }; + +static enum uart ser12_check_uart(unsigned int iobase) +{ + unsigned char b1,b2,b3; + enum uart u; + enum uart uart_tab[] = + { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; + + b1 = inb(MCR(iobase)); + outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ + b2 = inb(MSR(iobase)); + outb(0x1a, MCR(iobase)); + b3 = inb(MSR(iobase)) & 0xf0; + outb(b1, MCR(iobase)); /* restore old values */ + outb(b2, MSR(iobase)); + if (b3 != 0x90) + return c_uart_unknown; + inb(RBR(iobase)); + inb(RBR(iobase)); + outb(0x01, FCR(iobase)); /* enable FIFOs */ + u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; + if (u == c_uart_16450) { + outb(0x5a, SCR(iobase)); + b1 = inb(SCR(iobase)); + outb(0xa5, SCR(iobase)); + b2 = inb(SCR(iobase)); + if ((b1 != 0x5a) || (b2 != 0xa5)) + u = c_uart_8250; + } + return u; +} + +/* --------------------------------------------------------------------- */ + +static int ser12_open(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + enum uart u; + + if (!dev || !bc) + return -ENXIO; + if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT || + dev->irq < 2 || dev->irq > 15) + return -ENXIO; + if (check_region(dev->base_addr, SER12_EXTENT)) + return -EACCES; + memset(&bc->modem, 0, sizeof(bc->modem)); + bc->hdrv.par.bitrate = 1200; + if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) + return -EIO; + outb(0, FCR(dev->base_addr)); /* disable FIFOs */ + outb(0x0d, MCR(dev->base_addr)); + outb(0x0d, MCR(dev->base_addr)); + outb(0, IER(dev->base_addr)); + if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT, + "baycom_ser12", dev)) + return -EBUSY; + request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"); + /* + * enable transmitter empty interrupt + */ + outb(2, IER(dev->base_addr)); + /* + * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that + * we get exactly (hopefully) 2 or 3 interrupts per radio symbol, + * depending on the usage of the software DCD routine + */ + ser12_set_divisor(dev, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6); + printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u options " + "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq, + bc->options, uart_str[u]); + MOD_INC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int ser12_close(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc) + return -EINVAL; + /* + * disable interrupts + */ + outb(0, IER(dev->base_addr)); + outb(1, MCR(dev->base_addr)); + free_irq(dev->irq, dev); + release_region(dev->base_addr, SER12_EXTENT); + printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n", + bc_drvname, dev->base_addr, dev->irq); + MOD_DEC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== PAR96 specific routines ========================= + */ + +#define PAR96_DESCRAM_TAP1 0x20000 +#define PAR96_DESCRAM_TAP2 0x01000 +#define PAR96_DESCRAM_TAP3 0x00001 + +#define PAR96_DESCRAM_TAPSH1 17 +#define PAR96_DESCRAM_TAPSH2 12 +#define PAR96_DESCRAM_TAPSH3 0 + +#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */ +#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */ + +/* --------------------------------------------------------------------- */ + +static inline void par96_tx(struct device *dev, struct baycom_state *bc) +{ + int i; + unsigned int data = hdlcdrv_getbits(&bc->hdrv); + + for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) { + unsigned char val = PAR97_POWER; + bc->modem.par96.scram = ((bc->modem.par96.scram << 1) | + (bc->modem.par96.scram & 1)); + if (!(data & 1)) + bc->modem.par96.scram ^= 1; + if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1)) + bc->modem.par96.scram ^= + (PAR96_SCRAM_TAPN << 1); + if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2)) + val |= PAR96_TXBIT; + outb(val, LPT_DATA(dev->base_addr)); + outb(val | PAR96_BURST, LPT_DATA(dev->base_addr)); + } +} + +/* --------------------------------------------------------------------- */ + +static inline void par96_rx(struct device *dev, struct baycom_state *bc) +{ + int i; + unsigned int data, mask, mask2, descx; + + /* + * do receiver; differential decode and descramble on the fly + */ + for(data = i = 0; i < PAR96_BURSTBITS; i++) { + bc->modem.par96.descram = (bc->modem.par96.descram << 1); + if (inb(LPT_STATUS(dev->base_addr)) & PAR96_RXBIT) + bc->modem.par96.descram |= 1; + descx = bc->modem.par96.descram ^ + (bc->modem.par96.descram >> 1); + /* now the diff decoded data is inverted in descram */ + outb(PAR97_POWER | PAR96_PTT, LPT_DATA(dev->base_addr)); + descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^ + (descx >> PAR96_DESCRAM_TAPSH2)); + data >>= 1; + if (!(descx & 1)) + data |= 0x8000; + outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, + LPT_DATA(dev->base_addr)); + } + hdlcdrv_putbits(&bc->hdrv, data); + /* + * do DCD algorithm + */ + if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { + bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16) + | (data << 16); + /* search for flags and set the dcd counter appropriately */ + for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; + i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) + if ((bc->modem.par96.dcd_shreg & mask) == mask2) + bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4; + /* check for abort/noise sequences */ + for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; + i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) + if (((bc->modem.par96.dcd_shreg & mask) == mask2) && + (bc->modem.par96.dcd_count >= 0)) + bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10; + /* decrement and set the dcd variable */ + if (bc->modem.par96.dcd_count >= 0) + bc->modem.par96.dcd_count -= 2; + hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0); + } else { + hdlcdrv_setdcd(&bc->hdrv, !!(inb(LPT_STATUS(dev->base_addr)) + & PAR96_DCD)); + } +} + +/* --------------------------------------------------------------------- */ + +static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) + return; + + baycom_int_freq(bc); + /* + * check if transmitter active + */ + if (hdlcdrv_ptt(&bc->hdrv)) + par96_tx(dev, bc); + else { + par96_rx(dev, bc); + if (--bc->modem.arb_divider <= 0) { + bc->modem.arb_divider = 6; + sti(); + hdlcdrv_arbitrate(dev, &bc->hdrv); + } + } + sti(); + hdlcdrv_transmitter(dev, &bc->hdrv); + hdlcdrv_receiver(dev, &bc->hdrv); +} + +/* --------------------------------------------------------------------- */ + +static int par96_check_lpt(unsigned int iobase) +{ + unsigned char b1,b2; + int i; + + b1 = inb(LPT_DATA(iobase)); + b2 = inb(LPT_CONTROL(iobase)); + outb(0xaa, LPT_DATA(iobase)); + i = inb(LPT_DATA(iobase)) == 0xaa; + outb(0x55, LPT_DATA(iobase)); + i &= inb(LPT_DATA(iobase)) == 0x55; + outb(0x0a, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a; + outb(0x05, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05; + outb(b1, LPT_DATA(iobase)); + outb(b2, LPT_CONTROL(iobase)); + return !i; +} + +/* --------------------------------------------------------------------- */ + +static int par96_open(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc) + return -ENXIO; + if (!dev->base_addr || dev->base_addr > 0x1000-PAR96_EXTENT || + dev->irq < 2 || dev->irq > 15) + return -ENXIO; + if (check_region(dev->base_addr, PAR96_EXTENT)) + return -EACCES; + memset(&bc->modem, 0, sizeof(bc->modem)); + bc->hdrv.par.bitrate = 9600; + if (par96_check_lpt(dev->base_addr)) + return -EIO; + /* disable interrupt */ + outb(0, LPT_CONTROL(dev->base_addr)); + /* switch off PTT */ + outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr)); + printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n", + bc_drvname, dev->base_addr, dev->irq, bc->options); + if (request_irq(dev->irq, par96_interrupt, SA_INTERRUPT, + "baycom_par96", dev)) + return -EBUSY; + request_region(dev->base_addr, PAR96_EXTENT, "baycom_par96"); + /* enable interrupt */ + outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev->base_addr)); + MOD_INC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int par96_close(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc) + return -EINVAL; + /* disable interrupt */ + outb(0, LPT_CONTROL(dev->base_addr)); + /* switch off PTT */ + outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr)); + free_irq(dev->irq, dev); + release_region(dev->base_addr, PAR96_EXTENT); + printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n", + bc_drvname, dev->base_addr, dev->irq); + MOD_DEC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== hdlcdrv driver interface ========================= + */ + +/* --------------------------------------------------------------------- */ + +static int baycom_ioctl(struct device *dev, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd); + +/* --------------------------------------------------------------------- */ + +static struct hdlcdrv_ops ser12_ops = { + bc_drvname, + bc_drvinfo, + ser12_open, + ser12_close, + baycom_ioctl +}; + +/* --------------------------------------------------------------------- */ + +static struct hdlcdrv_ops par96_ops = { + bc_drvname, + bc_drvinfo, + par96_open, + par96_close, + baycom_ioctl +}; + +/* --------------------------------------------------------------------- */ + +static struct hdlcdrv_ops dummy_ops = { + bc_drvname, + bc_drvinfo, + NULL, + NULL, + baycom_ioctl +}; + +/* --------------------------------------------------------------------- */ + +static int baycom_setmode(struct baycom_state *bc, char *modestr) +{ + struct hdlcdrv_ops *newops = NULL; + unsigned long flags; + + if (!strncmp(modestr, "off", 3)) + newops = &dummy_ops; + else if (!strncmp(modestr, "ser12", 5)) + newops = &ser12_ops; + else if (!strncmp(modestr, "par96", 5)) + newops = &par96_ops; + else + return -EINVAL; + save_flags(flags); + cli(); + bc->hdrv.ops = newops; + bc->options = !!strchr(modestr, '*'); + restore_flags(flags); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int baycom_ioctl(struct device *dev, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd) +{ + struct baycom_state *bc; + struct baycom_ioctl bi; + int cmd2; + + if (!dev || !dev->priv || + ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { + printk(KERN_ERR "bc_ioctl: invalid device struct\n"); + return -EINVAL; + } + bc = (struct baycom_state *)dev->priv; + + if (cmd != SIOCDEVPRIVATE) + return -ENOIOCTLCMD; + if (get_user(cmd2, (int *)ifr->ifr_data)) + return -EFAULT; + switch (hi->cmd) { + default: + break; + + case HDLCDRVCTL_GETMODE: + if (bc->hdrv.ops == &ser12_ops) + strcpy(hi->data.modename, "ser12"); + else if (bc->hdrv.ops == &par96_ops) + strcpy(hi->data.modename, "par96"); + else if (bc->hdrv.ops == &dummy_ops) + strcpy(hi->data.modename, "off"); + else + strcpy(hi->data.modename, "invalid"); + if (bc->options & 1) + strcat(hi->data.modename, "*"); + if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl))) + return -EFAULT; + return 0; + + case HDLCDRVCTL_SETMODE: + if (!suser() || dev->start) + return -EACCES; + hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; + return baycom_setmode(bc, hi->data.modename); + + case HDLCDRVCTL_MODELIST: + strcpy(hi->data.modename, "ser12,par96"); + if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl))) + return -EFAULT; + return 0; + + case HDLCDRVCTL_MODEMPARMASK: + return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ; + + } + + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + switch (bi.cmd) { + default: + return -ENOIOCTLCMD; + +#ifdef BAYCOM_DEBUG + case BAYCOMCTL_GETDEBUG: + bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; + bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; + bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; + break; +#endif /* BAYCOM_DEBUG */ + + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + +} + +/* --------------------------------------------------------------------- */ + +__initfunc(int baycom_init(void)) +{ + int i, j, found = 0; + char set_hw = 1; + struct baycom_state *bc; + char ifname[HDLCDRV_IFNAMELEN]; + + + printk(bc_drvinfo); + /* + * register net devices + */ + for (i = 0; i < NR_PORTS; i++) { + struct device *dev = baycom_device+i; + sprintf(ifname, "bc%d", i); + + if (!baycom_ports[i].mode) + set_hw = 0; + if (!set_hw) + baycom_ports[i].iobase = baycom_ports[i].irq = 0; + j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops, + sizeof(struct baycom_state), + ifname, baycom_ports[i].iobase, + baycom_ports[i].irq, 0); + if (!j) { + bc = (struct baycom_state *)dev->priv; + if (set_hw && baycom_setmode(bc, baycom_ports[i].mode)) + set_hw = 0; + found++; + } else { + printk(KERN_WARNING "%s: cannot register net device\n", + bc_drvname); + } + } + if (!found) + return -ENXIO; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +/* + * command line settable parameters + */ +static char *mode = NULL; +static int iobase = 0x3f8; +static int irq = 4; + +#if LINUX_VERSION_CODE >= 0x20115 + +MODULE_PARM(mode, "s"); +MODULE_PARM_DESC(mode, "baycom operating mode; eg. ser12* or par96"); +MODULE_PARM(iobase, "i"); +MODULE_PARM_DESC(iobase, "baycom io base address"); +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(irq, "baycom irq number"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("Baycom ser12, par96 and picpar amateur radio modem driver"); + +#endif + +__initfunc(int init_module(void)) +{ + baycom_ports[0].mode = mode; + baycom_ports[0].iobase = iobase; + baycom_ports[0].irq = irq; + baycom_ports[1].mode = NULL; + + return baycom_init(); +} + +/* --------------------------------------------------------------------- */ + +void cleanup_module(void) +{ + int i; + + for(i = 0; i < NR_PORTS; i++) { + struct device *dev = baycom_device+i; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (bc) { + if (bc->hdrv.magic != HDLCDRV_MAGIC) + printk(KERN_ERR "baycom: invalid magic in " + "cleanup_module\n"); + else + hdlcdrv_unregister_hdlcdrv(dev); + } + } +} + +#else /* MODULE */ +/* --------------------------------------------------------------------- */ +/* + * format: baycom=io,irq,mode + * mode: {ser12,par96}[*] + * * indicates sofware DCD + */ + +__initfunc(void baycom_setup(char *str, int *ints)) +{ + int i; + + for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++); + if ((i >= NR_PORTS) || (ints[0] < 2)) { + printk(KERN_INFO "%s: too many or invalid interface " + "specifications\n", bc_drvname); + return; + } + baycom_ports[i].mode = str; + baycom_ports[i].iobase = ints[1]; + baycom_ports[i].irq = ints[2]; + if (i < NR_PORTS-1) + baycom_ports[i+1].mode = NULL; +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/bpqether.c linux/drivers/net/bpqether.c --- v2.0.34/linux/drivers/net/bpqether.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/bpqether.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,685 @@ +/* + * G8BPQ compatible "AX.25 via ethernet" driver release 003 + * + * This is ALPHA test software. This code may break your machine, randomly + * fail to work with new releases, misbehave and/or generally screw up. + * It might even work. + * + * This code REQUIRES 2.0.0 or higher/ NET3.029 + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This is a "pseudo" network driver to allow AX.25 over Ethernet + * using G8BPQ encapsulation. It has been extracted from the protocol + * implementation because + * + * - things got unreadable within the protocol stack + * - to cure the protocol stack from "feature-ism" + * - a protocol implementation shouldn't need to know on + * which hardware it is running + * - user-level programs like the AX.25 utilities shouldn't + * need to know about the hardware. + * - IP over ethernet encapsulated AX.25 was impossible + * - rxecho.c did not work + * - to have room for extensions + * - it just deserves to "live" as an own driver + * + * This driver can use any ethernet destination address, and can be + * limited to accept frames from one dedicated ethernet card only. + * + * Note that the driver sets up the BPQ devices automagically on + * startup or (if started before the "insmod" of an ethernet device) + * on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing + * the ethernet device (in fact: as soon as another ethernet or bpq + * device gets "ifconfig"ured). + * + * I have heard that several people are thinking of experiments + * with highspeed packet radio using existing ethernet cards. + * Well, this driver is prepared for this purpose, just add + * your tx key control and a txdelay / tailtime algorithm, + * probably some buffering, and /voila/... + * + * History + * BPQ 001 Joerg(DL1BKE) Extracted BPQ code from AX.25 + * protocol stack and added my own + * yet existing patches + * BPQ 002 Joerg(DL1BKE) Scan network device list on + * startup. + * BPQ 003 Joerg(DL1BKE) Ethernet destination address + * and accepted source address + * can be configured by an ioctl() + * call. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/net.h> +#include <net/ax25.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <asm/segment.h> +#include <asm/system.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/notifier.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> +#include <linux/firewall.h> +#include <linux/module.h> +#include <linux/net_alias.h> + +#include <net/ip.h> +#include <net/arp.h> + +#include <linux/bpqether.h> + +static unsigned char ax25_bcast[AX25_ADDR_LEN] = + {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; +static unsigned char ax25_defaddr[AX25_ADDR_LEN] = + {'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; + +static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + +static char bpq_eth_addr[6]; + +static int bpq_rcv(struct sk_buff *, struct device *, struct packet_type *); +static int bpq_device_event(struct notifier_block *, unsigned long, void *); +static char *bpq_print_ethaddr(unsigned char *); + +static struct packet_type bpq_packet_type = { + 0, /* ntohs(ETH_P_BPQ),*/ + 0, /* copy */ + bpq_rcv, + NULL, + NULL, +}; + +static struct notifier_block bpq_dev_notifier = { + bpq_device_event, + 0 +}; + + +#define MAXBPQDEV 100 + +static struct bpqdev { + struct bpqdev *next; + char ethname[14]; /* ether device name */ + struct device *ethdev; /* link to ethernet device */ + struct device axdev; /* bpq device (bpq#) */ + struct enet_statistics stats; /* some statistics */ + char dest_addr[6]; /* ether destination address */ + char acpt_addr[6]; /* accept ether frames from this address only */ +} *bpq_devices = NULL; + + +/* ------------------------------------------------------------------------ */ + + +/* + * Get the ethernet device for a BPQ device + */ +static __inline__ struct device *bpq_get_ether_dev(struct device *dev) +{ + struct bpqdev *bpq; + + bpq = (struct bpqdev *)dev->priv; + + return (bpq != NULL) ? bpq->ethdev : NULL; +} + +/* + * Get the BPQ device for the ethernet device + */ +static __inline__ struct device *bpq_get_ax25_dev(struct device *dev) +{ + struct bpqdev *bpq; + + for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) + if (bpq->ethdev == dev) + return &bpq->axdev; + + return NULL; +} + +static __inline__ int dev_is_ethdev(struct device *dev) +{ + return ( + dev->type == ARPHRD_ETHER + && strncmp(dev->name, "dummy", 5) +#ifdef CONFIG_NET_ALIAS + && !net_alias_is(dev) +#endif + ); +} + +/* + * Sanity check: remove all devices that ceased to exists and + * return '1' if the given BPQ device was affected. + */ +static int bpq_check_devices(struct device *dev) +{ + struct bpqdev *bpq, *bpq_prev; + int result = 0; + unsigned long flags; + + save_flags(flags); + cli(); + + bpq_prev = NULL; + + for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) { + if (!dev_get(bpq->ethname)) { + if (bpq_prev) + bpq_prev->next = bpq->next; + else + bpq_devices = bpq->next; + + if (&bpq->axdev == dev) + result = 1; + + unregister_netdev(&bpq->axdev); + kfree(bpq); + } + + bpq_prev = bpq; + } + + restore_flags(flags); + + return result; +} + + +/* ------------------------------------------------------------------------ */ + + +/* + * Receive an AX.25 frame via an ethernet interface. + */ +static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) +{ + int len; + char * ptr; + struct ethhdr *eth = (struct ethhdr *)skb->mac.raw; + struct bpqdev *bpq; + + skb->sk = NULL; /* Initially we don't know who it's for */ + + dev = bpq_get_ax25_dev(dev); + + if (dev == NULL || dev->start == 0) { + kfree_skb(skb, FREE_READ); + return 0; + } + + /* + * if we want to accept frames from just one ethernet device + * we check the source address of the sender. + */ + + bpq = (struct bpqdev *)dev->priv; + + if (!(bpq->acpt_addr[0] & 0x01) && memcmp(eth->h_source, bpq->acpt_addr, ETH_ALEN)) { + printk(KERN_DEBUG "bpqether: wrong dest %s\n", bpq_print_ethaddr(eth->h_source)); + kfree_skb(skb, FREE_READ); + return 0; + } + + ((struct bpqdev *)dev->priv)->stats.rx_packets++; + + len = skb->data[0] + skb->data[1] * 256 - 5; + + skb_pull(skb, 2); /* Remove the length bytes */ + skb_trim(skb, len); /* Set the length of the data */ + + ptr = skb_push(skb, 1); + *ptr = 0; + + skb->dev = dev; + skb->protocol = htons(ETH_P_AX25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); + + return 0; +} + +/* + * Send an AX.25 frame via an ethernet interface + */ +static int bpq_xmit(struct sk_buff *skb, struct device *dev) +{ + unsigned char *ptr; + struct bpqdev *bpq; + int size; + + /* + * Just to be *really* sure not to send anything if the interface + * is down, the ethernet device may have gone. + */ + if (!dev->start) { + bpq_check_devices(dev); + dev_kfree_skb(skb, FREE_WRITE); + return -ENODEV; + } + + skb_pull(skb, 1); + size = skb->len; + + /* + * The AX.25 code leaves enough room for the ethernet header, but + * sendto() does not. + */ + if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) { /* Ough! */ + struct sk_buff *newskb = alloc_skb(skb->len + AX25_BPQ_HEADER_LEN, GFP_ATOMIC); + + if (newskb == NULL) { /* Argh! */ + printk(KERN_WARNING "bpq_xmit: not enough space to add BPQ Ether header\n"); + dev_kfree_skb(skb, FREE_WRITE); + return -ENOMEM; + } + + newskb->free = 1; + newskb->arp = 1; + if (skb->sk != NULL) + atomic_add(newskb->truesize, &skb->sk->wmem_alloc); + newskb->sk = skb->sk; + + skb_reserve(newskb, AX25_BPQ_HEADER_LEN); + memcpy(skb_put(newskb, size), skb->data, size); + dev_kfree_skb(skb, FREE_WRITE); + skb = newskb; + } + + skb->protocol = htons(ETH_P_AX25); + + ptr = skb_push(skb, 2); + + *ptr++ = (size + 5) % 256; + *ptr++ = (size + 5) / 256; + + bpq = (struct bpqdev *)dev->priv; + bpq->stats.tx_packets++; + + if ((dev = bpq_get_ether_dev(dev)) == NULL) { + bpq->stats.tx_dropped++; + dev_kfree_skb(skb, FREE_WRITE); + return -ENODEV; + } + + skb->dev = dev; + dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); + + dev_queue_xmit(skb, dev, SOPRI_NORMAL); + + return 0; +} + +/* + * Statistics + */ +static struct enet_statistics *bpq_get_stats(struct device *dev) +{ + struct bpqdev *bpq; + + bpq = (struct bpqdev *)dev->priv; + + return &bpq->stats; +} + +/* + * Set AX.25 callsign + */ +static int bpq_set_mac_address(struct device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *)addr; + + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + + return 0; +} + +/* Ioctl commands + * + * SIOCSBPQETHOPT reserved for enhancements + * SIOCSBPQETHADDR set the destination and accepted + * source ethernet address (broadcast + * or multicast: accept all) + */ +static int bpq_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + int err; + struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data; + struct bpqdev *bpq = dev->priv; + struct bpq_req req; + + if (!suser()) + return -EPERM; + + if (bpq == NULL) /* woops! */ + return -ENODEV; + + switch (cmd) { + case SIOCSBPQETHOPT: + if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0) + return err; + memcpy_fromfs(&req, ifr->ifr_data, sizeof(struct bpq_req)); + switch (req.cmd) { + case SIOCGBPQETHPARAM: + case SIOCSBPQETHPARAM: + default: + return -EINVAL; + } + + break; + + case SIOCSBPQETHADDR: + if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0) + return err; + memcpy_fromfs(bpq->dest_addr, ethaddr->destination, ETH_ALEN); + memcpy_fromfs(bpq->acpt_addr, ethaddr->accept, ETH_ALEN); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * open/close a device + */ +static int bpq_open(struct device *dev) +{ + if (bpq_check_devices(dev)) + return -ENODEV; /* oops, it's gone */ + + dev->tbusy = 0; + dev->start = 1; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int bpq_close(struct device *dev) +{ + dev->tbusy = 1; + dev->start = 0; + + MOD_DEC_USE_COUNT; + + return 0; +} + +/* + * currently unused + */ +static int bpq_dev_init(struct device *dev) +{ + return 0; +} + + +/* ------------------------------------------------------------------------ */ + + +/* + * Proc filesystem + */ +static char * bpq_print_ethaddr(unsigned char *e) +{ + static char buf[18]; + + sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + e[0], e[1], e[2], e[3], e[4], e[5]); + + return buf; +} + +int bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + struct bpqdev *bpqdev; + int len = 0; + off_t pos = 0; + off_t begin = 0; + + cli(); + + len += sprintf(buffer, "dev ether destination accept from\n"); + + for (bpqdev = bpq_devices; bpqdev != NULL; bpqdev = bpqdev->next) { + len += sprintf(buffer + len, "%-5s %-10s %s ", + bpqdev->axdev.name, bpqdev->ethname, + bpq_print_ethaddr(bpqdev->dest_addr)); + + len += sprintf(buffer + len, "%s\n", + (bpqdev->acpt_addr[0] & 0x01) ? "*" : bpq_print_ethaddr(bpqdev->acpt_addr)); + + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + + if (pos > offset + length) + break; + } + + sti(); + + *start = buffer + (offset - begin); + len -= (offset - begin); + + if (len > length) len = length; + + return len; +} + + +/* ------------------------------------------------------------------------ */ + + +/* + * Setup a new device. + */ +static int bpq_new_device(struct device *dev) +{ + int k; + unsigned char *buf; + struct bpqdev *bpq, *bpq2; + + if ((bpq = (struct bpqdev *)kmalloc(sizeof(struct bpqdev), GFP_KERNEL)) == NULL) + return -ENOMEM; + + memset(bpq, 0, sizeof(struct bpqdev)); + + bpq->ethdev = dev; + + bpq->ethname[sizeof(bpq->ethname)-1] = '\0'; + strncpy(bpq->ethname, dev->name, sizeof(bpq->ethname)-1); + + memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr)); + memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr)); + + dev = &bpq->axdev; + buf = (unsigned char *)kmalloc(14, GFP_KERNEL); + + for (k = 0; k < MAXBPQDEV; k++) { + struct device *odev; + + sprintf(buf, "bpq%d", k); + + if ((odev = dev_get(buf)) == NULL || bpq_check_devices(odev)) + break; + } + + if (k == MAXBPQDEV) { + kfree(bpq); + return -ENODEV; + } + + dev->priv = (void *)bpq; /* pointer back */ + dev->name = buf; + dev->init = bpq_dev_init; + + if (register_netdev(dev) != 0) { + kfree(bpq); + return -EIO; + } + + for (k=0; k < DEV_NUMBUFFS; k++) + skb_queue_head_init(&dev->buffs[k]); + + dev->hard_start_xmit = bpq_xmit; + dev->open = bpq_open; + dev->stop = bpq_close; + dev->set_mac_address = bpq_set_mac_address; + dev->get_stats = bpq_get_stats; + dev->do_ioctl = bpq_ioctl; + + memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); + memcpy(dev->dev_addr, ax25_defaddr, AX25_ADDR_LEN); + + /* preset with reasonable values */ + + dev->flags = 0; + dev->family = AF_INET; + +#ifdef CONFIG_INET + dev->pa_addr = in_aton("192.168.0.1"); + dev->pa_brdaddr = in_aton("192.168.0.255"); + dev->pa_mask = in_aton("255.255.255.0"); + dev->pa_alen = 4; +#endif + +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + dev->hard_header = ax25_encapsulate; + dev->rebuild_header = ax25_rebuild_header; +#endif + + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; + dev->mtu = AX25_DEF_PACLEN; + dev->addr_len = AX25_ADDR_LEN; + + cli(); + + if (bpq_devices == NULL) { + bpq_devices = bpq; + } else { + for (bpq2 = bpq_devices; bpq2->next != NULL; bpq2 = bpq2->next); + bpq2->next = bpq; + } + + sti(); + + return 0; +} + + +/* + * Handle device status changes. + */ +static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr) +{ + struct device *dev = (struct device *)ptr; + + if (!dev_is_ethdev(dev)) + return NOTIFY_DONE; + + bpq_check_devices(NULL); + + switch (event) { + case NETDEV_UP: /* new ethernet device -> new BPQ interface */ + if (bpq_get_ax25_dev(dev) == NULL) + bpq_new_device(dev); + break; + + case NETDEV_DOWN: /* ethernet device closed -> close BPQ interface */ + if ((dev = bpq_get_ax25_dev(dev)) != NULL) + dev_close(dev); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + + +/* ------------------------------------------------------------------------ */ + +/* + * Initialize driver. To be called from af_ax25 if not compiled as a + * module + */ +int bpq_init(void) +{ + struct device *dev; + + bpq_packet_type.type = htons(ETH_P_BPQ); + dev_add_pack(&bpq_packet_type); + + register_netdevice_notifier(&bpq_dev_notifier); + + printk(KERN_INFO "AX.25 ethernet driver version 0.01\n"); + +#ifdef CONFIG_PROC_FS + proc_net_register(&(struct proc_dir_entry) { + PROC_NET_AX25_BPQETHER, 8, "bpqether", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + bpq_get_info + }); +#endif + + for (dev = dev_base; dev != NULL; dev = dev->next) { + if (dev_is_ethdev(dev)) + bpq_new_device(dev); + } + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + register_symtab(NULL); + + return bpq_init(); +} + +void cleanup_module(void) +{ + struct bpqdev *bpq; + + dev_remove_pack(&bpq_packet_type); + + unregister_netdevice_notifier(&bpq_dev_notifier); + +#ifdef CONFIG_PROC_FS + proc_net_unregister(PROC_NET_AX25_BPQETHER); +#endif + + for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) + unregister_netdev(&bpq->axdev); +} +#endif diff -u --recursive --new-file v2.0.34/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c --- v2.0.34/linux/drivers/net/dgrs.c Tue Aug 12 16:05:23 1997 +++ linux/drivers/net/dgrs.c Mon Jul 13 13:47:31 1998 @@ -405,7 +405,7 @@ */ 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 @@ /* 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 -u --recursive --new-file v2.0.34/linux/drivers/net/e2100.c linux/drivers/net/e2100.c --- v2.0.34/linux/drivers/net/e2100.c Thu Feb 29 21:50:43 1996 +++ linux/drivers/net/e2100.c Mon Jul 13 13:47:31 1998 @@ -101,7 +101,7 @@ 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 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c --- v2.0.34/linux/drivers/net/eepro100.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/eepro100.c Mon Jul 13 13:47:31 1998 @@ -1,4 +1,4 @@ -/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */ +/* drivers/net/eepro100.c: An Intel i82557 Ethernet driver for Linux. */ /* NOTICE: this version tested with kernels 1.3.72 and later only! Written 1996-1998 by Donald Becker. @@ -109,7 +109,7 @@ I. Board Compatibility This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's -single-chip fast ethernet controller for PCI, as used on the Intel +single-chip fast Ethernet controller for PCI, as used on the Intel EtherExpress Pro 100 adapter. II. Board-specific settings @@ -163,7 +163,7 @@ added asynchronous to the normal transmit queue, so we disable interrupts whenever the Tx descriptor ring is manipulated. -A notable aspect of the these special configure commands is that they do +A notable aspect of these special configure commands is that they do work with the normal Tx ring entry scavenge method. The Tx ring scavenge is done at interrupt time using the 'dirty_tx' index, and checking for the command-complete bit. While the setup frames may have the NoOp command on the diff -u --recursive --new-file v2.0.34/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c --- v2.0.34/linux/drivers/net/eth16i.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/eth16i.c Mon Jul 13 13:47:31 1998 @@ -714,7 +714,7 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c --- v2.0.34/linux/drivers/net/ewrk3.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/ewrk3.c Mon Jul 13 13:47:31 1998 @@ -1141,7 +1141,7 @@ /* ** Clean out the TX and RX queues here (note that one entry - ** may get added to either the TXD or RX queues if the the TX or RX + ** may get added to either the TXD or RX queues if the TX or RX ** just starts processing a packet before the STOP_EWRK3 command ** is received. This will be flushed in the ewrk3_open() call). */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c --- v2.0.34/linux/drivers/net/hdlcdrv.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/hdlcdrv.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,1037 @@ +/*****************************************************************************/ + +/* + * hdlcdrv.c -- HDLC packet radio network driver. + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + * The driver was derived from Donald Beckers skeleton.c + * Written 1993-94 by Donald Becker. + * + * History: + * 0.1 21.09.96 Started + * 18.10.96 Changed to new user space access routines + * (copy_{to,from}_user) + * 0.2 21.11.96 various small changes + * 0.3 03.03.97 fixed (hopefully) IP not working with ax.25 as a module + * 0.4 16.04.97 init code/data tagged + */ + +/*****************************************************************************/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/net.h> +#include <linux/in.h> +#include <linux/if.h> +#include <linux/malloc.h> +#include <linux/errno.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/hdlcdrv.h> +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) +/* prototypes for ax25_encapsulate and ax25_rebuild_header */ +#include <net/ax25.h> +#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ + +/* make genksyms happy */ +#include <linux/ip.h> +#include <linux/udp.h> +#include <linux/tcp.h> +#include <linux/net_alias.h> + +/* --------------------------------------------------------------------- */ + +/* + * currently this module is supposed to support both module styles, i.e. + * the old one present up to about 2.1.9, and the new one functioning + * starting with 2.1.21. The reason is I have a kit allowing to compile + * this module also under 2.0.x which was requested by several people. + * This will go in 2.2 + */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= 0x20100 +#include <asm/uaccess.h> +#else +#include <asm/segment.h> +#include <linux/mm.h> + +#undef put_user +#undef get_user + +#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) +#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) + +extern inline int copy_from_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_READ, from, n); + if (i) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +extern inline int copy_to_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_WRITE, to, n); + if (i) + return i; + memcpy_tofs(to, from, n); + return 0; +} +#endif + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 0x20115 +extern __inline__ void dev_init_buffers(struct device *dev) +{ + int i; + for(i=0;i<DEV_NUMBUFFS;i++) + { + skb_queue_head_init(&dev->buffs[i]); + } +} +#endif + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= 0x20123 +#include <linux/init.h> +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 0x20125 +#define test_and_set_bit set_bit +#define test_and_clear_bit clear_bit +#endif + +/* --------------------------------------------------------------------- */ + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ + +static char ax25_bcast[7] = +{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; +static char ax25_test[7] = +{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; + +/* --------------------------------------------------------------------- */ + +#define KISS_VERBOSE + +/* --------------------------------------------------------------------- */ + +#define PARAM_TXDELAY 1 +#define PARAM_PERSIST 2 +#define PARAM_SLOTTIME 3 +#define PARAM_TXTAIL 4 +#define PARAM_FULLDUP 5 +#define PARAM_HARDWARE 6 +#define PARAM_RETURN 255 + +/* --------------------------------------------------------------------- */ + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/* --------------------------------------------------------------------- */ +/* + * the CRC routines are stolen from WAMPES + * by Dieter Deyke + */ + +static const unsigned short crc_ccitt_table[] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/*---------------------------------------------------------------------------*/ + +static inline void append_crc_ccitt(unsigned char *buffer, int len) +{ + unsigned int crc = 0xffff; + + for (;len>0;len--) + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff]; + crc ^= 0xffff; + *buffer++ = crc; + *buffer++ = crc >> 8; +} + +/*---------------------------------------------------------------------------*/ + +static inline int check_crc_ccitt(const unsigned char *buf, int cnt) +{ + unsigned int crc = 0xffff; + + for (; cnt > 0; cnt--) + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; + return (crc & 0xffff) == 0xf0b8; +} + +/*---------------------------------------------------------------------------*/ + +#if 0 +static int calc_crc_ccitt(const unsigned char *buf, int cnt) +{ + unsigned int crc = 0xffff; + + for (; cnt > 0; cnt--) + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; + crc ^= 0xffff; + return (crc & 0xffff); +} +#endif + +/* ---------------------------------------------------------------------- */ + +#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16) + +/* ---------------------------------------------------------------------- */ +/* + * The HDLC routines + */ + +static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, + int num) +{ + int added = 0; + + while (s->hdlcrx.rx_state && num >= 8) { + if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) { + s->hdlcrx.rx_state = 0; + return 0; + } + *s->hdlcrx.bp++ = bits >> (32-num); + s->hdlcrx.len++; + num -= 8; + added += 8; + } + return added; +} + +static void hdlc_rx_flag(struct device *dev, struct hdlcdrv_state *s) +{ + struct sk_buff *skb; + int pkt_len; + unsigned char *cp; + + if (s->hdlcrx.len < 4) + return; + if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) + return; + pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */ + if (!(skb = dev_alloc_skb(pkt_len))) { + printk("%s: memory squeeze, dropping packet\n", + s->ifname); + s->stats.rx_dropped++; + return; + } + skb->dev = dev; + cp = skb_put(skb, pkt_len); + *cp++ = 0; /* KISS kludge */ + memcpy(cp, s->hdlcrx.buffer, pkt_len - 1); + skb->protocol = htons(ETH_P_AX25); + skb->mac.raw = skb->data; + netif_rx(skb); + s->stats.rx_packets++; +} + +void hdlcdrv_receiver(struct device *dev, struct hdlcdrv_state *s) +{ + int i; + unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word; + + if (!s || s->magic != HDLCDRV_MAGIC) + return; + if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx)) + return; + + while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) { + word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf); + +#ifdef HDLCDRV_DEBUG + hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word); +#endif /* HDLCDRV_DEBUG */ + s->hdlcrx.bitstream >>= 16; + s->hdlcrx.bitstream |= word << 16; + s->hdlcrx.bitbuf >>= 16; + s->hdlcrx.bitbuf |= word << 16; + s->hdlcrx.numbits += 16; + for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00, + mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; + i >= 0; + i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, + mask5 <<= 1, mask6 = (mask6 << 1) | 1) { + if ((s->hdlcrx.bitstream & mask1) == mask1) + s->hdlcrx.rx_state = 0; /* abort received */ + else if ((s->hdlcrx.bitstream & mask2) == mask3) { + /* flag received */ + if (s->hdlcrx.rx_state) { + hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf + << (8+i), + s->hdlcrx.numbits + -8-i); + hdlc_rx_flag(dev, s); + } + s->hdlcrx.len = 0; + s->hdlcrx.bp = s->hdlcrx.buffer; + s->hdlcrx.rx_state = 1; + s->hdlcrx.numbits = i; + } else if ((s->hdlcrx.bitstream & mask4) == mask5) { + /* stuffed bit */ + s->hdlcrx.numbits--; + s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) | + ((s->hdlcrx.bitbuf & mask6) << 1); + } + } + s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf, + s->hdlcrx.numbits); + } + clear_bit(0, &s->hdlcrx.in_hdlc_rx); +} + +/* ---------------------------------------------------------------------- */ + +static void inline do_kiss_params(struct hdlcdrv_state *s, + unsigned char *data, unsigned long len) +{ + +#ifdef KISS_VERBOSE +#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", s->ifname, b) +#else /* KISS_VERBOSE */ +#define PKP(a,b) +#endif /* KISS_VERBOSE */ + + if (len < 2) + return; + switch(data[0]) { + case PARAM_TXDELAY: + s->ch_params.tx_delay = data[1]; + PKP("TX delay = %ums", 10 * s->ch_params.tx_delay); + break; + case PARAM_PERSIST: + s->ch_params.ppersist = data[1]; + PKP("p persistence = %u", s->ch_params.ppersist); + break; + case PARAM_SLOTTIME: + s->ch_params.slottime = data[1]; + PKP("slot time = %ums", s->ch_params.slottime); + break; + case PARAM_TXTAIL: + s->ch_params.tx_tail = data[1]; + PKP("TX tail = %ums", s->ch_params.tx_tail); + break; + case PARAM_FULLDUP: + s->ch_params.fulldup = !!data[1]; + PKP("%s duplex", s->ch_params.fulldup ? "full" : "half"); + break; + default: + break; + } +#undef PKP +} + +/* ---------------------------------------------------------------------- */ + +void hdlcdrv_transmitter(struct device *dev, struct hdlcdrv_state *s) +{ + unsigned int mask1, mask2, mask3; + int i; + struct sk_buff *skb; + int pkt_len; + + if (!s || s->magic != HDLCDRV_MAGIC) + return; + if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx)) + return; + for (;;) { + if (s->hdlctx.numbits >= 16) { + if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) { + clear_bit(0, &s->hdlctx.in_hdlc_tx); + return; + } + hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf); + s->hdlctx.bitbuf >>= 16; + s->hdlctx.numbits -= 16; + } + switch (s->hdlctx.tx_state) { + default: + clear_bit(0, &s->hdlctx.in_hdlc_tx); + return; + case 0: + case 1: + if (s->hdlctx.numflags) { + s->hdlctx.numflags--; + s->hdlctx.bitbuf |= + 0x7e7e << s->hdlctx.numbits; + s->hdlctx.numbits += 16; + break; + } + if (s->hdlctx.tx_state == 1) { + clear_bit(0, &s->hdlctx.in_hdlc_tx); + return; + } + if (!(skb = skb_dequeue(&s->send_queue))) { + int flgs = tenms_to_2flags + (s, s->ch_params.tx_tail); + if (flgs < 2) + flgs = 2; + s->hdlctx.tx_state = 1; + s->hdlctx.numflags = flgs; + break; + } + if (skb->data[0] != 0) { + do_kiss_params(s, skb->data, skb->len); + dev_kfree_skb(skb, FREE_WRITE); + break; + } + pkt_len = skb->len-1; /* strip KISS byte */ + if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) { + s->hdlctx.tx_state = 0; + s->hdlctx.numflags = 1; + dev_kfree_skb(skb, FREE_WRITE); + break; + } + memcpy(s->hdlctx.buffer, skb->data+1, pkt_len); + dev_kfree_skb(skb, FREE_WRITE); + s->hdlctx.bp = s->hdlctx.buffer; + append_crc_ccitt(s->hdlctx.buffer, pkt_len); + s->hdlctx.len = pkt_len+2; /* the appended CRC */ + s->hdlctx.tx_state = 2; + s->hdlctx.bitstream = 0; + s->stats.tx_packets++; + break; + case 2: + if (!s->hdlctx.len) { + s->hdlctx.tx_state = 0; + s->hdlctx.numflags = 1; + break; + } + s->hdlctx.len--; + s->hdlctx.bitbuf |= *s->hdlctx.bp << + s->hdlctx.numbits; + s->hdlctx.bitstream >>= 8; + s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16; + mask1 = 0x1f000; + mask2 = 0x10000; + mask3 = 0xffffffff >> (31-s->hdlctx.numbits); + s->hdlctx.numbits += 8; + for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, + mask3 = (mask3 << 1) | 1) { + if ((s->hdlctx.bitstream & mask1) != mask1) + continue; + s->hdlctx.bitstream &= ~mask2; + s->hdlctx.bitbuf = + (s->hdlctx.bitbuf & mask3) | + ((s->hdlctx.bitbuf & + (~mask3)) << 1); + s->hdlctx.numbits++; + mask3 = (mask3 << 1) | 1; + } + break; + } + } +} + +/* ---------------------------------------------------------------------- */ + +static void start_tx(struct device *dev, struct hdlcdrv_state *s) +{ + s->hdlctx.tx_state = 0; + s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay); + s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0; + hdlcdrv_transmitter(dev, s); + s->hdlctx.ptt = 1; + s->ptt_keyed++; +} + +/* ---------------------------------------------------------------------- */ + +static unsigned short random_seed; + +static inline unsigned short random_num(void) +{ + random_seed = 28629 * random_seed + 157; + return random_seed; +} + +/* ---------------------------------------------------------------------- */ + +void hdlcdrv_arbitrate(struct device *dev, struct hdlcdrv_state *s) +{ + if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || + skb_queue_empty(&s->send_queue)) + return; + if (s->ch_params.fulldup) { + start_tx(dev, s); + return; + } + if (s->hdlcrx.dcd) { + s->hdlctx.slotcnt = s->ch_params.slottime; + return; + } + if ((--s->hdlctx.slotcnt) > 0) + return; + s->hdlctx.slotcnt = s->ch_params.slottime; + if ((random_num() % 256) > s->ch_params.ppersist) + return; + start_tx(dev, s); +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== network driver interface ========================= + */ + +static inline int hdlcdrv_paranoia_check(struct device *dev, + const char *routine) +{ + if (!dev || !dev->priv || + ((struct hdlcdrv_state *)dev->priv)->magic != HDLCDRV_MAGIC) { + printk(KERN_ERR "hdlcdrv: bad magic number for hdlcdrv_state " + "struct in routine %s\n", routine); + return 1; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int hdlcdrv_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct hdlcdrv_state *sm; + + if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet")) + return 0; + sm = (struct hdlcdrv_state *)dev->priv; + /* + * If some higher layer thinks we've missed an tx-done interrupt + * we are passed NULL. Caution: dev_tint() handles the cli()/sti() + * itself. + */ + if (skb == NULL) { + dev_tint(dev); + return 0; + } + skb_queue_tail(&sm->send_queue, skb); + dev->trans_start = jiffies; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int hdlcdrv_set_mac_address(struct device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *)addr; + + /* addr is an AX.25 shifted ASCII mac address */ + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + return 0; +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= 0x20119 +static struct net_device_stats *hdlcdrv_get_stats(struct device *dev) +#else +static struct enet_statistics *hdlcdrv_get_stats(struct device *dev) +#endif +{ + struct hdlcdrv_state *sm; + + if (hdlcdrv_paranoia_check(dev, "hdlcdrv_get_stats")) + return NULL; + sm = (struct hdlcdrv_state *)dev->priv; + /* + * Get the current statistics. This may be called with the + * card open or closed. + */ + return &sm->stats; +} + +/* --------------------------------------------------------------------- */ +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ + +static int hdlcdrv_open(struct device *dev) +{ + struct hdlcdrv_state *s; + int i; + + if (hdlcdrv_paranoia_check(dev, "hdlcdrv_open")) + return -EINVAL; + s = (struct hdlcdrv_state *)dev->priv; + + if (dev->start) + return 0; + if (!s->ops || !s->ops->open) + return -ENODEV; + + dev->start = 1; + /* + * initialise some variables + */ + s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; + s->hdlcrx.in_hdlc_rx = 0; + s->hdlcrx.rx_state = 0; + + s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; + s->hdlctx.in_hdlc_tx = 0; + s->hdlctx.tx_state = 1; + s->hdlctx.numflags = 0; + s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0; + s->hdlctx.ptt = 0; + s->hdlctx.slotcnt = s->ch_params.slottime; + s->hdlctx.calibrate = 0; + + i = s->ops->open(dev); + if (i) { + dev->start = 0; + return i; + } + + dev->tbusy = 0; + dev->interrupt = 0; + + return 0; +} + +/* --------------------------------------------------------------------- */ +/* + * The inverse routine to hdlcdrv_open(). + */ + +static int hdlcdrv_close(struct device *dev) +{ + struct hdlcdrv_state *s; + struct sk_buff *skb; + int i = 0; + + if (hdlcdrv_paranoia_check(dev, "hdlcdrv_close")) + return -EINVAL; + s = (struct hdlcdrv_state *)dev->priv; + + if (!dev->start) + return 0; + dev->start = 0; + dev->tbusy = 1; + + if (s->ops && s->ops->close) + i = s->ops->close(dev); + /* Free any buffers left in the hardware transmit queue */ + while ((skb = skb_dequeue(&s->send_queue))) + dev_kfree_skb(skb, FREE_WRITE); + return i; +} + +/* --------------------------------------------------------------------- */ + +static int hdlcdrv_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct hdlcdrv_state *s; + struct hdlcdrv_ioctl bi; + + if (hdlcdrv_paranoia_check(dev, "hdlcdrv_ioctl")) + return -EINVAL; + s = (struct hdlcdrv_state *)dev->priv; + + if (cmd != SIOCDEVPRIVATE) { + if (s->ops && s->ops->ioctl) + return s->ops->ioctl(dev, ifr, &bi, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + + switch (bi.cmd) { + default: + if (s->ops && s->ops->ioctl) + return s->ops->ioctl(dev, ifr, &bi, cmd); + return -ENOIOCTLCMD; + + case HDLCDRVCTL_GETCHANNELPAR: + bi.data.cp.tx_delay = s->ch_params.tx_delay; + bi.data.cp.tx_tail = s->ch_params.tx_tail; + bi.data.cp.slottime = s->ch_params.slottime; + bi.data.cp.ppersist = s->ch_params.ppersist; + bi.data.cp.fulldup = s->ch_params.fulldup; + break; + + case HDLCDRVCTL_SETCHANNELPAR: + if (!suser()) + return -EACCES; + s->ch_params.tx_delay = bi.data.cp.tx_delay; + s->ch_params.tx_tail = bi.data.cp.tx_tail; + s->ch_params.slottime = bi.data.cp.slottime; + s->ch_params.ppersist = bi.data.cp.ppersist; + s->ch_params.fulldup = bi.data.cp.fulldup; + s->hdlctx.slotcnt = 1; + return 0; + + case HDLCDRVCTL_GETMODEMPAR: + bi.data.mp.iobase = dev->base_addr; + bi.data.mp.irq = dev->irq; + bi.data.mp.dma = dev->dma; + bi.data.mp.dma2 = s->ptt_out.dma2; + bi.data.mp.seriobase = s->ptt_out.seriobase; + bi.data.mp.pariobase = s->ptt_out.pariobase; + bi.data.mp.midiiobase = s->ptt_out.midiiobase; + break; + + case HDLCDRVCTL_SETMODEMPAR: + if ((!suser()) || dev->start) + return -EACCES; + dev->base_addr = bi.data.mp.iobase; + dev->irq = bi.data.mp.irq; + dev->dma = bi.data.mp.dma; + s->ptt_out.dma2 = bi.data.mp.dma2; + s->ptt_out.seriobase = bi.data.mp.seriobase; + s->ptt_out.pariobase = bi.data.mp.pariobase; + s->ptt_out.midiiobase = bi.data.mp.midiiobase; + return 0; + + case HDLCDRVCTL_GETSTAT: + bi.data.cs.ptt = hdlcdrv_ptt(s); + bi.data.cs.dcd = s->hdlcrx.dcd; + bi.data.cs.ptt_keyed = s->ptt_keyed; + bi.data.cs.tx_packets = s->stats.tx_packets; + bi.data.cs.tx_errors = s->stats.tx_errors; + bi.data.cs.rx_packets = s->stats.rx_packets; + bi.data.cs.rx_errors = s->stats.rx_errors; + break; + + case HDLCDRVCTL_OLDGETSTAT: + bi.data.ocs.ptt = hdlcdrv_ptt(s); + bi.data.ocs.dcd = s->hdlcrx.dcd; + bi.data.ocs.ptt_keyed = s->ptt_keyed; +#if LINUX_VERSION_CODE < 0x20100 + bi.data.ocs.stats = s->stats; +#endif + break; + + case HDLCDRVCTL_CALIBRATE: + s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16; + return 0; + + case HDLCDRVCTL_GETSAMPLES: +#ifndef HDLCDRV_DEBUG + return -EPERM; +#else /* HDLCDRV_DEBUG */ + if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) + return -EAGAIN; + bi.data.bits = + s->bitbuf_channel.buffer[s->bitbuf_channel.rd]; + s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) % + sizeof(s->bitbuf_channel.buffer); + break; +#endif /* HDLCDRV_DEBUG */ + + case HDLCDRVCTL_GETBITS: +#ifndef HDLCDRV_DEBUG + return -EPERM; +#else /* HDLCDRV_DEBUG */ + if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) + return -EAGAIN; + bi.data.bits = + s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd]; + s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) % + sizeof(s->bitbuf_hdlc.buffer); + break; +#endif /* HDLCDRV_DEBUG */ + + case HDLCDRVCTL_DRIVERNAME: + if (s->ops && s->ops->drvname) { + strncpy(bi.data.drivername, s->ops->drvname, + sizeof(bi.data.drivername)); + break; + } + bi.data.drivername[0] = '\0'; + break; + + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + +} + +/* --------------------------------------------------------------------- */ + +/* + * Check for a network adaptor of this type, and return '0' if one exists. + * If dev->base_addr == 0, probe all likely locations. + * If dev->base_addr == 1, always return failure. + * If dev->base_addr == 2, allocate space for the device and return success + * (detachable devices only). + */ +static int hdlcdrv_probe(struct device *dev) +{ + const struct hdlcdrv_channel_params dflt_ch_params = { + 20, 2, 10, 40, 0 + }; + struct hdlcdrv_state *s; + + if (!dev) + return -ENXIO; + /* + * not a real probe! only initialize data structures + */ + s = (struct hdlcdrv_state *)dev->priv; + /* + * initialize the hdlcdrv_state struct + */ + s->ch_params = dflt_ch_params; + s->ptt_keyed = 0; + + s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; + s->hdlcrx.in_hdlc_rx = 0; + s->hdlcrx.rx_state = 0; + + s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; + s->hdlctx.in_hdlc_tx = 0; + s->hdlctx.tx_state = 1; + s->hdlctx.numflags = 0; + s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0; + s->hdlctx.ptt = 0; + s->hdlctx.slotcnt = s->ch_params.slottime; + s->hdlctx.calibrate = 0; + +#ifdef HDLCDRV_DEBUG + s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0; + s->bitbuf_channel.shreg = 0x80; + + s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0; + s->bitbuf_hdlc.shreg = 0x80; +#endif /* HDLCDRV_DEBUG */ + + /* + * initialize the device struct + */ + dev->open = hdlcdrv_open; + dev->stop = hdlcdrv_close; + dev->do_ioctl = hdlcdrv_ioctl; + dev->hard_start_xmit = hdlcdrv_send_packet; + dev->get_stats = hdlcdrv_get_stats; + + /* Fill in the fields of the device structure */ + + dev_init_buffers(dev); + + skb_queue_head_init(&s->send_queue); + +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + dev->hard_header = ax25_encapsulate; + dev->rebuild_header = ax25_rebuild_header; +#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */ + dev->hard_header = NULL; + dev->rebuild_header = NULL; +#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ + dev->set_mac_address = hdlcdrv_set_mac_address; + + dev->type = ARPHRD_AX25; /* AF_AX25 device */ + dev->hard_header_len = 73; /* We do digipeaters now */ + dev->mtu = 1500; /* eth_mtu is the default */ + dev->addr_len = 7; /* sizeof an ax.25 address */ + memcpy(dev->broadcast, ax25_bcast, 7); + memcpy(dev->dev_addr, ax25_test, 7); + + /* New style flags */ + dev->flags = 0; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = sizeof(unsigned long); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops, + unsigned int privsize, char *ifname, + unsigned int baseaddr, unsigned int irq, + unsigned int dma) +{ + struct hdlcdrv_state *s; + + if (!dev || !ops) + return -EACCES; + if (privsize < sizeof(struct hdlcdrv_state)) + privsize = sizeof(struct hdlcdrv_state); + memset(dev, 0, sizeof(struct device)); + if (!(s = dev->priv = kmalloc(privsize, GFP_KERNEL))) + return -ENOMEM; + /* + * initialize part of the hdlcdrv_state struct + */ + memset(s, 0, privsize); + s->magic = HDLCDRV_MAGIC; + strncpy(s->ifname, ifname, sizeof(s->ifname)); + s->ops = ops; + /* + * initialize part of the device struct + */ + dev->name = s->ifname; + dev->if_port = 0; + dev->init = hdlcdrv_probe; + dev->start = 0; + dev->tbusy = 1; + dev->base_addr = baseaddr; + dev->irq = irq; + dev->dma = dma; + if (register_netdev(dev)) { + printk(KERN_WARNING "hdlcdrv: cannot register net " + "device %s\n", s->ifname); + kfree(dev->priv); + return -ENXIO; + } + MOD_INC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +int hdlcdrv_unregister_hdlcdrv(struct device *dev) +{ + struct hdlcdrv_state *s; + + if (!dev) + return -EINVAL; + if (!(s = (struct hdlcdrv_state *)dev->priv)) + return -EINVAL; + if (s->magic != HDLCDRV_MAGIC) + return -EINVAL; + if (dev->start && s->ops->close) + s->ops->close(dev); + unregister_netdev(dev); + kfree(s); + MOD_DEC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= 0x20115 + +EXPORT_SYMBOL(hdlcdrv_receiver); +EXPORT_SYMBOL(hdlcdrv_transmitter); +EXPORT_SYMBOL(hdlcdrv_arbitrate); +EXPORT_SYMBOL(hdlcdrv_register_hdlcdrv); +EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv); + +#else + +static struct symbol_table hdlcdrv_syms = { +#include <linux/symtab_begin.h> + X(hdlcdrv_receiver), + X(hdlcdrv_transmitter), + X(hdlcdrv_arbitrate), + X(hdlcdrv_register_hdlcdrv), + X(hdlcdrv_unregister_hdlcdrv), +#include <linux/symtab_end.h> +}; + +#endif + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +#if LINUX_VERSION_CODE >= 0x20115 + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); + +#endif + +/* --------------------------------------------------------------------- */ + +__initfunc(int init_module(void)) +{ + printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n"); + printk(KERN_INFO "hdlcdrv: version 0.4 compiled " __TIME__ " " __DATE__ "\n"); +#if LINUX_VERSION_CODE < 0x20115 + register_symtab(&hdlcdrv_syms); +#endif + return 0; +} + +/* --------------------------------------------------------------------- */ + +void cleanup_module(void) +{ + printk(KERN_INFO "hdlcdrv: cleanup\n"); +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/hp-plus.c linux/drivers/net/hp-plus.c --- v2.0.34/linux/drivers/net/hp-plus.c Thu Feb 29 21:50:44 1996 +++ linux/drivers/net/hp-plus.c Mon Jul 13 13:47:31 1998 @@ -101,13 +101,13 @@ 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 @@ 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 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/net/hp.c linux/drivers/net/hp.c --- v2.0.34/linux/drivers/net/hp.c Thu Feb 29 21:50:44 1996 +++ linux/drivers/net/hp.c Mon Jul 13 13:47:31 1998 @@ -65,7 +65,7 @@ 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 @@ 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 -u --recursive --new-file v2.0.34/linux/drivers/net/hp100.h linux/drivers/net/hp100.h --- v2.0.34/linux/drivers/net/hp100.h Tue Aug 5 09:11:35 1997 +++ linux/drivers/net/hp100.h Mon Jul 13 13:47:31 1998 @@ -529,7 +529,7 @@ */ #define MAX_RX_PDL 30 /* Card limit = 31 */ -#define MAX_RX_FRAG 2 /* Dont need more... */ +#define MAX_RX_FRAG 2 /* Don't need more... */ #define MAX_TX_PDL 29 #define MAX_TX_FRAG 2 /* Limit = 31 */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/mkiss.c linux/drivers/net/mkiss.c --- v2.0.34/linux/drivers/net/mkiss.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/mkiss.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,1132 @@ +/* + * MKISS Driver + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This module implements the AX.25 protocol for kernel-based + * devices like TTYs. It interfaces between a raw TTY, and the + * kernel's AX.25 protocol layers, just like slip.c. + * AX.25 needs to be seperated from slip.c while slip.c is no + * longer a static kernel device since it is a module. + * This method clears the way to implement other kiss protocols + * like mkiss smack g8bpq ..... so far only mkiss is implemented. + * + * Hans Alblas <hans@esrac.ele.tue.nl> + * + * History + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/major.h> + +#include <linux/timer.h> + +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> + +#include <net/ax25.h> + +#include "mkiss.h" + +#ifdef CONFIG_INET +#include <linux/ip.h> +#include <linux/tcp.h> +#endif + +#ifdef MODULE +#define AX25_VERSION "AX25-MODULAR-NET3.019-NEWTTY" +#define min(a,b) (a < b ? a : b) +#else +#define AX25_VERSION "AX25-NET3.019-NEWTTY" +#endif + +#define NR_MKISS 4 +#define MKISS_SERIAL_TYPE_NORMAL 1 + +struct mkiss_channel { + int magic; /* magic word */ + int init; /* channel exists? */ + struct tty_struct *tty; /* link to tty control structure */ +}; + +typedef struct ax25_ctrl { + char if_name[8]; /* "ax0\0" .. "ax99999\0" */ + struct ax_disp ctrl; /* */ + struct device dev; /* the device */ +} ax25_ctrl_t; + +static ax25_ctrl_t **ax25_ctrls = NULL; +int ax25_maxdev = AX25_MAXDEV; /* Can be overridden with insmod! */ + +static struct tty_ldisc ax_ldisc; +static struct tty_driver mkiss_driver; +static int mkiss_refcount; +static struct tty_struct *mkiss_table[NR_MKISS]; +static struct termios *mkiss_termios[NR_MKISS]; +static struct termios *mkiss_termios_locked[NR_MKISS]; +struct mkiss_channel MKISS_Info[NR_MKISS]; + +static int ax25_init(struct device *); +static int mkiss_init(void); +static int mkiss_write(struct tty_struct *, int, const unsigned char *, int); +static int kiss_esc(unsigned char *, unsigned char *, int); +static void kiss_unesc(struct ax_disp *, unsigned char); + +/* Find a free channel, and link in this `tty' line. */ +static inline struct ax_disp *ax_alloc(void) +{ + ax25_ctrl_t *axp; + int i; + + if (ax25_ctrls == NULL) /* Master array missing ! */ + return NULL; + + for (i = 0; i < ax25_maxdev; i++) { + axp = ax25_ctrls[i]; + + /* Not allocated ? */ + if (axp == NULL) + break; + + /* Not in use ? */ + if (!set_bit(AXF_INUSE, &axp->ctrl.flags)) + break; + } + + /* Sorry, too many, all slots in use */ + if (i >= ax25_maxdev) + return NULL; + + /* If no channels are available, allocate one */ + if (axp == NULL && (ax25_ctrls[i] = (ax25_ctrl_t *)kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) { + axp = ax25_ctrls[i]; + memset(axp, 0, sizeof(ax25_ctrl_t)); + + /* Initialize channel control data */ + set_bit(AXF_INUSE, &axp->ctrl.flags); + sprintf(axp->if_name, "ax%d", i++); + axp->ctrl.tty = NULL; + axp->dev.name = axp->if_name; + axp->dev.base_addr = i; + axp->dev.priv = (void *)&axp->ctrl; + axp->dev.next = NULL; + axp->dev.init = ax25_init; + } + + if (axp != NULL) { + /* + * register device so that it can be ifconfig'ed + * ax25_init() will be called as a side-effect + * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl ! + */ + if (register_netdev(&axp->dev) == 0) { + /* (Re-)Set the INUSE bit. Very Important! */ + set_bit(AXF_INUSE, &axp->ctrl.flags); + axp->ctrl.dev = &axp->dev; + axp->dev.priv = (void *)&axp->ctrl; + + return &axp->ctrl; + } else { + clear_bit(AXF_INUSE,&axp->ctrl.flags); + printk(KERN_ERR "ax_alloc() - register_netdev() failure.\n"); + } + } + + return NULL; +} + +/* Free an AX25 channel. */ +static inline void ax_free(struct ax_disp *ax) +{ + /* Free all AX25 frame buffers. */ + if (ax->rbuff) + kfree(ax->rbuff); + ax->rbuff = NULL; + if (ax->xbuff) + kfree(ax->xbuff); + ax->xbuff = NULL; + if (!clear_bit(AXF_INUSE, &ax->flags)) + printk(KERN_ERR "%s: ax_free for already free unit.\n", ax->dev->name); +} + +static void ax_changedmtu(struct ax_disp *ax) +{ + struct device *dev = ax->dev; + unsigned char *xbuff, *rbuff, *oxbuff, *orbuff; + int len; + unsigned long flags; + + len = dev->mtu * 2; + + /* + * allow for arrival of larger UDP packets, even if we say not to + * also fixes a bug in which SunOS sends 512-byte packets even with + * an MSS of 128 + */ + if (len < 576 * 2) + len = 576 * 2; + + xbuff = (unsigned char *)kmalloc(len + 4, GFP_ATOMIC); + rbuff = (unsigned char *)kmalloc(len + 4, GFP_ATOMIC); + + if (xbuff == NULL || rbuff == NULL) { + printk(KERN_ERR "%s: unable to grow ax25 buffers, MTU change cancelled.\n", + ax->dev->name); + dev->mtu = ax->mtu; + if (xbuff != NULL) + kfree(xbuff); + if (rbuff != NULL) + kfree(rbuff); + return; + } + + save_flags(flags); + cli(); + + oxbuff = ax->xbuff; + ax->xbuff = xbuff; + orbuff = ax->rbuff; + ax->rbuff = rbuff; + + if (ax->xleft) { + if (ax->xleft <= len) { + memcpy(ax->xbuff, ax->xhead, ax->xleft); + } else { + ax->xleft = 0; + ax->tx_dropped++; + } + } + + ax->xhead = ax->xbuff; + + if (ax->rcount) { + if (ax->rcount <= len) { + memcpy(ax->rbuff, orbuff, ax->rcount); + } else { + ax->rcount = 0; + ax->rx_over_errors++; + set_bit(AXF_ERROR, &ax->flags); + } + } + + ax->mtu = dev->mtu + 73; + ax->buffsize = len; + + restore_flags(flags); + + if (oxbuff != NULL) + kfree(oxbuff); + if (orbuff != NULL) + kfree(orbuff); +} + + +/* Set the "sending" flag. This must be atomic, hence the ASM. */ +static inline void ax_lock(struct ax_disp *ax) +{ + if (set_bit(0, (void *)&ax->dev->tbusy)) + printk(KERN_ERR "%s: trying to lock already locked device!\n", ax->dev->name); +} + + +/* Clear the "sending" flag. This must be atomic, hence the ASM. */ +static inline void ax_unlock(struct ax_disp *ax) +{ + if (!clear_bit(0, (void *)&ax->dev->tbusy)) + printk(KERN_ERR "%s: trying to unlock already unlocked device!\n", ax->dev->name); +} + +/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */ +static void ax_bump(struct ax_disp *ax) +{ + struct ax_disp *tmp_ax; + struct sk_buff *skb; + struct mkiss_channel *mkiss; + int count; + + tmp_ax = ax; + + if (ax->rbuff[0] > 0x0f) { + if (ax->mkiss != NULL) { + mkiss= ax->mkiss->tty->driver_data; + if (mkiss->magic == MKISS_DRIVER_MAGIC) + tmp_ax = ax->mkiss; + } + } + + count = ax->rcount; + + if ((skb = dev_alloc_skb(count)) == NULL) { + printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", ax->dev->name); + ax->rx_dropped++; + return; + } + + skb->dev = tmp_ax->dev; + memcpy(skb_put(skb,count), ax->rbuff, count); + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_AX25); + netif_rx(skb); + tmp_ax->rx_packets++; +} + +/* Encapsulate one AX.25 packet and stuff into a TTY queue. */ +static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len) +{ + unsigned char *p; + int actual, count; + struct mkiss_channel *mkiss = ax->tty->driver_data; + + if (ax->mtu != ax->dev->mtu + 73) /* Someone has been ifconfigging */ + ax_changedmtu(ax); + + if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */ + len = ax->mtu; + printk(KERN_ERR "%s: truncating oversized transmit packet!\n", ax->dev->name); + ax->tx_dropped++; + ax_unlock(ax); + return; + } + + p = icp; + + if (mkiss->magic != MKISS_DRIVER_MAGIC) { + count = kiss_esc(p, (unsigned char *)ax->xbuff, len); + ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count); + ax->tx_packets++; + ax->dev->trans_start = jiffies; + ax->xleft = count - actual; + ax->xhead = ax->xbuff + actual; + } else { + count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len); + ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + actual = ax->mkiss->tty->driver.write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count); + ax->tx_packets++; + ax->mkiss->dev->trans_start = jiffies; + ax->mkiss->xleft = count - actual; + ax->mkiss->xhead = ax->mkiss->xbuff + actual; + } +} + +/* + * Called by the driver when there's room for more data. If we have + * more packets to send, we send them here. + */ +static void ax25_write_wakeup(struct tty_struct *tty) +{ + int actual; + struct ax_disp *ax = (struct ax_disp *)tty->disc_data; + struct mkiss_channel *mkiss; + + /* First make sure we're connected. */ + if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start) + return; + if (ax->xleft <= 0) { + /* Now serial buffer is almost free & we can start + * transmission of another packet + */ + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + + if (ax->mkiss != NULL) { + mkiss= ax->mkiss->tty->driver_data; + if (mkiss->magic == MKISS_DRIVER_MAGIC) + ax_unlock(ax->mkiss); + } + + ax_unlock(ax); + mark_bh(NET_BH); + return; + } + + actual = tty->driver.write(tty, 0, ax->xhead, ax->xleft); + ax->xleft -= actual; + ax->xhead += actual; +} + +/* Encapsulate an AX.25 packet and kick it into a TTY queue. */ +static int ax_xmit(struct sk_buff *skb, struct device *dev) +{ + struct ax_disp *ax = (struct ax_disp*)dev->priv; + struct mkiss_channel *mkiss = ax->tty->driver_data; + struct ax_disp *tmp_ax; + + tmp_ax = NULL; + + if (mkiss->magic == MKISS_DRIVER_MAGIC) { + if (skb->data[0] < 0x10) + skb->data[0] = skb->data[0] + 0x10; + tmp_ax = ax->mkiss; + } + + if (!dev->start) { + printk(KERN_ERR "%s: xmit call when iface is down\n", dev->name); + return 1; + } + + if (tmp_ax != NULL) + if (tmp_ax->dev->tbusy) + return 1; + + if (tmp_ax != NULL) + if (dev->tbusy) { + printk(KERN_ERR "mkiss: dev busy while serial dev is free\n"); + ax_unlock(ax); + } + + if (dev->tbusy) { + /* + * May be we must check transmitter timeout here ? + * 14 Oct 1994 Dmitry Gorodchanin. + */ + if (jiffies - dev->trans_start < 20 * HZ) { + /* 20 sec timeout not reached */ + return 1; + } + + printk(KERN_ERR "%s: transmit timed out, %s?\n", dev->name, + (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ? + "bad line quality" : "driver error"); + + ax->xleft = 0; + ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + ax_unlock(ax); + } + + /* We were not busy, so we are now... :-) */ + if (skb != NULL) { + ax_lock(ax); + if (tmp_ax != NULL) + ax_lock(tmp_ax); + ax_encaps(ax, skb->data, skb->len); + dev_kfree_skb(skb, FREE_WRITE); + } + + return 0; +} + +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + +/* Return the frame type ID */ +static int ax_header(struct sk_buff *skb, struct device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len) +{ +#ifdef CONFIG_INET + if (type != htons(ETH_P_AX25)) + return ax25_encapsulate(skb, dev, type, daddr, saddr, len); +#endif + return 0; +} + + +static int ax_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb) +{ +#ifdef CONFIG_INET + return ax25_rebuild_header(buff, dev, raddr, skb); +#else + return 0; +#endif +} + +#endif /* CONFIG_{AX25,AX25_MODULE} */ + +/* Open the low-level part of the AX25 channel. Easy! */ +static int ax_open(struct device *dev) +{ + struct ax_disp *ax = (struct ax_disp*)dev->priv; + unsigned long len; + + if (ax->tty == NULL) + return -ENODEV; + + /* + * Allocate the frame buffers: + * + * rbuff Receive buffer. + * xbuff Transmit buffer. + * cbuff Temporary compression buffer. + */ + len = dev->mtu * 2; + + /* + * allow for arrival of larger UDP packets, even if we say not to + * also fixes a bug in which SunOS sends 512-byte packets even with + * an MSS of 128 + */ + if (len < 576 * 2) + len = 576 * 2; + + if ((ax->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL)) == NULL) + goto norbuff; + + if ((ax->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL)) == NULL) + goto noxbuff; + + ax->mtu = dev->mtu + 73; + ax->buffsize = len; + ax->rcount = 0; + ax->xleft = 0; + + ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */ + /* Needed because address '0' is special */ + if (dev->pa_addr == 0) + dev->pa_addr = ntohl(0xC0A80001); + dev->tbusy = 0; + dev->start = 1; + + return 0; + + /* Cleanup */ + kfree(ax->xbuff); + +noxbuff: + kfree(ax->rbuff); + +norbuff: + return -ENOMEM; +} + + +/* Close the low-level part of the AX25 channel. Easy! */ +static int ax_close(struct device *dev) +{ + struct ax_disp *ax = (struct ax_disp*)dev->priv; + + if (ax->tty == NULL) + return -EBUSY; + + ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + + dev->tbusy = 1; + dev->start = 0; + + return 0; +} + +static int ax25_receive_room(struct tty_struct *tty) +{ + return 65536; /* We can handle an infinite amount of data. :-) */ +} + +/* + * Handle the 'receiver data ready' interrupt. + * This function is called by the 'tty_io' module in the kernel when + * a block of data has been received, which can now be decapsulated + * and sent on to the AX.25 layer for further processing. + */ +static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct ax_disp *ax = (struct ax_disp *)tty->disc_data; + + if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start) + return; + + /* + * Argh! mtu change time! - costs us the packet part received + * at the change + */ + if (ax->mtu != ax->dev->mtu + 73) + ax_changedmtu(ax); + + /* Read the characters out of the buffer */ + while (count--) { + if (fp != NULL && *fp++) { + if (!set_bit(AXF_ERROR, &ax->flags)) + ax->rx_errors++; + cp++; + continue; + } + + kiss_unesc(ax, *cp++); + } +} + +static int ax25_open(struct tty_struct *tty) +{ + struct ax_disp *ax = (struct ax_disp *)tty->disc_data; + struct ax_disp *tmp_ax; + struct mkiss_channel *mkiss; + int err, cnt; + + /* First make sure we're not already connected. */ + if (ax && ax->magic == AX25_MAGIC) + return -EEXIST; + + /* OK. Find a free AX25 channel to use. */ + if ((ax = ax_alloc()) == NULL) + return -ENFILE; + + ax->tty = tty; + tty->disc_data = ax; + + ax->mkiss = NULL; + tmp_ax = NULL; + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + + /* Restore default settings */ + ax->dev->type = ARPHRD_AX25; + + /* Perform the low-level AX25 initialization. */ + if ((err = ax_open(ax->dev))) + return err; + + mkiss= ax->tty->driver_data; + + if (mkiss->magic == MKISS_DRIVER_MAGIC) { + for (cnt = 1; cnt < ax25_maxdev; cnt++) { + if (ax25_ctrls[cnt]) { + if (ax25_ctrls[cnt]->dev.start) { + if (ax == &ax25_ctrls[cnt]->ctrl) { + cnt--; + tmp_ax = &ax25_ctrls[cnt]->ctrl; + break; + } + } + } + } + } + + if (tmp_ax != NULL) { + ax->mkiss = tmp_ax; + tmp_ax->mkiss = ax; + } + + MOD_INC_USE_COUNT; + + /* Done. We have linked the TTY line to a channel. */ + return ax->dev->base_addr; +} + +static void ax25_close(struct tty_struct *tty) +{ + struct ax_disp *ax = (struct ax_disp *)tty->disc_data; + int mkiss ; + + /* First make sure we're connected. */ + if (ax == NULL || ax->magic != AX25_MAGIC) + return; + + mkiss = ax->mode; + dev_close(ax->dev); + + tty->disc_data = 0; + ax->tty = NULL; + + /* VSV = very important to remove timers */ + ax_free(ax); + unregister_netdev(ax->dev); + + MOD_DEC_USE_COUNT; +} + + +static struct enet_statistics *ax_get_stats(struct device *dev) +{ + static struct enet_statistics stats; + struct ax_disp *ax = (struct ax_disp*)dev->priv; + + memset(&stats, 0, sizeof(struct enet_statistics)); + + stats.rx_packets = ax->rx_packets; + stats.tx_packets = ax->tx_packets; + stats.rx_dropped = ax->rx_dropped; + stats.tx_dropped = ax->tx_dropped; + stats.tx_errors = ax->tx_errors; + stats.rx_errors = ax->rx_errors; + stats.rx_over_errors = ax->rx_over_errors; + + return &stats; +} + + +/************************************************************************ + * STANDARD ENCAPSULATION * + ************************************************************************/ + +int kiss_esc(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = END; + + while (len-- > 0) { + switch (c = *s++) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + + *ptr++ = END; + + return ptr - d; +} + +static void kiss_unesc(struct ax_disp *ax, unsigned char s) +{ + switch (s) { + case END: + /* drop keeptest bit = VSV */ + if (test_bit(AXF_KEEPTEST, &ax->flags)) + clear_bit(AXF_KEEPTEST, &ax->flags); + + if (!clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2)) + ax_bump(ax); + + clear_bit(AXF_ESCAPE, &ax->flags); + ax->rcount = 0; + return; + + case ESC: + set_bit(AXF_ESCAPE, &ax->flags); + return; + case ESC_ESC: + if (clear_bit(AXF_ESCAPE, &ax->flags)) + s = ESC; + break; + case ESC_END: + if (clear_bit(AXF_ESCAPE, &ax->flags)) + s = END; + break; + } + + if (!test_bit(AXF_ERROR, &ax->flags)) { + if (ax->rcount < ax->buffsize) { + ax->rbuff[ax->rcount++] = s; + return; + } + + ax->rx_over_errors++; + set_bit(AXF_ERROR, &ax->flags); + } +} + + +int ax_set_mac_address(struct device *dev, void *addr) +{ + int err; + + if ((err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN)) != 0) + return err; + + /* addr is an AX.25 shifted ASCII mac address */ + memcpy_fromfs(dev->dev_addr, addr, AX25_ADDR_LEN); + + return 0; +} + +static int ax_set_dev_mac_address(struct device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN); + + return 0; +} + + +/* Perform I/O control on an active ax25 channel. */ +static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) +{ + struct ax_disp *ax = (struct ax_disp *)tty->disc_data; + int err; + unsigned int tmp; + + /* First make sure we're connected. */ + if (ax == NULL || ax->magic != AX25_MAGIC) + return -EINVAL; + + switch (cmd) { + case SIOCGIFNAME: + if ((err = verify_area(VERIFY_WRITE, arg, strlen(ax->dev->name) + 1)) != 0) + return err; + memcpy_tofs(arg, ax->dev->name, strlen(ax->dev->name) + 1); + return 0; + + case SIOCGIFENCAP: + if ((err = verify_area(VERIFY_WRITE, arg, sizeof(int))) != 0) + return err; + put_user(4, (int *)arg); + return 0; + + case SIOCSIFENCAP: + if ((err = verify_area(VERIFY_READ, arg, sizeof(int))) != 0) + return err; + tmp = get_user((int *)arg); + ax->mode = tmp; + ax->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */ + ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3; + ax->dev->type = ARPHRD_AX25; + return 0; + + case SIOCSIFHWADDR: + return ax_set_mac_address(ax->dev, arg); + + default: + return -ENOIOCTLCMD; + } +} + +static int ax_open_dev(struct device *dev) +{ + struct ax_disp *ax = (struct ax_disp*)dev->priv; + + if (ax->tty==NULL) + return -ENODEV; + + return 0; +} + +/* Initialize AX25 control device -- register AX25 line discipline */ +int mkiss_init_ctrl_dev(void) +{ + int status; + + if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */ + + if ((ax25_ctrls = (ax25_ctrl_t **)kmalloc(sizeof(void*) * ax25_maxdev, GFP_KERNEL)) == NULL) { + printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array ! No mkiss available\n"); + return -ENOMEM; + } + + /* Clear the pointer array, we allocate devices when we need them */ + memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */ + + /* Fill in our line protocol discipline, and register it */ + memset(&ax_ldisc, 0, sizeof(ax_ldisc)); + ax_ldisc.magic = TTY_LDISC_MAGIC; + ax_ldisc.flags = 0; + ax_ldisc.open = ax25_open; + ax_ldisc.close = ax25_close; + ax_ldisc.read = NULL; + ax_ldisc.write = NULL; + ax_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *, unsigned int, unsigned long))ax25_disp_ioctl; + ax_ldisc.select = NULL; + + ax_ldisc.receive_buf = ax25_receive_buf; + ax_ldisc.receive_room = ax25_receive_room; + ax_ldisc.write_wakeup = ax25_write_wakeup; + + if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0) + printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status); + + mkiss_init(); + +#ifdef MODULE + return status; +#else + /* + * Return "not found", so that dev_init() will unlink + * the placeholder device entry for us. + */ + return ENODEV; +#endif +} + + +/* Initialize the driver. Called by network startup. */ + +static int ax25_init(struct device *dev) +{ + struct ax_disp *ax = (struct ax_disp*)dev->priv; + int i; + + static char ax25_bcast[AX25_ADDR_LEN] = + {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1}; + static char ax25_test[AX25_ADDR_LEN] = + {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1}; + + if (ax == NULL) /* Allocation failed ?? */ + return -ENODEV; + + /* Set up the "AX25 Control Block". (And clear statistics) */ + memset(ax, 0, sizeof (struct ax_disp)); + ax->magic = AX25_MAGIC; + ax->dev = dev; + + /* Finish setting up the DEVICE info. */ + dev->mtu = AX_MTU; + dev->hard_start_xmit = ax_xmit; + dev->open = ax_open_dev; + dev->stop = ax_close; + dev->get_stats = ax_get_stats; +#ifdef HAVE_SET_MAC_ADDR + dev->set_mac_address = ax_set_dev_mac_address; +#endif + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->type = ARPHRD_AX25; + dev->tx_queue_len = 10; + + memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); + memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN); + +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + dev->hard_header = ax_header; + dev->rebuild_header = ax_rebuild_header; +#endif + + for (i = 0; i < DEV_NUMBUFFS; i++) + skb_queue_head_init(&dev->buffs[i]); + + /* New-style flags. */ + dev->flags = 0; + dev->family = AF_INET; + +#ifdef CONFIG_INET + dev->pa_addr = in_aton("192.168.0.1"); + dev->pa_brdaddr = in_aton("192.168.0.255"); + dev->pa_mask = in_aton("255.255.255.0"); + dev->pa_alen = 4; +#endif + + return 0; +} + +static int mkiss_open(struct tty_struct *tty, struct file *filp) +{ + struct mkiss_channel *mkiss; + int chan; + + chan = MINOR(tty->device) - tty->driver.minor_start; + + if (chan < 0 || chan >= NR_MKISS) + return -ENODEV; + + mkiss = &MKISS_Info[chan]; + + mkiss->magic = MKISS_DRIVER_MAGIC; + mkiss->init = 1; + mkiss->tty = tty; + + tty->driver_data = mkiss; + + tty->termios->c_iflag = IGNBRK | IGNPAR; + tty->termios->c_cflag = B9600 | CS8 | CLOCAL; + tty->termios->c_cflag &= ~CBAUD; + + return 0; +} + +static void mkiss_close(struct tty_struct *tty, struct file * filp) +{ + struct mkiss_channel *mkiss = tty->driver_data; + + if (mkiss == NULL || mkiss->magic != MKISS_DRIVER_MAGIC) + return; + + mkiss->tty = NULL; + mkiss->init = 0; + tty->stopped = 0; +} + +static int mkiss_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +{ + return 0; +} + +static int mkiss_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +{ + /* Ignore serial ioctl's */ + switch (cmd) { + case TCSBRK: + case TIOCMGET: + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + case TCSETS: + case TCSETSF: /* should flush first, but... */ + case TCSETSW: /* should wait until flush, but... */ + return 0; + default: + return -ENOIOCTLCMD; + } +} + + +static void mkiss_dummy(struct tty_struct *tty) +{ + struct mkiss_channel *mkiss = tty->driver_data; + + if (tty == NULL) + return; + + if (mkiss == NULL) + return; +} + +static void mkiss_dummy2(struct tty_struct *tty, unsigned char ch) +{ + struct mkiss_channel *mkiss = tty->driver_data; + + if (tty == NULL) + return; + + if (mkiss == NULL) + return; +} + + +static int mkiss_write_room(struct tty_struct * tty) +{ + struct mkiss_channel *mkiss = tty->driver_data; + + if (tty == NULL) + return 0; + + if (mkiss == NULL) + return 0; + + return 65536; /* We can handle an infinite amount of data. :-) */ +} + + +static int mkiss_chars_in_buffer(struct tty_struct *tty) +{ + struct mkiss_channel *mkiss = tty->driver_data; + + if (tty == NULL) + return 0; + + if (mkiss == NULL) + return 0; + + return 0; +} + + +static void mkiss_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + /* we don't do termios */ +} + +/* ******************************************************************** */ +/* * Init MKISS driver * */ +/* ******************************************************************** */ + +static int mkiss_init(void) +{ + memset(&mkiss_driver, 0, sizeof(struct tty_driver)); + + mkiss_driver.magic = MKISS_DRIVER_MAGIC; + mkiss_driver.name = "mkiss"; + mkiss_driver.major = MKISS_MAJOR; + mkiss_driver.minor_start = 0; + mkiss_driver.num = NR_MKISS; + mkiss_driver.type = TTY_DRIVER_TYPE_SERIAL; + mkiss_driver.subtype = MKISS_SERIAL_TYPE_NORMAL; /* not needed */ + + mkiss_driver.init_termios = tty_std_termios; + mkiss_driver.init_termios.c_iflag = IGNBRK | IGNPAR; + mkiss_driver.init_termios.c_cflag = B9600 | CS8 | CLOCAL; + + mkiss_driver.flags = TTY_DRIVER_REAL_RAW; + mkiss_driver.refcount = &mkiss_refcount; + mkiss_driver.table = mkiss_table; + mkiss_driver.termios = (struct termios **)mkiss_termios; + mkiss_driver.termios_locked = (struct termios **)mkiss_termios_locked; + + mkiss_driver.ioctl = mkiss_ioctl; + mkiss_driver.open = mkiss_open; + mkiss_driver.close = mkiss_close; + mkiss_driver.write = mkiss_write; + mkiss_driver.write_room = mkiss_write_room; + mkiss_driver.chars_in_buffer = mkiss_chars_in_buffer; + mkiss_driver.set_termios = mkiss_set_termios; + + /* some unused functions */ + mkiss_driver.flush_buffer = mkiss_dummy; + mkiss_driver.throttle = mkiss_dummy; + mkiss_driver.unthrottle = mkiss_dummy; + mkiss_driver.stop = mkiss_dummy; + mkiss_driver.start = mkiss_dummy; + mkiss_driver.hangup = mkiss_dummy; + mkiss_driver.flush_chars = mkiss_dummy; + mkiss_driver.put_char = mkiss_dummy2; + + if (tty_register_driver(&mkiss_driver)) { + printk(KERN_ERR "Couldn't register Mkiss device\n"); + return -EIO; + } + + printk(KERN_INFO "AX.25 Multikiss device enabled\n"); + + return 0; +} + +#ifdef MODULE + +int init_module(void) +{ + register_symtab(NULL); + + return mkiss_init_ctrl_dev(); +} + +void cleanup_module(void) +{ + int i; + + if (ax25_ctrls != NULL) { + for (i = 0; i < ax25_maxdev; i++) { + if (ax25_ctrls[i]) { + /* + * VSV = if dev->start==0, then device + * unregistred while close proc. + */ + if (ax25_ctrls[i]->dev.start) + unregister_netdev(&(ax25_ctrls[i]->dev)); + + kfree(ax25_ctrls[i]); + ax25_ctrls[i] = NULL; + } + } + + kfree(ax25_ctrls); + ax25_ctrls = NULL; + } + + if ((i = tty_register_ldisc(N_AX25, NULL))) + printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i); + + if (tty_unregister_driver(&mkiss_driver)) /* remove devive */ + printk(KERN_ERR "mkiss: can't unregister MKISS device\n"); +} + +#endif /* MODULE */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/mkiss.h linux/drivers/net/mkiss.h --- v2.0.34/linux/drivers/net/mkiss.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/mkiss.h Mon Jul 13 13:47:31 1998 @@ -0,0 +1,56 @@ +/**************************************************************************** + * Defines for the Multi-KISS driver. + ****************************************************************************/ + +#define AX25_MAXDEV 16 /* MAX number of AX25 channels; + This can be overridden with + insmod -oax25_maxdev=nnn */ +#define AX_MTU 236 + +/* SLIP/KISS protocol characters. */ +#define END 0300 /* indicates end of frame */ +#define ESC 0333 /* indicates byte stuffing */ +#define ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + +struct ax_disp { + int magic; + + /* Various fields. */ + struct tty_struct *tty; /* ptr to TTY structure */ + struct device *dev; /* easy for intr handling */ + struct ax_disp *mkiss; /* mkiss txport if mkiss channel*/ + + /* These are pointers to the malloc()ed frame buffers. */ + unsigned char *rbuff; /* receiver buffer */ + int rcount; /* received chars counter */ + unsigned char *xbuff; /* transmitter buffer */ + unsigned char *xhead; /* pointer to next byte to XMIT */ + int xleft; /* bytes left in XMIT queue */ + + /* SLIP interface statistics. */ + unsigned long rx_packets; /* inbound frames counter */ + unsigned long tx_packets; /* outbound frames counter */ + unsigned long rx_errors; /* Parity, etc. errors */ + unsigned long tx_errors; /* Planned stuff */ + unsigned long rx_dropped; /* No memory for skb */ + unsigned long tx_dropped; /* When MTU change */ + unsigned long rx_over_errors; /* Frame bigger then SLIP buf. */ + + /* Detailed SLIP statistics. */ + int mtu; /* Our mtu (to spot changes!) */ + int buffsize; /* Max buffers sizes */ + + + unsigned char flags; /* Flag values/ mode etc */ +#define AXF_INUSE 0 /* Channel in use */ +#define AXF_ESCAPE 1 /* ESC received */ +#define AXF_ERROR 2 /* Parity, etc. error */ +#define AXF_KEEPTEST 3 /* Keepalive test flag */ +#define AXF_OUTWAIT 4 /* is outpacket was flag */ + + int mode; +}; + +#define AX25_MAGIC 0x5316 +#define MKISS_DRIVER_MAGIC 1215 diff -u --recursive --new-file v2.0.34/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c --- v2.0.34/linux/drivers/net/ne2k-pci.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/ne2k-pci.c Mon Jul 13 13:47:31 1998 @@ -45,7 +45,7 @@ #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. */ @@ -189,14 +189,6 @@ if ( ! pcibios_present()) return -ENODEV; -#ifndef MODULE - { - static unsigned version_printed = 0; - if (version_printed++ == 0) - printk(KERN_INFO "%s", version); - } -#endif - for (;pci_index < 0xff; pci_index++) { unsigned char pci_bus, pci_device_fn; u8 pci_irq_line; @@ -219,6 +211,14 @@ break; if (pci_clone_list[i].vendor == 0) continue; + +#ifndef MODULE + { + static unsigned version_printed = 0; + if (version_printed++ == 0) + printk(KERN_INFO "%s", version); + } +#endif pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_ioaddr); diff -u --recursive --new-file v2.0.34/linux/drivers/net/net_init.c linux/drivers/net/net_init.c --- v2.0.34/linux/drivers/net/net_init.c Wed Nov 6 04:39:42 1996 +++ linux/drivers/net/net_init.c Mon Jul 13 13:47:31 1998 @@ -318,7 +318,7 @@ for (i = 0; i < MAX_ETH_CARDS; ++i) if (ethdev_index[i] == NULL) { sprintf(dev->name, "eth%d", i); - printk("loading device '%s'...\n", dev->name); +/* printk("loading device '%s'...\n", dev->name);*/ ethdev_index[i] = dev; break; } diff -u --recursive --new-file v2.0.34/linux/drivers/net/ni52.c linux/drivers/net/ni52.c --- v2.0.34/linux/drivers/net/ni52.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/ni52.c Mon Jul 13 13:47:31 1998 @@ -984,7 +984,7 @@ } #endif -#ifdef 0 +#if 0 if(!at_least_one) { int i; diff -u --recursive --new-file v2.0.34/linux/drivers/net/pi2.c linux/drivers/net/pi2.c --- v2.0.34/linux/drivers/net/pi2.c Thu Feb 29 21:50:45 1996 +++ linux/drivers/net/pi2.c Mon Jul 13 13:47:31 1998 @@ -53,6 +53,7 @@ Oct 29, 1995 (ac) A couple of minor fixes before this, and this release changes to the proper set_mac_address semantics which will break a few programs I suspect. + Aug 18, 1996 (jsn) Converted to be used as a module. */ /* The following #define invokes a hack that will improve performance (baud) @@ -81,19 +82,12 @@ #define DEF_B_SQUELDELAY 3 /* 30 mS */ #define DEF_B_CLOCKMODE 0 /* Normal clock mode */ -static const char *version = -"PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n"; - /* The following #define is only really required for the PI card, not the PI2 - but it's safer to leave it in. */ #define REALLY_SLOW_IO 1 -#define PI2_MODULE 0 - -#if PI2_MODULE > 0 +#include <linux/config.h> #include <linux/module.h> -#endif - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/types.h> @@ -116,11 +110,10 @@ #include <linux/skbuff.h> #include <linux/timer.h> #include <linux/if_arp.h> -#include "pi2.h" +#include <linux/pi2.h> #include "z8530.h" #include <net/ax25.h> - struct mbuf { struct mbuf *next; int cnt; @@ -553,7 +546,7 @@ skb = dev_alloc_skb(sksize); if (skb == NULL) { - printk("PI: %s: Memory squeeze, dropping packet.\n", dev->name); + printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; restore_flags(flags); return; @@ -642,7 +635,7 @@ sksize = pkt_len; skb = dev_alloc_skb(sksize); if (skb == NULL) { - printk("PI: %s: Memory squeeze, dropping packet.\n", dev->name); + printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; restore_flags(flags); return; @@ -1214,27 +1207,27 @@ int ports[] = {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0}; - printk(version); + printk(KERN_INFO "PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n"); /* Only one card supported for now */ for (port = &ports[0]; *port && !card_type; port++) { ioaddr = *port; if (check_region(ioaddr, PI_TOTAL_SIZE) == 0) { - printk("PI: Probing for card at address %#3x\n",ioaddr); + printk(KERN_INFO "PI: Probing for card at address %#3x\n",ioaddr); card_type = hw_probe(ioaddr); } } switch (card_type) { case 1: - printk("PI: Found a PI card at address %#3x\n", ioaddr); + printk(KERN_INFO "PI: Found a PI card at address %#3x\n", ioaddr); break; case 2: - printk("PI: Found a PI2 card at address %#3x\n", ioaddr); + printk(KERN_INFO "PI: Found a PI2 card at address %#3x\n", ioaddr); break; default: - printk("PI: ERROR: No card found\n"); + printk(KERN_ERR "PI: ERROR: No card found\n"); return -EIO; } @@ -1256,6 +1249,7 @@ register_netdev(&pi0b); pi0b.base_addr = ioaddr; pi0b.irq = 0; + pi0b.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA); /* Now initialize them */ @@ -1275,8 +1269,9 @@ return 0; } -static int pi_set_mac_address(struct device *dev, struct sockaddr *sa) +static int pi_set_mac_address(struct device *dev, void *addr) { + struct sockaddr *sa = (struct sockaddr *)addr; memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); /* addr is an AX.25 shifted ASCII */ return 0; /* mac address */ } @@ -1364,7 +1359,7 @@ lp->dmachan = dev->dma; if (lp->dmachan < 1 || lp->dmachan > 3) - printk("PI: DMA channel %d out of range\n", lp->dmachan); + printk(KERN_ERR "PI: DMA channel %d out of range\n", lp->dmachan); /* chipset_init() was already called */ @@ -1381,7 +1376,7 @@ /* 20 "jiffies" should be plenty of time... */ dev->irq = autoirq_report(20); if (!dev->irq) { - printk(". Failed to detect IRQ line.\n"); + printk(KERN_ERR "PI: Failed to detect IRQ line.\n"); } save_flags(flags); cli(); @@ -1391,7 +1386,7 @@ restore_flags(flags); } - printk("PI: Autodetected IRQ %d, assuming DMA %d.\n", + printk(KERN_INFO "PI: Autodetected IRQ %d, assuming DMA %d.\n", dev->irq, dev->dma); /* This board has jumpered interrupts. Snarf the interrupt vector @@ -1400,7 +1395,7 @@ { int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", NULL); if (irqval) { - printk("PI: unable to get IRQ %d (irqval=%d).\n", + printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval); return EAGAIN; } @@ -1436,10 +1431,13 @@ /* New-style flags. */ dev->flags = 0; dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; + +#ifdef CONFIG_INET + dev->pa_addr = in_aton("192.168.0.1"); + dev->pa_brdaddr = in_aton("192.168.0.255"); + dev->pa_mask = in_aton("255.255.255.0"); + dev->pa_alen = 4; +#endif return 0; } @@ -1487,6 +1485,9 @@ dev->interrupt = 0; dev->start = 1; first_time = 0; + + MOD_INC_USE_COUNT; + return 0; } @@ -1520,7 +1521,7 @@ #if 0 if (dev_a == NULL) { - printk("PI: pi_interrupt(): irq %d for unknown device.\n", irq); + printk(KERN_ERR "PI: pi_interrupt(): irq %d for unknown device.\n", irq); return; } #endif @@ -1589,6 +1590,9 @@ free_p(ptr); restore_flags(flags); + + MOD_DEC_USE_COUNT; + return 0; } @@ -1681,6 +1685,28 @@ return &lp->stats; } +#ifdef MODULE +int init_module(void) +{ + register_symtab(NULL); + return pi_init(); +} + +void cleanup_module(void) +{ + free_irq(pi0a.irq, NULL); /* IRQs and IO Ports are shared */ + release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE); + irq2dev_map[pi0a.irq] = NULL; + + kfree(pi0a.priv); + pi0a.priv = NULL; + unregister_netdev(&pi0a); + + kfree(pi0b.priv); + pi0b.priv = NULL; + unregister_netdev(&pi0b); +} +#endif /* * Local variables: diff -u --recursive --new-file v2.0.34/linux/drivers/net/pi2.h linux/drivers/net/pi2.h --- v2.0.34/linux/drivers/net/pi2.h Tue Jun 6 01:22:10 1995 +++ linux/drivers/net/pi2.h Mon Jul 13 13:47:31 1998 @@ -1,133 +0,0 @@ - -#define DMA_BUFF_SIZE 2200 - -/* Network statistics, with the same names as 'struct enet_statistics'. */ -#define netstats enet_statistics - -#define ON 1 -#define OFF 0 - - -/* Register offset info, specific to the PI - * E.g., to read the data port on channel A, use - * inportb(pichan[dev].base + CHANA + DATA) - */ -#define CHANB 0 /* Base of channel B regs */ -#define CHANA 2 /* Base of channel A regs */ - -/* 8530 ports on each channel */ -#define CTL 0 -#define DATA 1 - -#define DMAEN 0x4 /* Offset off DMA Enable register */ - -/* Timer chip offsets */ -#define TMR0 0x8 /* Offset of timer 0 register */ -#define TMR1 0x9 /* Offset of timer 1 register */ -#define TMR2 0xA /* Offset of timer 2 register */ -#define TMRCMD 0xB /* Offset of timer command register */ - -/* Timer chip equates */ -#define SC0 0x00 /* Select counter 0 */ -#define SC1 0x40 /* Select counter 1 */ -#define SC2 0x80 /* Select counter 2 */ -#define CLATCH 0x00 /* Counter latching operation */ -#define MSB 0x20 /* Read/load MSB only */ -#define LSB 0x10 /* Read/load LSB only */ -#define LSB_MSB 0x30 /* Read/load LSB, then MSB */ -#define MODE0 0x00 /* Interrupt on terminal count */ -#define MODE1 0x02 /* Programmable one shot */ -#define MODE2 0x04 /* Rate generator */ -#define MODE3 0x06 /* Square wave rate generator */ -#define MODE4 0x08 /* Software triggered strobe */ -#define MODE5 0x0a /* Hardware triggered strobe */ -#define BCD 0x01 /* BCD counter */ - -/* DMA controller registers */ -#define DMA_STAT 8 /* DMA controller status register */ -#define DMA_CMD 8 /* DMA controller command register */ -#define DMA_MASK 10 /* DMA controller mask register */ -#define DMA_MODE 11 /* DMA controller mode register */ -#define DMA_RESETFF 12 /* DMA controller first/last flip flop */ -/* DMA data */ -#define DMA_DISABLE (0x04) /* Disable channel n */ -#define DMA_ENABLE (0x00) /* Enable channel n */ -/* Single transfers, incr. address, auto init, writes, ch. n */ -#define DMA_RX_MODE (0x54) -/* Single transfers, incr. address, no auto init, reads, ch. n */ -#define DMA_TX_MODE (0x48) - -#define SINGLE 3686400 -#define DOUBLE 7372800 - -#define SIOCGPIPARAM 0x5000 /* get PI parameters */ -#define SIOCSPIPARAM 0x5001 /* set */ -#define SIOCGPIBAUD 0x5002 /* get only baud rate */ -#define SIOCSPIBAUD 0x5003 -#define SIOCGPIDMA 0x5004 /* get only DMA */ -#define SIOCSPIDMA 0x5005 -#define SIOCGPIIRQ 0x5006 /* get only IRQ */ -#define SIOCSPIIRQ 0x5007 - -struct pi_req { - int cmd; - int speed; - int clockmode; - int txdelay; - unsigned char persist; - int slotime; - int squeldelay; - int dmachan; - int irq; -}; - -#ifdef __KERNEL__ - -/* Information that needs to be kept for each channel. */ -struct pi_local { - struct netstats stats; /* %%%dp*/ - long open_time; /* Useless example local info. */ - unsigned long xtal; - - struct mbuf *rcvbuf;/* Buffer for current rx packet */ - struct mbuf *rxdmabuf1; /* DMA rx buffer */ - struct mbuf *rxdmabuf2; /* DMA rx buffer */ - - int bufsiz; /* Size of rcvbuf */ - char *rcp; /* Pointer into rcvbuf */ - - struct sk_buff_head sndq; /* Packets awaiting transmission */ - int sndcnt; /* Number of packets on sndq */ - struct sk_buff *sndbuf; /* Current buffer being transmitted */ - char *txdmabuf; /* Transmit DMA buffer */ - char *txptr; /* Used by B port tx */ - int txcnt; - char tstate; /* Transmitter state */ -#define IDLE 0 /* Transmitter off, no data pending */ -#define ACTIVE 1 /* Transmitter on, sending data */ -#define UNDERRUN 2 /* Transmitter on, flushing CRC */ -#define FLAGOUT 3 /* CRC sent - attempt to start next frame */ -#define DEFER 4 /* Receive Active - DEFER Transmit */ -#define ST_TXDELAY 5 /* Sending leading flags */ -#define CRCOUT 6 - char rstate; /* Set when !DCD goes to 0 (TRUE) */ -/* Normal state is ACTIVE if Receive enabled */ -#define RXERROR 2 /* Error -- Aborting current Frame */ -#define RXABORT 3 /* ABORT sequence detected */ -#define TOOBIG 4 /* too large a frame to store */ - int dev; /* Device number */ - int base; /* Base of I/O registers */ - int cardbase; /* Base address of card */ - int stata; /* address of Channel A status regs */ - int statb; /* address of Channel B status regs */ - int speed; /* Line speed, bps */ - int clockmode; /* tapr 9600 modem clocking option */ - int txdelay; /* Transmit Delay 10 ms/cnt */ - unsigned char persist; /* Persistence (0-255) as a % */ - int slotime; /* Delay to wait on persistence hit */ - int squeldelay; /* Delay after XMTR OFF for squelch tail */ - struct iface *iface; /* Associated interface */ - int dmachan; /* DMA channel for this port */ -}; - -#endif diff -u --recursive --new-file v2.0.34/linux/drivers/net/pt.c linux/drivers/net/pt.c --- v2.0.34/linux/drivers/net/pt.c Thu Apr 11 23:49:38 1996 +++ linux/drivers/net/pt.c Mon Jul 13 13:47:31 1998 @@ -32,6 +32,8 @@ * 07/10/95 cs Fixed for 1.3.30 (hopefully) * 26/11/95 cs Fixed for 1.3.43, ala 29/10 for pi2.c by ac * 21/12/95 cs Got rid of those nasty warnings when compiling, for 1.3.48 + * 08/08/96 jsn Convert to use as a module. Removed send_kiss, empty_scc and + * pt_loopback functions - they were unused. */ /* @@ -64,7 +66,9 @@ #define PARAM_HARDWARE 6 #define PARAM_RETURN 255 +#include <linux/config.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/sched.h> #include <linux/types.h> #include <linux/fcntl.h> @@ -86,14 +90,10 @@ #include <linux/skbuff.h> #include <linux/timer.h> #include <linux/if_arp.h> -#include "pt.h" +#include <linux/pt.h> #include "z8530.h" #include <net/ax25.h> -static char *version = -"PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n"; - - struct mbuf { struct mbuf *next; int cnt; @@ -133,9 +133,7 @@ static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize); static int hw_probe(int ioaddr); static void tdelay(struct pt_local *lp, int time); -static void empty_scc(struct pt_local *lp); static void chipset_init(struct device *dev); -static void send_kiss(struct device *dev, unsigned char arg, unsigned char val); static char ax25_bcast[7] = {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; @@ -185,7 +183,9 @@ ptr = skb->data; if (ptr[0] != 0 && skb->len >= 2) { - printk("Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1)); +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1)); +#endif /* Kludge to get device */ if ((struct pt_local*)(&pt0b.priv) == lp) dev = &pt0b; @@ -197,21 +197,17 @@ case PARAM_TXDELAY: /*TxDelay is in 10mS increments */ lp->txdelay = ptr[1] * 10; - send_kiss(dev, PARAM_TXDELAY, (u_char)(lp->txdelay/10)); break; case PARAM_PERSIST: lp->persist = ptr[1]; - send_kiss(dev, PARAM_PERSIST, (u_char)(lp->persist)); break; case PARAM_SLOTTIME: lp->slotime = ptr[1]; - send_kiss(dev, PARAM_SLOTTIME, (u_char)(lp->slotime/10)); break; case PARAM_FULLDUP: /* Yeah right, you wish! Fullduplex is a little while to * go folks, but this is how you fire it up */ - send_kiss(dev, PARAM_FULLDUP, 0); break; /* Perhaps we should have txtail here?? */ } /*switch */ @@ -226,7 +222,7 @@ restore_flags(flags); #ifdef PT_DEBUG - printk("PTd hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA); + printk(KERN_DEBUG "PT: hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA); #endif skb_queue_tail(&lp->sndq, skb); if (kickflag) { @@ -320,21 +316,6 @@ dev_kfree_skb(skb, FREE_WRITE); } -static void pt_loopback(struct pt_local *lp, int onoff) -{ - if (lp->base & CHANA) { - if (onoff == ON) - outb_p(pt_sercfg |= PT_LOOPA_ON, lp->cardbase + SERIAL_CFG); - else - outb_p(pt_sercfg &= ~PT_LOOPA_ON, lp->cardbase + SERIAL_CFG); - } else { /* it's channel B */ - if (onoff == ON) - outb_p(pt_sercfg |= PT_LOOPB_ON, lp->cardbase + SERIAL_CFG); - else - outb_p(pt_sercfg &= ~PT_LOOPB_ON, lp->cardbase + SERIAL_CFG); - } -} /*pt_loopback */ - /* Fill in the MAC-level header */ static int pt_header (struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) @@ -366,7 +347,7 @@ int tc, br; #ifdef PT_DEBUG - printk("PTd scc_init(): (%d).\n", lp->base & CHANA); + printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA); #endif save_flags(flags); cli(); @@ -486,8 +467,8 @@ struct pt_local *lp = (struct pt_local*) dev->priv; #ifdef PT_DEBUG - printk("PTd chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate); - printk("PTd chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate); + printk(KERN_DEBUG "PT: chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate); + printk(KERN_DEBUG "PT: chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate); #endif /* Reset SCC if both channels are to be canned */ if ( ((lp->base & CHANA) && !(pt_sercfg & PT_DTRB_ON)) || @@ -498,7 +479,7 @@ outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG); outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG); #ifdef PT_DEBUG - printk("PTd chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA); + printk(KERN_DEBUG "PT: chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA); #endif } /* Reset individual channel */ @@ -522,20 +503,20 @@ { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0, 0x2b0, 0x300, 0x330, 0x3f0, 0}; - printk(version); + printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n"); for (port = &ports[0]; *port && !card_type; port++) { ioaddr = *port; if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) { - printk("PT: Probing for card at address %#3x\n", ioaddr); + printk(KERN_INFO "PT: Probing for card at address %#3x\n", ioaddr); card_type = hw_probe(ioaddr); } } if (card_type) { - printk("PT: Found a PT at address %#3x\n",ioaddr); + printk(KERN_INFO "PT: Found a PT at address %#3x\n",ioaddr); } else { - printk("PT: ERROR: No card found.\n"); + printk(KERN_ERR "PT: ERROR: No card found.\n"); return -EIO; } @@ -641,7 +622,7 @@ long br; int cmd = lp->base + CTL; #ifdef PT_DEBUG - printk("PTd pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA); + printk(KERN_DEBUG "PT: pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA); #endif if (x == ON) { /* Ex ints off to avoid int */ @@ -738,8 +719,9 @@ return 0; } -static int pt_set_mac_address(struct device *dev, struct sockaddr *sa) +static int pt_set_mac_address(struct device *dev, void *addr) { + struct sockaddr *sa = (struct sockaddr *)addr; memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); /* addr is an AX.25 shifted ASCII */ return 0; /* mac address */ } @@ -857,11 +839,11 @@ restore_flags(flags); if (!dev->irq) { - printk("PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n"); + printk(KERN_ERR "PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n"); } } - printk("PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma); + printk(KERN_INFO "PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma); /* This board has jumpered interrupts. Snarf the interrupt vector * now. There is no point in waiting since no other device can use @@ -870,7 +852,7 @@ { int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", NULL); if (irqval) { - printk("PT: ERROR: Unable to get IRQ %d (irqval = %d).\n", + printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n", dev->irq, irqval); return EAGAIN; } @@ -903,10 +885,13 @@ /* New style flags */ dev->flags = 0; dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = sizeof(unsigned long); + +#ifdef CONFIG_INET + dev->pa_addr = in_aton("192.168.0.1"); + dev->pa_brdaddr = in_aton("192.168.0.255"); + dev->pa_mask = in_aton("255.255.255.0"); + dev->pa_alen = 4; +#endif return 0; } /* pt_probe() */ @@ -965,6 +950,8 @@ dev->interrupt = 0; dev->start = 1; first_time = 0; + + MOD_INC_USE_COUNT; return 0; } /* pt_open() */ @@ -974,7 +961,7 @@ struct pt_local *lp = (struct pt_local *) dev->priv; #ifdef PT_DEBUG - printk("PTd pt_send_packet(): (%d)\n", lp->base & CHANA); + printk(KERN_DEBUG "PT: pt_send_packet(): (%d)\n", lp->base & CHANA); #endif /* If some higher layer thinks we've missed an tx-done interrupt we are passed NULL. Caution: dev_tint() handles the cli()/sti() @@ -1019,8 +1006,10 @@ restore_flags(flags); #ifdef PT_DEBUG - printk("PTd pt_close(): Closing down channel (%d).\n", lp->base & CHANA); + printk(KERN_DEBUG "PT: pt_close(): Closing down channel (%d).\n", lp->base & CHANA); #endif + + MOD_DEC_USE_COUNT; return 0; } /* pt_close() */ @@ -1153,7 +1142,7 @@ cmd = lp->base + CTL; #ifdef PT_DEBUG - printk("PTd pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA); + printk(KERN_DEBUG "PT: pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA); #endif switch (lp->tstate) @@ -1253,7 +1242,7 @@ restore_flags(flags); return; default: - printk("PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") ); + printk(KERN_ERR "PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") ); pt_rts(lp, OFF); lp->tstate = IDLE; break; @@ -1270,7 +1259,7 @@ char rse; struct sk_buff *skb; int sksize, pkt_len; - struct mbuf *cur_buf; + struct mbuf *cur_buf = NULL; unsigned char *cfix; save_flags(flags); @@ -1280,7 +1269,7 @@ rse = rdscc(lp->cardbase, cmd, R1); #ifdef PT_DEBUG - printk("PTd pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA); + printk(KERN_DEBUG "PT: pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA); #endif if (lp->dmachan && (rse & Rx_OVR)) @@ -1333,7 +1322,7 @@ if (rse & END_FR) { #ifdef PT_DEBUG - printk("PTd pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt); + printk(KERN_DEBUG "PT: pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt); #endif if (lp->dmachan) { @@ -1371,7 +1360,7 @@ } #ifdef PT_DEBUG - printk("PTd pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state"); + printk(KERN_DEBUG "PT: pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state"); #endif } else { /* We have a valid frame */ @@ -1392,7 +1381,7 @@ skb = dev_alloc_skb(sksize); if (skb == NULL) { - printk("PT: %s: Memory squeeze, dropping packet.\n", dev->name); + printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; restore_flags(flags); return; @@ -1428,15 +1417,6 @@ restore_flags(flags); } /* pt_rxisr() */ -/* Read the SCC channel till no more data in receiver */ -static void empty_scc(struct pt_local *lp) -{ - while( rdscc(lp->cardbase, lp->base + CTL, R0) & Rx_CH_AV) { - /* Get data from Rx buffer and toss it */ - (void) inb_p(lp->base + DATA); - } -} /* empty_scc()*/ - /* * This handles the two timer interrupts. * This is a real bugger, cause you have to rip it out of the pi's @@ -1447,7 +1427,7 @@ unsigned long flags; #ifdef PT_DEBUG - printk("PTd pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA); + printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA); #endif save_flags(flags); @@ -1467,9 +1447,9 @@ default: if (lp->base & CHANA) - printk("PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate); + printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate); else - printk("PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate); + printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate); break; } /* end switch */ restore_flags(flags); @@ -1498,11 +1478,11 @@ { /* Read interrupt vector from R2, channel B */ #ifdef PT_DEBUG - printk("PTd pt_interrupt(): R3 = %#3x", st); + printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st); #endif /* st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/ #ifdef PT_DEBUG - printk(" R2 = %#3x.\n", st); + printk(KERN_DEBUG "PI: R2 = %#3x.\n", st); #endif if (st & CHARxIP) { /* Channel A Rx */ @@ -1573,7 +1553,7 @@ st = rdscc(lp->cardbase, cmd, R0); #ifdef PT_DEBUG - printk("PTd exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA); + printk(KERN_DEBUG "PT: exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA); #endif /* Reset external status latch */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); @@ -1588,7 +1568,7 @@ { case ACTIVE: /* Unexpected underrun */ #ifdef PT_DEBUG - printk("PTd exisr(): unexpected underrun detected.\n"); + printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n"); #endif free_p(lp->sndbuf); lp->sndbuf = NULL; @@ -1746,7 +1726,7 @@ if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) ) { #ifdef PT_DEBUG - printk("PTd exisr(): abort detected.\n"); + printk(KERN_DEBUG "PT: exisr(): abort detected.\n"); #endif /* read and dump all of SCC Rx FIFO */ (void) rdscc(lp->cardbase, cmd, R8); @@ -1765,7 +1745,7 @@ if ( (st & DCD) != (lp->saved_RR0 & DCD)) { #ifdef PT_DEBUG - printk("PTd: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" ); + printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" ); #endif if (st & DCD) { @@ -1773,7 +1753,7 @@ if (lp->rcvbuf->cnt > 0) { #ifdef PT_DEBUG - printk("PTd pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt); + printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt); #endif /* wind back buffers */ lp->rcp = lp->rcvbuf->data; @@ -1801,30 +1781,25 @@ } /* pt_exisr() */ -/* This function is used to send the KISS params back to the kernel itself, - * just like the TNCs do (I think) - * It's a (bit of a) kludge - */ -static void send_kiss(struct device *dev, unsigned char arg, unsigned char val) +#ifdef MODULE +int init_module(void) { - struct sk_buff *skb; - unsigned char *cfix; -/* struct pt_local *lp = (struct pt_local*)dev->priv;*/ - - - skb = dev_alloc_skb(2); - if (skb == NULL) - { - printk("PT: send_kiss(): Memory squeeze, dropping KISS reply.\n"); - return; - } - skb->dev = dev; - cfix = skb_put(skb, 2); - cfix[0]=arg; - cfix[1]=val; - skb->protocol=htons(ETH_P_AX25); - skb->mac.raw=skb->data; - IS_SKB(skb); - netif_rx(skb); + register_symtab(NULL); + return pt_init(); } - + +void cleanup_module(void) +{ + free_irq(pt0a.irq, NULL); /* IRQs and IO Ports are shared */ + release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE); + irq2dev_map[pt0a.irq] = NULL; + + kfree(pt0a.priv); + pt0a.priv = NULL; + unregister_netdev(&pt0a); + + kfree(pt0b.priv); + pt0b.priv = NULL; + unregister_netdev(&pt0b); +} +#endif diff -u --recursive --new-file v2.0.34/linux/drivers/net/pt.h linux/drivers/net/pt.h --- v2.0.34/linux/drivers/net/pt.h Mon Dec 25 20:03:00 1995 +++ linux/drivers/net/pt.h Mon Jul 13 13:47:31 1998 @@ -1,176 +0,0 @@ -/* - * pt.h: Linux device driver for the Gracilis PackeTwin - * Copyright (C) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.) - * - * Please read the notice appearing at the top of the file pt.c - */ -#define DMA_BUFF_SIZE 2200 - -/* Network statistics, with the same names as 'struct enet_statistics'. */ -#define netstats enet_statistics - -#define ON 1 -#define OFF 0 - - -/* Register offset info, specific to the PT - * E.g., to read the data port on channel A, use - * inportb(pichan[dev].base + CHANA + DATA) - */ -#define CHANB 0 /* Base of channel B regs */ -#define CHANA 2 /* Base of channel A regs */ - -/* 8530 ports on each channel */ -#define CTL 0 -#define DATA 1 - -#define DMAEN 0x8 /* Offset off DMA Enable register */ - -/* Timer chip offsets */ -#define TMR0 0x4 /* Offset of timer 0 register */ -#define TMR1 0x5 /* Offset of timer 1 register */ -#define TMR2 0x6 /* Offset of timer 2 register */ -#define TMRCMD 0x7 /* Offset of timer command register */ -#define INT_REG 0x8 -#define TMR1CLR 0x9 -#define TMR2CLR 0xa - -/* Interrupt register equates */ -#define PT_SCC_MSK 0x1 -#define PT_TMR1_MSK 0x2 -#define PT_TMR2_MSK 0x4 - -/* Serial/interrupt register equates */ -#define PT_DTRA_ON 0x1 -#define PT_DTRB_ON 0x2 -#define PT_EXTCLKA 0x4 -#define PT_EXTCLKB 0x8 -#define PT_LOOPA_ON 0x10 -#define PT_LOOPB_ON 0x20 -#define PT_EI 0x80 - -/* Timer chip equates */ -#define SC0 0x00 /* Select counter 0 */ -#define SC1 0x40 /* Select counter 1 */ -#define SC2 0x80 /* Select counter 2 */ -#define CLATCH 0x00 /* Counter latching operation */ -#define MSB 0x20 /* Read/load MSB only */ -#define LSB 0x10 /* Read/load LSB only */ -#define LSB_MSB 0x30 /* Read/load LSB, then MSB */ -#define MODE0 0x00 /* Interrupt on terminal count */ -#define MODE1 0x02 /* Programmable one shot */ -#define MODE2 0x04 /* Rate generator */ -#define MODE3 0x06 /* Square wave rate generator */ -#define MODE4 0x08 /* Software triggered strobe */ -#define MODE5 0x0a /* Hardware triggered strobe */ -#define BCD 0x01 /* BCD counter */ - -/* DMA controller registers */ -#define DMA_STAT 8 /* DMA controller status register */ -#define DMA_CMD 8 /* DMA controller command register */ -#define DMA_MASK 10 /* DMA controller mask register */ -#define DMA_MODE 11 /* DMA controller mode register */ -#define DMA_RESETFF 12 /* DMA controller first/last flip flop */ -/* DMA data */ -#define DMA_DISABLE (0x04) /* Disable channel n */ -#define DMA_ENABLE (0x00) /* Enable channel n */ -/* Single transfers, incr. address, auto init, writes, ch. n */ -#define DMA_RX_MODE (0x54) -/* Single transfers, incr. address, no auto init, reads, ch. n */ -#define DMA_TX_MODE (0x48) - -/* Write registers */ -#define DMA_CFG 0x08 -#define SERIAL_CFG 0x09 -#define INT_CFG 0x09 /* shares with serial config */ -#define DMA_CLR_FF 0x0a - -#define SINGLE 3686400 -#define DOUBLE 7372800 -#define XTAL ((long) 6144000L) - -#define SIOCGPIPARAM 0x5000 /* get PI parameters */ -#define SIOCSPIPARAM 0x5001 /* set */ -#define SIOCGPIBAUD 0x5002 /* get only baud rate */ -#define SIOCSPIBAUD 0x5003 -#define SIOCGPIDMA 0x5004 /* get only DMA */ -#define SIOCSPIDMA 0x5005 -#define SIOCGPIIRQ 0x5006 /* get only IRQ */ -#define SIOCSPIIRQ 0x5007 - -struct pt_req { - int cmd; - int speed; - int clockmode; - int txdelay; - unsigned char persist; - int slotime; - int squeldelay; - int dmachan; - int irq; -}; - -/* SCC Interrupt vectors, if we have set 'status low' */ -#define CHBTxIV 0x00 -#define CHBEXTIV 0x02 -#define CHBRxIV 0x04 -#define CHBSRCIV 0x06 -#define CHATxIV 0x08 -#define CHAEXTIV 0x0a -#define CHARxIV 0x0c -#define CHASRCIV 0x0e - - -#ifdef __KERNEL__ - -/* Information that needs to be kept for each channel. */ -struct pt_local { - struct netstats stats; /* %%%dp*/ - long open_time; /* Useless example local info. */ - unsigned long xtal; - - struct mbuf *rcvbuf;/* Buffer for current rx packet */ - struct mbuf *rxdmabuf1; /* DMA rx buffer */ - struct mbuf *rxdmabuf2; /* DMA rx buffer */ - - int bufsiz; /* Size of rcvbuf */ - char *rcp; /* Pointer into rcvbuf */ - - struct sk_buff_head sndq; /* Packets awaiting transmission */ - int sndcnt; /* Number of packets on sndq */ - struct sk_buff *sndbuf;/* Current buffer being transmitted */ - char *txdmabuf; /* Transmit DMA buffer */ - char *txptr; /* Used by B port tx */ - int txcnt; - char tstate; /* Transmitter state */ -#define IDLE 0 /* Transmitter off, no data pending */ -#define ACTIVE 1 /* Transmitter on, sending data */ -#define UNDERRUN 2 /* Transmitter on, flushing CRC */ -#define FLAGOUT 3 /* CRC sent - attempt to start next frame */ -#define DEFER 4 /* Receive Active - DEFER Transmit */ -#define ST_TXDELAY 5 /* Sending leading flags */ -#define CRCOUT 6 - char rstate; /* Set when !DCD goes to 0 (TRUE) */ -/* Normal state is ACTIVE if Receive enabled */ -#define RXERROR 2 /* Error -- Aborting current Frame */ -#define RXABORT 3 /* ABORT sequence detected */ -#define TOOBIG 4 /* too large a frame to store */ - - int dev; /* Device number */ - int base; /* Base of I/O registers */ - int cardbase; /* Base address of card */ - int stata; /* address of Channel A status regs */ - int statb; /* address of Channel B status regs */ - int speed; /* Line speed, bps */ - int clockmode; /* tapr 9600 modem clocking option */ - int txdelay; /* Transmit Delay 10 ms/cnt */ - unsigned char persist; /* Persistence (0-255) as a % */ - int slotime; /* Delay to wait on persistence hit */ - int squeldelay; /* Delay after XMTR OFF for squelch tail */ - struct iface *iface; /* Associated interface */ - int dmachan; /* DMA channel for this port */ - char saved_RR0; /* The saved version of RR) that we compare with */ - int nrzi; /* Do we use NRZI (or NRZ) */ -}; - -#endif diff -u --recursive --new-file v2.0.34/linux/drivers/net/slip.c linux/drivers/net/slip.c --- v2.0.34/linux/drivers/net/slip.c Sun May 12 21:17:23 1996 +++ linux/drivers/net/slip.c Mon Jul 13 13:47:31 1998 @@ -66,10 +66,6 @@ #include <linux/tty.h> #include <linux/errno.h> #include <linux/netdevice.h> -#ifdef CONFIG_AX25 -#include <linux/timer.h> -#include <net/ax25.h> -#endif #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/if_arp.h> @@ -294,11 +290,8 @@ set_bit(SLF_ERROR, &sl->flags); } } -#ifdef CONFIG_AX25 - sl->mtu = dev->mtu + 73; -#else sl->mtu = dev->mtu; -#endif + sl->buffsize = len; restore_flags(flags); @@ -386,10 +379,7 @@ skb->dev = sl->dev; memcpy(skb_put(skb,count), sl->rbuff, count); skb->mac.raw=skb->data; - if(sl->mode & SL_MODE_AX25) - skb->protocol=htons(ETH_P_AX25); - else - skb->protocol=htons(ETH_P_IP); + skb->protocol=htons(ETH_P_IP); netif_rx(skb); sl->rx_packets++; } @@ -402,11 +392,8 @@ int actual, count; -#ifdef CONFIG_AX25 - if (sl->mtu != sl->dev->mtu + 73) { /* Someone has been ifconfigging */ -#else if (sl->mtu != sl->dev->mtu) { /* Someone has been ifconfigging */ -#endif + sl_changedmtu(sl); } @@ -535,15 +522,6 @@ sl_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { -#ifdef CONFIG_AX25 -#ifdef CONFIG_INET - struct slip *sl = (struct slip*)(dev->priv); - - if (sl->mode & SL_MODE_AX25 && type != htons(ETH_P_AX25)) { - return ax25_encapsulate(skb, dev, type, daddr, saddr, len); - } -#endif -#endif return 0; } @@ -553,15 +531,6 @@ sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb) { -#ifdef CONFIG_AX25 -#ifdef CONFIG_INET - struct slip *sl = (struct slip*)(dev->priv); - - if (sl->mode & SL_MODE_AX25) { - return ax25_rebuild_header(buff, dev, raddr, skb); - } -#endif -#endif return 0; } @@ -611,12 +580,7 @@ goto noslcomp; } #endif - -#ifdef CONFIG_AX25 - sl->mtu = dev->mtu + 73; -#else sl->mtu = dev->mtu; -#endif sl->buffsize = len; sl->rcount = 0; sl->xleft = 0; @@ -701,11 +665,8 @@ * Argh! mtu change time! - costs us the packet part received * at the change */ -#ifdef CONFIG_AX25 - if (sl->mtu != sl->dev->mtu + 73) { -#else if (sl->mtu != sl->dev->mtu) { -#endif + sl_changedmtu(sl); } @@ -762,11 +723,6 @@ /* Restore default settings */ sl->mode = SL_MODE_DEFAULT; sl->dev->type = ARPHRD_SLIP + sl->mode; -#ifdef CONFIG_AX25 - if (sl->dev->type == 260) { /* KISS */ - sl->dev->type = ARPHRD_AX25; - } -#endif /* Perform the low-level SLIP initialization. */ if ((err = sl_open(sl->dev))) { return err; @@ -1008,31 +964,6 @@ } #endif /* CONFIG_SLIP_MODE_SLIP6 */ -#ifdef CONFIG_AX25 -int -sl_set_mac_address(struct device *dev, void *addr) -{ - int err; - - err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN); - if (err) { - return err; - } - - memcpy_fromfs(dev->dev_addr, addr, AX25_ADDR_LEN); /* addr is an AX.25 shifted ASCII mac address */ - - return 0; -} - -static int -sl_set_dev_mac_address(struct device *dev, void *addr) -{ - struct sockaddr *sa=addr; - memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN); - return 0; -} -#endif /* CONFIG_AX25 */ - /* Perform I/O control on an active SLIP channel. */ static int @@ -1086,34 +1017,12 @@ return -EINVAL; } #endif -#ifndef CONFIG_AX25 - if (tmp & SL_MODE_AX25) { - return -EINVAL; - } -#else - if (tmp & SL_MODE_AX25) { - sl->dev->addr_len=AX25_ADDR_LEN; /* sizeof an AX.25 addr */ - sl->dev->hard_header_len=AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3; - } else { - sl->dev->addr_len=0; /* No mac addr in slip mode */ - sl->dev->hard_header_len=0; - } -#endif sl->mode = tmp; sl->dev->type = ARPHRD_SLIP+sl->mode; -#ifdef CONFIG_AX25 - if (sl->dev->type == 260) { - sl->dev->type = ARPHRD_AX25; - } -#endif return 0; case SIOCSIFHWADDR: -#ifdef CONFIG_AX25 - return sl_set_mac_address(sl->dev, arg); -#else return -EINVAL; -#endif #ifdef CONFIG_SLIP_SMART /* VSV changes start here */ @@ -1207,9 +1116,6 @@ #if defined(SL_INCLUDE_CSLIP) && !defined(MODULE) printk("CSLIP: code copyright 1989 Regents of the University of California.\n"); #endif -#ifdef CONFIG_AX25 - printk(KERN_INFO "AX25: KISS encapsulation enabled.\n"); -#endif #ifdef CONFIG_SLIP_SMART printk(KERN_INFO "SLIP linefill/keepalive option.\n"); #endif @@ -1260,12 +1166,6 @@ { struct slip *sl = (struct slip*)(dev->priv); int i; -#ifdef CONFIG_AX25 - static char ax25_bcast[AX25_ADDR_LEN] = - {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1}; - static char ax25_test[AX25_ADDR_LEN] = - {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1}; -#endif if (sl == NULL) /* Allocation failed ?? */ return -ENODEV; @@ -1283,22 +1183,10 @@ dev->stop = sl_close; dev->hard_header = sl_header; dev->get_stats = sl_get_stats; -#ifdef HAVE_SET_MAC_ADDR -#ifdef CONFIG_AX25 - dev->set_mac_address = sl_set_dev_mac_address; -#endif -#endif dev->hard_header_len = 0; dev->addr_len = 0; dev->type = ARPHRD_SLIP + SL_MODE_DEFAULT; dev->tx_queue_len = 10; -#ifdef CONFIG_AX25 - if (sl->dev->type == 260) { - sl->dev->type = ARPHRD_AX25; - } - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); /* Only activated in AX.25 mode */ - memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN); /* "" "" "" "" */ -#endif dev->rebuild_header = sl_rebuild_header; for (i = 0; i < DEV_NUMBUFFS; i++) { diff -u --recursive --new-file v2.0.34/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c --- v2.0.34/linux/drivers/net/smc-ultra.c Mon Jul 13 13:46:30 1998 +++ linux/drivers/net/smc-ultra.c Mon Jul 13 13:47:31 1998 @@ -75,13 +75,13 @@ 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_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 -u --recursive --new-file v2.0.34/linux/drivers/net/smc-ultra32.c linux/drivers/net/smc-ultra32.c --- v2.0.34/linux/drivers/net/smc-ultra32.c Mon Jul 13 13:46:30 1998 +++ linux/drivers/net/smc-ultra32.c Mon Jul 13 13:47:31 1998 @@ -68,7 +68,7 @@ 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. */ @@ -238,9 +238,8 @@ static int ultra32_open(struct device *dev) { int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */ - int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : SA_SHIRQ; - if (request_irq(dev->irq, ei_interrupt, irq_flags, ei_status.name, dev)) + if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev)) return -EAGAIN; outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ @@ -268,7 +267,7 @@ outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */ outb(0x00, ioaddr + 6); /* Disable interrupts. */ - free_irq(dev->irq, NULL); + free_irq(dev->irq, dev); irq2dev_map[dev->irq] = 0; NS8390_init(dev, 0); diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/Makefile linux/drivers/net/soundmodem/Makefile --- v2.0.34/linux/drivers/net/soundmodem/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/soundmodem/Makefile Mon Jul 13 13:47:31 1998 @@ -0,0 +1,60 @@ +# +# Makefile for the soundmodem device driver. +# +# 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.. +# + +O_TARGET := soundmodem.o + +O_OBJS := sm.o +ifeq ($(CONFIG_SOUNDMODEM_SBC),y) +O_OBJS += sm_sbc.o +endif +ifeq ($(CONFIG_SOUNDMODEM_WSS),y) +O_OBJS += sm_wss.o +endif +ifeq ($(CONFIG_SOUNDMODEM_AFSK1200),y) +O_OBJS += sm_afsk1200.o +endif +ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_7),y) +O_OBJS += sm_afsk2400_7.o +endif +ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_8),y) +O_OBJS += sm_afsk2400_8.o +endif +ifeq ($(CONFIG_SOUNDMODEM_AFSK2666),y) +O_OBJS += sm_afsk2666.o +endif +ifeq ($(CONFIG_SOUNDMODEM_HAPN4800),y) +O_OBJS += sm_hapn4800.o +endif +ifeq ($(CONFIG_SOUNDMODEM_PSK4800),y) +O_OBJS += sm_psk4800.o +endif +ifeq ($(CONFIG_SOUNDMODEM_FSK9600),y) +O_OBJS += sm_fsk9600.o +endif + +M_OBJS := $(O_TARGET) + +all: all_targets +.PHONY: all + +gentbl: gentbl.c + $(HOSTCC) -Wall $< -o $@ -lm + +TBLHDR := sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h +TBLHDR += sm_tbl_afsk2666.h sm_tbl_psk4800.h +TBLHDR += sm_tbl_hapn4800.h sm_tbl_fsk9600.h + +$(TBLHDR): gentbl + ./gentbl + +fastdep: $(TBLHDR) + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/gentbl.c linux/drivers/net/soundmodem/gentbl.c --- v2.0.34/linux/drivers/net/soundmodem/gentbl.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/soundmodem/gentbl.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,688 @@ +/*****************************************************************************/ + +/* + * gentbl.c -- soundcard radio modem driver table generator. + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + */ + +#include <stdio.h> +#include <math.h> +#include <string.h> + +/* -------------------------------------------------------------------- */ + +static void gentbl_offscostab(FILE *f, unsigned int nbits) +{ + int i; + + fprintf(f, "\n/*\n * small cosine table in U8 format\n */\n" + "#define OFFSCOSTABBITS %u\n" + "#define OFFSCOSTABSIZE (1<<OFFSCOSTABBITS)\n\n", + nbits); + fprintf(f, "static unsigned char offscostab[OFFSCOSTABSIZE] = {\n\t"); + for (i = 0; i < (1<<nbits); i++) { + fprintf(f, "%4u", (int) + (128+127.0*cos(i*2.0*M_PI/(1<<nbits)))); + if (i < (1<<nbits)-1) + fprintf(f, "%s", (i & 7) == 7 ? ",\n\t" : ","); + } + fprintf(f, "\n};\n\n" + "#define OFFSCOS(x) offscostab[((x)>>%d)&0x%x]\n\n", + 16-nbits, (1<<nbits)-1); +} + +/* -------------------------------------------------------------------- */ + +static void gentbl_costab(FILE *f, unsigned int nbits) +{ + int i; + + fprintf(f, "\n/*\n * more accurate cosine table\n */\n\n" + "static const short costab[%d] = {", (1<<nbits)); + for (i = 0; i < (1<<nbits); i++) { + if (!(i & 7)) + fprintf(f, "\n\t"); + fprintf(f, "%6d", (int)(32767.0*cos(i*2.0*M_PI/(1<<nbits)))); + if (i != ((1<<nbits)-1)) + fprintf(f, ", "); + } + fprintf(f, "\n};\n\n#define COS(x) costab[((x)>>%d)&0x%x]\n" + "#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits, + (1<<nbits)-1); +} + +/* -------------------------------------------------------------------- */ + +#define AFSK12_SAMPLE_RATE 9600 +#define AFSK12_TX_FREQ_LO 1200 +#define AFSK12_TX_FREQ_HI 2200 +#define AFSK12_CORRLEN 8 + +static void gentbl_afsk1200(FILE *f) +{ + int i, v, sum; + +#define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE +#define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE + + fprintf(f, "\n/*\n * afsk1200 specific tables\n */\n" + "#define AFSK12_SAMPLE_RATE %u\n" + "#define AFSK12_TX_FREQ_LO %u\n" + "#define AFSK12_TX_FREQ_HI %u\n" + "#define AFSK12_CORRLEN %u\n\n", + AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO, + AFSK12_TX_FREQ_HI, AFSK12_CORRLEN); + fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t"); + for(sum = i = 0; i < AFSK12_CORRLEN; i++) { + sum += (v = 127.0*cos(ARGLO(i))); + fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' '); + } + fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_I %d\n\n" + "static const int afsk12_tx_lo_q[] = {\n\t", sum); + for(sum = i = 0; i < AFSK12_CORRLEN; i++) { + sum += (v = 127.0*sin(ARGLO(i))); + fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' '); + } + fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %d\n\n" + "static const int afsk12_tx_hi_i[] = {\n\t", sum); + for(sum = i = 0; i < AFSK12_CORRLEN; i++) { + sum += (v = 127.0*cos(ARGHI(i))); + fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' '); + } + fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %d\n\n" + "static const int afsk12_tx_hi_q[] = {\n\t", sum); + for(sum = i = 0; i < AFSK12_CORRLEN; i++) { + sum += (v = 127.0*sin(ARGHI(i))); + fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' '); + } + fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum); +#undef ARGLO +#undef ARGHI +} + +/* -------------------------------------------------------------------- */ + +static const float fsk96_tx_coeff_4[32] = { + -0.001152, 0.000554, 0.002698, 0.002753, + -0.002033, -0.008861, -0.008002, 0.006607, + 0.023727, 0.018905, -0.018056, -0.057957, + -0.044368, 0.055683, 0.207667, 0.322048, + 0.322048, 0.207667, 0.055683, -0.044368, + -0.057957, -0.018056, 0.018905, 0.023727, + 0.006607, -0.008002, -0.008861, -0.002033, + 0.002753, 0.002698, 0.000554, -0.001152 +}; + +static const float fsk96_tx_coeff_5[40] = { + -0.001009, -0.000048, 0.001376, 0.002547, + 0.002061, -0.001103, -0.005795, -0.008170, + -0.004017, 0.006924, 0.018225, 0.019238, + 0.002925, -0.025777, -0.048064, -0.039683, + 0.013760, 0.104144, 0.200355, 0.262346, + 0.262346, 0.200355, 0.104144, 0.013760, + -0.039683, -0.048064, -0.025777, 0.002925, + 0.019238, 0.018225, 0.006924, -0.004017, + -0.008170, -0.005795, -0.001103, 0.002061, + 0.002547, 0.001376, -0.000048, -0.001009 +}; + +#define HAMMING(x) (0.54-0.46*cos(2*M_PI*(x))); + +static inline float hamming(float x) +{ + return 0.54-0.46*cos(2*M_PI*x); +} + +static inline float sinc(float x) +{ + if (x == 0) + return 1; + x *= M_PI; + return sin(x)/x; +} + +static void gentbl_fsk9600(FILE *f) +{ + int i, j, k, l, m; + float s; + float c[44]; + float min, max; + + fprintf(f, "\n/*\n * fsk9600 specific tables\n */\n"); + min = max = 0; + memset(c, 0, sizeof(c)); +#if 0 + memcpy(c+2, fsk96_tx_coeff_4, sizeof(fsk96_tx_coeff_4)); +#else + for (i = 0; i < 29; i++) + c[3+i] = sinc(1.2*((i-14.0)/4.0))*hamming(i/28.0)/3.5; +#endif + fprintf(f, "static unsigned char fsk96_txfilt_4[] = {\n\t"); + for (i = 0; i < 4; i++) { + for (j = 0; j < 256; j++) { + for (k = 1, s = 0, l = i; k < 256; k <<= 1) { + if (j & k) { + for (m = 0; m < 4; m++, l++) + s += c[l]; + } else { + for (m = 0; m < 4; m++, l++) + s -= c[l]; + } + } + s *= 0.75; + if (s > max) + max = s; + if (s < min) + min = s; + fprintf(f, "%4d", (int)(128+127*s)); + if (i < 3 || j < 255) + fprintf(f, ",%s", (j & 7) == 7 + ? "\n\t" : ""); + } + } +#ifdef VERBOSE + fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max); +#endif + fprintf(f, "\n};\n\n"); + min = max = 0; + memset(c, 0, sizeof(c)); +#if 0 + memcpy(c+2, fsk96_tx_coeff_5, sizeof(fsk96_tx_coeff_5)); +#else + for (i = 0; i < 36; i++) + c[4+i] = sinc(1.2*((i-17.5)/5.0))*hamming(i/35.0)/4.5; +#endif + fprintf(f, "static unsigned char fsk96_txfilt_5[] = {\n\t"); + for (i = 0; i < 5; i++) { + for (j = 0; j < 256; j++) { + for (k = 1, s = 0, l = i; k < 256; k <<= 1) { + if (j & k) { + for (m = 0; m < 5; m++, l++) + s += c[l]; + } else { + for (m = 0; m < 5; m++, l++) + s -= c[l]; + } + } + s *= 0.75; + if (s > max) + max = s; + if (s < min) + min = s; + fprintf(f, "%4d", (int)(128+127*s)); + if (i < 4 || j < 255) + fprintf(f, ",%s", (j & 7) == 7 + ? "\n\t" : ""); + } + } +#ifdef VERBOSE + fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max); +#endif + fprintf(f, "\n};\n\n"); +} + +/* -------------------------------------------------------------------- */ + +#define AFSK26_SAMPLERATE 16000 + +#define AFSK26_NUMCAR 2 +#define AFSK26_FIRSTCAR 2000 +#define AFSK26_MSK_LEN 6 +#define AFSK26_RXOVER 2 + +#define AFSK26_DEMCORRLEN (2*AFSK26_MSK_LEN) + +#define AFSK26_WINDOW(x) ((1-cos(2.0*M_PI*(x)))/2.0) + +#define AFSK26_AMPL(x) (((x)?1.0:0.7)) + +#undef AFSK26_AMPL +#define AFSK26_AMPL(x) 1 + +static void gentbl_afsk2666(FILE *f) +{ + int i, j, k, l, o, v, sumi, sumq; + float window[AFSK26_DEMCORRLEN*AFSK26_RXOVER]; + int cfreq[AFSK26_NUMCAR]; + + fprintf(f, "\n/*\n * afsk2666 specific tables\n */\n" + "#define AFSK26_DEMCORRLEN %d\n" + "#define AFSK26_SAMPLERATE %d\n\n", AFSK26_DEMCORRLEN, + AFSK26_SAMPLERATE); + fprintf(f, "static const unsigned int afsk26_carfreq[%d] = { ", + AFSK26_NUMCAR); + for (i = 0; i < AFSK26_NUMCAR; i++) { + cfreq[i] = 0x10000*AFSK26_FIRSTCAR/AFSK26_SAMPLERATE+ + 0x10000*i/AFSK26_MSK_LEN/2; + fprintf(f, "0x%x", cfreq[i]); + if (i < AFSK26_NUMCAR-1) + fprintf(f, ", "); + } + fprintf(f, " };\n\n"); + for (i = 0; i < AFSK26_DEMCORRLEN*AFSK26_RXOVER; i++) + window[i] = AFSK26_WINDOW(((float)i)/(AFSK26_DEMCORRLEN* + AFSK26_RXOVER)) * 127.0; + fprintf(f, "\nstatic const struct {\n\t" + "int i[%d];\n\tint q[%d];\n} afsk26_dem_tables[%d][%d] = {\n", + AFSK26_DEMCORRLEN, AFSK26_DEMCORRLEN, AFSK26_RXOVER, AFSK26_NUMCAR); + for (o = AFSK26_RXOVER-1; o >= 0; o--) { + fprintf(f, "\t{\n"); + for (i = 0; i < AFSK26_NUMCAR; i++) { + j = cfreq[i]; + fprintf(f, "\t\t{{ "); + for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumi = 0; l >= 0; + l--, k = (k+j)&0xffffu) { + sumi += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]* + cos(M_PI*k/32768.0)); + fprintf(f, "%6d%s", v, l ? ", " : " }, { "); + } + for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumq = 0; l >= 0; + l--, k = (k+j)&0xffffu) { + sumq += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]* + sin(M_PI*k/32768.0)); + fprintf(f, "%6d%s", v, l ? ", " : " }}"); + } + if (i < 1) + fprintf(f, ","); + fprintf(f, "\n#define AFSK26_DEM_SUM_I_%d_%d %d\n" + "#define AFSK26_DEM_SUM_Q_%d_%d %d\n", + AFSK26_RXOVER-1-o, i, sumi, AFSK26_RXOVER-1-o, i, sumq); + } + fprintf(f, "\t}%s\n", o ? "," : ""); + } + fprintf(f, "};\n\n"); +} + +/* -------------------------------------------------------------------- */ + +#define ATAN_TABLEN 1024 + +static void gentbl_atantab(FILE *f) +{ + int i; + short x; + + fprintf(f, "\n/*\n" + " * arctan table (indexed by i/q; should really be indexed by i/(i+q)\n" + " */\n""#define ATAN_TABLEN %d\n\n" + "static const unsigned short atan_tab[ATAN_TABLEN+2] = {", + ATAN_TABLEN); + for (i = 0; i <= ATAN_TABLEN; i++) { + if (!(i & 7)) + fprintf(f, "\n\t"); + x = atan(i / (float)ATAN_TABLEN) / M_PI * 0x8000; + fprintf(f, "%6d, ", x); + } + fprintf(f, "%6d\n};\n\n", x); + +} + +/* -------------------------------------------------------------------- */ + +#define PSK48_TXF_OVERSAMPLING 5 +#define PSK48_TXF_NUMSAMPLES 16 +#define PSK48_RXF_LEN 64 + +static const float psk48_tx_coeff[80] = { + -0.000379, -0.000640, -0.000000, 0.000772, + 0.000543, -0.000629, -0.001187, -0.000000, + 0.001634, 0.001183, -0.001382, -0.002603, + -0.000000, 0.003481, 0.002472, -0.002828, + -0.005215, -0.000000, 0.006705, 0.004678, + -0.005269, -0.009584, -0.000000, 0.012065, + 0.008360, -0.009375, -0.017028, -0.000000, + 0.021603, 0.015123, -0.017229, -0.032012, + -0.000000, 0.043774, 0.032544, -0.040365, + -0.084963, -0.000000, 0.201161, 0.374060, + 0.374060, 0.201161, -0.000000, -0.084963, + -0.040365, 0.032544, 0.043774, -0.000000, + -0.032012, -0.017229, 0.015123, 0.021603, + -0.000000, -0.017028, -0.009375, 0.008360, + 0.012065, -0.000000, -0.009584, -0.005269, + 0.004678, 0.006705, -0.000000, -0.005215, + -0.002828, 0.002472, 0.003481, -0.000000, + -0.002603, -0.001382, 0.001183, 0.001634, + -0.000000, -0.001187, -0.000629, 0.000543, + 0.000772, -0.000000, -0.000640, -0.000379 +}; + +static const float psk48_rx_coeff[PSK48_RXF_LEN] = { + -0.000219, 0.000360, 0.000873, 0.001080, + 0.000747, -0.000192, -0.001466, -0.002436, + -0.002328, -0.000699, 0.002101, 0.004809, + 0.005696, 0.003492, -0.001633, -0.007660, + -0.011316, -0.009627, -0.001780, 0.009712, + 0.019426, 0.021199, 0.011342, -0.008583, + -0.030955, -0.044093, -0.036634, -0.002651, + 0.054742, 0.123101, 0.184198, 0.220219, + 0.220219, 0.184198, 0.123101, 0.054742, + -0.002651, -0.036634, -0.044093, -0.030955, + -0.008583, 0.011342, 0.021199, 0.019426, + 0.009712, -0.001780, -0.009627, -0.011316, + -0.007660, -0.001633, 0.003492, 0.005696, + 0.004809, 0.002101, -0.000699, -0.002328, + -0.002436, -0.001466, -0.000192, 0.000747, + 0.001080, 0.000873, 0.000360, -0.000219 +}; + +static void gentbl_psk4800(FILE *f) +{ + int i, j, k; + short x; + + fprintf(f, "\n/*\n * psk4800 specific tables\n */\n" + "#define PSK48_TXF_OVERSAMPLING %d\n" + "#define PSK48_TXF_NUMSAMPLES %d\n\n" + "#define PSK48_SAMPLERATE 8000\n" + "#define PSK48_CAR_FREQ 2000\n" + "#define PSK48_PSK_LEN 5\n" + "#define PSK48_RXF_LEN %u\n" + "#define PSK48_PHASEINC (0x10000*PSK48_CAR_FREQ/PSK48_SAMPLERATE)\n" + "#define PSK48_SPHASEINC (0x10000/(2*PSK48_PSK_LEN))\n\n" + "static const short psk48_tx_table[PSK48_TXF_OVERSAMPLING*" + "PSK48_TXF_NUMSAMPLES*8*2] = {", + PSK48_TXF_OVERSAMPLING, PSK48_TXF_NUMSAMPLES, PSK48_RXF_LEN); + for (i = 0; i < PSK48_TXF_OVERSAMPLING; i++) { + for (j = 0; j < PSK48_TXF_NUMSAMPLES; j++) { + fprintf(f, "\n\t"); + for (k = 0; k < 8; k++) { + x = 32767.0 * cos(k*M_PI/4.0) * + psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i]; + fprintf(f, "%6d, ", x); + } + fprintf(f, "\n\t"); + for (k = 0; k < 8; k++) { + x = 32767.0 * sin(k*M_PI/4.0) * + psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i]; + fprintf(f, "%6d", x); + if (k != 7 || j != PSK48_TXF_NUMSAMPLES-1 || + i != PSK48_TXF_OVERSAMPLING-1) + fprintf(f, ", "); + } + } + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const short psk48_rx_coeff[PSK48_RXF_LEN] = {\n\t"); + for (i = 0; i < PSK48_RXF_LEN; i++) { + fprintf(f, "%6d", (int)(psk48_rx_coeff[i]*32767.0)); + if (i < PSK48_RXF_LEN-1) + fprintf(f, ",%s", (i & 7) == 7 ? "\n\t" : ""); + } + fprintf(f, "\n};\n\n"); +} + +/* -------------------------------------------------------------------- */ + +static void gentbl_hapn4800(FILE *f) +{ + int i, j, k, l; + float s; + float c[40]; + float min, max; + + fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n"); + /* + * firstly generate tables for the FM transmitter modulator + */ + min = max = 0; + memset(c, 0, sizeof(c)); + for (i = 0; i < 24; i++) + c[8+i] = sinc(1.5*((i-11.5)/8.0))*hamming(i/23.0)/2.4; + for (i = 0; i < 24; i++) + c[i] -= c[i+8]; + fprintf(f, "static unsigned char hapn48_txfilt_8[] = {\n\t"); + for (i = 0; i < 8; i++) { + for (j = 0; j < 16; j++) { + for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) { + if (j & k) + s += c[l]; + else + s -= c[l]; + } + if (s > max) + max = s; + if (s < min) + min = s; + fprintf(f, "%4d", (int)(128+127*s)); + if (i < 7 || j < 15) + fprintf(f, ",%s", (j & 7) == 7 + ? "\n\t" : ""); + } + } +#ifdef VERBOSE + fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max); +#endif + fprintf(f, "\n};\n\n"); + min = max = 0; + memset(c, 0, sizeof(c)); + for (i = 0; i < 30; i++) + c[10+i] = sinc(1.5*((i-14.5)/10.0))*hamming(i/29.0)/2.4; + for (i = 0; i < 30; i++) + c[i] -= c[i+10]; + fprintf(f, "static unsigned char hapn48_txfilt_10[] = {\n\t"); + for (i = 0; i < 10; i++) { + for (j = 0; j < 16; j++) { + for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) { + if (j & k) + s += c[l]; + else + s -= c[l]; + } + if (s > max) + max = s; + if (s < min) + min = s; + fprintf(f, "%4d", (int)(128+127*s)); + if (i < 9 || j < 15) + fprintf(f, ",%s", (j & 7) == 7 + ? "\n\t" : ""); + } + } +#ifdef VERBOSE + fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max); +#endif + fprintf(f, "\n};\n\n"); + /* + * secondly generate tables for the PM transmitter modulator + */ + min = max = 0; + memset(c, 0, sizeof(c)); + for (i = 0; i < 25; i++) + c[i] = sinc(1.4*((i-12.0)/8.0))*hamming(i/24.0)/6.3; + for (i = 0; i < 25; i++) + for (j = 1; j < 8; j++) + c[i] += c[i+j]; + fprintf(f, "static unsigned char hapn48_txfilt_pm8[] = {\n\t"); + for (i = 0; i < 8; i++) { + for (j = 0; j < 16; j++) { + for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) { + if (j & k) + s += c[l]; + else + s -= c[l]; + } + if (s > max) + max = s; + if (s < min) + min = s; + fprintf(f, "%4d", (int)(128+127*s)); + if (i < 7 || j < 15) + fprintf(f, ",%s", (j & 7) == 7 + ? "\n\t" : ""); + } + } +#ifdef VERBOSE + fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max); +#endif + fprintf(f, "\n};\n\n"); + min = max = 0; + memset(c, 0, sizeof(c)); + for (i = 0; i < 31; i++) + c[10+i] = sinc(1.4*((i-15.0)/10.0))*hamming(i/30.0)/7.9; + for (i = 0; i < 31; i++) + for (j = 1; j < 10; j++) + c[i] += c[i+j]; + fprintf(f, "static unsigned char hapn48_txfilt_pm10[] = {\n\t"); + for (i = 0; i < 10; i++) { + for (j = 0; j < 16; j++) { + for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) { + if (j & k) + s += c[l]; + else + s -= c[l]; + } + if (s > max) + max = s; + if (s < min) + min = s; + fprintf(f, "%4d", (int)(128+127*s)); + if (i < 9 || j < 15) + fprintf(f, ",%s", (j & 7) == 7 + ? "\n\t" : ""); + } + } +#ifdef VERBOSE + fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max); +#endif + fprintf(f, "\n};\n\n"); + +} + +/* -------------------------------------------------------------------- */ + +#define AFSK24_SAMPLERATE 16000 +#define AFSK24_CORRLEN 14 + +static void gentbl_afsk2400(FILE *f, float tcm3105clk) +{ + int i, sum, v; + + fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n" + "#define AFSK24_TX_FREQ_LO %d\n" + "#define AFSK24_TX_FREQ_HI %d\n" + "#define AFSK24_BITPLL_INC %d\n" + "#define AFSK24_SAMPLERATE %d\n\n", tcm3105clk, + (int)(tcm3105clk/3694.0), (int)(tcm3105clk/2015.0), + 0x10000*2400/AFSK24_SAMPLERATE, AFSK24_SAMPLERATE); + +#define ARGLO(x) 2.0*M_PI*(double)x*(tcm3105clk/3694.0)/(double)AFSK24_SAMPLERATE +#define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE +#define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0)) + + fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t"); + for(sum = i = 0; i < AFSK24_CORRLEN; i++) { + sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i)); + fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' '); + } + fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_I %d\n\n" + "static const int afsk24_tx_lo_q[] = {\n\t", sum); + for(sum = i = 0; i < AFSK24_CORRLEN; i++) { + sum += (v = 127.0*sin(ARGLO(i))*WINDOW(i)); + fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' '); + } + fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %d\n\n" + "static const int afsk24_tx_hi_i[] = {\n\t", sum); + for(sum = i = 0; i < AFSK24_CORRLEN; i++) { + sum += (v = 127.0*cos(ARGHI(i))*WINDOW(i)); + fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' '); + } + fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %d\n\n" + "static const int afsk24_tx_hi_q[] = {\n\t", sum); + for(sum = i = 0; i < AFSK24_CORRLEN; i++) { + sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i)); + fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' '); + } + fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum); +#undef ARGLO +#undef ARGHI +#undef WINDOW +} + +/* -------------------------------------------------------------------- */ + +static char *progname; + +static void gentbl_banner(FILE *f) +{ + fprintf(f, "/*\n * THIS FILE IS GENERATED AUTOMATICALLY BY %s, " + "DO NOT EDIT!\n */\n\n", progname); +} + +/* -------------------------------------------------------------------- */ + +int main(int argc, char *argv[]) +{ + FILE *f; + + progname = argv[0]; + if (!(f = fopen("sm_tbl_afsk1200.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_offscostab(f, 6); + gentbl_costab(f, 6); + gentbl_afsk1200(f); + fclose(f); + if (!(f = fopen("sm_tbl_afsk2666.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_offscostab(f, 6); + gentbl_costab(f, 6); + gentbl_afsk2666(f); + fclose(f); + if (!(f = fopen("sm_tbl_psk4800.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_psk4800(f); + gentbl_costab(f, 8); + gentbl_atantab(f); + fclose(f); + if (!(f = fopen("sm_tbl_hapn4800.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_hapn4800(f); + fclose(f); + if (!(f = fopen("sm_tbl_fsk9600.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_fsk9600(f); + fclose(f); + if (!(f = fopen("sm_tbl_afsk2400_8.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_offscostab(f, 6); + gentbl_costab(f, 6); + gentbl_afsk2400(f, 8000000); + fclose(f); + if (!(f = fopen("sm_tbl_afsk2400_7.h", "w"))) + exit(1); + gentbl_banner(f); + gentbl_offscostab(f, 6); + gentbl_costab(f, 6); + gentbl_afsk2400(f, 7372800); + fclose(f); + exit(0); +} + + +/* -------------------------------------------------------------------- */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm.c linux/drivers/net/soundmodem/sm.c --- v2.0.34/linux/drivers/net/soundmodem/sm.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/soundmodem/sm.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,899 @@ +/*****************************************************************************/ + +/* + * sm.c -- soundcard radio modem driver. + * + * Copyright (C) 1996-1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + * + * Command line options (insmod command line) + * + * mode mode string; eg. "wss:afsk1200" + * iobase base address of the soundcard; common values are 0x220 for sbc, + * 0x530 for wss + * irq interrupt number; common values are 7 or 5 for sbc, 11 for wss + * dma dma number; common values are 0 or 1 + * + * + * History: + * 0.1 21.09.96 Started + * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) + * 0.4 21.01.97 Separately compileable soundcard/modem modules + * 0.5 03.03.97 fixed LPT probing (check_lpt result was interpreted the wrong way round) + * 0.6 16.04.97 init code/data tagged + * 0.7 30.07.97 fixed halfduplex interrupt handlers/hotfix for CS423X + * 0.8 14.04.98 cleanups + */ + +/*****************************************************************************/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/ptrace.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/ioport.h> +#include <linux/net.h> +#include <linux/in.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/bitops.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include "sm.h" + +/* --------------------------------------------------------------------- */ + +/* + * currently this module is supposed to support both module styles, i.e. + * the old one present up to about 2.1.9, and the new one functioning + * starting with 2.1.21. The reason is I have a kit allowing to compile + * this module also under 2.0.x which was requested by several people. + * This will go in 2.2 + */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= 0x20100 +#include <asm/uaccess.h> +#else +#include <asm/segment.h> +#include <linux/mm.h> + +#undef put_user +#undef get_user + +#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) +#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) + +extern inline int copy_from_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_READ, from, n); + if (i) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +extern inline int copy_to_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_WRITE, to, n); + if (i) + return i; + memcpy_tofs(to, from, n); + return 0; +} +#endif + +#if LINUX_VERSION_CODE >= 0x20123 +#include <linux/init.h> +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +/* --------------------------------------------------------------------- */ + +/*static*/ const char sm_drvname[] = "soundmodem"; +static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1998 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "soundmodem: version 0.8 compiled " __TIME__ " " __DATE__ "\n"; + +/* --------------------------------------------------------------------- */ + +/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = { +#ifdef CONFIG_SOUNDMODEM_AFSK1200 + &sm_afsk1200_tx, +#endif /* CONFIG_SOUNDMODEM_AFSK1200 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2400_7 + &sm_afsk2400_7_tx, +#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2400_8 + &sm_afsk2400_8_tx, +#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2666 + &sm_afsk2666_tx, +#endif /* CONFIG_SOUNDMODEM_AFSK2666 */ +#ifdef CONFIG_SOUNDMODEM_PSK4800 + &sm_psk4800_tx, +#endif /* CONFIG_SOUNDMODEM_PSK4800 */ +#ifdef CONFIG_SOUNDMODEM_HAPN4800 + &sm_hapn4800_8_tx, + &sm_hapn4800_10_tx, + &sm_hapn4800_pm8_tx, + &sm_hapn4800_pm10_tx, +#endif /* CONFIG_SOUNDMODEM_HAPN4800 */ +#ifdef CONFIG_SOUNDMODEM_FSK9600 + &sm_fsk9600_4_tx, + &sm_fsk9600_5_tx, +#endif /* CONFIG_SOUNDMODEM_FSK9600 */ + NULL +}; + +/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = { +#ifdef CONFIG_SOUNDMODEM_AFSK1200 + &sm_afsk1200_rx, +#endif /* CONFIG_SOUNDMODEM_AFSK1200 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2400_7 + &sm_afsk2400_7_rx, +#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2400_8 + &sm_afsk2400_8_rx, +#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */ +#ifdef CONFIG_SOUNDMODEM_AFSK2666 + &sm_afsk2666_rx, +#endif /* CONFIG_SOUNDMODEM_AFSK2666 */ +#ifdef CONFIG_SOUNDMODEM_PSK4800 + &sm_psk4800_rx, +#endif /* CONFIG_SOUNDMODEM_PSK4800 */ +#ifdef CONFIG_SOUNDMODEM_HAPN4800 + &sm_hapn4800_8_rx, + &sm_hapn4800_10_rx, + &sm_hapn4800_pm8_rx, + &sm_hapn4800_pm10_rx, +#endif /* CONFIG_SOUNDMODEM_HAPN4800 */ +#ifdef CONFIG_SOUNDMODEM_FSK9600 + &sm_fsk9600_4_rx, + &sm_fsk9600_5_rx, +#endif /* CONFIG_SOUNDMODEM_FSK9600 */ + NULL +}; + +static const struct hardware_info *sm_hardware_table[] = { +#ifdef CONFIG_SOUNDMODEM_SBC + &sm_hw_sbc, + &sm_hw_sbcfdx, +#endif /* CONFIG_SOUNDMODEM_SBC */ +#ifdef CONFIG_SOUNDMODEM_WSS + &sm_hw_wss, + &sm_hw_wssfdx, +#endif /* CONFIG_SOUNDMODEM_WSS */ + NULL +}; + +/* --------------------------------------------------------------------- */ + +#define NR_PORTS 4 + +/* --------------------------------------------------------------------- */ + +static struct device sm_device[NR_PORTS]; + +static struct { + char *mode; + int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase; +} sm_ports[NR_PORTS] = { + { NULL, -1, 0, 0, 0, -1, -1, -1 }, +}; + +/* --------------------------------------------------------------------- */ + +#define UART_RBR(iobase) (iobase+0) +#define UART_THR(iobase) (iobase+0) +#define UART_IER(iobase) (iobase+1) +#define UART_IIR(iobase) (iobase+2) +#define UART_FCR(iobase) (iobase+2) +#define UART_LCR(iobase) (iobase+3) +#define UART_MCR(iobase) (iobase+4) +#define UART_LSR(iobase) (iobase+5) +#define UART_MSR(iobase) (iobase+6) +#define UART_SCR(iobase) (iobase+7) +#define UART_DLL(iobase) (iobase+0) +#define UART_DLM(iobase) (iobase+1) + +#define SER_EXTENT 8 + +#define LPT_DATA(iobase) (iobase+0) +#define LPT_STATUS(iobase) (iobase+1) +#define LPT_CONTROL(iobase) (iobase+2) +#define LPT_IRQ_ENABLE 0x10 + +#define LPT_EXTENT 3 + +#define MIDI_DATA(iobase) (iobase) +#define MIDI_STATUS(iobase) (iobase+1) +#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */ +#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */ + +#define MIDI_EXTENT 2 + +/* ---------------------------------------------------------------------- */ + +#define PARAM_TXDELAY 1 +#define PARAM_PERSIST 2 +#define PARAM_SLOTTIME 3 +#define PARAM_TXTAIL 4 +#define PARAM_FULLDUP 5 +#define PARAM_HARDWARE 6 +#define PARAM_RETURN 255 + +#define SP_SER 1 +#define SP_PAR 2 +#define SP_MIDI 4 + +/* --------------------------------------------------------------------- */ +/* + * ===================== port checking routines ======================== + */ + +/* + * returns 0 if ok and != 0 on error; + * the same behaviour as par96_check_lpt in baycom.c + */ + +/* + * returns 0 if ok and != 0 on error; + * the same behaviour as par96_check_lpt in baycom.c + */ + +static int check_lpt(unsigned int iobase) +{ + unsigned char b1,b2; + int i; + + if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT) + return 0; + if (check_region(iobase, LPT_EXTENT)) + return 0; + b1 = inb(LPT_DATA(iobase)); + b2 = inb(LPT_CONTROL(iobase)); + outb(0xaa, LPT_DATA(iobase)); + i = inb(LPT_DATA(iobase)) == 0xaa; + outb(0x55, LPT_DATA(iobase)); + i &= inb(LPT_DATA(iobase)) == 0x55; + outb(0x0a, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a; + outb(0x05, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05; + outb(b1, LPT_DATA(iobase)); + outb(b2, LPT_CONTROL(iobase)); + return !i; +} + +/* --------------------------------------------------------------------- */ + +enum uart { c_uart_unknown, c_uart_8250, + c_uart_16450, c_uart_16550, c_uart_16550A}; +static const char *uart_str[] = + { "unknown", "8250", "16450", "16550", "16550A" }; + +static enum uart check_uart(unsigned int iobase) +{ + unsigned char b1,b2,b3; + enum uart u; + enum uart uart_tab[] = + { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; + + if (iobase <= 0 || iobase > 0x1000-SER_EXTENT) + return c_uart_unknown; + if (check_region(iobase, SER_EXTENT)) + return c_uart_unknown; + b1 = inb(UART_MCR(iobase)); + outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */ + b2 = inb(UART_MSR(iobase)); + outb(0x1a, UART_MCR(iobase)); + b3 = inb(UART_MSR(iobase)) & 0xf0; + outb(b1, UART_MCR(iobase)); /* restore old values */ + outb(b2, UART_MSR(iobase)); + if (b3 != 0x90) + return c_uart_unknown; + inb(UART_RBR(iobase)); + inb(UART_RBR(iobase)); + outb(0x01, UART_FCR(iobase)); /* enable FIFOs */ + u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3]; + if (u == c_uart_16450) { + outb(0x5a, UART_SCR(iobase)); + b1 = inb(UART_SCR(iobase)); + outb(0xa5, UART_SCR(iobase)); + b2 = inb(UART_SCR(iobase)); + if ((b1 != 0x5a) || (b2 != 0xa5)) + u = c_uart_8250; + } + return u; +} + +/* --------------------------------------------------------------------- */ + +static int check_midi(unsigned int iobase) +{ + unsigned long timeout; + unsigned long flags; + unsigned char b; + + if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT) + return 0; + if (check_region(iobase, MIDI_EXTENT)) + return 0; + timeout = jiffies + (HZ / 100); + while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) + if ((signed)(jiffies - timeout) > 0) + return 0; + save_flags(flags); + cli(); + outb(0xff, MIDI_DATA(iobase)); + b = inb(MIDI_STATUS(iobase)); + restore_flags(flags); + if (!(b & MIDI_WRITE_EMPTY)) + return 0; + while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) + if ((signed)(jiffies - timeout) > 0) + return 0; + return 1; +} + +/* --------------------------------------------------------------------- */ + +void sm_output_status(struct sm_state *sm) +{ + int invert_dcd = 0; + int invert_ptt = 0; + + int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ invert_ptt; + int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd; + + if (sm->hdrv.ptt_out.flags & SP_SER) { + outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase)); + outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase)); + } + if (sm->hdrv.ptt_out.flags & SP_PAR) { + outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase)); + } + if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) { + outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase)); + } +} + +/* --------------------------------------------------------------------- */ + +static void sm_output_open(struct sm_state *sm) +{ + enum uart u = c_uart_unknown; + + sm->hdrv.ptt_out.flags = 0; + if (sm->hdrv.ptt_out.seriobase > 0 && + sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT && + ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) { + sm->hdrv.ptt_out.flags |= SP_SER; + request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt"); + outb(0, UART_IER(sm->hdrv.ptt_out.seriobase)); + /* 5 bits, 1 stop, no parity, no break, Div latch access */ + outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase)); + outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase)); + outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */ + /* LCR and MCR set by output_status */ + } + if (sm->hdrv.ptt_out.pariobase > 0 && + sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT && + !check_lpt(sm->hdrv.ptt_out.pariobase)) { + sm->hdrv.ptt_out.flags |= SP_PAR; + request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt"); + } + if (sm->hdrv.ptt_out.midiiobase > 0 && + sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT && + check_midi(sm->hdrv.ptt_out.midiiobase)) { + sm->hdrv.ptt_out.flags |= SP_MIDI; + request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT, + "sm midi ptt"); + } + sm_output_status(sm); + + printk(KERN_INFO "%s: ptt output:", sm_drvname); + if (sm->hdrv.ptt_out.flags & SP_SER) + printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase, + uart_str[u]); + if (sm->hdrv.ptt_out.flags & SP_PAR) + printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase); + if (sm->hdrv.ptt_out.flags & SP_MIDI) + printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase); + if (!sm->hdrv.ptt_out.flags) + printk(" none"); + printk("\n"); +} + +/* --------------------------------------------------------------------- */ + +static void sm_output_close(struct sm_state *sm) +{ + /* release regions used for PTT output */ + sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0; + sm_output_status(sm); + if (sm->hdrv.ptt_out.flags & SP_SER) + release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT); + if (sm->hdrv.ptt_out.flags & SP_PAR) + release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT); + if (sm->hdrv.ptt_out.flags & SP_MIDI) + release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT); + sm->hdrv.ptt_out.flags = 0; +} + +/* --------------------------------------------------------------------- */ + +static int sm_open(struct device *dev); +static int sm_close(struct device *dev); +static int sm_ioctl(struct device *dev, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd); + +/* --------------------------------------------------------------------- */ + +static const struct hdlcdrv_ops sm_ops = { + sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl +}; + +/* --------------------------------------------------------------------- */ + +static int sm_open(struct device *dev) +{ + struct sm_state *sm; + int err; + + if (!dev || !dev->priv || + ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { + printk(KERN_ERR "sm_open: invalid device struct\n"); + return -EINVAL; + } + sm = (struct sm_state *)dev->priv; + + if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open) + return -ENODEV; + sm->hdrv.par.bitrate = sm->mode_rx->bitrate; + err = sm->hwdrv->open(dev, sm); + if (err) + return err; + sm_output_open(sm); + MOD_INC_USE_COUNT; + printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n", + sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name, + sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int sm_close(struct device *dev) +{ + struct sm_state *sm; + int err = -ENODEV; + + if (!dev || !dev->priv || + ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { + printk(KERN_ERR "sm_close: invalid device struct\n"); + return -EINVAL; + } + sm = (struct sm_state *)dev->priv; + + + if (sm->hwdrv && sm->hwdrv->close) + err = sm->hwdrv && sm->hwdrv->close(dev, sm); + sm_output_close(sm); + MOD_DEC_USE_COUNT; + printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n", + sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma); + return err; +} + +/* --------------------------------------------------------------------- */ + +static int sethw(struct device *dev, struct sm_state *sm, char *mode) +{ + char *cp = strchr(mode, ':'); + const struct hardware_info **hwp = sm_hardware_table; + + if (!cp) + cp = mode; + else { + *cp++ = '\0'; + while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode)) + hwp++; + if (!hwp || !*hwp || !(*hwp)->hw_name) + return -EINVAL; + if ((*hwp)->loc_storage > sizeof(sm->hw)) { + printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n", + sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage); + return -EINVAL; + } + sm->hwdrv = *hwp; + } + if (!*cp) + return 0; + if (sm->hwdrv && sm->hwdrv->sethw) + return sm->hwdrv->sethw(dev, sm, cp); + return -EINVAL; +} + +/* --------------------------------------------------------------------- */ + +static int sm_ioctl(struct device *dev, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd) +{ + struct sm_state *sm; + struct sm_ioctl bi; + unsigned long flags; + unsigned int newdiagmode; + unsigned int newdiagflags; + char *cp; + const struct modem_tx_info **mtp = sm_modem_tx_table; + const struct modem_rx_info **mrp = sm_modem_rx_table; + const struct hardware_info **hwp = sm_hardware_table; + + if (!dev || !dev->priv || + ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { + printk(KERN_ERR "sm_ioctl: invalid device struct\n"); + return -EINVAL; + } + sm = (struct sm_state *)dev->priv; + + if (cmd != SIOCDEVPRIVATE) { + if (!sm->hwdrv || !sm->hwdrv->ioctl) + return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd); + return -ENOIOCTLCMD; + } + switch (hi->cmd) { + default: + if (sm->hwdrv && sm->hwdrv->ioctl) + return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd); + return -ENOIOCTLCMD; + + case HDLCDRVCTL_GETMODE: + cp = hi->data.modename; + if (sm->hwdrv && sm->hwdrv->hw_name) + cp += sprintf(cp, "%s:", sm->hwdrv->hw_name); + else + cp += sprintf(cp, "<unspec>:"); + if (sm->mode_tx && sm->mode_tx->name) + cp += sprintf(cp, "%s", sm->mode_tx->name); + else + cp += sprintf(cp, "<unspec>"); + if (!sm->mode_rx || !sm->mode_rx || + strcmp(sm->mode_rx->name, sm->mode_tx->name)) { + if (sm->mode_rx && sm->mode_rx->name) + cp += sprintf(cp, ",%s", sm->mode_rx->name); + else + cp += sprintf(cp, ",<unspec>"); + } + if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi))) + return -EFAULT; + return 0; + + case HDLCDRVCTL_SETMODE: + if (dev->start || !suser()) + return -EACCES; + hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; + return sethw(dev, sm, hi->data.modename); + + case HDLCDRVCTL_MODELIST: + cp = hi->data.modename; + while (*hwp) { + if ((*hwp)->hw_name) + cp += sprintf("%s:,", (*hwp)->hw_name); + hwp++; + } + while (*mtp) { + if ((*mtp)->name) + cp += sprintf(">%s,", (*mtp)->name); + mtp++; + } + while (*mrp) { + if ((*mrp)->name) + cp += sprintf("<%s,", (*mrp)->name); + mrp++; + } + cp[-1] = '\0'; + if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi))) + return -EFAULT; + return 0; + +#ifdef SM_DEBUG + case SMCTL_GETDEBUG: + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + bi.data.dbg.int_rate = sm->debug_vals.last_intcnt; + bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc; + bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc; + bi.data.dbg.dma_residue = sm->debug_vals.dma_residue; + sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc = + sm->debug_vals.dma_residue = 0; + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; +#endif /* SM_DEBUG */ + + case SMCTL_DIAGNOSE: + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + newdiagmode = bi.data.diag.mode; + newdiagflags = bi.data.diag.flags; + if (newdiagmode > SM_DIAGMODE_CONSTELLATION) + return -EINVAL; + bi.data.diag.mode = sm->diag.mode; + bi.data.diag.flags = sm->diag.flags; + bi.data.diag.samplesperbit = sm->mode_rx->sperbit; + if (sm->diag.mode != newdiagmode) { + save_flags(flags); + cli(); + sm->diag.ptr = -1; + sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID; + sm->diag.mode = newdiagmode; + restore_flags(flags); + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + } + if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) { + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + } + if (bi.data.diag.datalen > DIAGDATALEN) + bi.data.diag.datalen = DIAGDATALEN; + if (sm->diag.ptr < bi.data.diag.datalen) { + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + } + if (copy_to_user(bi.data.diag.data, sm->diag.data, + bi.data.diag.datalen * sizeof(short))) + return -EFAULT; + bi.data.diag.flags |= SM_DIAGFLAG_VALID; + save_flags(flags); + cli(); + sm->diag.ptr = -1; + sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID; + sm->diag.mode = newdiagmode; + restore_flags(flags); + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + } +} + +/* --------------------------------------------------------------------- */ + +#ifdef __i386__ + +int sm_x86_capability = 0; + +__initfunc(static void i386_capability(void)) +{ + unsigned long flags; + unsigned long fl1; + union { + struct { + unsigned int ebx, edx, ecx; + } r; + unsigned char s[13]; + } id; + unsigned int eax; + + save_flags(flags); + flags |= 0x200000; + restore_flags(flags); + save_flags(flags); + fl1 = flags; + flags &= ~0x200000; + restore_flags(flags); + save_flags(flags); + if (!(fl1 & 0x200000) || (flags & 0x200000)) { + printk(KERN_WARNING "%s: cpu does not support CPUID\n", sm_drvname); + return; + } + __asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) : + "0" (0)); + id.s[12] = 0; + if (eax < 1) { + printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability " + "list\n", sm_drvname, id.s); + return; + } + printk(KERN_INFO "%s: cpu: vendor string %s ", sm_drvname, id.s); + __asm__ ("cpuid" : "=a" (eax), "=d" (sm_x86_capability) : "0" (1) : "ebx", "ecx"); + printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15, + eax & 15, sm_x86_capability); +} +#endif /* __i386__ */ + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +__initfunc(static int sm_init(void)) +#else /* MODULE */ +__initfunc(int sm_init(void)) +#endif /* MODULE */ +{ + int i, j, found = 0; + char set_hw = 1; + struct sm_state *sm; + char ifname[HDLCDRV_IFNAMELEN]; + + printk(sm_drvinfo); +#ifdef __i386__ + i386_capability(); +#endif /* __i386__ */ + /* + * register net devices + */ + for (i = 0; i < NR_PORTS; i++) { + struct device *dev = sm_device+i; + sprintf(ifname, "sm%d", i); + + if (!sm_ports[i].mode) + set_hw = 0; + if (!set_hw) + sm_ports[i].iobase = sm_ports[i].irq = 0; + j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state), + ifname, sm_ports[i].iobase, + sm_ports[i].irq, sm_ports[i].dma); + if (!j) { + sm = (struct sm_state *)dev->priv; + sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2; + sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase; + sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase; + sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase; + if (set_hw && sethw(dev, sm, sm_ports[i].mode)) + set_hw = 0; + found++; + } else { + printk(KERN_WARNING "%s: cannot register net device\n", + sm_drvname); + } + } + if (!found) + return -ENXIO; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +/* + * command line settable parameters + */ +static char *mode = NULL; +static int iobase = -1; +static int irq = -1; +static int dma = -1; +static int dma2 = -1; +static int serio = 0; +static int pario = 0; +static int midiio = 0; + +#if LINUX_VERSION_CODE >= 0x20115 + +MODULE_PARM(mode, "s"); +MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600"); +MODULE_PARM(iobase, "i"); +MODULE_PARM_DESC(iobase, "soundmodem base address"); +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(irq, "soundmodem interrupt"); +MODULE_PARM(dma, "i"); +MODULE_PARM_DESC(dma, "soundmodem dma channel"); +MODULE_PARM(dma2, "i"); +MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only"); +MODULE_PARM(serio, "i"); +MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port"); +MODULE_PARM(pario, "i"); +MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port"); +MODULE_PARM(midiio, "i"); +MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("Soundcard amateur radio modem driver"); + +#endif + +__initfunc(int init_module(void)) +{ + if (mode) { + if (iobase == -1) + iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530; + if (irq == -1) + irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11; + if (dma == -1) + dma = 1; + } + sm_ports[0].mode = mode; + sm_ports[0].iobase = iobase; + sm_ports[0].irq = irq; + sm_ports[0].dma = dma; + sm_ports[0].dma2 = dma2; + sm_ports[0].seriobase = serio; + sm_ports[0].pariobase = pario; + sm_ports[0].midiiobase = midiio; + sm_ports[1].mode = NULL; + + return sm_init(); +} + +/* --------------------------------------------------------------------- */ + +void cleanup_module(void) +{ + int i; + + printk(KERN_INFO "sm: cleanup_module called\n"); + + for(i = 0; i < NR_PORTS; i++) { + struct device *dev = sm_device+i; + struct sm_state *sm = (struct sm_state *)dev->priv; + + if (sm) { + if (sm->hdrv.magic != HDLCDRV_MAGIC) + printk(KERN_ERR "sm: invalid magic in " + "cleanup_module\n"); + else + hdlcdrv_unregister_hdlcdrv(dev); + } + } +} + +#else /* MODULE */ +/* --------------------------------------------------------------------- */ +/* + * format: sm=io,irq,dma[,dma2[,serio[,pario]]],mode + * mode: hw:modem + * hw: sbc, wss, wssfdx + * modem: afsk1200, fsk9600 + */ + +__initfunc(void sm_setup(char *str, int *ints)) +{ + int i; + + for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++); + if ((i >= NR_PORTS) || (ints[0] < 3)) { + printk(KERN_INFO "%s: too many or invalid interface " + "specifications\n", sm_drvname); + return; + } + sm_ports[i].mode = str; + sm_ports[i].iobase = ints[1]; + sm_ports[i].irq = ints[2]; + sm_ports[i].dma = ints[3]; + sm_ports[i].dma2 = (ints[0] >= 4) ? ints[4] : 0; + sm_ports[i].seriobase = (ints[0] >= 5) ? ints[5] : 0; + sm_ports[i].pariobase = (ints[0] >= 6) ? ints[6] : 0; + sm_ports[i].midiiobase = (ints[0] >= 7) ? ints[7] : 0; + if (i < NR_PORTS-1) + sm_ports[i+1].mode = NULL; +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm.h linux/drivers/net/soundmodem/sm.h --- v2.0.34/linux/drivers/net/soundmodem/sm.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/soundmodem/sm.h Mon Jul 13 13:47:31 1998 @@ -0,0 +1,382 @@ +/*****************************************************************************/ + +/* + * sm.h -- soundcard radio modem driver internal header. + * + * Copyright (C) 1996-1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + */ + +#ifndef _SM_H +#define _SM_H + +/* ---------------------------------------------------------------------- */ + +#include <linux/hdlcdrv.h> +#include <linux/soundmodem.h> + +#define SM_DEBUG + +/* ---------------------------------------------------------------------- */ +/* + * Information that need to be kept for each board. + */ + +struct sm_state { + struct hdlcdrv_state hdrv; + + const struct modem_tx_info *mode_tx; + const struct modem_rx_info *mode_rx; + + const struct hardware_info *hwdrv; + + /* + * Hardware (soundcard) access routines state + */ + struct { + void *ibuf; + unsigned int ifragsz; + unsigned int ifragptr; + unsigned int i16bit; + void *obuf; + unsigned int ofragsz; + unsigned int ofragptr; + unsigned int o16bit; + int ptt_cnt; + } dma; + + union { + long hw[32/sizeof(long)]; + } hw; + + /* + * state of the modem code + */ + union { + long m[48/sizeof(long)]; + } m; + union { + long d[256/sizeof(long)]; + } d; + +#define DIAGDATALEN 64 + struct diag_data { + unsigned int mode; + unsigned int flags; + volatile int ptr; + short data[DIAGDATALEN]; + } diag; + + +#ifdef SM_DEBUG + struct debug_vals { + unsigned long last_jiffies; + unsigned cur_intcnt; + unsigned last_intcnt; + unsigned mod_cyc; + unsigned demod_cyc; + unsigned dma_residue; + } debug_vals; +#endif /* SM_DEBUG */ +}; + +/* ---------------------------------------------------------------------- */ +/* + * Mode definition structure + */ + +struct modem_tx_info { + const char *name; + unsigned int loc_storage; + int srate; + int bitrate; + void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int); + void (*modulator_s16)(struct sm_state *, short *, unsigned int); + void (*init)(struct sm_state *); +}; + +struct modem_rx_info { + const char *name; + unsigned int loc_storage; + int srate; + int bitrate; + unsigned int overlap; + unsigned int sperbit; + void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int); + void (*demodulator_s16)(struct sm_state *, const short *, unsigned int); + void (*init)(struct sm_state *); +}; + +/* ---------------------------------------------------------------------- */ +/* + * Soundcard driver definition structure + */ + +struct hardware_info { + char *hw_name; /* used for request_{region,irq,dma} */ + unsigned int loc_storage; + /* + * mode specific open/close + */ + int (*open)(struct device *, struct sm_state *); + int (*close)(struct device *, struct sm_state *); + int (*ioctl)(struct device *, struct sm_state *, struct ifreq *, + struct hdlcdrv_ioctl *, int); + int (*sethw)(struct device *, struct sm_state *, char *); +}; + +/* --------------------------------------------------------------------- */ + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/* --------------------------------------------------------------------- */ + +extern const char sm_drvname[]; +extern const char sm_drvinfo[]; + +/* --------------------------------------------------------------------- */ +/* + * ===================== diagnostics stuff =============================== + */ + +extern inline void diag_trigger(struct sm_state *sm) +{ + if (sm->diag.ptr < 0) + if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd) + sm->diag.ptr = 0; +} + +/* --------------------------------------------------------------------- */ + +#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1)) +#define SHRT_MIN (-SHRT_MAX-1) + +extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod) +{ + int val; + + if ((sm->diag.mode != SM_DIAGMODE_INPUT && + sm->diag.mode != SM_DIAGMODE_DEMOD) || + sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0) + return; + val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp; + /* clip */ + if (val > SHRT_MAX) + val = SHRT_MAX; + if (val < SHRT_MIN) + val = SHRT_MIN; + sm->diag.data[sm->diag.ptr++] = val; +} + +/* --------------------------------------------------------------------- */ + +extern inline void diag_add_one(struct sm_state *sm, int val) +{ + if ((sm->diag.mode != SM_DIAGMODE_INPUT && + sm->diag.mode != SM_DIAGMODE_DEMOD) || + sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0) + return; + /* clip */ + if (val > SHRT_MAX) + val = SHRT_MAX; + if (val < SHRT_MIN) + val = SHRT_MIN; + sm->diag.data[sm->diag.ptr++] = val; +} + +/* --------------------------------------------------------------------- */ + +static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq) +{ + if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) || + sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0) + return; + /* clip */ + if (vali > SHRT_MAX) + vali = SHRT_MAX; + if (vali < SHRT_MIN) + vali = SHRT_MIN; + if (valq > SHRT_MAX) + valq = SHRT_MAX; + if (valq < SHRT_MIN) + valq = SHRT_MIN; + sm->diag.data[sm->diag.ptr++] = vali; + sm->diag.data[sm->diag.ptr++] = valq; +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== utility functions =============================== + */ + +extern inline unsigned int hweight32(unsigned int w) + __attribute__ ((unused)); +extern inline unsigned int hweight16(unsigned short w) + __attribute__ ((unused)); +extern inline unsigned int hweight8(unsigned char w) + __attribute__ ((unused)); + +extern inline unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +extern inline unsigned int hweight16(unsigned short w) +{ + unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555); + res = (res & 0x3333) + ((res >> 2) & 0x3333); + res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); + return (res & 0x00FF) + ((res >> 8) & 0x00FF); +} + +extern inline unsigned int hweight8(unsigned char w) +{ + unsigned short res = (w & 0x55) + ((w >> 1) & 0x55); + res = (res & 0x33) + ((res >> 2) & 0x33); + return (res & 0x0F) + ((res >> 4) & 0x0F); +} + +extern inline unsigned int gcd(unsigned int x, unsigned int y) + __attribute__ ((unused)); +extern inline unsigned int lcm(unsigned int x, unsigned int y) + __attribute__ ((unused)); + +extern inline unsigned int gcd(unsigned int x, unsigned int y) +{ + for (;;) { + if (!x) + return y; + if (!y) + return x; + if (x > y) + x %= y; + else + y %= x; + } +} + +extern inline unsigned int lcm(unsigned int x, unsigned int y) +{ + return x * y / gcd(x, y); +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== profiling ======================================= + */ + + +#ifdef __i386__ + +extern int sm_x86_capability; + +#define HAS_RDTSC (sm_x86_capability & 0x10) + +/* + * only do 32bit cycle counter arithmetic; we hope we won't overflow. + * in fact, overflowing modems would require over 2THz CPU clock speeds :-) + */ + +#define time_exec(var,cmd) \ +({ \ + if (HAS_RDTSC) { \ + unsigned int cnt1, cnt2, cnt3; \ + __asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3)); \ + cmd; \ + __asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3)); \ + var = cnt2-cnt1; \ + } else { \ + cmd; \ + } \ +}) + +#else /* __i386__ */ + +#define time_exec(var,cmd) cmd + +#endif /* __i386__ */ + +/* --------------------------------------------------------------------- */ + +extern const struct modem_tx_info sm_afsk1200_tx; +extern const struct modem_tx_info sm_afsk2400_7_tx; +extern const struct modem_tx_info sm_afsk2400_8_tx; +extern const struct modem_tx_info sm_afsk2666_tx; +extern const struct modem_tx_info sm_psk4800_tx; +extern const struct modem_tx_info sm_hapn4800_8_tx; +extern const struct modem_tx_info sm_hapn4800_10_tx; +extern const struct modem_tx_info sm_hapn4800_pm8_tx; +extern const struct modem_tx_info sm_hapn4800_pm10_tx; +extern const struct modem_tx_info sm_fsk9600_4_tx; +extern const struct modem_tx_info sm_fsk9600_5_tx; + +extern const struct modem_rx_info sm_afsk1200_rx; +extern const struct modem_rx_info sm_afsk2400_7_rx; +extern const struct modem_rx_info sm_afsk2400_8_rx; +extern const struct modem_rx_info sm_afsk2666_rx; +extern const struct modem_rx_info sm_psk4800_rx; +extern const struct modem_rx_info sm_hapn4800_8_rx; +extern const struct modem_rx_info sm_hapn4800_10_rx; +extern const struct modem_rx_info sm_hapn4800_pm8_rx; +extern const struct modem_rx_info sm_hapn4800_pm10_rx; +extern const struct modem_rx_info sm_fsk9600_4_rx; +extern const struct modem_rx_info sm_fsk9600_5_rx; + +extern const struct hardware_info sm_hw_sbc; +extern const struct hardware_info sm_hw_sbcfdx; +extern const struct hardware_info sm_hw_wss; +extern const struct hardware_info sm_hw_wssfdx; + +extern const struct modem_tx_info *sm_modem_tx_table[]; +extern const struct modem_rx_info *sm_modem_rx_table[]; +extern const struct hardware_info *sm_hardware_table[]; + +/* --------------------------------------------------------------------- */ + +void sm_output_status(struct sm_state *sm); +/*void sm_output_open(struct sm_state *sm);*/ +/*void sm_output_close(struct sm_state *sm);*/ + +/* --------------------------------------------------------------------- */ + +extern void inline sm_int_freq(struct sm_state *sm) +{ +#ifdef SM_DEBUG + unsigned long cur_jiffies = jiffies; + /* + * measure the interrupt frequency + */ + sm->debug_vals.cur_intcnt++; + if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) { + sm->debug_vals.last_jiffies = cur_jiffies; + sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt; + sm->debug_vals.cur_intcnt = 0; + } +#endif /* SM_DEBUG */ +} + +/* --------------------------------------------------------------------- */ +#endif /* _SM_H */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_afsk1200.c linux/drivers/net/soundmodem/sm_afsk1200.c --- v2.0.34/linux/drivers/net/soundmodem/sm_afsk1200.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/soundmodem/sm_afsk1200.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,272 @@ +/*****************************************************************************/ + +/* + * sm_afsk1200.c -- soundcard radio modem driver, 1200 baud AFSK modem + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + */ + +#include "sm.h" +#include "sm_tbl_afsk1200.h" + +/* --------------------------------------------------------------------- */ + +struct demod_state_afsk12 { + unsigned int shreg; + unsigned int bit_pll; + unsigned char last_sample; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + unsigned char last_rxbit; +}; + +struct mod_state_afsk12 { + unsigned int shreg; + unsigned char tx_bit; + unsigned int bit_pll; + unsigned int dds_inc; + unsigned int txphase; +}; + +/* --------------------------------------------------------------------- */ + +static const int dds_inc[2] = { + AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE, + AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE +}; + +static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf, + unsigned int buflen) +{ + struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m); + + for (; buflen > 0; buflen--) { + if (!((st->txphase++) & 7)) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + } + st->dds_inc = dds_inc[st->tx_bit & 1]; + *buf++ = OFFSCOS(st->bit_pll); + st->bit_pll += st->dds_inc; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m); + + for (; buflen > 0; buflen--) { + if (!((st->txphase++) & 7)) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + } + st->dds_inc = dds_inc[st->tx_bit & 1]; + *buf++ = COS(st->bit_pll); + st->bit_pll += st->dds_inc; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum) +{ + int sum = -0x80 * csum; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + + sum >>= 7; + return sum * sum; +} + +extern __inline__ int convolution8_s16(const short *st, const int *coeff, int csum) +{ + int sum = 0; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + + sum >>= 15; + return sum * sum; +} + +extern __inline__ int do_filter_1200_u8(const unsigned char *buf) +{ + int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I); + sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q); + sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I); + sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q); + return sum; +} + +extern __inline__ int do_filter_1200_s16(const short *buf) +{ + int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I); + sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q); + sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I); + sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q); + return sum; +} + +/* --------------------------------------------------------------------- */ + +static const int pll_corr[2] = { -0x1000, 0x1000 }; + +static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d); + int j; + int sum; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_1200_u8(buf); + st->dcd_shreg <<= 1; + st->bit_pll += 0x2000; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + st->bit_pll += pll_corr + [st->bit_pll < 0x9000]; + j = 4 * hweight8(st->dcd_shreg & 0x38) + - hweight16(st->dcd_shreg & 0x7c0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, (((int)*buf)-0x80) << 8, sum); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d); + int j; + int sum; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_1200_s16(buf); + st->dcd_shreg <<= 1; + st->bit_pll += 0x2000; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + st->bit_pll += pll_corr + [st->bit_pll < 0x9000]; + j = 4 * hweight8(st->dcd_shreg & 0x38) + - hweight16(st->dcd_shreg & 0x7c0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, *buf, sum); + } +} + +/* --------------------------------------------------------------------- */ + +static void demod_init_1200(struct sm_state *sm) +{ + struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d); + + st->dcd_time = 120; + st->dcd_sum0 = 2; +} + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_afsk1200_tx = { + "afsk1200", sizeof(struct mod_state_afsk12), + AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL +}; + +const struct modem_rx_info sm_afsk1200_rx = { + "afsk1200", sizeof(struct demod_state_afsk12), + AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200, + demodulator_1200_u8, demodulator_1200_s16, demod_init_1200 +}; + +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_afsk2400_7.c linux/drivers/net/soundmodem/sm_afsk2400_7.c --- v2.0.34/linux/drivers/net/soundmodem/sm_afsk2400_7.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/soundmodem/sm_afsk2400_7.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,296 @@ +/*****************************************************************************/ + +/* + * sm_afsk2400_7.c -- soundcard radio modem driver, 2400 baud AFSK modem + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + */ + +/* + * This driver is intended to be compatible with TCM3105 modems + * overclocked to 7.3728MHz. The mark and space frequencies therefore + * lie at 3658 and 1996 Hz. + * Note that I do _not_ recommend the building of such links, I provide + * this only for the users who live in the coverage area of such + * a "legacy" link. + */ + +#include "sm.h" +#include "sm_tbl_afsk2400_7.h" + +/* --------------------------------------------------------------------- */ + +struct demod_state_afsk24 { + unsigned int shreg; + unsigned int bit_pll; + unsigned char last_sample; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + unsigned char last_rxbit; +}; + +struct mod_state_afsk24 { + unsigned int shreg; + unsigned char tx_bit; + unsigned int bit_pll; + unsigned int tx_seq; + unsigned int phinc; +}; + +/* --------------------------------------------------------------------- */ + +static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE, + AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE }; + +static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (st->tx_seq < 0x5555) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + st->phinc = dds_inc[st->tx_bit & 1]; + } + st->tx_seq += 0x5555; + st->tx_seq &= 0xffff; + *buf = OFFSCOS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (st->tx_seq < 0x5555) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + st->phinc = dds_inc[st->tx_bit & 1]; + } + st->tx_seq += 0x5555; + st->tx_seq &= 0xffff; + *buf = COS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum) +{ + int sum = -0x80 * csum; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + sum += (st[-8] * coeff[8]); + sum += (st[-9] * coeff[9]); + sum += (st[-10] * coeff[10]); + sum += (st[-11] * coeff[11]); + sum += (st[-12] * coeff[12]); + sum += (st[-13] * coeff[13]); + + sum >>= 7; + return sum * sum; +} + +extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum) +{ + int sum = 0; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + sum += (st[-8] * coeff[8]); + sum += (st[-9] * coeff[9]); + sum += (st[-10] * coeff[10]); + sum += (st[-11] * coeff[11]); + sum += (st[-12] * coeff[12]); + sum += (st[-13] * coeff[13]); + + sum >>= 15; + return sum * sum; +} + +extern __inline__ int do_filter_2400_u8(const unsigned char *buf) +{ + int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); + sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); + sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); + sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); + return sum; +} + +extern __inline__ int do_filter_2400_s16(const short *buf) +{ + int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); + sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); + sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); + sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); + return sum; +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + int j; + int sum; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_2400_u8(buf); + st->dcd_shreg <<= 1; + st->bit_pll += AFSK24_BITPLL_INC; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) + st->bit_pll += AFSK24_BITPLL_INC/2; + else + st->bit_pll -= AFSK24_BITPLL_INC/2; + j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) + - hweight16(st->dcd_shreg & 0x1e0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, (((int)*buf)-0x80) << 8, sum); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + int j; + int sum; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sum = do_filter_2400_s16(buf); + st->dcd_shreg <<= 1; + st->bit_pll += AFSK24_BITPLL_INC; + newsample = (sum > 0); + if (st->last_sample ^ newsample) { + st->last_sample = newsample; + st->dcd_shreg |= 1; + if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) + st->bit_pll += AFSK24_BITPLL_INC/2; + else + st->bit_pll -= AFSK24_BITPLL_INC/2; + j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) + - hweight16(st->dcd_shreg & 0x1e0); + st->dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, st->last_sample); + if ((--st->dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + + st->dcd_sum1 + + st->dcd_sum2) < 0); + st->dcd_sum2 = st->dcd_sum1; + st->dcd_sum1 = st->dcd_sum0; + st->dcd_sum0 = 2; /* slight bias */ + st->dcd_time = 120; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->shreg >>= 1; + st->shreg |= (!(st->last_rxbit ^ + st->last_sample)) << 16; + st->last_rxbit = st->last_sample; + diag_trigger(sm); + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + } + diag_add(sm, *buf, sum); + } +} + +/* --------------------------------------------------------------------- */ + +static void demod_init_2400(struct sm_state *sm) +{ + struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); + + st->dcd_time = 120; + st->dcd_sum0 = 2; +} + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_afsk2400_7_tx = { + "afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, + modulator_2400_u8, modulator_2400_s16, NULL +}; + +const struct modem_rx_info sm_afsk2400_7_rx = { + "afsk2400_7", sizeof(struct demod_state_afsk24), + AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, + demodulator_2400_u8, demodulator_2400_s16, demod_init_2400 +}; + +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_afsk2400_8.c linux/drivers/net/soundmodem/sm_afsk2400_8.c --- v2.0.34/linux/drivers/net/soundmodem/sm_afsk2400_8.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/soundmodem/sm_afsk2400_8.c Mon Jul 13 13:47:31 1998 @@ -0,0 +1,296 @@ +/*****************************************************************************/ + +/* + * sm_afsk2400_8.c -- soundcard radio modem driver, 2400 baud AFSK modem + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + */ + +/* + * This driver is intended to be compatible with TCM3105 modems + * overclocked to 8MHz. The mark and space frequencies therefore + * lie at 3970 and 2165 Hz. + * Note that I do _not_ recommend the building of such links, I provide + * this only for the users who live in the coverage area of such + * a "legacy" link. + */ + +#include "sm.h" +#include "sm_tbl_afsk2400_8.h" + +/* --------------------------------------------------------------------- */ + +struct demod_state_afsk24 { + unsigned int shreg; + unsigned int bit_pll; + unsigned char last_sample; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + unsigned char last_rxbit; +}; + +struct mod_state_afsk24 { + unsigned int shreg; + unsigned char tx_bit; + unsigned int bit_pll; + unsigned int tx_seq; + unsigned int phinc; +}; + +/* --------------------------------------------------------------------- */ + +static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE, + AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE }; + +static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (st->tx_seq < 0x5555) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + st->phinc = dds_inc[st->tx_bit & 1]; + } + st->tx_seq += 0x5555; + st->tx_seq &= 0xffff; + *buf = OFFSCOS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (st->tx_seq < 0x5555) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; + st->shreg >>= 1; + st->phinc = dds_inc[st->tx_bit & 1]; + } + st->tx_seq += 0x5555; + st->tx_seq &= 0xffff; + *buf = COS(st->bit_pll); + st->bit_pll += st->phinc; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum) +{ + int sum = -0x80 * csum; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + sum += (st[-8] * coeff[8]); + sum += (st[-9] * coeff[9]); + sum += (st[-10] * coeff[10]); + sum += (st[-11] * coeff[11]); + sum += (st[-12] * coeff[12]); + sum += (st[-13] * coeff[13]); + + sum >>= 7; + return sum * sum; +} + +extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum) +{ + int sum = 0; + + sum += (st[0] * coeff[0]); + sum += (st[-1] * coeff[1]); + sum += (st[-2] * coeff[2]); + sum += (st[-3] * coeff[3]); + sum += (st[-4] * coeff[4]); + sum += (st[-5] * coeff[5]); + sum += (st[-6] * coeff[6]); + sum += (st[-7] * coeff[7]); + sum += (st[-8] * coeff[8]); + sum += (st[-9] * coef